Think Deep,Work Lean

把「尽量」删掉,Agent 少返工三遍

Posted on By zack

大家好,我是Zack。

10 月 5 号晚上我让 agent 批量回 24 条评论,跑完一数:重发了 1 条,漏了 6 条,24 条目标总共跑了三十几次。

我第一反应是模型不行。

后来翻了一遍自己写的 prompt,问题在我这边 —— 里面有两个字:尽量。

一、原来的做法:写完 prompt 就直接跑

那批回复的 prompt 我是这么写的(大意):

去我主页下面找最近 24 条评论,尽量都回复一遍。
尽量找到回复按钮,找不到就跳过。
语气尽量自然一点。

跑完看 queue/reply_batch.log,长这样:

#7  @jasonjz23   OK
#8  @Tsui1264    FAIL_NO_REPLY_BTN
#9  @taiyanghere FAIL_NO_DIALOG
#10 @Junhao88888 FAIL_MISMATCH
#15 @EricTanvo   ERR page.goto failed: net::ERR_ABORTED
#24 @Lu202409Lu  FAIL_NO_REPLY_BTN

6 条挂了,还分三种不同的挂法。

我当时的做法是:把报错贴回去,让它再跑一遍。

结果第二轮有的从 FAIL 翻成 OK,有的又冒出新的 FAIL_MISMATCH。

两轮加起来 40 分钟,其中差不多 20 分钟是在修重发和补跑。

二、真正卡住的地方

我盯着那三行 尽量 看了一会儿,发现问题很直白:

「尽量找到回复按钮」这句话,模型没有一个可以核对的标准。

它点到了就算做到,没点到也可以说「我尽力了」。

同样的 prompt 跑两次,第一次点开对话框、第二次没点开,结果就不一样 —— 这不是模型抽风,是任务本身就没定义清楚。

10 月 5 号我还踩了同一类坑的另一个版本:verify_state.js 连跑两轮,第一轮 #4 = ALREADY_POSTED,第二轮同一条翻成 UNKNOWN,5 条里确认数从 1 掉到 0。

我一开始以为是脚本 bug,最后发现根因还是同一件事:判定标准是「页面上有没有出现某句文案」,而那句文案会渲染延迟。

标准不稳,结果就飘。

三、换成的做法:三处把模糊换成可数

1. prompt 里的形容词,换成能数出来的约束

我把那三行改成了这样:

只处理 queue/reply_targets.json 里列出的 24 条,不要新增、不要跳过。
点回复按钮的条件:页面上存在 aria-label 为 "Reply" 的按钮;不存在就记 FAIL_NO_REPLY_BTN 并跳过。
语气:一句话,不超过 30 字,不用表情,不提产品。

改之前和改之后的对照:

我原来写的 现在写的
尽量少改文件 只允许改这三个文件:a.js、b.js、c.py
尽量保留原格式 不含 TODO 的行一个字都不动
合适的时候合并 两个文件改动都 ≤ 5 行时才合并
尽量找到回复按钮 存在 aria-label="Reply" 才点,否则记 FAIL_NO_REPLY_BTN
跑快点 单条超时 15 秒,超时记 FAIL_TIMEOUT

判断标准就一条:改完之后,能不能让一个没看过上下文的人,只凭这句话判断 agent 做对了没有。

判断不了,说明它还是模糊的,继续改。

我现在的习惯是写完 prompt 先数一遍:里面有几个「尽量」「合适」「适当」「一些」「相关」。

数出来 5 个以上,这遍大概率要白跑。

懒得数的,把 prompt 存成文件跑这一行:

grep -o -E "尽量|合适|适当|一些|相关|差不多|尽可能" prompt.txt | sort | uniq -c | sort -rn

输出会直接告诉你哪个词出现了几次。我自己那条 24 条回复的 prompt,尽量 命中 3 次。

2. 批量脚本第一次跑,固定加 --dry-run

让 agent 写批量处理脚本的时候,我在要求里写死一句:

脚本必须支持 --dry-run:只打印将要改动的文件清单和改动内容,不落盘。

跑的时候先干跑一遍:

$ node scripts/rename_batch.js --dry-run

[DRY-RUN] 将改动 3 个文件:
  - src/a.js   行 12: get_user -> get_user_info
  - src/b.js   行  8: get_user -> get_user_info
  - src/c.py   行 41: get_user -> get_user_info
[DRY-RUN] 未匹配 0 个,跳过 0 个

清单对得上,再去掉参数真跑。

匹配规则最容易写宽。我有一次规则写得太松,把测试文件一起扫进去了,回滚花了半小时。

多花一分钟看清单,省的是回滚那半小时。

3. 报错要原文,别让它总结

这条我现在直接写进 prompt,一个字不改地复制过去:

报错处理要求:
- 原始报错整段贴回来,包含 exit code、行号、文件路径
- 不要改写成「构建失败」「网络错误」这类总结句
- 判断不出原因就说判断不了,不要猜

为什么值得单独写三条?

因为总结会丢掉排查真正靠得住的三样东西:行号、exit code、文件路径。

同一个事故,两种报法的区别:

总结版:页面操作超时了

原文版:ERR target 8F471FB996720379DFA9C2A3CF46E2B4 is already page p6

看到 p6 我立刻知道是浏览器标签页串了,跟超时一点关系没有。

让模型总结,它越想帮你省事,越容易把关键信息省掉。

四、前后对比

还是 10 月 5 号那批活,改完之后的数字:

  改之前 改之后
prompt 里的模糊词 5 个 0 个
24 条回复跑了几轮 3 轮 2 轮
耗时 约 40 分钟 约 25 分钟
漏发 / 重发 6 条 1 条
报错定位 平均追问 3 轮 1 次拿到行号

诚实说一句:这是我两天的个人记录,样本小,别当统计结论看。

而且改完也没到零失误 —— 第二轮还剩 1 条 FAIL_MISMATCH,那个是回复目标和评论人对不上,属于数据问题,换约束解决不了。

这三招治的是「指令含糊导致的返工」,治不了「数据源本身就是脏的」。

五、两条能迁移到自己工作里的原则

第一条:验收标准要在 agent 动手之前就写得出来。

写不出来,说明不是 agent 的问题,是我自己还没想清楚要什么。

我现在写完 prompt 会多花两分钟问自己一句:它做完了,我拿什么判断它做对了?

第二条:凡是「跑完才知道对不对」的步骤,都想办法变成「跑之前能看一眼」。

--dry-run 的清单是一种,报错原文是一种,可数的约束是第三种。

它们的共同点是:把判断提前,把回滚的成本后置。

你最近一次让 agent 返工,最后发现卡在哪个词上?

欢迎在留言里说说,我一条条看。


公众号:Zack说AI

大厂程序员,现独立开发者,分享 AI 技术与过程。每篇讲一个能落地的提效做法。