一、这篇文章讲什么
2026 年 2 月,Django 联合创始人 Simon Willison 在 GitHub 上发布了一个项目——Agentic Engineering Patterns(代理式工程模式)。
这个项目要解决的问题很具体:我们都在用 Claude Code、Codex 这类编码代理写代码了,但怎么才能让代理写出来的代码靠谱?
Simon 给出了三个极短的提示词,分别对应三种场景。本文把它们拆开讲清楚,给出可以直接复制使用的模板。
二、一个前提:写代码变便宜了,好代码仍然贵
在讲具体模式之前,先说一个核心判断。
过去,写几百行干净代码可能要花一整天。所以我们的很多习惯——前期设计、排期、对每个改动做成本收益分析——都是围绕“编码时间稀缺”建立的。
编码代理把“敲代码”这件事的成本压到接近零。但这只是故事的一半。
Simon 列了一份“好代码”的清单:
- 能工作(功能正确,没有明显 bug)
- 能被证明能工作(有测试、有可复现步骤)
- 解决的是正确的问题(需求本身没跑偏)
- 异常和边界条件有处理
- 足够简单,只做需要做的事
- 有测试保护
- 文档和代码同步
- 为未来变化留余地,但不过度设计
结论:代理能帮你做大部分,但你仍然要为最终质量负责。
下面是三个具体的做法。
三、模式一:First run the tests
3.1 是什么
每次让代理进入一个已有项目的新会话,第一句话就说:
1 | First run the tests |
就这四个词。
3.2 为什么有效
这四个词同时做了三件事:
第一,让代理发现测试套件。 它会自己去找怎么运行测试(pytest、npm test、go test ./...),找的过程本身就是在熟悉项目。
第二,建立项目基线。 测试框架会显示测试数量和结果。代理因此知道项目有多大、质量如何、哪些地方有问题。
第三,切换心智模式。 跑过一次测试后,代理在后续的改动中更倾向于自觉回归测试。
Simon 有一句很实在的判断:如果 AI 生成的代码从未被执行过,它部署后能工作几乎只能靠运气。
3.3 不同技术栈的写法
Python(uv + pytest):
1 | Run "uv run pytest" |
Simon 自己用 pyproject.toml 的 dependency groups 管理 dev 依赖,让 uv run pytest 成为拿到仓库就能跑的默认体验。
Node/TypeScript:
1 | First run the tests. Try: npm test. |
Go:
1 | First run the tests. Run: go test ./... |
Rust:
1 | First run the tests. Run: cargo test |
有 Makefile 的项目:
1 | First run the tests. Run: make test. |
3.4 常见坑
只看测试输出,不让代理解释失败原因。 很多失败是环境问题。建议要求代理每次修复都写清楚“为什么失败——怎么改——影响面”。
允许代理跳过测试。 如果代理说“我无法运行测试,但我认为改动没问题”,把它拉回来。要么修环境,要么缩小改动范围。
项目本身没有测试。 这种情况下,把 First run the tests 换成“先搭最小测试骨架”,然后进入下一个模式。
四、模式二:Use red/green TDD
4.1 是什么
当你要让代理做任何行为变更——新功能、修 bug、重构——开头加一句:
1 | Use red/green TDD |
代理会自动进入测试驱动开发模式:先写测试,确认失败(红),再写实现让它通过(绿)。
4.2 为什么对代理特别有效
代理有两个常见问题:
- 写出“看起来对但实际不能跑”的代码
- 写出“不必要的、没人用的”代码
TDD 同时压制了这两个问题。Test-first 迫使代理先把需求变成可失败的断言,再写最小实现。正确性和范围都被约束了。
4.3 四步节奏
一个完整的红/绿循环:
- 澄清行为:把需求写成输入/输出/边界/错误。
- 写测试(红):新增测试用例,运行,确认失败。
- 写实现(绿):写最小代码让测试通过。
- 重构与回归:整理代码,全量测试仍通过。
其中第 2 步特别重要:一定要确认测试会失败。 如果你写了一个“本来就会过”的测试,等于什么也没验证。
4.4 可直接使用的提示模板
1 | We are going to implement <feature>. |
把 <feature> 换成你的具体需求就行。
4.5 常见坑
代理跳过红阶段。 直接写实现再补测试。这样测试变成“验证已写代码”而不是“定义目标行为”。可以硬性要求:先展示测试 diff,再写实现。
测试太宽松。 只断言“返回值非空”,或者 mock 太多导致不接触真实逻辑。你需要像 code review 一样 review 测试质量。
一次改太多。 代理喜欢“顺便把 XX 也做了”。在 TDD 下明确:每次只引入一个可验证的行为变化。
五、模式三:Linear walkthroughs(线性走查)
5.1 是什么
当你需要理解一个代码库——可能是别人的、可能是你自己 vibe coded 出来的——让代理生成一份结构化的逐模块讲解文档。
5.2 为什么需要这个
Simon 自己就遇到了这个场景:他用 Claude Code 做了一个 SwiftUI 幻灯片应用 Present,发到 GitHub 后发现自己不懂它怎么工作。
他的做法是开一个新会话,让 Claude Code 先读 repo,再做线性走查。
5.3 关键约束:防止幻觉
这里最重要的技术决策:
代码片段必须来自命令输出(sed/grep/cat),不允许代理手工复制粘贴代码。
为什么?因为模型在“复述代码”时非常容易出现微妙偏差——改了变量名、漏了参数、调换了顺序。讲解文档里如果有这种错误,读者会建立错误的理解。
用命令输出插入代码片段,就把“解释”变成了“解释 + 证据链”。
5.4 可直接使用的提示模板
1 | Read the repository source code and plan a linear walkthrough |
如果你的项目装了 Showboat(Simon 自己写的工具),可以用它来生成可执行的 walkthrough 文档:
1 | Run "uvx showboat --help" and use that tool to create walkthrough.md. |
5.5 适用场景
- 入职新项目:让代理生成系统结构 + 关键模块 + 入口 + 测试说明。
- Vibe coded 项目需要维护:先理解再改。
- 事故复盘:根据日志和代码,生成故障路径走查文档。
- 重构前评估:输出耦合点、隐式假设、测试覆盖缺口。
六、三个模式怎么组合使用
6.1 接手陌生代码库
1 | 步骤 1:线性走查(理解) |
先有理解,再有基线,再有可验证的实操经验。
6.2 修 bug
1 | 步骤 1:Use red/green TDD |
本质就是把代理变成一个“测试驱动的补丁生成器”。
6.3 重构
1 | 步骤 1:First run the tests(确认当前绿) |
重构真正昂贵的不是“改动本身”,而是“确信没改坏”。
七、落地清单
把上面的内容压缩成可以直接贴到团队文档里的清单。
会话开局
- 一句话说清本次目标
- 说清约束(不改哪些模块、兼容性、性能边界)
- 执行 First run the tests
- 代理解释了测试怎么跑、跑了多少、失败原因
代码变更
- 用 Use red/green TDD 开头
- 红阶段:新测试确实失败,失败原因与需求一致
- 绿阶段:最小实现通过
- 全量测试通过
- 文档/注释是否需要同步
代码理解
- 代理先输出走查计划(目录结构、入口、关键模块)
- 代码片段来自命令输出,不是手动复制
- 文档包含:如何运行、如何测试、关键数据流
PR 审阅(“好代码”检查)
- 代码真的跑过了吗?
- 怎么知道它能工作?(测试/证据在哪?)
- 解决的是正确的问题吗?
- 异常和边界条件怎么处理的?
- 是否足够简单?有没有不必要的额外改动?
- 测试覆盖了新增行为吗?
- 文档同步了吗?
八、总结
Simon Willison 这个项目的核心思路用三句话就能说清:
- 写代码便宜了,但好代码仍然贵。
- 用极短的提示词触发完整的工程纪律——“First run the tests”“Use red/green TDD”“Linear walkthrough”。
- 你的价值不在于写代码,而在于定义问题、建立证据、守住质量。
这三个提示词你今天就可以开始用。不需要任何配置,不需要任何工具链改造。打开你的编码代理,把它们打进去,然后观察代理的行为变化。
(完)