專案變更總覽(你要做什麼)
✅ 新增檔案
src/aiClient.js:集中管理 OpenAI Client(之後各天都共用)。
src/promptPresets.js:常用提示詞模板(語氣、長度、格式控制)。
src/day2_text_generation.js:Day 2 主程式,參數化文字生成。
index.js:指令列入口,可選要跑哪個 day(今天預設跑 Day 2)。
♻️ 修改檔案
package.json:加入方便的 npm run day2 指令。
🗑️ 需要刪除 / 改名(擇一)
你昨天若只留有 index.js(Day 1 測試檔):
建議改名成 src/day1_hello.js 保留紀錄;
或者你不想留:就刪除舊的 index.js(因為今天會放新的 index.js)。
若選擇保留 Day1:把昨天 index.js 檔名改為 src/day1_hello.js 即可,內容不用改。
新增/修改的程式碼
// src/aiClient.js
import OpenAI from "openai";
import dotenv from "dotenv";
dotenv.config();
if (!process.env.OPENAI_API_KEY) {
throw new Error("OPENAI_API_KEY 未設定,請在 .env 加上你的金鑰。");
}
export const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
2) src/promptPresets.js(新增)
整理可重用的 Prompt 模板與控制規則。
// src/promptPresets.js
// 長度控制說明
const LENGTH_HINT = {
short: "請控制在 80~120 字之內。",
medium: "請控制在 200~300 字之內。",
long: "請控制在 500~800 字之內,分段清楚。",
};
// 語氣控制說明
const TONE_HINT = {
friendly: "語氣親切、鼓勵式、貼近讀者。",
professional: "語氣專業、精煉,避免口語化。",
persuasive: "語氣具說服力,強調價值與行動。",
};
// 輸出格式控制
const FORMAT_HINT = {
plain: "以一般段落輸出,不需要條列。",
bullets: "請用條列式(- 開頭)輸出重點,最多 5 點。",
markdown: "請使用 Markdown 標題與小節清楚呈現。",
};
// 可選的主題預設
export const PRESETS = {
blog: ({ topic, tone, length, format }) => 你是一位資深技術部落客,請針對「${topic}」寫一段短文。 ${TONE_HINT[tone] ?? ""} ${LENGTH_HINT[length] ?? ""} ${FORMAT_HINT[format] ?? ""} 請避免虛構數據,聚焦實務價值。
,
diary: ({ topic, tone, length, format }) => 你是一位工程師,正在撰寫今日學習日誌,主題:「${topic}」。 ${TONE_HINT[tone] ?? ""} ${LENGTH_HINT[length] ?? ""} ${FORMAT_HINT[format] ?? ""} 請包含:今天做了什麼、遇到的問題、下一步計畫。
,
product: ({ topic, tone, length, format }) => 你是產品行銷,請為「${topic}」撰寫產品簡介。 ${TONE_HINT[tone] ?? ""} ${LENGTH_HINT[length] ?? ""} ${FORMAT_HINT[format] ?? ""} 請包含:目標用戶、核心賣點、使用情境、CTA。
,
};
export const defaultParams = {
tone: "friendly", // friendly | professional | persuasive
length: "medium", // short | medium | long
format: "markdown", // plain | bullets | markdown
preset: "blog", // blog | diary | product
};
3) src/day2_text_generation.js(新增)
Day 2 主程式:把參數轉成可控輸出。
// src/day2_text_generation.js
import { openai } from "./aiClient.js";
import { PRESETS, defaultParams } from "./promptPresets.js";
/**
const builder = PRESETS[preset];
if (!builder) throw new Error(未知的 preset:${preset}
);
const userPrompt = builder({ topic, tone, length, format });
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
temperature,
max_tokens,
messages: [
{
role: "system",
content:
"你是嚴謹可靠的中文寫作助手,輸出需具可讀性與實用性,避免編造事實。",
},
{ role: "user", content: userPrompt },
],
});
return response.choices?.[0]?.message?.content?.trim() ?? "";
}
4) index.js(新增/覆蓋)
提供 CLI 參數(之後也能當作 Node 腳本被 Next.js API 呼叫)。
// index.js
import { generateText } from "./src/day2_text_generation.js";
// 解析簡易 CLI 參數
// 範例:node index.js --topic "RAG 入門" --preset blog --tone professional --length short --format markdown --temp 0.6
const args = Object.fromEntries(
process.argv.slice(2).reduce((acc, cur, i, arr) => {
if (cur.startsWith("--")) {
const key = cur.replace(/^--/, "");
const val = arr[i + 1] && !arr[i + 1].startsWith("--") ? arr[i + 1] : true;
acc.push([key, val]);
}
return acc;
}, [])
);
async function main() {
const content = await generateText({
topic: args.topic || "生成式 AI 實戰挑戰 Day 2",
preset: args.preset || "blog",
tone: args.tone || "friendly",
length: args.length || "medium",
format: args.format || "markdown",
temperature: args.temp ? Number(args.temp) : 0.7,
max_tokens: args.max_tokens ? Number(args.max_tokens) : 600,
});
console.log("\n=== 生成內容 ===\n");
console.log(content);
}
main().catch((e) => {
console.error("發生錯誤:", e.message);
process.exit(1);
});
5) package.json(修改)
加入快捷指令(保留你原本的內容,只需新增 scripts)。
{
"scripts": {
"day2": "node index.js --topic "RAG 入門" --preset blog --tone professional --length short --format markdown --temp 0.6"
}
}
你也可以隨時換參數,例如:
npm run day2 -- --topic "用 LLM 做學習日誌" --preset diary --tone friendly --length medium --format bullets --temp 0.7
如何執行(測試)
node index.js --topic "用 LLM 生產部落格文章的注意事項" --preset blog --tone professional --length medium --format markdown --temp 0.5
你會得到一段 可控長度、語氣、格式 的 Markdown 文章。
(temp 越低越穩定,越高越有創意;max_tokens 可避免生成過多。)