
1. 項目概述為什么我們需要提示模板如果你已經開始接觸大語言模型的應用開發無論是用 LangChain 還是直接調用 OpenAI 的 API大概率都寫過類似這樣的代碼prompt f請根據以下用戶輸入生成一段友好的回復。 用戶輸入{user_input} 請確保回復的語氣熱情且專業。看起來很簡單對吧但隨著項目復雜度的提升你會發現事情開始變得棘手。昨天產品經理說要在回復前加上當前日期今天測試同學反饋說某些場景下需要引用歷史對話明天老板又要求支持多語言切換……很快你那原本清晰的代碼就會變成一堆令人頭疼的字符串拼接和條件判斷。這就是“提示工程基礎”中Prompt Templates要解決的核心問題。它不是一個炫酷的新算法而是一個樸素卻至關重要的工程實踐將提示詞的結構與內容分離。簡單說就是把那些固定不變的框架比如指令、格式要求、示例和動態變化的部分用戶輸入、查詢結果、當前日期拆分開來。我見過太多項目初期因為忽視這一點導致后期提示詞維護成本指數級上升最終不得不重構。一個設計良好的提示模板就像是給大語言模型準備的“填空題”試卷。試卷的題目和答題規范是固定的而每次需要填入的空格內容則根據實際情況變化。這樣做的好處顯而易見提升一致性、便于維護、支持復用并且能系統化地優化提示效果。無論是構建一個簡單的問答機器人還是設計復雜的多步推理智能體提示模板都是構建可靠、可擴展應用的地基。2. 核心需求解析從字符串拼接走向工程化在深入技術細節前我們先拆解一下在實際開發中一個原始的提示字符串會遇到哪些具體的“痛點”。理解了這些你才能明白模板化不是“可有可無”而是“勢在必行”。2.1 維護噩夢一處改動處處搜索假設你的應用里有十幾個地方都用到了類似的歡迎語提示。現在需要把“你好”統一改成“您好”。如果沒有模板你就得在代碼庫里全局搜索所有包含“你好”的字符串逐一檢查、修改、測試。這個過程極易出錯可能漏掉某個偏僻的文件或者誤改了其他業務邏輯中的“你好”。而使用模板后你只需要在一個地方模板定義處修改這個變量所有使用該模板的地方都會自動生效。這本質上是軟件開發中“Don‘t Repeat Yourself”原則在提示詞領域的體現。2.2 動態內容注入的混亂當提示需要嵌入變量時字符串格式化f-string或.format()是常見做法。但變量一多提示文本就會變得支離破碎可讀性急劇下降。# 混亂的示例 prompt f系統角色{system_role}。 請參考以下上下文{context}。 回答用戶關于{query_topic}的問題{user_question}。 歷史記錄{history}。 請用{language}回答并引用{source_name}中的觀點。這段代碼不僅難以閱讀而且當context或history變量為空時提示中可能會留下尷尬的空白或殘缺的句子結構影響模型理解。模板系統可以更優雅地處理可選變量和默認值。3. 動態內容注入的混亂當提示需要嵌入變量時字符串格式化f-string或.format()是常見做法。但變量一多提示文本就會變得支離破碎可讀性急劇下降。# 混亂的示例 prompt f系統角色{system_role}。 請參考以下上下文{context}。 回答用戶關于{query_topic}的問題{user_question}。 歷史記錄{history}。 請用{language}回答并引用{source_name}中的觀點。這段代碼不僅難以閱讀而且當context或history變量為空時提示中可能會留下尷尬的空白或殘缺的句子結構影響模型理解。模板系統可以更優雅地處理可選變量和默認值。3.1 缺乏結構和類型校驗純字符串無法對注入的內容做任何約束。如果你期望max_words變量是一個數字但傳入了一個字符串錯誤可能要到模型返回了奇怪的結果時才會被發現。模板系統可以在填充階段就進行初步的類型檢查或格式化提前規避一類問題。3.2 難以實現高級模式一些高級提示技巧如少樣本提示需要在提示中包含多個結構化的輸入-輸出對。用純手工拼接來管理這些示例非常繁瑣。而模板可以輕松地將示例列表作為一個整體變量進行管理和渲染甚至可以從外部文件加載示例。基于這些痛點我們可以總結出對提示模板系統的核心需求變量管理支持定義變量并能從外部傳入值進行填充。結構封裝將固定的提示結構和指令封裝起來保持代碼整潔。可復用性一次定義多處使用。可維護性修改模板定義所有使用點同步更新。可選變量與默認值優雅處理某些場景下可能缺失的變量。類型與格式控制對輸入變量進行簡單的預處理或校驗。支持復雜結構能夠方便地嵌入列表、字典等結構以支持少樣本示例等模式。4. LangChain PromptTemplate 深度拆解LangChain 的PromptTemplate類是實踐上述理念的一個優秀工具。它遠不止是一個“字符串格式化工具”而是一個功能豐富的提示詞構建器。我們來深入看看它的核心用法和設計哲學。4.1 基礎構建從字符串到模板對象最基本的用法是定義一個包含占位符的字符串模板。from langchain.prompts import PromptTemplate # 定義一個簡單的模板 template “””你是一個專業的{domain}專家。 請用{style}的風格回答以下問題 問題{question} “”” prompt_template PromptTemplate.from_template(template)這里{domain},{style},{question}就是變量占位符。from_template這個類方法會自動解析字符串提取出所有的變量名。填充模板# 準備輸入變量 input_variables { “domain”: “機器學習”, “style”: “簡潔明了”, “question”: “過擬合有哪些常見的解決方法” } # 生成最終提示詞 final_prompt prompt_template.format(**input_variables) print(final_prompt)輸出你是一個專業的機器學習專家。 請用簡潔明了的風格回答以下問題 問題過擬合有哪些常見的解決方法注意PromptTemplate會驗證你傳入的input_variables字典是否包含了模板中定義的所有變量除非變量有默認值如果缺少會拋出KeyError。這是一個很好的安全特性避免了生成殘缺的提示。4.2 模板語法進階不止是{variable}LangChain 的模板語法支持更多功能使其更加強大。1. 默認值這是處理可選變量的利器。比如我們想提供一個“語言”變量但大部分情況下用戶可能不指定我們希望默認為中文。template “””請將以下文本翻譯成{language}。 文本{text} “”” # 在初始化時指定默認值 prompt_template PromptTemplate.from_template( template, partial_variables{“language”: “中文”} # 為 language 設置默認值 ) # 調用時可以不傳 language prompt1 prompt_template.format(text“Hello, world!”) print(prompt1) # 語言默認為“中文” # 調用時也可以覆蓋默認值 prompt2 prompt_template.format(text“Hello, world!”, language“法語”) print(prompt2) # 語言被覆蓋為“法語”2. 少樣本提示模板FewShotPromptTemplate是PromptTemplate的擴展專門用于構建包含示例的提示。這是提示工程中提升模型表現的關鍵技術之一。from langchain.prompts import FewShotPromptTemplate, PromptTemplate # 1. 首先定義單個示例的模板 example_template “”” 輸入{input} 輸出{output} “”” example_prompt PromptTemplate.from_template(example_template) # 2. 準備示例列表 examples [ {“input”: “今天天氣真好” “output”: “是的陽光明媚適合外出。”}, {“input”: “我有點難過” “output”: “聽到你這么說我很難過想聊聊發生了什么嗎”}, ] # 3. 定義整體的少樣本提示模板 few_shot_template FewShotPromptTemplate( examplesexamples, # 示例列表 example_promptexample_prompt, # 每個示例的格式模板 prefix“你是一個情感支持助手。請根據以下示例回應用戶的話。”, # 前綴指令 suffix“輸入{user_input}\n輸出”, # 后綴包含最終用戶輸入的變量 input_variables[“user_input”], # 最終模板需要的變量除了examples example_separator“\n\n” # 示例之間的分隔符 ) # 4. 使用 result few_shot_template.format(user_input“這個任務太難了。”) print(result)輸出會清晰地展示兩個示例然后是當前用戶的輸入模型通過學習示例中的模式和風格來生成回復。這種方式比在純文本中描述“請用支持性的語氣回答”要有效得多。3. 模板組合與鏈式調用這才是 LangChain 發揮威力的地方。PromptTemplate可以很容易地與其他組件如LLMChain,OutputParser連接起來形成可執行的“鏈”。from langchain.chains import LLMChain from langchain_openai import ChatOpenAI # 假設使用 OpenAI 模型 from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate from langchain.schema import SystemMessage # 構建一個更復雜的聊天提示模板使用消息列表 system_template “你是一個{speaker}你的性格特點是{character}。” system_message_prompt SystemMessagePromptTemplate.from_template(system_template) human_template “{text}” human_message_prompt HumanMessagePromptTemplate.from_template(human_template) # 組合成一個聊天提示模板 chat_prompt ChatPromptTemplate.from_messages( [system_message_prompt, human_message_prompt] ) # 創建鏈 llm ChatOpenAI(temperature0.7) chain LLMChain(llmllm, promptchat_prompt) # 運行鏈 response chain.run({ “speaker”: “莎士比亞風格的詩人”, “character”: “用詞華麗充滿比喻和十四行詩的韻律感”, “text”: “寫一首關于咖啡的短詩” }) print(response)這種組合方式讓你能構建極其復雜和靈活的對話流程例如包含系統指令、上下文歷史、工具調用結果和當前用戶查詢的 RAG 應用提示。5. 實戰構建一個可復用的提示模板系統了解了基礎組件后我們從一個更高的視角看如何在一個真實項目中設計和組織你的提示模板。這里分享一套我經過多個項目總結出的實踐方法。5.1 按功能模塊化組織模板不要把所有提示模板都堆在一個文件里。建議按業務功能或提示類型進行劃分prompts/chat/存放通用聊天、角色扮演相關模板。prompts/summarization/存放各種摘要任務模板文章摘要、會議紀要、長文本總結。prompts/rag/存放檢索增強生成相關的模板上下文壓縮、重寫、答案生成。prompts/code/存放代碼生成、解釋、調試相關模板。prompts/generic/存放基礎、通用的模板。每個模板文件如prompts/rag/answer_generation.py里可以用字典或類來管理一組相關模板。# prompts/rag/answer_generation.py from langchain.prompts import PromptTemplate ANSWER_PROMPT { “basic”: PromptTemplate.from_template( “””基于以下上下文信息回答問題。如果你不知道答案就說不知道不要編造。 上下文 {context} 問題{question} 答案“”” ), “with_citation”: PromptTemplate.from_template( “””基于以下上下文信息回答問題并在答案中引用相關原文用【】標注。 上下文 {context} 問題{question} 答案“”” ), } # 在業務代碼中引用 from prompts.rag import answer_generation prompt answer_generation.ANSWER_PROMPT[“with_citation”]5.2 設計模板時的核心考量點指令清晰度給模型的指令必須明確、無歧義。避免使用“處理好一點”、“用專業的方式”這種模糊詞匯。取而代之的是“用不超過三句話總結”、“以項目符號列表形式列出關鍵點”、“輸出格式為 JSON包含 title 和 summary 兩個字段”。上下文管理對于 RAG 應用如何將檢索到的上下文 (context) 嵌入提示是關鍵。上下文太長會浪費 Token 并可能分散模型注意力太短可能信息不足。常見的策略有簡單拼接直接附在提示后。適用于上下文較短的情況。指令強調用“### 上下文 ###”等明顯標記分隔并加上“請嚴格基于以上上下文回答”的指令。Map-Reduce對于超長上下文先對多個片段分別生成答案或摘要再合并。這需要更復雜的模板鏈。輸出格式控制強烈建議在模板中明確指定輸出格式。這對于后續的程序化處理至關重要。template “””... 請將分析結果以如下 JSON 格式輸出 {{ “risk_level”: “高/中/低”, “reasons”: [“原因1”, “原因2”], “suggestion”: “你的建議” }} 確保只輸出 JSON不要有其他任何文字。“””配合 LangChain 的OutputParser如PydanticOutputParser可以自動將模型輸出解析成結構化數據。角色與人格設定通過system_message或提示前綴設定模型的角色能顯著改變其輸出風格和傾向。例如“你是一個嚴謹的科學家”和“你是一個風趣的脫口秀演員”對同一問題的回答會截然不同。5.3 模板版本化與 A/B 測試提示模板本身也是重要的“代碼”需要版本管理。當你想優化一個提示時不要直接覆蓋原模板而是創建一個新版本。# prompts/v1/summarizer.py PROMPT_V1 “總結以下文本{text}” # prompts/v2/summarizer.py PROMPT_V2 “””請扮演編輯的角色為以下文章撰寫一個吸引人的摘要要求突出核心觀點并在80字以內。 文章{text} 摘要“””然后你可以在系統中通過配置或功能開關讓一部分流量使用V1另一部分使用V2對比兩者的效果如摘要質量評分、用戶滿意度用數據驅動決策。這個過程就是提示詞的 A/B 測試。6. 常見陷阱與效能優化指南即使使用了模板也可能會踩坑。下面是一些我親身經歷或觀察到的常見問題及其解決方案。6.1 陷阱一變量注入導致提示變形問題當動態變量內容包含引號、換行符或與模板語法沖突的字符時可能會破壞提示的整體結構。user_input ‘我說“你好嗎”’ # 包含雙引號 template “用戶說{input}” # 格式化后可能引起混淆解決方案對于簡單情況確保變量內容已做適當的轉義或清理。更穩健的做法是使用 LangChain 的ChatPromptTemplate和消息對象SystemMessage,HumanMessage,AIMessage它們以結構化的方式管理內容天然避免了字符串拼接問題。在模板設計時考慮使用更明確的分隔符例如### 用戶輸入 ###\n{input}\n### 結束 ###。6.2 陷阱二Token 超限與成本失控問題模板本身可能很簡潔但注入的變量尤其是context可能非常長導致每次調用都消耗大量 Token成本高昂且可能觸發模型上下文長度限制。解決方案上下文壓縮/摘要在將長文本作為context注入前先使用另一個 LLM 調用對其進行摘要僅保留關鍵信息。LangChain 的ContextualCompressionRetriever就是干這個的。分而治之采用 Map-Reduce 方法。將長文檔拆分成塊對每個塊用模板生成部分答案再用一個總結模板合并所有部分答案。設定預算在代碼層面計算提示詞的近似 Token 數例如使用tiktoken庫 for OpenAI如果超過閾值則觸發壓縮流程或直接拒絕。6.3 陷阱三模板過于僵化缺乏靈活性問題為每個細微變化都創建新模板導致模板數量爆炸難以管理。解決方案參數化配置將可配置項作為模板變量。例如一個摘要模板可以包含summary_length“用一句話總結”/“用三段話總結”和target_audience“面向專家”/“面向小學生”等變量。模板組合將提示拆解成更小的、可復用的部分如system_prompt_part,instruction_part,format_part然后在運行時按需組合。這比維護無數個完整模板要靈活得多。6.4 陷阱四忽視少樣本示例的質量和代表性問題在少樣本提示中隨意挑選幾個示例導致模型學到了錯誤的模式或偏見。解決方案精心設計示例示例應清晰、正確并覆蓋預期任務的各種邊界情況。示例數量通常 2-5 個高質量示例比 10 個普通示例更有效。太多示例會不必要地增加 Token 消耗和成本。動態示例選擇根據當前用戶輸入從示例庫中動態選擇最相關的幾個示例而不是固定使用同一組。這被稱為動態少樣本提示能顯著提升效果。6.5 效能優化技巧模板預熱對于高頻使用的模板可以在服務啟動時提前實例化好PromptTemplate對象避免每次請求時重復解析字符串。緩存生成結果如果某些提示組合特別是帶有固定上下文或示例的會被重復使用可以考慮緩存format后的完整提示字符串。異步批量處理當需要為大量數據項應用同一模板時例如批量生成產品描述使用異步并發來調用 LLM可以極大提升吞吐量。確保你的模板和鏈支持異步操作。7. 超越 LangChain模板設計的通用思想雖然我們以 LangChain 為例但提示模板的思想是通用的即使你在其他框架或直接使用 API。核心思想始終是分離、抽象、復用。在Dify、FastChat或其他 AI 應用平臺中它們通常提供了可視化的提示詞編排界面其底層邏輯依然是模板化。如果你直接調用OpenAI API你可以自己構建一個輕量級的模板管理系統class SimplePromptManager: def __init__(self): self.templates {} def register(self, name, template_str, default_varsNone): self.templates[name] { “template”: template_str, “defaults”: default_vars or {} } def format(self, name, **kwargs): tpl_info self.templates[name] # 合并默認值和傳入值 all_vars {**tpl_info[“defaults”], **kwargs} # 簡單的格式化可替換為更安全的實現 return tpl_info[“template”].format(**all_vars) manager SimplePromptManager() manager.register(“greeting”, “Hello, {name}! Welcome to {place}.“, {“place”: “our platform”}) print(manager.format(“greeting”, name“Alice”)) # Hello, Alice! Welcome to our platform.最終評判一個提示模板系統好壞的唯一標準是它是否讓你的提示詞更易于管理、迭代和優化從而穩定、高效地獲得你期望的模型輸出。花時間設計一個好的模板系統在項目后期會為你節省數十倍于此刻的調試和重構時間。當你的同事能輕松理解并復用你定義的SUMMARY_V3模板時你就知道這筆投資是值得的。