代码是 Agent 最理想的试验场:有明确的输入输出、可执行的验证、可回滚的版本控制。也正因如此,编码 Agent 的工程方法最值得借鉴。
为什么代码场景特殊
| 特性 | 对 Agent 的意义 |
|---|---|
| 测试可执行 | 提供客观的成功信号,不靠模型自评 |
| 版本控制 | 失败可回滚,改动可审查 |
| 结构化 | 文件、函数、类型都可精确定位 |
| 工具成熟 | 搜索、编辑、运行、调试都有标准接口 |
| 反馈快 | 编译/测试通常秒级到分钟级 |
这让「生成—验证—修正」的闭环真正成立。
能力清单
一个编码 Agent 至少需要:
text
repo_map() 仓库结构与关键符号
search_code(query) 全文/符号搜索
read_file(path, range) 按需读取,不要整仓加载
apply_patch(diff) 精确修改
run_tests(path?, filter?) 运行测试
run_command(cmd) 受限的构建/ lint
git_diff() 查看当前改动
git_status() 确认工作区状态工具设计原则与〈工具设计〉一致:小、清晰、有权限边界。尤其是 run_command,必须限制在沙箱与白名单命令内。
循环
图 1 · 编码 Agent 的闭环
01理解读需求、仓库结构、相关代码
02定位搜索符号、调用链、测试
03修改生成最小补丁
04验证编译、测试、lint,失败则修正
关键在「最小补丁」:改动越小,越容易审查、越不容易引入回归。
上下文:不要塞整个仓库
一个中型仓库可能有几十万行代码。正确做法:
- 仓库地图:目录结构、模块职责、关键文件;
- 符号索引:函数/类/接口定义位置;
- 按需读取:先搜索,再读相关片段;
- 调用链:从入口追到依赖;
- 测试:相关测试文件与最近失败输出。
text
上下文预算示例(32k):
- 任务描述 1k
- 仓库地图 2k
- 相关文件(3–5 个) 12k
- 测试与错误 4k
- 最近 diff 3k
- 规则与输出格式 2k
- 预留 8k不要一次读 20 个文件。先定位,再读。
补丁格式
两种常见方式:
| 方式 | 优点 | 缺点 |
|---|---|---|
| 统一 diff | 精确、可审查、可回滚 | 行号/上下文必须准确 |
| 整文件替换 | 简单 | 容易丢内容、diff 巨大 |
| 结构化编辑(AST) | 最精确 | 实现复杂、语言相关 |
生产系统多用统一 diff + 严格校验:
diff
--- a/src/auth/session.ts
+++ b/src/auth/session.ts
@@ -42,7 +42,7 @@ export function validateSession(token: string) {
- const decoded = jwt.verify(token, SECRET);
+ const decoded = jwt.verify(token, SECRET, { algorithms: ["HS256"] });
return decoded;
}应用补丁前要检查:
- 文件存在;
- 上下文匹配;
- 不修改禁止目录;
- 不引入大文件/二进制;
- 变更行数在预算内。
测试反馈循环
python
def solve(task, repo, max_rounds=4):
context = build_repo_context(repo, task)
patch = generate_patch(task, context)
for round_no in range(max_rounds):
apply_patch(repo, patch)
result = run_tests(repo, related_only=True)
if result.ok:
return patch
feedback = {
"failed": result.failed[:5],
"traceback": result.traceback[-3000:],
"diff": git_diff(repo)[:4000],
"hint": "只修改与失败测试相关的代码;不要删除测试;不要改公共 API。",
}
patch = revise_patch(task, patch, feedback)
rollback(repo)
return None要点:
- 只跑相关测试,先快后全;
- 失败信息要具体到测试名与断言;
- 限制轮数,避免无限修;
- 每轮保留 diff,便于回滚;
- 成功后跑全量测试与 lint。
防止「测试作弊」
模型可能通过修改测试、跳过断言、捕获异常来让测试通过。防护:
- 测试文件只读;
- 校验测试是否被修改;
- 检查是否有
skip、xfail、空except; - 用隐藏测试(held-out)验证;
- 人工审查关键改动。
评估
- Pass@k:k 次尝试中至少一次通过;
- Pass^k:k 次全部通过(更接近生产可靠性);
- 回归率:修好目标测试,但弄坏了其他测试;
- 补丁大小:越大越难审查;
- 人工接受率:开发者是否愿意合并;
- 平均轮数/成本。
公开基准(SWE-bench 等)只能参考。你自己的仓库、编码规范、测试覆盖才是真实分布。
安全
- 在沙箱/容器中运行,限制网络与文件系统;
- 命令白名单,禁止
curl | bash、rm -rf; - 不把生产密钥放进环境;
- 代码变更必须经过 review;
- 依赖安装要锁定版本与来源;
- 防止提示注入通过代码注释/issue 内容进入。
失败模式
过度修改
为了修一个 bug 重写整个模块,引入新问题。要求最小 diff,并限制变更行数。
幻觉 API
调用不存在的库函数/参数。用类型检查、编译、测试兜底。
只修表象
捕获异常让测试通过,但根因还在。审查空 catch、强制返回默认值。
上下文污染
读入无关文件,被旧代码/注释带偏。先定位,再按需读取。
忽略仓库规范
命名、目录、错误处理与项目不一致。把规范写进系统提示,并提供示例。
检查清单
- 有仓库地图与符号搜索,不整仓加载
- 补丁最小、可审查、可回滚
- 测试文件只读,防作弊
- 沙箱运行,命令白名单
- 限制轮数、变更行数、成本
- 成功后跑全量测试与 lint
- 有隐藏测试与人工 review
- 评估覆盖 Pass@k 与回归率
小结
- 编码 Agent 的核心是「理解—定位—最小修改—验证」闭环。
- 上下文按需读取,靠仓库地图与符号搜索定位。
- 测试是最强的验证信号,但要防止模型作弊。
- 沙箱、命令白名单、人工 review 是安全底线。
- 用 Pass^k 与回归率衡量生产可靠性,而不只看演示。
下一篇看四类成熟产品的架构共性与商业教训。