LangChain 課程 – Prompts:提示工程
Few‑Shot Prompting 與範例注入
簡介
在 LLM(大型語言模型)日益普及的今天,提示工程(Prompt Engineering) 成為讓模型發揮最大效能的關鍵技巧。
其中 Few‑Shot Prompting(少量示例提示)與 範例注入(Example Injection)是最常見、也最實用的兩種手法。透過在提示中加入少量具代表性的範例,我們可以在不調整模型參數的情況下,引導模型產出更符合需求的回應,提升準確度與一致性。
本單元將說明 Few‑Shot Prompting 的原理、在 LangChain 中的實作方式,並提供多個 實用範例,協助讀者快速在自己的應用中加入範例注入的技巧。
核心概念
1. Few‑Shot Prompting 是什麼?
Few‑Shot Prompting 指的是在一次請求(single API call)中,於提示文字(prompt)裡 示範 幾個輸入與期望輸出的配對,讓模型「從示例中學習」如何回應新問題。
相較於 Zero‑Shot(完全不給範例)或 One‑Shot(只給一個範例),Few‑Shot 能在保持請求成本不變的前提下,顯著提升模型的正確率與穩定性。
關鍵要點
- 範例越具代表性,模型的表現通常越好。
- 範例數量不宜過多(一般 2~5 個),過長的提示會消耗更多 token,增加成本與回應延遲。
2. 範例注入(Example Injection)的實作方式
在 LangChain(包括 Python 與 JavaScript 版)中,我們可以透過 PromptTemplate、FewShotPromptTemplate 或自訂的 PromptTemplate 來完成範例注入。
下面以 LangChainJS(JavaScript)為例,說明三種常見寫法:
| 寫法 | 說明 |
|---|---|
PromptTemplate + 手動拼接範例 |
最直接的方式,適合範例數量固定且簡單的情況。 |
FewShotPromptTemplate(內建) |
LangChain 已提供的類別,會自動把範例插入 example_prompt 中。 |
DynamicFewShotPromptTemplate(自訂) |
需要根據不同任務動態產生範例時使用。 |
程式碼範例
以下範例均使用 Node.js + LangChainJS(@langchain/core、@langchain/openai),請先確保已安裝相關套件:
npm install @langchain/core @langchain/openai
範例 1️⃣:使用 PromptTemplate 手動拼接 Few‑Shot
import { PromptTemplate } from "@langchain/core/prompts";
import { OpenAI } from "@langchain/openai";
// 1. 定義少量範例(每個範例都是「問題 + 回答」的配對)
const examples = [
{
q: "台北的天氣如何?",
a: "今天台北的天氣是晴,最高溫 28°C,最低溫 20°C。"
},
{
q: "今天台北有雨嗎?",
a: "今天台北局部有短暫陣雨,請隨身攜帶雨具。"
}
];
// 2. 把範例拼成字串,使用「---」作為分隔線,方便模型辨識
const exampleString = examples
.map(e => `Q: ${e.q}\nA: ${e.a}`)
.join("\n---\n");
// 3. 建立 PromptTemplate,將範例字串與使用者問題結合
const template = `以下是一些天氣查詢的範例:
${exampleString}
現在請根據上述範例回答以下問題:
Q: {question}
A:`;
// 4. 建立 PromptTemplate 物件
const prompt = new PromptTemplate({
template,
inputVariables: ["question"]
});
// 5. 呼叫 LLM
const llm = new OpenAI({ temperature: 0 });
const chain = prompt.pipe(llm);
const response = await chain.invoke({ question: "明天台北會下雨嗎?" });
console.log(response);
說明
exampleString把範例直接寫入提示,適合範例不變的情境。- 使用
temperature: 0可以讓回應更 deterministic(確定性),減少雜訊。
範例 2️⃣:使用內建 FewShotPromptTemplate
import { FewShotPromptTemplate, PromptTemplate } from "@langchain/core/prompts";
import { OpenAI } from "@langchain/openai";
// 1. 單一範例的格式(example_prompt)
// 這裡只定義「問」與「答」的樣式,實際內容會在 later 注入
const examplePrompt = PromptTemplate.fromTemplate("Q: {input}\nA: {output}");
// 2. Few‑Shot PromptTemplate 本體
const fewShotPrompt = new FewShotPromptTemplate({
// 範例資料(可自行增減)
examples: [
{ input: "台北今天的最高溫是多少?", output: "最高溫 28°C。" },
{ input: "台北今天會下雨嗎?", output: "會有局部短暫陣雨。" }
],
examplePrompt, // 前面定義的樣式
prefix: "以下是關於台北天氣的問答範例:",
suffix: "\n現在請回答以下問題:\nQ: {question}\nA:",
inputVariables: ["question"]
});
// 3. 建立 LLM
const llm = new OpenAI({ temperature: 0 });
const chain = fewShotPrompt.pipe(llm);
// 4. 呼叫
const answer = await chain.invoke({ question: "今天台北的最低溫是多少?" });
console.log(answer);
說明
FewShotPromptTemplate會自動把examples依examplePrompt產生的格式插入prefix與suffix中。- 只要調整
examples陣列,即可快速重用此模板。
範例 3️⃣:動態產生範例(Dynamic Few‑Shot)
有時候範例需要根據使用者的上下文動態生成,例如「法律文件」或「程式碼補全」的情境。下面示範如何在每次呼叫前自行組合範例:
import { PromptTemplate } from "@langchain/core/prompts";
import { OpenAI } from "@langchain/openai";
/**
* 依據使用者的需求,從資料庫或 API 抓取最近的 3 個相似案例
*/
async function fetchRelevantExamples(query) {
// 這裡僅示意,實務上會呼叫向量搜尋或資料庫
const mockDB = [
{ q: "如何在 Python 中讀取 CSV?", a: "使用 pandas.read_csv('file.csv')。" },
{ q: "Node.js 如何寫入檔案?", a: "使用 fs.writeFileSync('path', data)。" },
{ q: "JavaScript 如何去除陣列重複值?", a: "使用 [...new Set(arr)]。" }
];
// 假設簡單回傳全部(實際可根據 query 排序)
return mockDB;
}
// 主函式
async function runDynamicFewShot(userQuestion) {
const examples = await fetchRelevantExamples(userQuestion);
const exampleString = examples
.map(e => `Q: ${e.q}\nA: ${e.a}`)
.join("\n---\n");
const template = `以下是程式相關的問答範例:
${exampleString}
請依照上述範例回答下列問題:
Q: {question}
A:`;
const prompt = new PromptTemplate({
template,
inputVariables: ["question"]
});
const llm = new OpenAI({ temperature: 0 });
const chain = prompt.pipe(llm);
const result = await chain.invoke({ question: userQuestion });
console.log(result);
}
// 測試
runDynamicFewShot("JavaScript 如何將字串轉成日期?");
說明
fetchRelevantExamples可以換成向量搜尋(如 Pinecone、FAISS)或資料庫查詢,讓範例更貼近使用者需求。- 每次呼叫都會重新組裝提示,保持 動態性 與 彈性。
範例 4️⃣:結合系統訊息(System Prompt)與 Few‑Shot
在 ChatML(OpenAI Chat Completion)中,我們可以同時提供 system、assistant 與 user 訊息。以下示範如何把 Few‑Shot 範例放在 system 中,以建立「角色」與「風格」:
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from "@langchain/core/prompts";
const examples = [
{ q: "請說明什麼是機器學習?", a: "機器學習是讓電腦透過資料自動學習模式的技術。" },
{ q: "什麼是深度學習?", a: "深度學習是機器學習的分支,使用多層神經網路。" }
];
// 產生系統訊息內的 Few‑Shot
const systemTemplate = `你是一位友善的 AI 老師,以下是你過往的回答範例:
${examples.map(e => `Q: ${e.q}\nA: ${e.a}`).join("\n---\n")}
請依照上述風格回答使用者的問題。`;
const systemMessage = SystemMessagePromptTemplate.fromTemplate(systemTemplate);
const humanMessage = HumanMessagePromptTemplate.fromTemplate("Q: {question}\nA:");
// 建立 Chat Prompt
const chatPrompt = ChatPromptTemplate.fromPromptMessages([systemMessage, humanMessage]);
const llm = new ChatOpenAI({ temperature: 0 });
const chain = chatPrompt.pipe(llm);
// 呼叫
const answer = await chain.invoke({ question: "什麼是自然語言處理?" });
console.log(answer.content);
說明
- 把範例放在 system 訊息中,可讓模型將範例視為「角色設定」的一部份,對於需要維持固定語氣或風格的應用非常有用。
常見陷阱與最佳實踐
| 陷阱 | 可能的影響 | 解決方式 / 最佳實踐 |
|---|---|---|
| 範例過長 | 消耗過多 token,導致成本上升、回應被截斷 | 只保留 2~4 個最具代表性 的範例;使用 --- 或空行分隔,讓模型易於辨識 |
| 範例與目標任務不匹配 | 模型可能「學錯」樣式,產生錯誤答案 | 確保範例與最終問題在 領域、語氣、格式 上高度相似 |
| 忽略溫度參數 | 高溫度會讓模型產生多樣化但不一致的回應 | 在 Few‑Shot 場景通常使用 temperature = 0 或 0.2,提升可預測性 |
| 忘記加入換行或分隔符 | 模型無法辨識「範例」與「新問題」的邊界 | 使用 \n---\n、空行或明確的 Q:/A: 標記 |
| 一次塞入過多範例(尤其在 Chat API) | 超過模型的上下文窗口(如 4k token) | 若需要大量範例,考慮 分批 或 向量檢索 先找最相關的幾筆再注入 |
| 範例中包含錯誤資訊 | 模型會直接複製錯誤答案 | 驗證範例正確性,必要時加入註解說明「此為示範」而非事實 |
最佳實踐總結
- 範例選擇:挑選最具代表性的 2~4 個,且盡量涵蓋不同變體(正向、負向、邏輯變化)。
- 格式統一:使用
Q:/A:或類似的結構化標記,讓模型辨識更簡單。 - 控制溫度:在需要精確答案時將
temperature設為 0,若想要創意產出可適度提升。 - 動態範例:結合向量搜尋或資料庫,根據使用者查詢自動挑選最相似的範例,提高相關性。
- 測試與迭代:透過 A/B 測試比較不同範例組合的效果,持續優化 Prompt。
實際應用場景
| 場景 | 為何適合使用 Few‑Shot | 範例實作概念 |
|---|---|---|
| 客服聊天機器人 | 需要保持語氣一致、快速回答常見問題 | 把常見問答作為 Few‑Shot,系統訊息中加入角色設定 |
| 程式碼補全 / 語法說明 | 開發者問法多樣,示例能指導模型產生正確語法 | 從程式碼庫抓取相似片段,動態注入作為範例 |
| 醫療諮詢(非診斷) | 輸出需要專業且一致的健康資訊 | 用醫學問答範例建立安全的回應框架 |
| 教育平台 | 需要模型以教師口吻解釋概念,且提供範例答案 | 在 system prompt 中放入教學範例,確保風格一致 |
| 商業報告自動生成 | 報告格式固定,範例能指導模型產出標準化段落 | 透過 Few‑Shot 範例示範「摘要 → 結論 → 建議」的結構 |
案例:在一個線上程式教學平台,我們使用
FewShotPromptTemplate搭配向量搜尋,每次使用者請求「如何在 JavaScript 中實作 debounce?」時,系統會先從過去的教學內容中找出 3 個最相似的 debounce 範例,注入 Prompt,最終產生的答案不僅正確,且使用了與過往教學相同的語氣與格式,大幅提升使用者的閱讀體驗與信任感。
總結
Few‑Shot Prompting 與 範例注入 是提升 LLM 準確性與一致性的 低成本、易實作 方法。透過 LangChain 的 PromptTemplate、FewShotPromptTemplate,開發者只需要:
- 挑選 2~4 個高品質範例,保持格式統一。
- 組合 範例與使用者問題,注意分隔符與換行。
- 控制 溫度與 token 使用,避免超出模型窗口。
- 測試 不同範例組合,持續迭代 Prompt。
掌握這些技巧後,無論是客服機器人、程式碼輔助、教育平台或是企業報告自動化,都能快速建置出 高品質、可控 的 AI 服務。未來隨著向量資料庫與自動範例擷取技術的成熟,Few‑Shot Prompting 的效能將更加提升,成為 AI 應用開發者不可或缺的核心能力。祝你在 LangChain 的旅程中玩得開心、寫得順利!