「帮我修一下这个测试」是 Agent 上下文里最高危的句子之一。没有夹具、没有失败断言、没有路径白名单时,模型会倾向于:改生产代码 + 改测试 + 顺手重构,最后给你一个「全绿」但不可审的大补丁。
反过来,把失败用例当作合同:先复现为红,再要求只输出最小 diff,Agent 的行为会收敛得惊人。本文给可复制的提示词套路与验收清单,可与同日「子 Agent 委派」文联用。
核心思想
红测是输入,不是修完再补。
最小 diff 是工程礼貌,也是 Token 与评审成本的节约。
提示词里的「禁止项」与「交付格式」比形容词「请小心」更有约束力。
夹具驱动修复流
- 复现:本地跑出红;保存命令、关键断言、栈顶。
- 提示:粘贴失败材料 + 路径范围 + 禁止项 + 交付格式。
- 改码:只允许触达白名单文件。
- 审阅:人工或主 Agent 扫 diff;重跑目标测与邻近测。
不要在「还没复现」时让 Agent 开改——那会把猜测写进仓库。
弱提示 vs 强提示
弱提示:
登录测试挂了,帮我修一下,顺便看看相关代码有没有问题。
后果:范围爆炸、测试被改宽、生产逻辑被「简化」。
强提示:
下列失败是唯一目标。先阅读夹具与断言,再改代码。
允许修改:src/auth/token.py
禁止:修改任何测试文件;禁止格式化无关文件;禁止新建抽象层。
验收:pytest tests/test_token_expire.py -q从失败变全绿。
交付:仅 unified diff + 变更说明不超过 5 行。
强约束换来可审阅补丁。
提示词套路五段(复制填空)
【背景】 命令:pytest tests/test_xxx.py -q 当前结果:失败(将完整失败摘要粘贴于下) 目标:使上述命令通过;不追求「更大重构」。 【材料】 - 失败断言与期望值:(粘贴) - 夹具路径:tests/fixtures/... - 相关实现:@src/...(仅这些) 【范围】 允许修改: - src/a.py 禁止: - tests/**(除非我显式允许) - 全文件格式化、重命名扫射、新增依赖 【交付】 1. 变更文件列表 2. unified diff(或按工具显示的补丁) 3. 不要长文解释;必要说明 ≤ 5 行 【验收】 重跑:pytest tests/test_xxx.py -q 期望:全绿。若无法在范围内修复,停止并说明阻塞,不要扩大范围。把这五段存进团队 Prompt 库 / AtomGit,比每次临场发挥稳定。
实战演练:过期 Token 断言
假设失败为:expected status 401, got 200,夹具是过期 JWT。
操作顺序:
- 你先本地复现,确认不是环境抖了一下。
- 用五段提示词,只
@token 校验实现与失败测试输出(测试文件只读引用,不授权修改)。 - 若 Agent 提议「改测试期望为 200」——直接拒绝:合同是生产行为对齐断言,不是断言投降。
- 若 Agent 想引入新库处理 JWT——要求先在无新依赖前提下修复;新依赖单开一张任务卡。
最小 diff 的审阅要点
- 失败用例已先本地为红
- diff 仅触及约定文件
- 无格式化大扫除 / 重命名扫射
- 目标测转绿,邻近测未误伤
- 提交信息指向夹具与意图(如
fix: reject expired token (test_token_expire)) - 套路模板入库
审阅时用git diff --stat一眼看体积;单个「修测试」提交动到 20 个文件,多半违例。
与委派、排障的衔接
- 委派:每张子卡的验收命令,最好就是一条红测转绿;夹具套路是卡内提示词。
- 排障(10-07):乱改类故障,用本套路的路径白名单与「禁止改测」刹住。
- Manual 精修(10-05):Agent 给出最小 diff 后,高风险行用手改。
何时允许改测试
例外要显式写进提示词,例如:「夹具数据过期,允许只改tests/fixtures/expired.json的时间戳,不允许改断言语义。」
默认不允许改测试,是为了防止 Agent 用「改期望」骗绿。
反模式速查
| 反模式 | 问题 | 替代 |
|---|---|---|
| 先改码再补测 | 合同后补,易空转 | 先红测 |
| 允许改全仓 | 必扩写 | 白名单 |
| 要求「详细设计文档」 | Token 浪费 | 限 5 行说明 |
| 多失败一次修 | 纠缠 | 一测一卡 |
| 绿了就不看 diff | 藏重构 | 强制 --stat |
夹具的三种形态
- 单元夹具:函数输入输出、临时文件、工厂函数。
- 契约夹具:固定 JSON / 录制过的响应(注意脱敏)。
- 失败快照:一次真实失败的断言消息与栈顶(可进
artifacts/但不含密钥)。
喂给 Agent 时,优先「断言原文 + 最小相关源码」,不要整份 HTML 报告。
多失败如何排队
一次会话只修一个红。排序建议:
- 纯逻辑、无 IO 的失败;
- 有夹具可稳复现的;
- 涉及时间/网络的(先固定时钟或 mock)。
每个失败一张卡,修绿后提交,再开下一张。禁止「顺便把整个文件的 flake 都消掉」——那是重构项目,另开需求。
当 Agent 给出「正确但过大」的 diff
处理流程:
- 肯定其理解的断言语义;
- 要求在同一语义下重写为更小补丁(点名删掉的文件/ hunk);
- 仍过大则改 Manual:只采纳关键 hunk;
- 把「体积上限」(例如不超过 N 个文件)写进下一轮提示。
体积上限是经验值,可按模块调整,但要有数字,不能只说「小一点」。
提示词库的版本化
docs/prompts/min-diff-from-fixture.md应有简短变更记录:何时加了「禁止改测」、何时加了交付格式。Agent 提示词也是代码——会腐朽、会分叉。AtomGit 上的评审可以像改代码一样改提示词。
与 CI 的关系
本地红测转绿后,CI 仍可能因环境差失败。提示词可加一句:「若修复依赖环境变量,必须在 PR 描述列出,不得把秘密写进测试。」夹具约束的是 Agent 行为;CI 约束的是合并门禁——两层都要。
练习题(可当作工作坊)
给队员一个故意写错的apply_discount与红测,只允许改实现文件。评分看:--stat是否干净、是否改测骗绿、提交信息是否指向夹具。比听分享课更管用。
端到端示例对话(缩写)
你:(粘贴五段套路,附失败断言assert apply_discount(100, 0) == 100实际得到 0)
**Agent:**提议同时重构折扣体系并改三处调用。
**你:**驳回。范围仅pricing/discount.py。禁止改调用方。再给最小 diff。
**Agent:**只在percent is None与percent == 0分支修正。
你:pytest tests/test_discount.py -q全绿;--stat仅一文件。提交。
关键教学点:第一次草案被驳回是成功排障,不是失败。夹具合同给了你驳回的依据。
「只输出 diff」在不同工具里的落地
- Cursor Agent:要求补丁摘要 + 文件列表;用 Git 面板审。
- Manual:只应用相关 hunk。
- Aider:审 diff 后再提交。
- 纯聊天导出:要求 fenced diff,你手动应用——更慢但最可控。
套路五段跨工具复用;改变的只是粘贴位置。
夹具脱敏清单
放入提示词前检查:
- 无真实邮箱/手机/身份证;
- 无内网 URL 若属敏感;
- 无 Token/Session;
- 客户名已替换为
acme。
否则「最小 diff」过程可能把敏感信息送进模型与日志。安全基线与提示词套路必须绑在一起。
度量提示词是否变好
记录 20 次修复:
- 平均触达文件数;
- 驳回次数;
- 是否改测骗绿。
若触达文件数下降、骗绿为零,说明套路生效。把统计贴进团队周会,比分享「我觉得有用」更有说服力。
扩展:属性测试与 Agent
若你用属性测试(hypothesis 等)发现失败输入,把那组输入写成最小夹具再喂 Agent,往往比丢整段理论说明更有效。合同依旧是「这一输入从红到绿」,不是「请理解属性测试框架」。
与 Rules 的配合
在.cursor/rules或等价物中可放短铁律:
- 修复测试失败时默认禁止修改测试文件,除非用户显式允许;
- 交付必须包含变更文件列表;
- 禁止以「代码风格」为由改动无关文件。
铁律保持一屏;详细五段套路放 Prompt 库按需@。Always 过长会引入新的前缀税,与「最小 diff」目标相悖。
红测无法本地复现时
若只在 CI 红:
- 先下载 CI 日志关键段;
- 尝试在同版本容器/同环境变量下复现;
- 仍无法复现则开「调查卡」,目标是复现,不是改码;
- 复现成功后再开「修复卡」。
把调查与修复拆开,避免 Agent 在未复现时大面积「猜修」。
提交前最后 60 秒
git diff --stat- 目标测命令
- 邻近测或模块测
- 扫一眼是否出现锁文件/快照无意变更
六十秒检查能拦下大半「最小 diff」名不副其实的提交。
给评审者的留言模板
类型: 夹具约束修复 红测: tests/test_xxx.py 策略: 禁止改测 / 白名单文件 验证: (粘贴命令输出) 风险: 无 API 变更 / 有(说明)PR 上这几行,比「请看」更尊重评审时间。
提示词完整粘贴版(可直接用)
下面把五段合并为一块,方便存库。花括号内替换:
你正在修复一个已复现的失败测试。先读材料,再改代码。 背景:命令 `{cmd}` 当前失败。失败摘要: {failure_log} 材料:夹具 `{fixture_path}`;只读参考 `{ref_files}`。 范围:允许修改 {allow_paths}。禁止修改测试文件与 {deny_paths};禁止全文件格式化、重命名扫射、新增依赖。 交付:1) 变更文件列表 2) unified diff 3) 说明≤5行。不要长文。 验收:重跑 `{cmd}` 必须全绿。若范围内无法修复,停止并报告阻塞,禁止自行扩大范围。把该块放进docs/prompts/min-diff-from-fixture.md后,团队新人第一周就能按同一合同工作。再配上--stat审阅与「改测必须显式授权」,骗绿与扩写会显著下降。若 Agent 第一次仍过大,用同一合同驳回并加「体积上限:最多 N 个文件」——数字写进提示,比「再小一点」有效。夹具是合同,diff 是履约;履约可驳回,合同却要稳定,不要每轮改口径。
FAQ
Q:最小 diff 会不会阻碍必要重构?
A:会刻意阻碍「夹带重构」。必要重构单开需求卡,带自己的测试与评审,不混在故障修复里。
Q:测试本身有 bug 怎么办?
A:显式授权改测,并在提示词写清「允许改哪个断言、不允许放宽什么语义」。默认禁令是为了防骗绿,不是教条。
Q:多模块失败能否并行?
A:文件无交集时可并行;否则串行。并行时每张卡独立合同,互不共享脏历史。
Q:要不要让 Agent 先写计划?
A:可以,但计划阶段只读;计划通过后再授权改码。计划不等于可以扩写范围。
把 FAQ 收进文末,读者检索「骗绿」「重构」时更容易落地。实践上,合同稳定比模型更换更能降低翻车率;夹具驱动是把测试重新放回驾驶席。
边界声明
- 示例框架名(pytest 等)可替换为你的栈;原则不变。
- 不提供破坏测试体系或伪造覆盖率的技巧。
今晚可执行
- 挑一个当前红测,跑通复现。
- 用五段套路发一次 Agent 请求。
- 用
git diff --stat验收是否最小。 - 把套路存
docs/prompts/min-diff-from-fixture.md。
夹具是合同,diff 是履约;写不清合同,就不要怪 Agent 乱履约。