1. 为什么我把提示词工程当成一门手艺来练
最早接触大模型那会儿,我和大多数人一样,觉得提示词这东西没什么门槛——不就是把需求用中文说清楚吗?直到有一次,我让模型帮我从一份三十多页的产品需求文档里抽取所有涉及数据埋点的字段,结果它给我返回了一堆看起来很像但完全对不上的字段名,还煞有介事地编了几个根本不存在的表名。那次之后我才意识到,提示词工程不是“会说话就行”,它更像是一门需要刻意练习的手艺,涉及参数调优、结构设计、思维链引导、工具调用编排等一整套方法论。
这篇文章我想把自己这两年在大模型应用开发中积累的提示词工程经验完整梳理一遍。从最基础的参数调优,到思维链、ReAct、APE这些进阶技巧,再到实际项目里怎么组合使用、怎么排查问题,我都会掰开揉碎讲清楚。适合谁看?如果你正在做AI应用开发、智能体搭建、或者只是想让日常用大模型的效率翻倍,这篇内容应该能给你不少可以直接抄作业的东西。我不打算写成教科书,而是按照一个从业者真实的踩坑顺序来组织,想到哪讲到哪,但保证每个环节都有可复现的操作细节。
先说一下我的核心观点:提示词工程的上限不取决于你背了多少模板,而取决于你对模型行为模式的理解深度。参数是旋钮,思维技巧是方向盘,两者配合才能把车开稳。下面我从整体设计思路开始拆。
2. 提示词工程的底层逻辑与整体设计思路
2.1 把提示词当成“接口协议”而不是“聊天内容”
很多人写提示词的习惯是把它当成跟朋友发微信,想到什么说什么。但在工程场景下,提示词本质上是人和模型之间的一份接口协议。协议的特点是:输入格式明确、输出格式可预期、边界条件清晰、异常情况有兜底。
我举个实际例子。之前做一个合同信息抽取的功能,最初的提示词是“请从下面的合同文本中提取甲方、乙方、金额、签署日期”。看起来没问题,但实际跑下来发现:有的合同甲方是个人,模型就只返回姓名不返回身份证号;有的金额带大写,模型有时候转有时候不转;日期格式更是五花八门。后来我把提示词改成了一份“协议”:
你是一个合同信息抽取引擎。请严格按照以下JSON Schema输出,不要输出任何额外解释。 Schema: { "party_a": {"name": "string", "id_number": "string or null"}, "party_b": {"name": "string", "id_number": "string or null"}, "amount": {"numeric": "number", "chinese": "string"}, "sign_date": "YYYY-MM-DD" } 规则: 1. 如果某字段在原文中不存在,填null,不要编造。 2. 金额同时输出数字和大写中文。 3. 日期统一转为YYYY-MM-DD格式。改完之后,抽取准确率从大概六成提升到了九成以上。核心差别就在于:前者是“请求”,后者是“契约”。请求依赖模型的理解和善意,契约则通过结构化约束把模型的输出空间压缩到可控范围内。
2.2 参数调优:Temperature、Top-p、Max Tokens到底怎么配
参数调优是提示词工程里最容易被忽视、但性价比最高的环节。很多人调了半天提示词,其实问题出在参数上。我先把几个核心参数的作用和我的常用配置说清楚。
Temperature(温度)控制输出的随机性。值越低,模型越倾向于选择概率最高的词,输出越确定;值越高,输出越发散有创意。我的经验配置是:
| 场景 | Temperature | 理由 |
|---|---|---|
| 信息抽取、分类、格式化输出 | 0 ~ 0.2 | 需要稳定可复现,不能有创意 |
| 代码生成、逻辑推理 | 0.2 ~ 0.5 | 需要一定灵活性但逻辑要严谨 |
| 文案创作、头脑风暴 | 0.7 ~ 1.0 | 需要多样性和创意 |
| 角色扮演、故事续写 | 0.8 ~ 1.2 | 需要丰富的表达变化 |
Top-p(核采样)是另一种控制随机性的方式,它从累积概率达到p的最小词集合中采样。Temperature和Top-p一般不建议同时大调,我的习惯是固定一个调另一个。大多数场景下我把Top-p设在0.9到0.95之间,配合较低的Temperature使用。
Max Tokens(最大输出长度)这个参数看起来简单,但坑不少。设太小会导致输出被截断,设太大又浪费成本。我的做法是根据任务类型预估:信息抽取类通常200-500 tokens够用,代码生成类留2000-4000,长文写作类按需给到8000以上。关键技巧是:在提示词里明确要求模型“简洁输出”或“详细输出”,比单纯调Max Tokens更有效。
还有一个容易被忽略的参数是频率惩罚(Frequency Penalty)和存在惩罚(Presence Penalty)。前者降低重复词的概率,后者鼓励模型引入新话题。在需要模型列举多个不同观点时,我会把Presence Penalty调到0.3-0.5;在需要模型严格按格式输出时,两个都设为0。
2.3 提示词的结构化设计:角色、任务、约束、示例四件套
我总结了一个比较通用的提示词结构框架,叫“四件套”:角色设定、任务描述、约束条件、示例演示。这四个部分不是必须全有,但有了之后提示词的稳定性会明显提升。
角色设定解决的是“模型以什么身份说话”的问题。比如“你是一个资深法律顾问”和“你是一个法律助手”,前者会让模型在措辞上更严谨、更倾向于给出免责声明。任务描述要具体到可执行,避免“帮我分析一下”这种模糊表述,改成“请从以下三个维度分析:合规风险、财务影响、执行难度”。
约束条件是很多人会漏掉的部分。它包括:输出格式约束、长度约束、语气约束、禁止事项。我一般会把约束写成编号列表,因为模型对编号列表的遵循度明显高于段落描述。示例演示就是Few-shot,给一两个输入输出样例,模型就能快速对齐你的预期格式。
实操心得:约束条件里写“不要做什么”比写“要做什么”有时候更有效。比如“不要使用专业术语”比“使用通俗语言”更能让模型收敛。
3. 从思维链到ReAct:高级思维技巧的实战拆解
3.1 思维链(Chain of Thought):让模型“把草稿纸用起来”
思维链的核心思想很简单:让模型在给出最终答案之前,先展示推理过程。这背后的原理是,大模型是逐token生成的,当它先输出了推理步骤,这些步骤就成为了后续生成的上下文,相当于给模型提供了“草稿纸”。
最基础的用法是在提示词末尾加一句“让我们一步步思考”。但实际用下来,这句话的效果不稳定,有时候模型会敷衍地写几步就跳到结论。我改进后的做法是显式定义推理步骤:
请按以下步骤解决这个问题: 第一步:识别题目中的已知条件和未知量。 第二步:确定适用的公式或规则。 第三步:代入数值进行计算,展示每一步的中间结果。 第四步:检查结果是否合理,给出最终答案。这种结构化思维链在数学题、逻辑推理、多条件决策场景下效果非常明显。我实测过一个供应链补货决策的场景,不加思维链时模型经常忽略安全库存这个条件,加了结构化步骤后,遗漏率从30%降到了5%以下。
但思维链也有代价:输出token变多,延迟增加,成本上升。所以我的建议是只在复杂推理任务上用,简单任务不要滥用。另外,思维链的输出对用户来说可能太冗长,工程上可以要求模型把推理过程放在特定标签里,前端只展示最终答案。
3.2 ReAct模式:推理与行动的交替循环
ReAct(Reasoning + Acting)是我在做智能体时用得最多的模式。它的核心是让模型在“思考”和“行动”之间交替:先推理当前该做什么,然后调用工具执行,观察结果,再推理下一步。
一个典型的ReAct提示词结构是这样的:
你可以使用以下工具: - search(query): 搜索信息 - calculator(expression): 计算数学表达式 - lookup(term): 查询术语定义 请按以下格式回应: Thought: 你的推理过程 Action: 工具名(参数) Observation: 工具返回结果 ...(重复Thought/Action/Observation) Thought: 我现在知道最终答案了 Final Answer: 最终答案这个模式的关键在于Observation必须由外部系统真实注入,而不是让模型自己编。我早期犯过一个错误,让模型自己模拟工具返回结果,结果它编了一堆看起来合理但完全错误的数据。后来改成由代码层拦截Action、执行真实工具、把结果拼回提示词,整个智能体的可靠性才上来。
ReAct的工程实现有几个细节要注意:一是要设置最大循环次数,防止模型陷入死循环;二是要对工具调用做参数校验,模型有时候会传错参数类型;三是要在提示词里明确告诉模型“如果工具返回错误,你应该尝试其他方法或直接告知用户”。
3.3 APE(Automatic Prompt Engineering):让模型自己优化提示词
APE这个思路很有意思:与其人工反复调提示词,不如让模型自己生成和评估提示词。基本流程是:给定一个任务和少量样例,让模型生成多个候选提示词,然后在这些样例上评估每个提示词的效果,选择最好的那个,再基于它做迭代优化。
我实际用下来的感受是,APE在格式转换、分类、信息抽取这类有明确评估标准的任务上效果不错,但在创意类任务上帮助有限。一个简化的APE流程可以这样实现:
# 伪代码示意 def ape_optimize(task_description, examples, iterations=3): # 第一步:让模型生成候选提示词 candidates = llm.generate( f"任务:{task_description}\n" f"请生成5个不同的提示词来完成这个任务,每个提示词用---分隔。" ) best_prompt = None best_score = 0 # 第二步:评估每个候选 for prompt in candidates.split("---"): score = evaluate(prompt, examples) if score > best_score: best_score = score best_prompt = prompt # 第三步:基于最佳提示词迭代优化 for i in range(iterations): improved = llm.generate( f"当前提示词:{best_prompt}\n" f"它在以下样例上失败了:{get_failures(best_prompt, examples)}\n" f"请改进这个提示词。" ) if evaluate(improved, examples) > best_score: best_prompt = improved return best_promptAPE的价值在于把提示词优化从“手工活”变成了“可迭代的工程流程”。但它也有局限:评估标准的设计本身就需要人工介入,而且模型生成的提示词有时候会过拟合到样例上。我的建议是把APE当作辅助工具,最终还是要人工审核和调整。
3.4 三种技巧的选型对比
| 技巧 | 适用场景 | 优点 | 缺点 | 我的使用频率 |
|---|---|---|---|---|
| 思维链 | 数学推理、逻辑判断、多条件决策 | 提升推理准确率,过程可解释 | 输出变长,成本增加 | 高 |
| ReAct | 需要调用外部工具的智能体 | 推理与行动结合,可处理复杂任务 | 工程实现复杂,需要工具层配合 | 高 |
| APE | 格式转换、分类、抽取等有明确评估标准的任务 | 自动化优化,减少人工 | 依赖评估标准,可能过拟合 | 中 |
4. 完整实操:从零搭建一个提示词工程工作流
4.1 需求拆解与提示词架构设计
假设我们要做一个“技术文档问答助手”,用户输入一个问题,助手从给定的技术文档中找答案并引用来源。这个需求拆解下来包含几个子任务:意图识别(判断用户是在问文档内容还是闲聊)、文档检索、答案生成、引用标注。
我的提示词架构会分成三层:系统提示词(System Prompt)定义助手的整体行为规范,任务提示词(Task Prompt)针对每个子任务单独设计,上下文注入(Context Injection)把检索到的文档片段拼进提示词。这种分层设计的好处是每层可以独立迭代,不会牵一发而动全身。
系统提示词我一般写得比较简洁但约束明确:
你是一个技术文档问答助手。你的职责是: 1. 只基于提供的文档片段回答问题。 2. 如果文档中没有相关信息,明确告知用户“文档中未找到相关内容”。 3. 每个回答必须标注信息来源,格式为[文档名-章节]。 4. 不要编造任何文档中不存在的信息。任务提示词则根据具体环节变化。比如意图识别的提示词会要求模型输出一个分类标签,答案生成的提示词会要求模型按“答案+依据+引用”的结构输出。
4.2 参数配置与迭代调优记录
在这个问答助手的开发过程中,我记录了几轮关键的参数调整。第一轮用的是默认参数(Temperature=0.7),结果模型经常在文档没有相关内容时强行编造答案。把Temperature降到0.1后,编造情况明显减少,但回答变得很死板,有时候文档里有答案它也说没有。
后来我发现问题不在Temperature,而在提示词里“只基于提供的文档片段”这个约束不够强。改成“你必须先在文档片段中定位到相关句子,然后基于该句子回答。如果找不到相关句子,直接回复未找到”之后,配合Temperature=0.2,效果就稳定了。
这个经历让我总结出一条经验:参数调优和提示词优化要交替进行,不要指望一次调好。我的习惯是先用默认参数跑一批测试用例,记录失败案例,分析是参数问题还是提示词问题,然后针对性调整,再跑测试,循环往复。
4.3 效果评估与迭代闭环
提示词工程如果没有评估环节,就是盲人摸象。我一般会构建一个小型测试集,包含20-50个典型输入和期望输出,每次修改提示词或参数后都跑一遍,记录准确率、格式合规率、平均输出长度等指标。
对于问答助手这个场景,我的评估维度包括:答案正确性(人工标注)、引用准确性(是否引用了真正包含答案的文档)、拒答合理性(文档没有答案时是否正确拒答)。这三个维度分别对应不同的提示词模块,哪个指标下降就去检查对应的提示词部分。
实操心得:测试集不要太大,20个用例就能发现大部分问题。关键是用例要有代表性,覆盖正常情况、边界情况和异常情况。我一般会从真实用户日志里采样,比凭空构造的用例有效得多。
5. 常见问题与排查技巧实录
5.1 模型不遵循格式要求怎么办
这是最高频的问题。我排查的顺序是:先检查提示词里格式要求是否足够具体(最好给JSON Schema或模板),再检查是否有示例演示,然后检查Temperature是否过高,最后检查Max Tokens是否够用(有时候格式没输出完就被截断了)。
如果都排查了还是不行,我的杀手锏是在提示词末尾加一句“请确保输出是合法的JSON格式,不要包含任何其他文字”,并且在代码层做JSON解析校验,解析失败就重试。重试时把上一次的失败输出也拼进提示词,告诉模型“你上次的输出格式有误,请修正”。
5.2 模型编造信息怎么破
编造信息(幻觉)的根源是模型在不确定时倾向于“猜一个合理答案”。我的应对策略分三层:提示词层明确要求“不确定时说不确定”,参数层降低Temperature,工程层加事实校验环节。
在RAG场景下,我还会在提示词里要求模型“先引用原文,再给出答案”,这样即使它想编,也得先过引用这一关。如果引用是假的,代码层可以检测引用是否真实存在于文档中,从而拦截幻觉输出。
5.3 提示词太长导致效果下降
提示词不是越长越好。当提示词超过一定长度后,模型对中间部分的注意力会下降,这就是所谓的“迷失在中间”现象。我的做法是:把最重要的约束放在提示词的开头和结尾,中间放示例和背景信息。如果提示词实在太长,就考虑拆分成多轮对话,或者用检索的方式动态注入相关内容。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决方案 |
|---|---|---|---|
| 输出格式不对 | 约束不具体/无示例/Temperature过高 | 检查格式描述和示例 | 加JSON Schema,给示例,降Temperature |
| 编造信息 | 提示词未要求拒答/参数随机性高 | 检查是否有“不确定时拒答”约束 | 加拒答约束,降Temperature,加事实校验 |
| 输出被截断 | Max Tokens不足 | 检查输出长度 | 增大Max Tokens,或要求简洁输出 |
| 忽略部分指令 | 提示词过长/指令不突出 | 检查指令位置和数量 | 精简提示词,关键指令放首尾 |
| 推理错误 | 缺少思维链引导 | 检查是否要求逐步推理 | 加结构化思维链步骤 |
| 工具调用参数错误 | 工具描述不清晰 | 检查工具定义 | 明确参数类型和格式,加参数校验 |
5.5 我踩过的三个典型坑
第一个坑是过度依赖Few-shot示例。早期我总觉得示例越多越好,一个提示词里塞了十几个示例,结果模型开始机械模仿示例的表面形式,遇到稍微不同的输入就懵了。后来我把示例精简到2-3个,并且刻意选择有差异性的示例,效果反而更好。
第二个坑是忽视系统提示词和用户提示词的边界。有一次我把所有约束都写在用户消息里,结果多轮对话时模型很快就“忘记”了前面的约束。后来我把长期有效的约束放到系统提示词里,用户消息只放当前任务相关内容,稳定性大幅提升。
第三个坑是没有做输出解析的容错。模型输出JSON时偶尔会多一个逗号或者少一个引号,早期我的代码直接崩溃。后来加了重试机制和宽松解析,整个系统的可用性才达标。
6. 提示词工程的工程化思考与个人体会
6.1 提示词版本管理:别再用txt文件了
当项目里的提示词超过十个之后,用txt文件管理就是灾难。我现在的做法是把提示词当成代码来管理:存在数据库或配置中心里,每个提示词有版本号、变更记录、关联的测试结果。每次修改都走一次评估流程,效果不达标就回滚。
这样做的好处是,当线上效果波动时,可以快速定位是哪个提示词的哪个版本出了问题。我甚至见过团队把提示词和A/B测试平台打通,不同用户走不同版本的提示词,用真实数据来评估效果。
6.2 提示词与上下文工程的关系
最近“上下文工程”这个词很火,我的理解是:提示词工程是上下文工程的一个子集。上下文工程考虑的是模型在某一时刻能看到的全部信息,包括系统提示词、对话历史、检索到的文档、工具返回结果等。提示词工程更聚焦于如何设计和优化这些信息中的指令部分。
在实际项目中,两者是配合使用的。比如RAG场景下,检索策略决定了哪些文档片段进入上下文,这是上下文工程的范畴;而如何把这些片段组织成模型容易理解的格式,这是提示词工程的范畴。我的经验是,上下文工程决定上限,提示词工程决定下限。
6.3 给刚入行的朋友几条实在建议
第一,先把手头的模型参数摸熟。不同模型对Temperature的敏感度不一样,有的模型0.3就很发散,有的模型0.7还很稳定。花半天时间做一组参数扫描实验,比看十篇教程都有用。
第二,建立自己的提示词片段库。把常用的角色设定、格式约束、思维链模板整理成可复用的片段,新任务来了直接拼装,效率会高很多。我现在的片段库里有几十个经过验证的模块,覆盖了大部分常见场景。
第三,不要追求一步到位。提示词工程是一个迭代过程,第一版能跑通就行,然后根据bad case逐步优化。我见过太多人想一次性写出完美提示词,结果卡在细节上迟迟不交付。
第四,多观察模型的失败模式。模型犯错的方式往往是有规律的,比如总是忽略某个条件、总是把日期格式搞错。找到这些规律后,针对性地在提示词里加约束,比泛泛地写“请仔细回答”有效得多。
6.4 后续可以继续深入的方向
提示词工程这个领域变化很快,我觉得有几个方向值得持续关注:一是自动化提示词优化,APE只是一个开始,未来可能会有更成熟的工具链;二是多模态提示词,随着模型能处理图片、音频,提示词的设计空间会更大;三是提示词安全,如何防止提示词注入攻击,这在面向用户的产品里会越来越重要。
我自己的计划是把ReAct模式在更多业务场景里落地,同时探索一下提示词和微调的结合——有些场景下,少量微调配合精简提示词,可能比纯提示词方案更稳定、更经济。这条路我还在摸索,有新的心得再跟大家分享。