Prompt 删了一句无关紧要的话,三天后发现输出内容变差了:提示词版本管理指南很重要
- 一条正在给用户干活的 prompt 被顺手删掉一句「废话」,上线后没有任何东西报警,三天后靠一张客服工单才暴露。
- 1018 条打过分的 prompt 里,最差的一项是「有没有交代过遇到烂输入该怎么办」,均分只有 31.5 分,而它恰好是删改时最先被当成废话删掉的那部分。
- 治法是四道关口,其中一道能在上线前把这类改动直接拦回去。不买工具的话,git 加一个打分脚本就够。
删掉几句看着没用的话,上线后没有任何报警
有人在改一条正在给用户干活的 prompt。他看里面有几句话没什么用,就删掉了,顺带还省了几个 token。上线前顺手做的那种清理,谁都做过,不会多想一秒。上线之后,没报错,没告警,监控一切正常。三天后一张客服工单来了:用户拿到的回复含混、跑题,有时候还错得很自信。
这是 Reddit 的 r/PromptEngineering 版上一篇开发者复盘帖,讲的是他被这件事逼出来的一套 prompt 版本管理流程。只要你有 prompt 正在给用户干活,接了 API 的应用也好、每天自动跑的流水线也好,这就是你迟早会踩、而且踩了不知道的那个坑。
三天里,一条已经变差的 prompt 在服务真实流量,悄悄发出去一批更差的答案,中间信号是零。
prompt 变差为什么不报警,眼睛也看不出来
同样是改坏了,代码和 prompt 的差别在于:坏了之后有没有人告诉你。
| 代码改坏了 | prompt 改坏了 | |
|---|---|---|
| 当场发生什么 | 抛异常、编译不过、一跑就崩 | 照样返回一段像样的文本 |
| 程序收到什么 | 一个失败 | 一个正常结果 |
| 谁会告诉你 | 测试、编译器、监控告警 | 没有人 |
| 你什么时候知道 | 当场 | 三天后,靠用户投诉 |
差别的根子在于:prompt 是自然语言,你把它改得再残缺,模型也会尽力返回一串文本。它不报错,也不给任何失败信号。所以它不会突然坏掉,只会一次比一次差一点,直到「差一点点」攒够了,变成「客户开始察觉了」。
那上线前扫几个输出看看行不行?肉眼能抓住的只有显性错误:返回一串报错、格式整个乱掉、答非所问到一眼可见。质量滑坡抓不住,原因有两条,输出看上去仍然是一个正经答案,只是更差的那个;而你自己是带着预期去看的,知道这条 prompt 该说什么,读到的每句都往那个方向对号入座。真正的滑坡要等大量真实用户花式提问才露得出来,而那时候露出来的形式就是工单。
1018 条 prompt 打分,应付意外输入这项最低
这不是一个人手滑。1018 条 prompt 打过分之后,同一个毛病到处都是。打分满分 100,拆成四项看,这四项本身就是一份「一条 prompt 可能烂在哪」的清单:
| 这一项 | 要求什么 | 典型翻车 |
|---|---|---|
| 清晰度 Clarity | 这个任务只有一种合理解读,模型不用猜 | 动词含糊:「帮我处理一下」「改进一下这个」 |
| 具体度 Specificity | 对输出的要求要能衡量,不能只给形容词,别让模型自己猜「什么算做完了」 | 「写一段简洁的摘要」,而不是「用大白话写一段三句话的摘要」 |
| 结构 Structure | 指令按逻辑顺序排:角色在前、背景第二、任务第三、格式最后 | 格式要求埋在任务后面、角色压根没写、约束条件散落各处 |
| 健壮度 Robustness | 对最可能出岔子的那几种情况,写死了该怎么办 | prompt 默认用户会给干净规整的输入,而真实用户什么都往里塞 |
四项里垫底的是最后一项,健壮度英文原词 Robustness。它问的是:这条 prompt 有没有交代过「遇到不正常的输入该怎么办」,用户没说清需求返回什么、输入是空的返回什么、格式完全不对返回什么。这几种情况要一个个点名写死动作,笼统一句「请妥善处理」不算。,也就是这条 prompt 有没有交代过「遇到不正常的输入该怎么办」。1018 条的均分只有 31.5 分。
应付意外的指令平时不触发,也测不出来
为什么偏偏是这一项最差?因为这一项写的东西,平时永远轮不到它出场。
这一项写的是意外情况各自该返回什么。官方示例里明写的那条是「用户没说清要什么」,同类的还有输入是空的、格式完全不对。而你自己试的时候,喂进去的都是规规矩矩的内容,这三种情况一次都碰不上。
于是给 prompt 减肥的时候,它们是第一批被盯上的:占着 token,又从来没见它起过作用。删掉,prompt 立刻短一截,测一遍,一切正常。这两件事是同一个根:它最弱,和它最容易被删,是同一个原因。
像车上的安全带和气囊:平时开车用不上,看着就是多出来的重量和成本,出事那一下全靠它。省掉它的当天你不会有任何感觉,代价要等到出事那天才结算。
删掉的那句指令让分数掉了 14 分
上了这套流程之后,它当场拦下过一次「清理」型编辑:改动看着人畜无害,分数掉了 14 分。掉分的原因是删掉了一句兜底指令。
英文原文形容那句指令是 load-bearing 的,直译是「承重的」。具体到这条 prompt 里:它看着像一句多余的补充说明,实际上是承重的那一句:它规定了「用户输进来的东西不成形时该返回什么」。拿掉之后,没有别的东西接着。
删掉之后,正常输入照样跑得好好的,测试全过,日志干净。只有那些不成形的输入开始拿到含混的回答,也就是后来变成客服工单的那批用户。
花十秒看见它,好过再吃一张客服工单。
Ten seconds to see it, instead of another support ticket.
要说清楚的是,删 prompt 这件事本身没错,站内解读过的 Anthropic 官方提示指南整篇都在教怎么删。问题只在删之前有没有一个东西接着你。
代码里的断言用不了,所以改成打分
帖子底下的第一条质疑是这个:
闻着就是把远古软件工程实践重新包装了一遍。新在哪?
改了代码结果不一样了,这不就是回归测试吗?也是老古董。我们因为 AI 把老实践全扔了,现在又一个个重新发现一遍 :D
r/PromptEngineering 读者留言
这个质疑一半是对的,版本控制确实不新。真正为 prompt 新造的只有一层:
代码测试可以写 assert output == expected,让机器直接判断「结果是不是等于这个」。这句检查之所以成立,是因为答案唯一。
模型生成出来的文本没有唯一答案,同一条 prompt 跑两次措辞都不一样。这句检查assert,中文叫断言,就是自动测试里那句「结果必须等于这个」的检查。像判卷:选择题能对标准答案,作文题不能,只能拿一套评分标准打分。在 prompt 上直接失效。
所以做法是给 prompt 配一套固定的考题:同一批输入每次都喂进去,跑完检查结果。只是这个检查换成了打分,跑出来的答案交给一套评分标准,出一个 0 到 100 的分数。自动跑,在任何新版本被换上线之前。
而且分数是按项给的,所以你能看见具体哪一部分被改坏了。报出来的是「应付意外输入这一项从 61 掉回 17」,具体到项,而不只给一个总分。修的时候也能修得准:改掉第 3 行那一条指令,这一项从 17 回到 61,其余不动。
一句话概括这件事:给答案不唯一的东西做回归测试,上线前跟旧版比一次,退步了就拦。
四步工作流:版本、diff、打分、端点
下面四步的骨架来自原帖,每步的「落地做法」是本站按这个骨架整理的具体操作示意,不是原帖原话。
出事之前,这些 prompt 是放在一个 Google 文档里的:Version A、Version A-final、Version A-final-2,你知道那个套路。一直好用,直到不好用为止。知道病根在哪之后,整套工作流就得换掉。这四步是工具无关的,先按不依赖任何产品的方式说一遍:
1 · 每条 prompt 都有编号版本
版本号就是第 4 版、第 5 版,两版之间能拉出一份 diff(逐行对照,改动一行不落地摆出来),跟你把改动合进代码主干之前先逐行审一遍,是一回事。「final_v2」这种命名做不到这件事。
- 一条 prompt 一个文件,别几条挤在一个文档里,挤在一起就没法单独比对。
- 文件里除了 prompt 正文,还要记版本号、这次改了什么、上次的测试结果。
- 改动进 git(或任何能出 diff 的地方)。判据只有一条:随时能回答「第 5 版比第 4 版到底动了哪几行」。
2 · 其中一个版本标记成生产版
「线上现在跑的到底是哪一版」有了唯一答案,其余全是草稿。不主动换上去就不会上线,没有例外,「显然没问题」的小改也不例外。
- 找个地方明确记下「当前生产版 = v4」,一个字段就够,别靠记忆也别靠聊天记录。
- 草稿改多少版都行,不动这个标记就影响不到线上。
3 · 换生产版之前,新旧两版都打分,比一比
事先设一条线,新版跌破就标红、不许上线。这一步就是当初能拦住那次删改的那一步,在它够到真实用户之前。
- 先备一批考题:拿真实用户问过的输入当题目,其中留几条是不成形的(空的、缺信息的、格式乱的),这几条专门用来试探应付意外的能力,也就是最容易被删掉的那部分。题目数量原帖没说,按你自己的场景定。
- 同一批考题,两个版本各跑一遍,各自出一份分数。
- 比两样:总分有没有跌破你划的线;每一项分别掉了多少,总分只告诉你坏了,分项才告诉你坏在哪。
- 跌破就停下,不是提醒一下继续走。这一步不拦,前面三步都白做。
4 · 生产版靠一个 slug 端点提供
线上跑的那一版不写死在代码里。把新版本换上去,应用直接就取到了,不用重新发布。要退回去,就把老版本再换上来,不用半夜一边翻 git 历史一边看工单堆积。
- 代码里留的是一个名字(比如
support-agent),不是那一大段 prompt 正文。 - 应用按这个名字去取当前生产版;换版本只改那个「当前生产版 = ?」的标记。
- 这一步换来的是回滚速度:出事时把老版本重新标一下就回去了,不用重新发布一次应用。
- 从当前生产版 v4 拉一份副本,改完存成 v5。
- 拉 diff,自己先看一眼到底动了哪几行。
- 同一批考题喂给 v4 和 v5,各出一份分数。
- 比:总分跌破线就地停下;分项再看一眼,是哪一项掉的。当初那 14 分就是在这一步冒出来的,十秒钟。
- 过了,把「当前生产版」从 v4 改成 v5。
- 应用下次取 prompt 时自动拿到 v5,不重新发布。
- 万一线上还是出问题,把标记改回 v4,几秒钟退回去。
分数线按每条单独划,不设统一标准
那这条线到底划在哪?两个叠着用:
一条硬门槛:必须到 X 分才准上线,而这个 X 按每条 prompt 单独设,不是一个全局数字。理由是生成、抽取、分类这几种活本来就不在一个水平线上,拿同一个数字去量,必然量错。
在硬门槛之上再加一层相对跌幅检查,专门抓那种「够格但退步了」的情况(regression,回归),不看绝对分数够不够,看这次比上一版掉了多少。前面那次 14 分,就是被这一层抓住的。
还有一层态度:标红是为了让你看见,不是教条。有时候掉 5 分是划算的,因为新版更简单、跑起来更便宜。分数线是护栏,拍板的还是人,真正的收益在于,东西上线之前,你知道自己改了什么。
那如果分数掉了,你却觉得新版的输出明显更好呢?改写版给的诊断是:多半是你那批考题不像真实用量,真实用户会问的东西,你的测试输入里没有。这句是改写方的判断,原帖作者本人没有回答过这个问题。
不买工具:git 加一个打分脚本
这套流程不依赖任何产品:git 加一个打分脚本,再加一个 Makefile,就能覆盖大部分。具体做法是把每条 prompt 存成一个文件放进 git,文件里连版本号和上次的测试结果一起记着;写个小脚本,拿同一批输入把两个版本各跑一遍,把差异打出来;跑出来的结果也一起存着,这样质量从哪一版开始变差就看得见。
至于什么时候跑:换上线之前必须跑,那是拦住有害改动的那一道。改得频繁的话边改边打分,能省掉回头返工。另外可以对线上那一版本身做定期抽查,有些变差跟你的改动无关,是模型换了版本,或者用户问的东西变了。
跑的频率按冒烟测试来,不是按期末考来。每一次有分量的改动都跑一遍,别攒到大版本发布前才跑,这条决定的是你多久能发现问题:几小时,还是像开头那样,三天。
清晰度 Clarity 这个任务只有一种合理解读,模型不用猜
翻车:动词含糊,「帮我处理一下」「改进一下这个」
具体度 Specificity 对输出的要求要能衡量,不能只给形容词
翻车:「写一段简洁的摘要」,而不是「用大白话写一段三句话的摘要」
结构 Structure 角色在前、背景第二、任务第三、格式最后
翻车:格式要求埋在任务后面、角色压根没写
健壮度 Robustness 对最可能出岔子的几种情况写死了该怎么办
翻车:默认用户会给干净规整的输入
Before(18/100) Help the customer with their issue and be professional. After(79/100) You are a support specialist. Read the message and return: summary (1 sentence), customer_intent (only what's stated), urgency (urgent / normal / low). If no clear request: intent = 'unclear', urgency = 'normal'.
- uses: FranciscoFerreiraff/prompteval-action@v1
with:
api_key: ${{ secrets.PROMPTEVAL_API_KEY }}
prompt_file: prompts/support-agent.md
baseline_slug: support-agent
min_score: 75
fail_on_conflict: true
Prompt 烂掉从来不报错,你是靠客服工单才发现的
一个开发者删错一句话吃了三天教训,把治法拆成四道关口,一页图讲完
↓ 一页读完 · 有一张会动的图
删掉一句「没用的话」,三天后才靠客服工单发现
有人在改一条正在服务用户的 prompt,看着有几句话像废话,删了,顺带省几个 token(模型处理文字的计量单位)。这是 Reddit r/PromptEngineering 上一个开发者的复盘帖,只要你有 prompt 在给真实用户干活,这套方法就用得上。
上线后没有报错,没有告警,监控一切正常。三天后一张客服工单来了:回复含混、跑题,有时候还错得很自信。
代码改坏了会喊,prompt 改坏了不出声
同样是改坏了,差别在于坏了之后有没有人告诉你。代码抛异常、编译不过、一跑就崩;prompt 照样吐出一段像样的文本,程序收到的是正常结果,日志干净,测试也能过。
肉眼也接不住,它仍是正经答案,只是更差的那个,你自己带着预期去看,怎么看都顺眼。滑坡要等真实用户花式提问才露出来,露出来的形式就是工单。
1018 条 prompt 打分,应付意外输入这项最低
这不是一个人手滑。1018 条 prompt 打过分,四个维度里最差的一项是健壮度(prompt 有没有交代「遇到不正常输入该怎么办」,比如用户没说清、输入是空的、格式不对)。均分只有 31.5/100。
这两件事同根:健壮度管边缘情况,正常输入碰不到它,测试走的也是正常路径,删了当场测不出来。数据来自发帖人自家打分平台,没有第三方复核。
这跟回归测试不一样:答案不唯一,只能打分不能判对错
评论区第一条质疑:这不就是回归测试吗?代码测试能写一句「结果必须等于这个」(业内叫 assert),前提是答案唯一;模型生成的文本没有唯一答案,同一条 prompt 跑两次措辞都不一样,这句检查直接失效。
分数按维度给,能看清哪部分被改坏。工作流拆成四步:
- 编号版本,留一份逐行对照的改动记录
- 标一个生产版,其余都是草稿
- 换版前新旧两版打分比一比,跌破线不许上线,当初拦住那次编辑的就是这一步
- 生产版靠一个名字取用,不写死在代码里,回滚就是重新标记老版本
分数线按每条 prompt 单独设,不是一个全局数字,再叠加一层相对上一版的跌幅检查。不买工具的话,git 加一个打分脚本就够,正文里有可以直接抄的 GitHub Action 配置。
- × 用户没说清楚要什么…
- × 输入是空的…