1. 为什么你的大模型总像"听不懂人话"
做提示工程这件事,大多数人一开始都误判了它的难度。我见过不少开发者,第一反应是"提示词嘛,不就是把需求写清楚吗",结果真跑到生产环境里一测,发现模型要么答非所问,要么一本正经地编数据,要么干脆跟你绕圈子。这时候才意识到,提示工程根本不是"写清楚需求"这么简单,它本质上是在做一件事:把人类的意图,翻译成模型概率分布能够理解的约束条件。
我自己入坑这个方向,是因为踩过一个特别典型的坑。之前接了一个内部知识库问答的需求,最开始给的提示词就是很朴素的一段:"请根据以下文档回答用户问题。"模型跑起来倒是快,但返回的结果经常带着文档里没写的内容,甚至把不同章节的信息强行拼在一起,看起来合理,实际上全是错的。后来我仔细复盘,发现问题不在模型,而在我的提示词根本没有给模型划定"只能使用文档内容"的边界,也没告诉它"不知道就直说"。
这就是提示工程的核心矛盾:大模型本质上是一个概率系统,它不是在"查找答案",而是在"生成最像答案的文本"。你如果不把约束、边界、格式、语气、知识来源全部显式写清楚,它就会按照训练数据里学到的统计规律自由发挥。所以提示工程要解决的第一问题,不是"说什么",而是"怎么说才能让模型少一点自作主张的自由度"。
这篇文章我想把我自己在实际项目里总结的提示词设计和性能优化经验完整写出来。不是那种泛泛的"模板大全",而是从问题诊断到方案落地的一条完整路径。内容包括五部分:提示词结构怎么拆才稳定、少样本和思维链到底怎么用才有效、上下文管理和缓存怎么省token、推理参数怎么调才能兼顾质量和速度,以及一套我自己常用的测试和复盘方法。适合刚接触大模型应用开发的工程师,也适合已经在做RAG或者Agent项目、但提示词效果一直不太稳定的朋友。
另外说一句,提示工程不是一次性工作。同样的提示词,换个模型、换个场景、甚至换个数据分布,效果可能天差地别。所以这篇文章里讲的每一条技巧,本质上都不是"标准答案",而是一套你可以照着做、再根据自己业务调优的方法框架。
2. 提示词设计:从"说人话"到"定义边界"
2.1 提示词的三层结构:指令、约束、输出格式
先说一个我验证过很多次的基本结构。一段稳定的提示词,至少应该包含三层信息:指令、约束条件、输出格式。很多人的提示词效果不稳定,问题就出在只写了指令,漏了后两层。
指令层是最直观的,告诉模型"你要做什么"。但这层有个容易被忽略的点:指令的主语最好是你期望的角色,而不是泛泛的"你"。比如"作为客服系统的一线支持人员,请先判断用户情绪,再给出解决方案",就比"请解决用户问题"要稳定得多。角色设定的本质是激活模型在特定语域下的先验知识,相当于给概率分布加了一个"领域滤镜"。
约束层是大多数人最容易遗漏的。约束层要回答四个问题:能做什么、不能做什么、知识来源边界是什么、遇到不确定时怎么处理。举个例子,我之前做法律文书摘要项目时,如果只写"请摘要以下内容",模型偶尔会把原文中没写明的推测也写进去。后来我在约束里明确写了一条:"仅基于给定文本进行摘要,禁止添加原文中未出现的事实性内容;若信息不足,在摘要末尾标注信息缺失项。"加了这一句之后,幻觉率明显下降。注意,约束不是越多越好,而是越"可验证"越好。像"请确保内容准确"这种话就是废话,模型根本不知道怎么执行;"禁止添加原文中未出现的事实"这种是可执行的。
输出格式层决定了结果能不能直接被下游程序消费。我推荐的做法是,在提示词里给一个具体的格式示例,而不是只描述格式。比如你要JSON输出,与其写"以JSON格式返回",不如写:
{"summary": "一句话摘要", "key_points": ["要点1", "要点2"], "confidence": 0.85}模型看到具体样例后,生成的结果几乎不会跑偏。这个技巧的原理很简单:大模型在做续写时,会强烈倾向于模仿上下文里出现的格式模式,尤其是当这个模式出现在指令末尾时。
2.2 少样本示例:不要给"标准答案",要给"思维过程"
少样本学习(few-shot)是提示工程里提升效果最直接的手段,但很多人用错了方法。最常见的错误是:示例里只给问题和答案,不给中间的推理路径。这会导致模型学到的是"答案风格",而不是"解题方法"。
举个例子,你在做用户评论情感分类,提示词里给了几个示例:
评论:"快递太慢了" → 负面 评论:"包装很结实" → 正面模型大概率能处理好这种简单例子,但遇到"东西还行,就是物流等得人心态崩了"这种混合情绪时,就容易判断错。因为你的示例没有教它怎么处理"混合信息"。
正确做法是在示例里加上简短的推理:
评论:"东西还行,就是物流等得人心态崩了" 推理:商品质量用词"还行"偏中性,物流体验用词"心态崩了"明显负面。 结论:负面为什么这样有效?因为CoT(思维链)的原理,是让模型把隐含的判断步骤显式化。你在示例里给它看到"先拆分评价维度,再逐个判断,最后综合",它就倾向于在推理时复现这个过程。少样本示例的数量,我实测下来3到5个是最佳区间,太少学不到模式,太多会挤占上下文窗口,而且超过一定数量后收益急剧递减。
还有一点:示例的选择要"覆盖边界情况",而不是"覆盖常见情况"。大部分人的示例都是挑最典型的情况给,其实效果一般。真正有作用的是把容易混淆的边界情况放进去,比如分类任务中两个类别都很接近的例子,或者在总结任务中一段内容包含两个冲突观点的例子。这样模型才学得到"判别边界",而不是只学会"照猫画虎"。
2.3 思维链与"强行推理":让模型慢下来
思维链(Chain-of-Thought)这个东西,刚火起来的时候很多人把它当作万能钥匙,但其实它只对一部分任务有效。我自己的经验是:凡是需要多步推理、数值计算、逻辑比对的场景,强制模型输出推理过程,效果提升非常明显;但凡是事实性问答、直接检索就能回答的场景,思维链不仅没有帮助,反而会引入多余的错误。
这里有一个判断标准:如果回答这个问题需要三个以上的中间步骤,就应该用思维链;如果一步就能从上下文中找到答案,直接给答案即可。
实际使用中,"让模型慢下来"需要写进提示词里。有两种常见写法,一种是直接指示:"请一步一步思考,并把每一步的推理过程写在最终答案之前。"一种是给一个推理模板,比如:
第一步:理解问题,列出已知信息。 第二步:判断每个已知信息与问题的相关性。 第三步:结合上下文推导结论。第二种写法更稳,因为它把推理结构固定下来了,模型不容易跑偏。但要注意,推理过程会显著增加输出token数,如果你的接口按token计费,成本会上升。所以更经济的做法是:只在需要复杂推理的场景下启用思维链,简单场景直接用短提示。
另外还有一个进阶组合:把思维链和自洽性采样(self-consistency)结合起来。做法是让模型用思维链跑多次推理,生成多个答案,然后对答案投票取多数。这个方法在我的测评里,可以把数学推理类的准确率再提升5到10个百分点,但代价是多次调用接口,latency会翻倍。生产环境里到底用不用,要结合你的业务容忍度来权衡。
3. 上下文管理与RAG优化:真正省token的大头
3.1 上下文窗口不是越大越好
很多人的第一反应是:模型上下文窗口那么大,我把所有资料一股脑塞进去不就行了?这个思路在demo阶段没问题,到了生产环境就是灾难。原因有三个:成本暴增、注意力稀释、幻觉率上升。
先说成本和延迟。LLM的推理时间基本是随输入长度线性增长的,输入翻一倍,响应时间也差不多翻一倍。如果你每次请求都塞进去几万字,用户等到的就是几十秒的转圈。成本方面,按常见的商业API定价计算,一个日均上万次调用的应用,输入长了之后费用可能是原来的五到十倍。
再说注意力稀释。注意力机制的特性决定了,模型对长文本中间的关注度会远低于开头和结尾。我做过一个实验:把一段关键信息放在5000字文档的中间位置,模型漏掉它的概率大概是放在开头和结尾时的三倍。所以"丢进去就行"是最不经济也不太可靠的做法。
正确的做法是三条原则:长度控制、关键前置、动态裁剪。长度控制是最好理解的,不是资料越长越有用,而是信息密度越高越有用。关键前置的意思是,把用户问题里最重要的实体和意图放在上下文最前面,让模型一开始就看到。动态裁剪的意思是,每次请求前根据用户问题的相关性,只选取最相关的几个片段拼入上下文,而不是把整个知识库都搬进去。
3.2 RAG的召回质量决定提示词上限
RAG(检索增强生成)本身不是提示工程,但RAG的输出质量直接决定了后续提示词能发挥多大作用。很多团队花了大量精力调提示词,效果就是不理想,最后发现是召回阶段把一堆不相关的东西塞给了模型,提示词再怎么写都救不回来。
我自己踩过的一个典型问题:知识库里有同一主题的两个版本文档,旧版内容有很多失效信息。检索阶段如果把两个版本都召回了,模型就会非常纠结,最后输出的内容新旧混杂,用户一看就不信。解决方式是在检索阶段加了时间过滤器,只取最新版本;同时把"文档版本"作为一个过滤字段参与排序。当然也有纯提示词层面的缓解方式,就是在提示词里加一句"如果多个来源信息冲突,优先采用最新文档的内容"。但建议还是从检索源头上解决问题,提示词只是兜底。
召回结果不是越多越好。我实测下来,给模型3到5个高相关片段的效果,比给10个中等相关片段好得多。信息冗余会导致模型"选择困难",还会增加幻觉概率。所以RAG系统里,召回数量这个参数值得认真调,默认值大多数时候偏多。
还有一点,检索到的片段在拼入提示词之前,最好做一遍"去重合并"。如果多个片段内容重叠,建议合并或只保留信息最完整的一个。这个操作能让提示词更紧凑,也能减少模型被重复信息带偏的可能。
3.3 上下文缓存与KV Cache:两个层面的优化
上下文优化有两个层面,一个是应用层,一个是系统层,经常被人混为一谈。应用层的"缓存"指的是:如果多条用户请求都共享同一个长系统提示词,可以把这部分内容缓存起来,避免每次重复计算token。
这个在工程上有一个很成熟的方案,就是提示词前缀复用。比如你把系统级提示词设成固定的几百字前缀,然后动态拼接用户的个性化内容,这样只要是落在相同前缀命中的请求,就不需要从头计算。当然,这个方案的收益取决于具体的服务端实现是否支持KV Cache复用。如果你的模型是自己部署的,可以从推理引擎层面优化,比如用支持前缀缓存的技术方案,实测在这种场景下TTFT(首个token延迟)能明显下降,吞吐量也能翻上去。
另一个角度是压缩提示词。有一条经验法则:每减少1000个token的输入,消耗在注意力计算上的时间能省掉相当可观的占比。所以"少写废话"本身就是一种性能优化。怎么压缩?把不必要的修饰语删掉,把重复的背景说明合并,把示例的数量控制在必要的最小值。我见过有人在系统提示词里写了一大段公司介绍,跟回答任务一点关系都没有,那就纯属浪费资源和钱。
4. 推理参数调优:温度、Top-p与max_tokens的取舍
4.1 温度参数:从"确定性"到"发散性"是一个连续谱
关于temperature(温度)这个参数,很多人的理解是"调高更有创造性,调低更准确",这个说法没错,但太粗糙了。温度本质上是控制采样时概率分布的锐利程度。温度越低,高概率词被选中的倾向越强,输出越确定;温度越高,低概率词也有机会被选中,输出越多样甚至越离谱。
实际项目中,我给不同场景用的温度完全不一样:
- 事实问答、代码生成、数据提取:0到0.3。这个区间里模型几乎不会发散,能稳定输出。
- 内容摘要、文案改写:0.3到0.5。轻微的变化有助于语言表达更自然,但不会引入事实错误。
- 创意写作、头脑风暴:0.7到0.9。这个区间模型才会给出一些你没想到的视角。
- 1.5以上的温度,我基本没有在生产环境用过,除非你真的想要那种胡言乱语的效果。
有一个细节值得注意:temperature和top_p不要同时大幅调高。两者都高的时候,输出几乎会失控。我推荐的做法是,优先确定temperature,top_p固定在一个0.9到0.95的区间,只在模型重复输出时再单独调整。
4.2 max_tokens:默认值往往不是最优解
max_tokens(最大输出长度)这个参数,看起来简单,其实影响很大。如果你把max_tokens设置得远大于实际需要的输出长度,会有两个隐患:一是模型在某些场景下会"被迫"继续生成无意义的内容来填充,比如重复生成结束语;二是接口的返回延迟会显著增加,因为生成是逐token进行的,max_tokens设得越大,最坏情况下的等待时间越长。
我的做法是:先统计历史请求的token分布,然后给每种任务类型单独设置max_tokens。比如短问答任务设到256就够,摘要任务设到512,只有长文生成任务才设到2048以上。你可以用接口返回的usage字段来统计实际输出token,连续记录几天,基本就能确定每个任务类型的合理值。
还有一个技巧:max_tokens本身可以作为"格式约束"来用。比如要求模型返回一个列表,你就把max_tokens设短一点,比如200,模型就知道只能输出轻量内容,反而会更聚焦于关键信息。
4.3 停止词与JSON模式:比你想的更可靠
停止词(stop)是很多人在调优时忽视的参数。它的作用是告诉模型"生成到这个标记就停下来"。比如你要求模型只输出JSON,不输出任何解释性文字,那你可以在提示词里写好格式要求,同时配置stop参数在JSON闭合结束的位置。
不过更值得推荐的是直接用模型服务商提供的JSON模式或结构化输出能力。如果平台支持,优先用;如果不支持,再用"提示词+停止词"的组合来约束输出。我实际测试下来,"提示词+停止词"的效果也很稳定,前提是你在提示词里给出清晰的结束标记。比如要求模型在输出结束时输出一个固定的标记符,比如"END"。
这里有一个坑:有些模型会在JSON之外额外生成一段解释,你如果直接解析会报错。解决方法是既在提示词里强调"只输出JSON,不要输出任何其他内容",又在代码里做一层容错处理,比如用正则提取出第一个完整的JSON对象,再交给解析器。
5. 效果评估与回归测试:提示词的"单元测试"
5.1 为什么"感觉变好了"不够
提示词调优最大的陷阱是"凭感觉判断效果"。今天你改了一句提示词,测了5个case觉得"诶,好像更顺了",就上线了。结果第二天线上反馈问题变多,你又改回去。这种反复横跳的状态,几乎每个做提示工程的朋友都经历过。原因很简单:没有建立一套可量化的评估集。
我的建议是,每个使用提示词的项目都维护一个评测集,哪怕刚开始只有20条用例。评测集里的内容要覆盖三类:典型场景、边界场景、容易出错的场景。典型场景用来保证主流程正常,边界场景用来保证模型不会在边缘情形下崩溃,易错场景用来防止回归。每次修改提示词,都跑一遍这20到30条用例,对比修改前后的输出质量。
5.2 怎么给输出打分
评估输出质量,分为确定性任务和生成性任务两种情况。
对于确定性任务,比如分类、提取、格式化输出,评估非常简单:直接对比模型输出和预期答案是否一致,或者用字段级的准确率来衡量。这一类的目标是准确率越高越好,可以用自动化脚本跑回归。
对于生成性任务,比如摘要、改写、问答,评估要复杂一些。我推荐一个组合打分法:用四把尺子独立打分——信息完整性(关键信息是否都覆盖)、事实准确性(是否有幻觉)、逻辑连贯性(是否前后矛盾)、格式规范性(是否满足要求)。每项0到2分,最后算总分。这个方法不用太精确,目的是让每次修改有一个可比较的相对变化。等评测集足够大之后,也可以接入大模型作为自动裁判,让模型按这四把尺子给输出打分,人工只需要抽检。
5.3 一个可落地的调优工作流
经过多轮实践,我自己形成了一个相对固定的调优流程,分享出来供你参考:
- 明确任务类型和评测标准。先把"什么算好"定义出来,再动手改提示词。
- 建立基线。用当前的提示词跑一遍评测集,把分数记录下来。
- 每次只改一个变量。要么改结构、要么改示例、要么改参数,不要同时改多个,不然出了问题你根本不知道是哪个改动导致的。
- 跑评测集对比分数。分数的变化方向比绝对值更重要,它可以告诉你这个改动是正向还是负向的。
- 保留历史版本。每次调优都记录一份提示词版本,方便回滚。用简单的方式管理版本也可以,比如每次修改了就在备注里写一句改动原因。
这套流程看起来很简单,但坚持下来,你会少走很多弯路。我见过太多人把大量时间花在无限改提示词上,但因为没有评估机制,改来改去都是原地打转。
6. 避坑实录:我踩过的那些提示词与性能优化的坑
6.1 长篇上下文中的"幻觉重灾区"
先说一个费了我最多时间的坑。之前做一个合同审查项目,需要把整份合同作为上下文传进去。合同有几十页,经常超过模型的上下文限制,于是我用分段处理的方式,把每段分别做审查再汇总。结果发现,分段审查时模型的判断标准很不一致——第一段要求修改违约金,第二段又说违约金合理,汇总阶段模型就糊涂了。
后来排查出两个问题。一是分段时切割的位置没有考虑语义完整性,把条款标题和内容拆开了;二是汇总阶段没有把"分段的判断依据"也喂给模型,只给了每段的结论,导致汇总模型看不到推理过程。解决方案是把切割逻辑改成按合同章节切分,同时在分段提示词中把每段的输出格式固定为"条款号+判断结果+推理依据",最后汇总时把推理依据一并纳入上下文。改完以后,审查结果的一致性明显提升。
这个坑给了一个通用教训:任何涉及长文本拆分的任务,切割边界要优先照顾语义完整性,而不是字数均匀。宁可某段长一点,也要保证一个完整的语义单元不被拆散。
6.2 温度调太高导致的"创造性事故"
另一个经典案例是有一次做营销文案生成,为了追求"更有创意",我把temperature调到了0.9,结果模型生成的文案里出现了完全不存在的数据:"超80%用户推荐"。这其实不是模型的恶意行为,而是高温度下低概率词会被放大采样,模型在用"看起来合理但未经核实"的信息填充内容。
这一类问题的根源不只是参数设置,还在于提示词里没有明确约束"不得编造统计数据和事实信息"。后来我在营销文案的提示词里加了一条系统级约束:"若引用数据、事实、用户评价,必须使用上下文中提供的信息;未提供的信息一律不得添加。"同时把temperature调回0.5以下,才解决了。
教训是:参数调整要先于或至少同步调整提示词约束。你的提示词边界越窄,就越能承受较高的temperature;边界越宽,温度稍微一高就容易出事故。
6.3 输出解析时的半结构化容错
最后一个坑是关于输出解析。早期我做结构化输出时,直接按"模型输出就是合法JSON"来解析,结果生产环境里频繁出现解析失败。后来发现,模型虽然被要求输出JSON,但偶尔会在JSON前面加一句"好的,以下是您需要的JSON:"之类的话。原因是模型训练数据里有大量这种对话样本,它天然倾向于先给一句回复再输出结果。
解决方案有三层:提示词里明确强调"直接输出JSON,不要任何前缀或解释";停止词或后处理中去掉第一个"{"之前的所有内容;解析失败时做一次"修复重试",把错误信息反馈给模型,让它重新生成。这三层叠加之后,解析成功率基本可以达到99%以上。
这类问题本质上说明了一个道理:提示工程永远要跟工程容错配合使用,提示词再完善,也不能假设模型100%听话。写代码的时候,把模型当作一个"偶尔会不遵守指令的协作者",做好容错和重试,才是生产级系统该有的姿态。
7. 我个人实践中的几点体会
做到现在,我对提示工程最深的体会是:它不像写代码,不像写一篇有明确对错的文章,它更像是在跟一个能力很强但性格不稳定的同事打交道。你需要学会交代任务的时候既给目标,又给边界,还要给反馈机制。有时候同一个意思换个说法,对方理解就不一样;有时候你把前因后果讲清楚了,对方反而更怕你是在"设套"。这些经验,只能靠一次次实战去积累。
另外,一定要把提示词当作代码来看待,而不是"随手写的一句话"。我在自己的项目里,会把提示词当作一等公民来管理:版本化、评审、测试、灰度,一样不少。建议团队里至少有一名成员专门负责提示词版本管理和评测集的维护,这个投入带来的收益远比想象中要大。
我还想提醒一件事:不同模型的提示词偏好差异很大。很多人在某个模型上调好的提示词,换了模型后效果骤降,然后就怀疑是自己的问题,其实不一定是。不同模型的训练数据、对齐方式、指令遵循能力都不尽相同,换模型后重新调试是正常流程,不用惊慌。最好的方式是在选型阶段就把"提示词的可迁移性"作为一个评估维度。
最后说说这项能力的价值。现在大模型应用的门槛越来越低,会调用接口不算能力,真正拉开差距的是谁能设计出稳定的、可控的、可评估的提示词和推理流程。提示工程表面上看起来是"写话",实际上是在做"概率分布的引导、约束与兜底"。希望这篇文章能给你一个系统性的框架,在你自己调优的时候,不再靠玄学和感觉吃饭。