1. 项目概述为什么提示词是LangChain的灵魂如果你已经开始用LangChain搭建应用可能会发现一个现象模型调用、链式编排、记忆管理这些组件都搭好了但最终生成的内容总是不尽如人意要么答非所问要么格式混乱。折腾了半天最后发现问题往往出在最开始的那几行“指令”上——也就是提示词。这就像你给一位能力超强的助手布置任务如果指令含糊不清他再厉害也难做出你想要的成果。在LangChain的生态里提示词工程远不止是“写一段话给模型”那么简单。它是一套系统工程涉及到模板的动态构建、变量的灵活注入、不同模型的适配以及对输出格式的精准控制。很多人把LangChain当作一个“胶水”框架认为它只是把各种工具连起来却忽略了提示词才是驱动整个智能流程的“大脑”和“指挥棒”。一个设计精良的提示词模板能让你的应用效果提升好几个档次而一个糟糕的提示词则会让后面所有的复杂架构都变得徒劳。这一章我们就抛开那些华而不实的理论直接切入LangChain中提示词工程的核心实战。我会带你从最基础的PromptTemplate用起一步步拆解如何构建支持变量、适配多模型、并能进行少样本学习的复杂提示系统。我会分享很多官方文档里不会写的“坑”比如如何处理中文场景下的格式错乱、如何为开源模型定制专属的提示结构以及如何利用LangChain提供的各种“提示词优化器”来让你的指令更聪明。无论你是想做一个自动客服、一个智能总结工具还是一个复杂的决策分析Agent掌握好提示词工程都是让你从“能跑通”到“效果好”的关键一跃。2. 核心思路将提示词从“静态文本”升级为“可编程对象”在直接上手代码之前我们需要先扭转一个观念在LangChain中提示词不应该被看作是一段固定的字符串而应该被视为一个可以编程、可以组合、可以调试的“一等公民”对象。这种设计思路带来了几个根本性的优势也是我们后续所有技巧的基础。2.1 动态化告别字符串拼接的混乱时代最原始的提示词使用方式就是字符串拼接。比如你想让模型根据用户输入的城市查询天气可能会写成这样user_input “北京” prompt f“请查询{city}的天气情况。”这种方式在简单场景下没问题但一旦提示词变长、变量增多、逻辑变复杂比如需要根据条件决定是否加入某段指令代码就会变得难以维护满眼都是加号和引号极易出错。LangChain的PromptTemplate将这种拼接过程标准化和抽象化。你把提示词写成带占位符的模板把变量通过字典传入由框架负责安全、正确地替换。这不仅仅是语法糖它使得提示词本身可以像函数一样被定义、存储和复用。2.2 结构化精准控制输入与输出格式对于更复杂的任务我们往往需要模型按照特定格式输出比如JSON、YAML或者包含多个字段的文本块。单纯的文字指令如“请用JSON格式回复”可靠性并不高模型可能会漏字段、格式错误或添加多余的解释。LangChain通过StructuredOutputParser、PydanticOutputParser等组件将输出格式的定义也整合到提示词工程中。你可以在提示词模板里清晰地描述你期望的JSON结构LangChain会先自动生成一段关于格式的详细指令插入提示词然后在模型生成后自动尝试将文本解析成你定义的结构化对象。这实现了从“模糊要求”到“强制约束”的跨越。2.3 组件化构建可复用的提示词片段一个复杂的智能应用其提示词通常由多个部分组成系统指令定义角色、任务描述、上下文信息历史对话、检索到的文档、少样本示例、用户当前查询。如果每次都从头写一个巨大的字符串管理和迭代将是噩梦。LangChain鼓励组件化思维。你可以分别创建一个SystemMessagePromptTemplate来定义AI的角色。一个FewShotPromptTemplate来管理示例。一个HumanMessagePromptTemplate来定义用户输入的格式。 然后用ChatPromptTemplate.from_messages方法将这些片段像搭积木一样组合起来。这样当你想调整系统角色时只需修改一个组件所有使用该角色的链都会自动更新。2.4 模型适配一份模板多处使用不同的模型对提示词的格式要求可能不同。例如ChatGPT使用system、user、assistant的消息序列而一些开源模型可能使用|im_start|user\n这样的特殊标记。手动为每个模型重写提示词非常低效。LangChain的提示词模板通常与“消息格式化器”和“LLM包装器”深度集成。当你为ChatOpenAI设计了一个基于消息的提示模板后如果想换用ChatAnthropic或本地的ChatLlamaCpp很多时候你只需要更换LLM对象提示词模板本身无需改动因为LangChain底层会帮你处理好与对应模型适配的格式转换。这种抽象极大地提升了代码的可移植性。理解了这些核心思路我们再去看具体的类和函数就不会觉得它们是一堆零散的API而是一个有机整体中的不同工具共同服务于“构建高效、可靠、可维护的提示系统”这个目标。3. 核心细节解析从PromptTemplate到复杂提示组装现在我们进入实战环节逐一拆解LangChain中构建提示词的关键组件。我会结合代码示例和背后的设计逻辑让你不仅会用更明白为什么要这么用。3.1 PromptTemplate一切的基础PromptTemplate是LangChain提示词体系的基石。它的核心功能是将一个带有变量的模板字符串与具体的输入值结合生成最终的提示词。基础用法与变量注入from langchain.prompts import PromptTemplate # 定义一个简单的模板用花括号{}声明变量 template “请帮我把以下文本翻译成{target_language}{text}” prompt PromptTemplate.from_template(template) # 准备输入变量 input_variables {“target_language”: “法语” “text”: “你好世界”} # 格式化生成最终提示词 formatted_prompt prompt.format(**input_variables) print(formatted_prompt) # 输出请帮我把以下文本翻译成法语你好世界这里的关键是from_template方法它能自动从模板字符串中解析出变量名target_language和text。format方法则安全地进行替换避免了手动拼接可能带来的特殊字符转义问题。实操心得变量命名与模板管理命名清晰变量名应具有描述性如user_query、document_context避免使用a、b、x这样的命名这在后续维护和组合复杂模板时至关重要。模板存储对于需要频繁使用或团队共享的模板不要硬编码在代码里。可以将其存储在单独的.txt或.yaml文件中甚至存入数据库。LangChain也支持从文件加载模板这有利于进行A/B测试和版本管理。# 假设有一个 prompt_templates/translation.txt 文件 with open(“prompt_templates/translation.txt”, “r”, encoding“utf-8”) as f: template_string f.read() prompt PromptTemplate.from_template(template_string)3.2 ChatPromptTemplate对话式应用的核心对于聊天模型提示词通常是由多条消息组成的列表每条消息都有其角色如系统、用户、助手。ChatPromptTemplate就是为这种场景设计的。构建多角色对话提示from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate # 1. 创建各个角色的消息模板 system_template “你是一位专业的{domain}专家回答问题时需严谨、准确并适当举例。” system_message_prompt SystemMessagePromptTemplate.from_template(system_template) human_template “{question}” human_message_prompt HumanMessagePromptTemplate.from_template(human_template) # 2. 组合成聊天提示模板 chat_prompt ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt]) # 3. 格式化 formatted_messages chat_prompt.format_prompt(domain“机器学习” question“什么是过拟合”).to_messages() print(formatted_messages) # 输出类似[SystemMessage(content‘你是一位专业的机器学习专家...’), HumanMessage(content‘什么是过拟合’)]format_prompt返回的是一个PromptValue对象调用其to_messages()方法会得到LangChain标准的BaseMessage对象列表这可以直接喂给ChatOpenAI等聊天模型。注意事项消息顺序与模型期望不同的模型对消息序列的期望不同。大多数模型遵循系统消息 - 用户消息 - 助手消息历史- 用户消息最新的顺序。ChatPromptTemplate帮你管理了这个顺序但你需要确保在组合时比如加入历史对话顺序是正确的。一个常见的错误是把旧的系统消息错误地放在了历史对话中间导致模型困惑。3.3 FewShotPromptTemplate让模型快速学会新任务少样本学习是提升模型在特定任务上表现的关键技术。FewShotPromptTemplate允许你将一组输入-输出示例嵌入到提示词中引导模型模仿。构建带示例的提示词from langchain.prompts import FewShotPromptTemplate, PromptTemplate # 1. 首先定义单个示例的格式 example_prompt PromptTemplate( input_variables[“input”, “output”], template“输入{input}\n输出{output}” ) # 2. 准备示例列表 examples [ {“input”: “高兴” “output”: “情绪积极愉悦。”}, {“input”: “愤怒” “output”: “情绪消极伴有强烈不满。”}, {“input”: “焦虑” “output”: “情绪消极对未来的不确定性感到担忧。”} ] # 3. 定义整体的少样本提示模板 few_shot_prompt FewShotPromptTemplate( examplesexamples, example_promptexample_prompt, # 每个示例如何渲染 prefix“请根据以下示例将输入词语分类为积极或消极情绪并简要说明。\n示例” suffix“输入{user_input}\n输出” input_variables[“user_input”], example_separator“\n\n” # 示例之间的分隔符 ) # 4. 格式化 result few_shot_prompt.format(user_input“悲伤”) print(result)运行后你会得到一个包含了所有示例和最终问题的完整提示词。模型会参考示例的格式和逻辑来回答新的“悲伤”属于何种情绪。核心技巧示例的选择与排序质量优于数量3-5个高质量、多样化的示例远比10个平庸或重复的示例有效。示例应覆盖任务的主要边界情况。顺序很重要模型可能会更关注开头和结尾的示例。把最典型、最清晰的示例放在开头把包含特殊情况的示例放在中间。动态示例选择对于复杂应用examples参数可以是一个ExampleSelector对象如SemanticSimilarityExampleSelector它能根据当前用户输入从大量示例中动态选择最相关的几个这比固定示例列表强大得多。3.4 Output Parsers从自由文本到结构化数据这是提示词工程中提升可靠性的“神器”。它的作用是定义你希望模型输出的结构并自动将模型的文本回复解析成该结构。使用PydanticOutputParser进行强类型解析假设我们需要模型分析一段客户反馈并输出结构化的信息。from langchain.output_parsers import PydanticOutputParser from langchain.prompts import PromptTemplate from pydantic import BaseModel, Field from typing import List # 1. 用Pydantic定义你期望的输出结构 class FeedbackAnalysis(BaseModel): sentiment: str Field(description“整体情感如积极、消极、中性”) key_points: List[str] Field(description“提取的关键要点列表”) urgency: int Field(description“紧急程度1-5分5分最急”) suggested_action: str Field(description“建议的后续动作”) # 2. 创建解析器 parser PydanticOutputParser(pydantic_objectFeedbackAnalysis) # 3. 构建提示词自动注入格式指令 template “”” 请分析以下客户反馈 {feedback} {format_instructions} “”” prompt PromptTemplate( templatetemplate, input_variables[“feedback”], partial_variables{“format_instructions”: parser.get_format_instructions()} # 关键 ) # 4. 组合使用 feedback_text “产品经常卡顿尤其是支付页面希望尽快修复但客服态度很好。” _input prompt.format_prompt(feedbackfeedback_text) # 假设model是你的LLM调用对象 # output model(_input.to_string()) # 假设output是模型返回的文本 simulated_output “”” {“sentiment”: “消极” “key_points”: [“产品卡顿” “支付页面问题严重” “客服态度好”], “urgency”: 4, “suggested_action”: “优先排查支付页面性能并修复”} “”” # 5. 解析输出 try: parsed_result parser.parse(simulated_output) print(f“情感{parsed_result.sentiment}”) print(f“紧急度{parsed_result.urgency}”) except Exception as e: print(f“解析失败{e}”)parser.get_format_instructions()会自动生成一段详细的文本指令例如要求输出JSON并描述每个字段并将其插入到你的提示词模板中。这样模型在生成时就已经被明确约束了格式。避坑指南解析失败处理模型并不总是乖乖听话它可能输出格式错误、不完整或包含额外解释的JSON。因此在生产环境中必须对parser.parse()进行异常捕获。使用OutputFixingParserLangChain提供了这个包装器当解析失败时它会尝试用另一个LLM来修复输出文本。这能显著提高鲁棒性。from langchain.output_parsers import OutputFixingParser from langchain.llms import OpenAI fixing_parser OutputFixingParser.from_llm(parserparser, llmOpenAI(temperature0)) parsed_result fixing_parser.parse(bad_output)设置重试机制对于关键任务可以尝试用略微不同的提示词或参数重新生成并解析。4. 高级技巧与模式构建生产级提示系统掌握了基本组件后我们可以组合它们来解决更实际、更复杂的问题。下面介绍几种在生产环境中非常实用的高级模式。4.1 动态提示词生成根据上下文决定提示内容很多时候提示词本身需要根据运行时的条件动态生成。例如在客服系统中根据用户问题的类型技术问题、账单问题、投诉注入不同的系统指令和示例。实现方案使用partial方法和条件逻辑from langchain.prompts import PromptTemplate # 基础模板 base_template “”” 你是一位{role}。 已知信息{context} 用户问题{question} 请根据已知信息回答问题。 “”” base_prompt PromptTemplate.from_template(base_template) # 动态决定角色和上下文 def build_dynamic_prompt(question: str, knowledge_base: dict) - str: # 1. 根据问题简单分类实际中可能用分类模型 if “如何安装” in question or “报错” in question: role “技术客服专员” context knowledge_base.get(“technical”, “”) elif “扣费” in question or “价格” in question: role “账单客服专员” context knowledge_base.get(“billing”, “”) else: role “通用客服助理” context knowledge_base.get(“general”, “”) # 2. 使用partial预先填充部分变量 from functools import partial prompt_with_role partial(base_prompt.format, rolerole) # 3. 格式化最终提示 final_prompt prompt_with_role(contextcontext, questionquestion) return final_prompt # 模拟知识库 kb { “technical”: “最新版本为v2.1安装命令是‘pip install package’。常见报错‘ModuleNotFound’需检查Python路径。”, “billing”: “月度订阅费为99元每年续费有8折优惠。扣费日在每月1号。” } question “我昨天被扣费了是怎么回事” print(build_dynamic_prompt(question, kb))这个例子展示了如何将业务逻辑与提示词构建分离。更复杂的场景可以使用RunnableBranchLangChain Expression Language的一部分来构建基于条件的提示流。4.2 提示词链式调用多步推理与精炼复杂任务往往需要模型进行多步思考。我们可以通过将多个提示词模板“链”起来实现。示例先分析再总结假设我们需要先让模型从一篇长文中提取事实列表再根据这些事实生成一个摘要。from langchain.prompts import PromptTemplate from langchain.chains import LLMChain # 假设已初始化llm # 第一步事实提取 extraction_template “”” 请从以下文本中提取关键事实以列表形式呈现 文本{document} 关键事实列表 “”” extraction_prompt PromptTemplate.from_template(extraction_template) extraction_chain LLMChain(llmllm, promptextraction_prompt) # 第二步基于事实总结 summary_template “”” 基于以下关键事实生成一段简洁的摘要 事实 {facts} 摘要 “”” summary_prompt PromptTemplate.from_template(summary_template) summary_chain LLMChain(llmllm, promptsummary_prompt) # 组合成顺序链 from langchain.chains import SimpleSequentialChain overall_chain SimpleSequentialChain(chains[extraction_chain, summary_chain], verboseTrue) # 运行 document “””一篇长新闻文章... result overall_chain.run(document)这里SimpleSequentialChain会自动将第一步的输出事实列表作为facts变量传递给第二步的提示词模板。verboseTrue参数会让你在控制台看到中间步骤非常利于调试。注意事项变量传递与上下文管理确保链中前后提示词的变量名能对应上。SimpleSequentialChain默认将上一个链的整个输出字符串作为下一个链的唯一输入。对于更复杂的变量映射需要使用SequentialChain并显式指定input_variables和output_variables。警惕上下文过长。如果第一步输出非常长可能会超出第二步模型的上下文窗口。在这种情况下需要在中间加入一个“压缩”或“筛选”的步骤。4.3 自定义提示模板应对特殊模型格式当你使用一些非主流或特定格式的开源模型时可能需要自定义提示模板。示例为Alpaca格式的模型创建模板一些模型使用类似以下的格式Below is an instruction that describes a task. Write a response that appropriately completes the request. ### Instruction: {instruction} ### Response:我们可以通过继承BasePromptTemplate或StringPromptTemplate来创建自定义模板。from langchain.prompts import StringPromptTemplate from typing import List class AlpacaPromptTemplate(StringPromptTemplate): template: str def format(self, **kwargs) - str: # 在这里实现你的自定义格式逻辑 instruction kwargs[“instruction”] # 可以在这里添加固定的系统提示等 formatted_template self.template.replace(“{instruction}”, instruction) return formatted_template property def _prompt_type(self) - str: return “alpaca” def input_variables(self) - List[str]: # 返回模板中需要的变量列表 return [“instruction”] # 使用自定义模板 alpaca_template “”” Below is an instruction that describes a task. Write a response that appropriately completes the request. ### Instruction: {instruction} ### Response: “”” prompt AlpacaPromptTemplate(templatealpaca_template, input_variables[“instruction”]) print(prompt.format(instruction“解释一下机器学习。”))通过自定义模板你可以将任何模型所需的特殊标记、固定前缀/后缀封装起来让上层应用代码无需关心底层模型的差异。5. 实战问题排查与调优心得即使按照最佳实践构建了提示词在实际运行中还是会遇到各种问题。这里记录了一些常见坑点和调优技巧。5.1 常见问题速查表问题现象可能原因排查与解决思路模型输出“我不知道”或拒绝回答1. 系统指令过于模糊或限制过强。2. 缺少必要的上下文信息。3. 问题本身存在歧义。1. 检查并细化系统提示中的角色和能力描述。2. 确保所有必要的变量如context都已正确传入且不为空。3. 在提示词中加入“如果你不确定请基于已知信息进行合理推断”的引导。输出格式不符合预期如JSON解析失败1.format_instructions不够清晰或模型未遵循。2. 模型在JSON外添加了额外文本。3. 温度temperature参数过高导致输出随机。1. 使用parser.get_format_instructions()打印出来检查确保指令明确要求“只输出JSON”。2. 使用OutputFixingParser自动修复。3. 尝试降低temperature如设为0以获得更确定性的输出。输出内容冗长、包含多余解释提示词中未对输出长度和风格做约束。在提示词后缀或系统指令中明确要求例如“请直接给出答案无需开头和结尾的客套话。”、“答案请控制在100字以内。”少样本学习示例不起作用1. 示例与当前任务不相关或质量差。2. 示例过多干扰了主要指令。3. 示例的格式与期望输出格式不一致。1. 精选或动态选择与当前输入最相关的示例。2. 减少示例数量2-3个或使用example_separator使其更清晰。3. 检查example_prompt模板确保其输入输出变量与示例字典的键匹配。处理中文时出现乱码或格式怪异1. 文件编码问题。2. 模型对中文标点、换行的处理与预期不符。1. 确保所有模板文件、代码文件均使用UTF-8编码。2. 在提示词中明确使用中文标点并谨慎使用换行符。对于复杂格式先在简单案例中测试。5.2 提示词迭代与评估心得写好提示词不是一个一蹴而就的过程而是一个需要持续迭代和评估的循环。1. 从小处开始逐步复杂化不要一开始就设计一个包含系统指令、上下文、历史、示例的庞大提示词。先从最简单的任务和最基本的PromptTemplate开始确保它能工作。然后像搭积木一样逐步加入角色定义、上下文、示例等组件每加一步都进行测试。2. 建立评估管道对于生产系统需要有一套客观的方法来评估提示词修改的效果。这可以很简单人工抽查定期查看模型在一些典型和边界用例上的输出。自动化测试集构建一个包含输入和期望输出或评估标准的小型测试集。每次修改提示词后运行测试集计算匹配度或使用另一个LLM进行评分。A/B测试如果流量足够可以线上同时部署两套提示词对比关键业务指标如用户满意度、任务完成率。3. 温度Temperature与提示词的协同调优Temperature参数控制输出的随机性。它与提示词的明确程度紧密相关。如果提示词指令非常清晰、具体可以使用较低的temperature如0-0.3来获得稳定、可靠的输出。如果任务需要创造性、多样性如写诗、生成创意可以适当提高temperature如0.7-0.9但此时提示词需要给出更宽泛的引导而非严格约束。一个实用的技巧在开发调试阶段将temperature设为0以便确定是提示词的问题还是模型的随机性问题。4. 将提示词视为“可配置的代码”这是我最重要的一个心得像管理代码一样管理你的提示词。版本控制使用Git管理你的提示词模板文件清晰地记录每次修改的意图。参数化将所有可变的元素如角色名称、任务描述、格式要求都设计成模板变量而不是硬编码在文本里。环境隔离为开发、测试、生产环境配置不同的提示词版本避免相互影响。文档化在复杂的提示词模板旁边添加注释说明每个部分的作用、变量的含义以及设计时的考量。最后记住提示词工程既是科学也是艺术。科学的部分在于遵循清晰的指令、提供充足的上下文、使用正确的格式。艺术的部分在于如何用最精炼的语言引导模型朝着你期望的方向思考。这需要大量的实践、观察和微调。最好的学习方式就是不断动手去写、去试、去分析模型的输出逐渐积累起对模型“思维”方式的直觉。