Code Agent 的中控系统:搜索、验证与长期运行

Jan 2, 2026·
夏 伟
夏 伟

聊 Code Agent 时,我们很容易把注意力都放在模型上:换一个更强的模型,代码能力是不是就会明显提升?

在 2024 年写 A few things about Code Agent 时,我把 Code Agent 拆成了中控、推理生成、验证、知识库和工具集,也花了不少篇幅观察 Multi-Agent 工作流。两年后再回看,这些组件仍然存在,但我关注的重点变了:角色数量不是最关键的,真正决定 Agent 能否进入真实工程的,是搜索、验证、上下文和权限能不能形成稳定闭环。

真实工程里,答案通常没有这么简单。模型只是系统的一部分。它还需要知道去哪里找代码、什么时候运行测试、失败后重试还是回滚、哪些文件不能改,以及什么时候应该停下来把问题交还给人。

我把这一层称为 Code Agent 的“中控系统”。它不是一个神秘的大脑,也不是一段超长 Prompt,而是一条反复运行的控制闭环:

flowchart LR task["用户目标"] --> plan["计划与
子目标"] plan --> act["搜索、
编辑、执行"] act --> env["代码库、
终端与 CI"] env --> verify["验证结果"] verify -->|"通过"| finish["交付"] verify -->|"可恢复"| plan verify -->|"高风险
或不确定"| human["请求
人工判断"] memory["项目规则
与任务状态"] --> plan

这篇文章关注的不是某一个产品,而是这条闭环为什么有效、会在哪里失效,以及搜索、奖励模型和长期记忆应该放在什么位置。


从一次生成到闭环执行

早期代码模型更像“根据描述写一段代码”。Code Agent 则要不断观察环境:读取文件、执行测试、看到报错,再决定下一步。

ReAct 把推理与行动交替起来;Reflexion 进一步让模型把失败总结成语言反馈,供下一次尝试参考。12 两者带来的关键变化不是模型突然学会了反思,而是系统终于建立了一个可观察的反馈回路。

在代码场景里,环境反馈比模型自评可靠得多:

  • 编译器可以确认语法和类型是否正确;
  • 单元测试可以验证已有行为有没有被破坏;
  • lint 和静态分析可以检查一部分工程约束;
  • git diff 可以告诉我们 Agent 到底改了什么。

真正可靠的中控会优先使用这些确定性信号,而不是让模型凭感觉判断“修复应该已经完成”。


搜索:什么时候值得多试几条路

线性重试的做法很简单:尝试一次,失败后阅读报错,再尝试一次。多数日常任务用这种方式就够了。但当根因不明确、修复方案很多时,Agent 可能过早押注在错误方向上。

LATS 把树搜索引入语言 Agent,让系统同时保留多个候选轨迹;RethinkMCTS 则尝试在发现错误后重写有问题的中间推理,而不是只在错误轨迹末尾继续补救。34

flowchart LR issue["问题"] --> a["假设 A"] issue --> b["假设 B"] issue --> c["假设 C"] a --> ta["运行验证"] b --> tb["运行验证"] c --> tc["运行验证"] ta --> rank["比较证据与成本"] tb --> rank tc --> rank rank --> patch["继续最有希望的路径"]

不过,树搜索并不天然优于线性尝试。它用更多 Rollout 换取更高成功率,也会带来更高 token 成本、更多环境执行和更长等待时间。实际系统需要一个搜索预算:

  • 小范围、可快速验证的修改,优先单线执行;
  • 根因不明确但验证便宜时,可以并行探索多个假设;
  • 构建和测试很慢时,应该先做静态分析,避免每个分支都跑完整 CI;
  • 涉及架构取舍时,候选方案应交给人评估,而不是只按模型分数排序。

因此,搜索的重点不是“分支越多越聪明”,而是把额外算力花在真正不确定的决策点上


验证:最终结果和中间过程要分开看

代码任务有天然的可验证信号,这也是 RLVR(Reinforcement Learning with Verifiable Rewards)适合代码领域的原因。测试通过可以作为结果奖励,模型不需要依赖人工给每条推理链打分。

但只看最终结果也有问题:一个任务经过几十步才失败,我们不知道是最初定位错了文件,还是最后漏改了调用方。过程奖励模型(PRM)试图为中间步骤提供更密集的反馈,SWE-Shepherd 就是面向软件工程轨迹的一个例子。5

我更倾向于把验证拆成三层:

层级回答的问题适合的验证方式
动作验证这一步是否合法、是否执行成功schema、权限、命令退出码
过程验证当前方向是否更接近目标测试增量、静态分析、PRM
结果验证任务是否真的完成完整测试、验收条件、人工 review

PRM 可以帮助搜索,却不能替代最终测试。模型很容易学会迎合一个近似的评分器,生成“看起来有道理”的过程。把过程奖励限制在最终验证通过的轨迹上,是减少奖励黑客的一种思路;更根本的办法还是提高测试与环境本身的质量。


RLVR:把闭环能力训练进模型

搜索和反思可以在推理时外挂,RLVR 则试图把这些行为内化进模型。DeepSeek-R1 展示了可验证奖励如何推动模型形成更长的推理行为;在软件工程场景中,Agent-RLVR 使用单元测试等环境奖励,并通过计划、错误提示等 guidance 帮助模型跨过过于稀疏的奖励区。67

一个简化的训练闭环是:

flowchart LR tasks["任务"] --> policy["Agent 策略"] policy --> rollout["环境 Rollout"] rollout --> tests["测试与约束"] tests --> reward["可验证奖励"] reward --> update["策略更新"] update --> policy

RLVR 在代码领域有效,是因为这里存在编译器和测试。但这也是它的边界:

  • 测试覆盖不足时,“通过”可能只是钻了验证器的空子;
  • flaky test 会把环境噪声当成学习信号;
  • 隐式依赖和错误的容器配置会奖励错误行为;
  • 只优化 benchmark,容易让模型学会数据集特有的捷径。

所以 RLVR 的上限不只取决于算法,也取决于环境能否给出稳定、难以投机的反馈。某种意义上,验证环境本身就是训练数据的一部分。


上下文:项目说明不是越长越好

AGENTS.mdCLAUDE.md 和类似规则文件已经成为代码仓库的新配置层。它们最适合存放代码里无法直接推断的信息,例如特殊构建命令、发布限制、历史兼容要求和禁止触碰的目录。

但关于这类文件的效果,目前的实证结果并不完全一致。一项覆盖多种 Agent 和 SWE-bench 任务的研究发现,额外上下文往往降低成功率并增加超过 20% 的推理成本;另一项针对 10 个仓库、124 个 PR 的研究则观察到运行时间和输出 token 下降,同时完成表现接近。89

这不是简单的“AGENTS.md 有用或没用”,而是说明内容质量和任务分布很重要。我认为比较稳妥的写法是:

  • 只写 Agent 无法从仓库直接发现的约束;
  • 命令尽量可复制执行,不写空泛的“保持高质量”;
  • 高风险规则配合权限或脚本硬校验,不只依赖文字;
  • 定期删除过期信息,把它当配置代码维护;
  • 不要让模型自动生成一篇仓库百科后永久注入上下文。

上下文工程的目标不是让 Agent “知道一切”,而是让它在当前步骤拿到刚好够用的信息。


长期运行:中控系统最终是权限系统

Code Agent 正从一次性对话走向后台任务:它可以持续检查 CI、处理 issue,甚至跨多个小时维护一个实现计划。运行时间越长,中控系统就越不能只关注“下一步做什么”。

它还需要维护:

  • 任务状态:哪些子目标已完成,当前阻塞在哪里;
  • 工作记忆:关键证据、已修改文件和验证结果;
  • 恢复点:失败后回到哪个 commit 或工作区快照;
  • 权限边界:哪些命令可以自动执行,哪些动作必须确认;
  • 审计记录:为什么做出某个修改,使用了哪些外部信息。

长期记忆和 Skills 可以减少重复探索,但也会把错误经验固化下来。一个过期的构建步骤如果被包装成“技能”,会比一次普通幻觉更难发现,因为系统会反复、稳定地执行它。

因此,长期运行 Agent 的核心不是让它永远不停,而是让它能够在正确的位置停下来:验证不足时停、权限不够时停、目标发生冲突时停。


两年后,我更看重什么

Code Agent 的中控系统正在从 Prompt 工程变成真正的软件基础设施,但复杂度不应该成为目标本身。

我会用四个问题判断一套中控是否可靠:

  1. 它是否优先使用编译器、测试和 diff 等真实环境证据?
  2. 它是否只在关键的不确定节点增加搜索,而不是无差别消耗 Rollout?
  3. 它能否把任务状态、项目知识和长期经验分开管理?
  4. 它是否有清晰的权限、回滚和人工接管机制?

更强的模型会提高每一步的质量,但把几十步串成一次可靠的软件变更,依然是系统工程。真正成熟的 Code Agent,不只是更会写代码,而是更清楚什么时候该搜索、什么时候该验证,以及什么时候不该继续执行。


参考资料

关于 Code Agent 的整体组件,可继续阅读我的上一篇文章:A few things about Code Agent