20250914-03: Langchain概念:提示模板+少樣本提示
聊天模型 + 消息 + 提示 + 結構化輸出
🎯 學習目標
掌握如何“喂給模型正確的輸入”并“解析出想要的輸出”。
🔗 核心概念
- ?
聊天模型
(ChatModel) - ?
消息
(Message) - ?
聊天歷史
(Chat History) - ?
提示模板
(PromptTemplate) - ?
少樣本提示
(Few-shot Prompting) - ?
示例選擇器
(ExampleSelector) - ?
結構化輸出
(Structured Output) - ?
輸出解析器
(OutputParser)
?提示模板
(PromptTemplate)
提示模板有助于將用戶輸入和參數
轉換為語言模型的指令
。這可以用于指導模型的響應
,幫助其理解上下文
并生成相關且連貫
的基于語言的輸出。
提示模板接收字典
作為輸入,其中每個鍵代表提示模板中要填充的變量
。
提示模板輸出一個 PromptValue
。這個 PromptValue
可以傳遞給 LLM 或 ChatModel,也可以轉換為字符串
或消息列表
。
?PromptValue
存在的原因是為了方便在字符串和消息之間切換。
提示模板有幾種不同類型
字符串提示模板
這些提示模板用于格式化單個字符串
,通常用于更簡單的輸入。例如,構建和使用 PromptTemplate 的常見方法如下
from langchain_core.prompts import PromptTemplate
# String
prompt_template = PromptTemplate.from_template("Tell me a joke about {topic}")prompt_template.invoke({"topic": "cats"})
聊天提示模板 ChatPromptTemplates
這些提示模板用于格式化消息列表
。這些“模板”本身由一個模板列表
組成。例如,構建和使用 ChatPromptTemplate
的常見方法如下
from langchain_core.prompts import ChatPromptTemplateprompt_template = ChatPromptTemplate([("system", "You are a helpful assistant"),("user", "Tell me a joke about {topic}")])prompt_template.invoke({"topic": "cats"})
**API 參考:**?ChatPromptTemplate
在上面的示例中,當調用此 ChatPromptTemplate 時,它將構建兩條消息。第一條是系統消息
,沒有要格式化的變量。第二條是 HumanMessage
,它將由用戶傳入的 topic
變量進行格式化。
消息占位符 MessagesPlaceholder
場景: 將消息列表插入特定位置非常有用
此提示模板負責在特定位置添加消息列表
。在上面的 ChatPromptTemplate
中,我們看到了如何格式化兩條消息,每條都是一個字符串。但如果我們希望用戶傳入一個消息列表,并將其放入特定位置,該怎么辦
?這就是你使用 MessagesPlaceholder
的方式。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, AIMessageprompt_template = ChatPromptTemplate([("system", "You are a helpful assistant"),MessagesPlaceholder("msgs")
])# Simple example with one message
prompt_template.invoke({"msgs": [HumanMessage(content="hi!")]})# More complex example with conversation history
messages_to_pass = [HumanMessage(content="What's the capital of France?"),AIMessage(content="The capital of France is Paris."),HumanMessage(content="And what about Germany?")
]formatted_prompt = prompt_template.invoke({"msgs": messages_to_pass})
print(formatted_prompt)
**API 參考:**?ChatPromptTemplate | MessagesPlaceholder | HumanMessage
這將生成一個包含兩條消息的列表,第一條是系統消息,第二條是我們傳入的 HumanMessage。
如果我們傳入了 5 條消息,那么它總共會生成 6 條消息(系統消息加上傳入的 5 條消息)。這對于將消息列表插入特定位置非常有用。
?
實現相同目標的另一種方法,不顯式使用 MessagesPlaceholder
類,如下所示
prompt_template = ChatPromptTemplate([("system", "You are a helpful assistant"),("placeholder", "{msgs}") # <-- This is the changed part])
有關如何使用提示模板的詳細信息,請參閱此處的相關操作指南。
少量提示few-shot prompting
?
提高模型性能最有效的方法之一是向模型提供您希望它執行操作的示例。向模型提示添加示例輸入和預期輸出的技術稱為“小樣本提示”(few-shot prompting
)。該技術基于 《語言模型是小樣本學習器》論文。在進行小樣本提示時,有幾點需要考慮:
- 如何生成示例?
- 每個提示中包含多少示例?
- 運行時如何選擇示例?
- 示例在提示中如何格式化?
1. 生成示例
?小樣本提示的第一步也是最重要的一步是準備一套好的示例數據集
。好的示例應該在運行時相關、清晰、信息豐富,并提供模型先前未知的信息。
從高層次來看,生成示例的基本方法有:
- ?
手動
:一個人/多個人生成他們認為有用的示例。 - ?
更好的模型
:使用更好(可能更昂貴/更慢)的模型的響應作為較差(可能更便宜/更快)模型的示例。 - ?
用戶反饋
:用戶(或標注者)對應用程序的交互留下反饋,并根據該反饋生成示例(例如,所有帶有積極反饋的交互都可以轉化為示例)。 - ?
LLM 反饋
:與用戶反饋相同,但該過程通過讓模型自我評估來自動化。
哪種方法最佳取決于您的任務。
對于需要透徹理解少數核心原則的任務,手工制作一些真正好的示例會很有價值。
對于正確行為空間更廣泛、更細致入微的任務,以更自動化的方式生成大量示例會很有用,這樣在運行時有更高可能性存在高度相關的示例
單輪對話與多輪對話示例
生成示例時需要考慮的另一個維度是示例實際展示的內容。
最簡單的示例類型只包含用戶輸入和預期的模型輸出。這些是單輪對話示例。
一種更復雜的示例類型是示例是一個完整的對話,通常是模型最初響應不正確,然后用戶告訴模型如何糾正其答案。這稱為多輪對話示例。多輪對話示例對于更細致入微的任務很有用,在這些任務中,展示常見錯誤并精確說明其錯誤原因以及應如何糾正會很有幫助。
?
?
2. 示例數量
一旦我們有了示例數據集,就需要考慮每個提示中應該包含多少示例。
關鍵的權衡是:更多示例通常能提高性能,但更大的提示會增加成本和延遲
。
而且,超過某個閾值
,過多示例反而會開始使模型感到困惑。找到合適數量的示例高度依賴于模型、任務、示例的質量以及您的成本和延遲限制。
根據經驗,模型越好,它需要的示例越少,并且在添加更多示例時回報遞減的速度越快。但是,可靠地回答這個問題最佳/唯一的方法是使用不同數量的示例進行一些實驗。
?
3. 選擇示例
假設我們不會將整個示例數據集添加到每個提示中,我們需要一種根據給定輸入從數據集中選擇示例
的方法。我們可以通過以下方式進行:
- 隨機選擇
- 根據輸入的(語義或關鍵詞)相似性選擇
- 基于其他約束,如令牌大小
LangChain 提供了許多 ExampleSelectors
,可以輕松使用這些技術中的任何一種。
通常,通過語義相似性選擇
會帶來最佳
的模型性能。但這有多重要又取決于模型和任務,并且值得進行實驗。
?
4. 格式化示例
目前大多數最先進的模型都是聊天模型
,因此我們將重點關注
如何為它們 格式化示例 。我們的基本選項是將示例插入:
- 在系統提示中作為字符串
- 作為它們自己的消息
如果我們將示例作為字符串插入到系統提示中,我們需要確保模型清楚每個示例的開始位置以及哪些部分是輸入,哪些部分是輸出。不同的模型對不同的語法響應更好,例如 ChatML、XML、TypeScript 等。
如果我們將示例作為消息插入,其中每個示例都表示為一系列人類、AI 消息,我們可能還需要為消息分配名稱,例如 "example_user"
和 "example_assistant"
,以表明這些消息與最新輸入消息的參與者不同。
格式化工具調用示例
將示例格式化為消息時,當示例輸出包含工具調用時,可能會變得棘手
。這是因為不同的模型對生成任何工具調用時允許的消息序列類型有不同的約束。
- 有些模型要求任何帶有工具調用的 AIMessage 必須緊跟每個工具調用的 ToolMessages,
- 有些模型還要求任何 ToolMessages 必須在下一個 HumanMessage 之前緊跟一個 AIMessage,
- 有些模型要求如果聊天歷史中有任何工具調用/ToolMessages,則必須將工具傳遞給模型。
這些要求是模型特有的,應針對您正在使用的模型進行檢查。如果您的模型在工具調用后需要 ToolMessages 和/或在 ToolMessages 后需要 AIMessages,并且您的示例只包含預期的工具調用而不是實際的工具輸出,您可以嘗試在每個示例的末尾添加帶有通用內容的虛擬 ToolMessages / AIMessages,以滿足 API 約束。在這些情況下,尤其值得嘗試將示例作為字符串而不是消息插入,因為虛擬消息可能會對某些模型產生不利影響。
?
?
?
?
深入理解
“單輪對話”和“多輪對話”示例的區別是什么?
好的,我們來把“單輪對話”和“多輪對話”示例的區別講清楚。這是一個非常重要的概念,尤其在教導大模型時。
核心比喻:教小孩做題
想象一下,你正在教一個非常聰明但沒有經驗的小孩(大語言模型)如何解題。
1. 單輪對話示例
-
怎么教?
-
你直接給他看一道題目(用戶輸入) 和對應的標準答案(預期的模型輸出) 。
-
示例:
- 輸入(用戶) : “法國的首都是哪里?”
- 輸出(模型) : “法國的首都是巴黎。”
-
-
特點:
- 簡單直接:就像一張閃卡或備忘錄,告訴模型“遇到這個問題,就回答這個”。
- 單次交互:只有一問一答,沒有后續的交流。
- 適用場景:適用于事實性、定義明確、沒有歧義的問題。比如詢問事實、翻譯句子、總結一段固定文本等。
-
局限性:
- 如果問題很復雜,或者模型第一次答錯了,你沒有教它如何糾正。
- 就像只給小孩看了答案,沒給他講解題思路和易錯點。
2. 多輪對話示例
-
怎么教?
-
你給小孩展示一個完整的教學對話。這個對話通常包含模型一開始犯的一個典型錯誤,然后用戶(老師)進行糾正,最后模型給出正確的回應。
-
示例:
- 第1輪(用戶) : “幫我寫一首關于貓的詩。”
- 第1輪(模型 - 錯誤示范) : “貓兒喵喵叫,愛吃魚和肉。(這首詩過于簡單和幼稚)”
- 第2輪(用戶 - 糾正) : “這首詩太簡單了,請寫得更有文學性一些,用上比喻手法。”
- 第2輪(模型 - 正確回應) : “夜色中的精靈,絨毯般的身軀蜷縮如神秘的毛線團,一雙琥珀是窺探月亮的透鏡…”
-
-
特點:
-
展示錯誤與糾正:它不僅展示了“應該做什么”,還展示了“不應該做什么”以及“如何從錯誤中改正”。
-
多輪交互:包含多輪一問一答,形成了一個完整的對話上下文。
-
適用場景:適用于復雜、主觀或容易出錯的任務。例如:
- 風格調整:教模型如何根據反饋調整寫作風格(如上例)。
- 復雜推理:展示模型推理過程中的錯誤,并教它如何一步步正確思考。
- 拒絕回答:教模型如何識別并禮貌拒絕不恰當或有害的請求。
- 交互式任務:需要多次澄清和確認的任務(如訂票、編程)。
-
-
優勢:
- 更加細致入微:能教會模型更復雜的指令和更精細的偏好。
- 預防常見錯誤:提前演示常見錯誤及其糾正方法,讓模型學會“避坑”。
- 理解對話流:教會模型如何在一個連續的對話中理解和響應用戶的反饋。
總結與對比
為了幫你更好地理解,我們把兩者放在一個表格里:
方面 | 單輪對話示例 | 多輪對話示例 |
---|---|---|
形式 | 簡單的 Q-A 對(一問一答) | 完整的對話歷史(多問多答) |
內容 | 只展示正確的最終答案 | 展示錯誤 -> 反饋 -> 糾正的全過程 |
好比 | 閃卡/備忘錄:只記答案 | 教學視頻/案例研究:分析錯題,講解思路 |
教學目標 | 教會模型“是什么” | 教會模型“為什么”以及“如何改進” |
復雜度 | 低 | 高 |
適用任務 | 事實問答、翻譯、簡單總結 | 風格寫作、復雜推理、安全拒絕、交互式任務 |
簡單來說:
- 如果你想讓模型學會回答簡單明了的問題,就用單輪示例。像喂給它一對對的(問題,答案)。
- 如果你想教模型完成一件復雜的、容易出錯的事情,就給它講個小故事(多輪示例)。這個故事里要包含一個常見的錯誤情節,以及如何修正這個錯誤的情節。這樣模型就能從故事中學到更深刻的東西。
選擇哪種方式,取決于你的任務有多復雜。對于高級應用,混合使用這兩種示例往往能取得最好的效果。
如何給模型提供例題集?格式化示例?
好的,我們來把“格式化示例”這個概念講清楚。這就像是教你如何給大模型準備“學習資料”,不同的準備方法效果不同。
核心比喻:如何給模型提供例題集?
想象一下,你是一位老師,要給一個非常聰明的學生(聊天模型)一本練習冊,里面包含一些例題和答案(示例),教它如何解決特定類型的問題。
現在關鍵問題是:這本練習冊該怎么編寫? 主要有兩種方法:
方法一:把例題寫在“說明書”里(作為字符串插入系統提示)
-
怎么做?
-
系統提示(System Prompt)就像是交給模型的一份總說明書,告訴它它的角色和任務。
-
這種方法就是把所有的例題和答案,都寫成一大段文字,塞進這份說明書里。
-
例如:
“你是一個樂于助人的助手。以下是你要學習的例子:
例子1:
用戶問:法國的首都是什么?
你應該答:巴黎。例子2:
用戶問:你好嗎?
你應該答:我是一個AI,沒有感覺,但我很好!今天能幫你什么?現在,請開始回答用戶的真實問題:”
-
-
面臨的挑戰(需要確保模型清楚) :
- 你必須用非常清晰的標記(一種語法)來告訴模型:哪里是一個例子的開始?哪部分是用戶的問題(輸入)?哪部分是它應該給出的答案(輸出)?
- 這就好比你在說明書里寫例題,必須用不同的字體、顏色或者標簽把題目和答案區分開,不然學生就看懵了。
-
常用的“標記語法”(文中提到的) :
- ChatML:OpenAI模型常用的一種格式,用
<|im_start|>
,<|im_end|>
等特殊標簽來劃分角色和內容。 - XML標簽:用
<user>問題</user><assistant>答案</assistant>
這樣的標簽來區分。 - 其他格式:如仿照TypeScript等編程語言的格式。
- 關鍵點:不同的學生(模型)習慣不同的標記語法。你需要根據你正在使用的那個模型,選擇它最熟悉、效果最好的那種語法。
- ChatML:OpenAI模型常用的一種格式,用
方法二:把例題做成“標準答題卡”(作為它們自己的消息)
-
怎么做?
-
聊天模型本身的理解方式就是基于消息序列的(比如:用戶消息1 -> AI消息1 -> 用戶消息2 -> AI消息2…)。
-
這種方法就是把每一個例子都模擬成一段完整的對話歷史消息,直接交給模型。
-
例如,你提供給模型的消息歷史會是這樣的:
- ?
{"role": "user", "content": "法國的首都是什么?"}
? - ?
{"role": "assistant", "content": "巴黎。"}
? - ?
{"role": "user", "content": "你好嗎?"}
? - ?
{"role": "assistant", "content": "我是一個AI,沒有感覺,但我很好!今天能幫你什么?"}
? - ?
{"role": "user", "content": "(用戶現在的真實問題)"}
<-- 從這里開始才是真正的任務
- ?
-
-
面臨的挑戰(需要區分例題和真實對話) :
-
如果直接這么干,模型可能會搞不清楚:前面那幾輪對話(例子)到底是之前真實的聊天歷史,還是你給它看的例題?
-
解決方案:給這些“例題消息”打上特殊的標簽或名字。
-
例如,你可以這樣做:
- ?
{"role": "user", "name": "example_user", "content": "法國的首都是什么?"}
? - ?
{"role": "assistant", "name": "example_assistant", "content": "巴黎。"}
?
- ?
-
通過給角色(role)再加上一個名字(name),比如
example_user
,你就明確地告訴模型:“注意啦,這條消息是一個示例,不是真正的對話歷史!”這樣模型就能更好地理解你的意圖。
-
總結與對比
為了幫你更好地理解,我們把兩者放在一個表格里:
方面 | 方法一:作為字符串放入系統提示 | 方法二:作為獨立消息 |
---|---|---|
比喻 | 把例題寫在任務說明書里 | 把例題做成標準的答題卡樣本 |
實現方式 | 將示例拼接成一個大字符串,放入system 消息 | 將每個示例拆分為多條user 和assistant ?消息 |
關鍵挑戰 | 需要一種清晰的語法(如XML)來區分輸入和輸出 | 需要一種方式(如name 字段)來區分示例和真實對話 |
優點 | 結構緊湊,所有指令和示例都在一起 | 更符合模型處理對話的原生方式,通常效果更好、更可靠 |
缺點 | 格式容易出錯,且最佳語法因模型而異 | 會消耗更多的Token(字數限制) |
簡單來說:
- 方法一(字符串) :像是在給模型下指令時,把例子口頭給它念一遍。你需要說得特別清楚,不然它會誤解。
- 方法二(消息) :像是直接給它看幾張標準的“答題卡”樣板。這種方式更直觀,模型更容易模仿,是更推薦、更現代的做法。但記得給這些樣板答題卡貼上“這是樣例”的標簽(使用
name
字段),避免模型混淆。
在實際應用中,方法二(作為獨立消息) 通常更受青睞,因為它更穩定,能更清晰地傳達示例的結構。