提示詞與對話模板
你送的每則訊息,在模型看到之前都會被包進標記。對話模板把你帶角色標籤的訊息變成一條扁平的 token 字串,而模型受訓讀的就是那條字串——不是你整潔的 API 物件。信封包對,3B instruct 模型行為正常;包錯,70B 模型也會吐出文字漿糊。
信封:一段對話,三種方言
你的 API 暴露角色:system、user、assistant。模型看到的是特殊 token。同樣兩則訊息的對話,用三種方式渲染。
ChatML——Qwen 家族模型與 Hermes 血統微調:
<|im_start|>system
You are a concise assistant.<|im_end|>
<|im_start|>user
Summarize this file.<|im_end|>
<|im_start|>assistant
Llama 2 風格——系統標記嵌在第一則指令內:
<s>[INST] <<SYS>>You are a concise assistant.<</SYS>>
Summarize this file. [/INST] The file implements a tokenizer... </s>
Gemma 風格——沒有 system 角色;指令搭在第一個 user 輪次上,而且助理叫做 model:
<bos><start_of_turn>user
You are a concise assistant.
Summarize this file.<end_of_turn>
<start_of_turn>model
同一段對話,三種不相容的信封。用 ChatML 訓練的模型不會「參透」Llama 標記——它把陌生標記當普通文字,然後如法炮製地回應。
引擎從模型檔讀取模板
GGUF 元資料內嵌 chat_template(一段 Jinja 片段)。llama.cpp、Ollama、LM Studio 與 Open WebUI 會讀它並自動渲染你的訊息,這正是正常 GGUF 開箱即聊的原因。兩個經典陷阱:原始 completion 端點(/completion)會跳過模板、把裸文字交給模型——要用 chat 端點;還有 GGUF 可能帶著缺失或錯誤的模板出貨——明確傳入一個(llama.cpp 用 --chat-template、Ollama 用 Modelfile),或從做對的來源抓同一模型的其他量化版。
模板錯誤的症狀
- 立即空回覆——渲染後的提示詞已經像一段結束的對話,所以模型立刻吐 EOS。
- 角色滲漏——輸出讀起來像
user: sure thing! assistant:——模型在模仿信封而不是回答。 - 垃圾——一小時前還聊得好好的模型吐出 base 模型等級的文字漿糊,因為你換了檔案卻沒換模板。
診斷時傾印渲染後的提示詞——llama.cpp 伺服器會詳細記進日誌,OLLAMA_DEBUG=1 會讓 Ollama 顯示它。然後修模板或修檔案:疑難排解。
系統提示詞與優先順序
system 角色設定持久行為:人格、語言、輸出格式、安全姿態。它是上下文,不是韌體——長對話能把它沖淡,而且小模型給它的權重低於你的期望。兩條實用規則:把承重的東西(語言、格式合約、硬性限制)放進系統提示詞,而且在最後的 user 訊息裡重述一次;離生成最近的指令獲勝。
零樣本 vs 少樣本
零樣本用問的;少樣本用示範的。用 3B 模型從錯誤報告抽取結構化欄位,零樣本——「以 JSON 抽出標題、組件與嚴重度」——大約三分之二的時間能得到有效且鍵名一致的 JSON,鍵名會漂移(這輪 severity、下輪 priority)。
少樣本:同一個模型、兩個示範例、第三份報告留白:
Report: "Login button does nothing on Safari 17."
{"title": "Login button unresponsive", "component": "auth", "severity": "medium"}
Report: "App crashes when uploading 4K video."
{"title": "Crash on 4K upload", "component": "upload", "severity": "high"}
Report: "Dark mode makes settings text unreadable."
範例承載了格式合約,所以取樣器選的是內容而不是發明 schema。這類任務的格式失敗率通常從約 30% 掉到幾個百分點。兩三個範例是曲線的膝點;超過之後你付的是上下文 token。嚴格 schema 的話,完全別猜:結構化輸出。
小模型撐得住的模式
| 模式 | 為什麼有效 |
|---|---|
| 每個提示詞一項任務 | 任務互相搶注意力;輸家被丟掉 |
| 明確的輸出格式(「JSON,鍵為 a、b、c」) | 把格式變成近乎確定的選擇 |
| 編號步驟(「1) 找出 2) 解釋 3) 修補」) | 鏈條留在軌道上而不是亂晃 |
| 範例勝過描述 | 模型模仿比聽從散文在行 |
| 簡短的後設指令 | 每多一條規則就稀釋其他規則 |
上下文中段是指令的墳場
注意力在提示詞的開頭與結尾最強——「迷失在中間」效應,跨模型家族皆然。實用的擺法:目標與格式合約在最上、要處理的文件在最下,長文件則在內容之後重複一行指令。位置跟內容一樣承重——而且每多一輪上下文都要付記憶體:KV 快取。
停止序列
模型吐出它的 EOS token 時生成結束——模板定義那是哪個 token。額外的停止序列(llama.cpp 伺服器與 OpenAI 相容 API 的 stop 參數)改為在字串處截斷:當多話的模型開始替你寫下一個 user 輪次時很有用。選只能代表「輪次結束」的停止值(ChatML 模型上的 \n<|im_start|>),絕不用會出現在合法回答裡的字串——一個裸換行會把多行程式碼攔腰截斷。
7B 以下,簡潔勝過冗長
小 instruct 模型遵循 40 字指令的表現勝過 400 字的評分規則:它們有限的注意力會攤在你交給它們的所有東西上。結構與範例可以替代散文規則。長篇提示詞留給大模型;只有它們每行都讀。
可重複使用的片段模板
抽取:
Extract from the text below: person names, dates, money amounts.
Return a JSON array, one object per item, keys: name, date, amount.
No prose before or after the JSON.
Text: {text}
摘要:
Summarize in exactly 3 bullet points, each under 15 words,
for someone who skipped the meeting. Most important decision first.
Text: {text}
程式碼審查:
Review the diff below. List at most 5 findings, ordered by severity.
Format each as: [severity] file:line — problem — suggested fix.
Skip praise and summary. Diff: {diff}
相關章節: token 與分詞器 · 結構化輸出 · 引擎參數 · 疑難排解