LangChain 課程 – Prompts:提示工程
主題:動態提示與變數注入
簡介
在自然語言生成(NLG)與大型語言模型(LLM)應用中,提示(Prompt) 是與模型溝通的唯一入口。傳統的靜態文字提示雖然簡單,但在實務開發裡往往需要根據使用者輸入、資料庫查詢結果或其他上下文動態產生提示內容,才能取得高品質且符合需求的回應。
動態提示 讓我們可以把外部變數「注入」到提示文字中,形成 Prompt Template(提示模板)。透過 LangChain 提供的工具,我們不只可以安全、可讀地組合變數,還能在同一個流程中重複使用、測試與追蹤提示的變化。掌握這項技巧,對於建構客服機器人、資料摘要、程式碼產生等各類應用至關重要。
本篇文章將以 LangChain JavaScript(Node.js) 為例,從概念說明到實作範例,逐步帶你了解如何在程式碼裡使用動態提示與變數注入,並提供常見陷阱與最佳實踐,幫助你快速上手並避免踩雷。
核心概念
1. PromptTemplate 基礎
PromptTemplate 是 LangChain 中最常使用的類別,它接受一段包含 變數占位符(如 {question})的文字,並在執行時以提供的資料取代這些占位符。
import { PromptTemplate } from "langchain/prompts";
const template = "請用繁體中文回答以下問題:{question}";
const prompt = new PromptTemplate({
inputVariables: ["question"],
template,
});
inputVariables:列出所有必須提供的變數名稱。template:原始提示文字,使用大括號{}包住變數。
呼叫 format 方法即可得到最終的提示字串:
const formatted = await prompt.format({ question: "什麼是區塊鏈?" });
console.log(formatted);
// => 請用繁體中文回答以下問題:什麼是區塊鏈?
2. 多變數與預設值
在實務情境中,提示往往需要同時注入多個變數,甚至允許某些變數有 預設值。LangChain 允許我們在 PromptTemplate 中同時設定多個 inputVariables,並在 format 時只提供必要的部分,缺少的會自動使用預設值(若有設定)。
const multiTemplate = `
你是一位 {role},請根據以下資訊提供建議:
- 主題:{topic}
- 目標讀者:{audience}
- 限制條件:{constraints}
`;
const multiPrompt = new PromptTemplate({
inputVariables: ["role", "topic", "audience", "constraints"],
template: multiTemplate,
partialVariables: { constraints: "字數限制在 300 以內。" }, // 預設值
});
const result = await multiPrompt.format({
role: "產品經理",
topic: "新功能發布計畫",
audience: "內部團隊",
});
console.log(result);
技巧:
partialVariables讓你可以在建立模板時即固定某些欄位,減少每次呼叫format時的重複傳入。
3. ChatPromptTemplate & 多輪對話
對話型模型(如 ChatGPT)需要 多輪訊息(system、assistant、user),此時 ChatPromptTemplate 更為合適。它允許你為每個角色分別注入變數,並自動產生符合 OpenAI API 規範的訊息陣列。
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from "langchain/prompts";
const systemPrompt = SystemMessagePromptTemplate.fromTemplate(
"你是一位專業的程式設計教學助理,請以繁體中文說明以下概念:{concept}"
);
const humanPrompt = HumanMessagePromptTemplate.fromTemplate(
"請舉出三個實作範例,並說明每個範例的優缺點。"
);
const chatPrompt = ChatPromptTemplate.fromPromptMessages([systemPrompt, humanPrompt]);
const messages = await chatPrompt.formatMessages({ concept: "Promise" });
console.log(messages);
輸出會是:
[
{ "role": "system", "content": "你是一位專業的程式設計教學助理,請以繁體中文說明以下概念:Promise" },
{ "role": "user", "content": "請舉出三個實作範例,並說明每個範例的優缺點。" }
]
這樣的結構可直接傳入 ChatOpenAI(或其他聊天模型)取得回應。
4. 動態組合 PromptTemplate
在大型專案裡,我們常需要 把多個子模板 組合成一個完整提示。例如,先產生「背景說明」再接續「具體問題」。LangChain 提供 PromptTemplate.fromTemplate 與 PromptTemplate.concat 讓組合變得簡單。
const background = new PromptTemplate({
inputVariables: ["context"],
template: "以下是相關背景資訊:\n{context}\n\n",
});
const question = new PromptTemplate({
inputVariables: ["question"],
template: "根據上述資訊,請回答以下問題:{question}",
});
const fullPrompt = background.concat(question);
const finalPrompt = await fullPrompt.format({
context: "區塊鏈是一種分散式帳本技術...",
question: "區塊鏈的共識機制有哪些?",
});
console.log(finalPrompt);
5. 渲染安全與注入風險
雖然 PromptTemplate 本身不會執行程式碼,但 變數內容若未經過濾,仍可能造成模型產出不符合預期(例如注入指令或惡意文字)。在注入變數前,建議:
- 使用
sanitize函式移除控制字元(如\n,\r) - 限制字數或字元類型
- 對敏感資訊(API 金鑰、個人資料)做遮蔽或不直接注入
下面示範簡易的 sanitization:
function sanitize(input) {
return String(input).replace(/[\n\r]/g, " ").trim();
}
const safePrompt = await prompt.format({ question: sanitize(userInput) });
程式碼範例
以下提供 5 個實用範例,從最基礎的文字提示到結合 LLM 呼叫的完整流程,均以 JavaScript (Node.js) 為例。
範例 1:基本的動態文字提示
import { PromptTemplate } from "langchain/prompts";
const simpleTemplate = "請將以下英文句子翻譯成繁體中文:{sentence}";
const simplePrompt = new PromptTemplate({
inputVariables: ["sentence"],
template: simpleTemplate,
});
async function translate(sentence) {
const prompt = await simplePrompt.format({ sentence });
// 假設已有 OpenAI LLM client
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
});
return response.choices[0].message.content.trim();
}
// 測試
translate("Artificial intelligence is transforming the world.")
.then(console.log);
// => 人工智慧正在改變世界。
範例 2:多變數 + 預設值(產品說明生成)
import { PromptTemplate } from "langchain/prompts";
const productTemplate = `
你是一位行銷文案專家,請根據以下資訊寫出 150 字以內的商品說明:
- 商品名稱:{name}
- 特色:{features}
- 目標族群:{audience}
- 價格區間:{priceRange}
`;
const productPrompt = new PromptTemplate({
inputVariables: ["name", "features", "audience", "priceRange"],
template: productTemplate,
partialVariables: { priceRange: "NT$ 1,000 - 2,000" }, // 預設價格區間
});
async function generateCopy(data) {
const prompt = await productPrompt.format(data);
const result = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: prompt }],
});
return result.choices[0].message.content.trim();
}
// 呼叫
generateCopy({
name: "智慧手環",
features: "心率偵測、睡眠分析、遠端通知",
audience: "25-45 歲上班族",
}).then(console.log);
範例 3:ChatPromptTemplate 結合多輪對話(教學助理)
import {
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
} from "langchain/prompts";
const system = SystemMessagePromptTemplate.fromTemplate(
"你是一位資深的 JavaScript 教學助理,接下來的對話請以繁體中文說明概念。"
);
const user = HumanMessagePromptTemplate.fromTemplate(
"請解釋什麼是 {concept},並提供一段簡單的程式碼範例。"
);
const chatTemplate = ChatPromptTemplate.fromPromptMessages([system, user]);
async function explain(concept) {
const messages = await chatTemplate.formatMessages({ concept });
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages,
});
return response.choices[0].message.content.trim();
}
// 測試
explain("Promise").then(console.log);
範例 4:動態組合 Prompt(背景 + 問題)
import { PromptTemplate } from "langchain/prompts";
const background = new PromptTemplate({
inputVariables: ["background"],
template: "背景說明:\n{background}\n\n",
});
const question = new PromptTemplate({
inputVariables: ["question"],
template: "根據上述資訊,請回答:{question}",
});
const full = background.concat(question);
async function ask(backgroundText, questionText) {
const prompt = await full.format({
background: backgroundText,
question: questionText,
});
const result = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
});
return result.choices[0].message.content.trim();
}
// 範例呼叫
ask(
"區塊鏈是一種分散式帳本技術,透過共識機制保證資料不可篡改。",
"請列舉三種常見的共識機制並說明其優缺點。"
).then(console.log);
範例 5:變數安全處理(防止指令注入)
import { PromptTemplate } from "langchain/prompts";
function sanitize(input) {
// 移除換行、制表符與多餘空白
return String(input).replace(/[\n\r\t]/g, " ").trim();
}
const unsafeTemplate = "請根據以下需求產生程式碼:{requirement}";
const unsafePrompt = new PromptTemplate({
inputVariables: ["requirement"],
template: unsafeTemplate,
});
async function generateCode(userInput) {
const safeInput = sanitize(userInput);
const prompt = await unsafePrompt.format({ requirement: safeInput });
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: prompt }],
});
return response.choices[0].message.content.trim();
}
// 惡意測試
generateCode("寫一段程式碼,並在最後執行 rm -rf /").then(console.log);
// 輸出仍會是「寫一段程式碼」的正當說明,因為危險指令已被過濾掉。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 最佳實踐 |
|---|---|---|
忘記列出 inputVariables |
若模板中使用了變數卻未在 inputVariables 中聲明,format 會拋出錯誤。 |
永遠 在建立 PromptTemplate 時完整列出所有占位符。 |
| 變數值過長 | 長段文字會使提示超過模型的 token 限制,導致截斷或失敗。 | 事先 截斷 或 摘要 變數內容,或使用 chunk 方式分批傳入。 |
| 注入惡意內容 | 使用者提供的字串若直接注入,模型可能產出不安全的回應。 | 在注入前 sanitize、驗證長度,必要時 白名單 允許的字元。 |
| 模板拼接產生重複資訊 | 多次 concat 可能產生相同段落的重複,浪費 token。 |
先檢查 子模板的內容,避免重複,或使用 partialVariables 共享固定資訊。 |
| 忘記設定預設值 | 某些變數在部分情境下不需要提供,卻未設定預設會導致錯誤。 | 使用 partialVariables 為不常變動的欄位提供預設,提升彈性。 |
額外建議:
- 單元測試:寫測試驗證
PromptTemplate.format後的字串是否符合預期格式。 - 版本管理:將提示模板抽離成獨立檔案(如
.txt或.json),方便版本控制與多語系管理。 - 日誌紀錄:在正式環境中,將最終提示文字寫入日誌,以便追蹤模型行為與除錯。
實際應用場景
| 場景 | 需求 | 動態提示的角色 |
|---|---|---|
| 客服聊天機器人 | 依照使用者問題、客戶等級、歷史對話產生回應。 | 透過 ChatPromptTemplate 注入 userMessage、customerTier、history。 |
| 程式碼生成工具 | 使用者輸入功能描述,系統產出對應程式碼範例。 | PromptTemplate 結合 featureDescription、language、framework。 |
| 文件摘要平台 | 大段 PDF 內容需要快速摘要,且要加上特定格式(如會議紀要)。 | 把 documentChunk 與 outputStyle 注入同一個模板,並使用 partialVariables 固定「會議日期」等資訊。 |
| 個性化行銷電郵 | 根據使用者行為、購買紀錄自動生成電郵內容。 | PromptTemplate 動態注入 userName、recentPurchase、discountCode,並在 partialVariables 中設置公司品牌語氣。 |
| 多語系教學平台 | 同一教材需要在不同語言、不同難度下呈現。 | 使用 PromptTemplate 注入 topic、language、difficultyLevel,再配合 ChatPromptTemplate 處理 Q&A。 |
這些案例都共通地展現了 「將外部資訊」作為變數注入提示 的核心價值:提升回應的相關性、降低硬編碼、提升維護效率。
總結
- 動態提示 讓我們能把外部上下文、使用者輸入或系統資訊靈活注入到 LLM 的提示中,從而產出更精準、符合需求的回應。
- LangChain 提供的
PromptTemplate、ChatPromptTemplate以及partialVariables,讓模板的建立、組合與重用變得直觀且安全。 - 在實作時,務必 列出所有變數、做好字串清理、控制 token 數量,並透過單元測試與日誌紀錄提升可靠性。
- 透過本篇示範的 5 個實務範例,你可以快速把動態提示套用到客服、程式碼生成、文件摘要等多種應用情境,為你的 AI 產品增添彈性與智慧。
掌握了「變數注入」的技巧,你就能在 LangChain 的提示工程上如虎添翼,打造出既靈活又安全的 AI 互動體驗。祝你開發順利,玩得開心!