neal:在复杂编码项目上协调不同模型

HN AI Tools 2026-08-08T00:50:10.948394


neal 的第一个版本就是一个计划文件加下面这段提示词:

Execute @plans/EMBER_MIGRATION.md. keep going. don't stop unless you are blocked.

我在公司一次大型前端框架升级时,把这段提示词输入了 Codex。当时最新的模型是 GPT-5.4。我的想法是,把好几个月烦人的活儿交给它,我定期看一眼进度,其余时间让它自己推进。每次这种安排出了岔子,我就往里面加点东西。后来这就成了 neal——一个围绕编码代理(coding agents)协调任务的本地命令行工具。

迁移任务

我们的前端代码库早就该从 Ember.js v3 升级到 v5(咳咳……其实是 v6)了。这会影响到几乎所有代码:上千个文件、大量语法改动,还要替换掉那些已废弃的写法。大部分改动都是重复性的,但又不容易用脚本一键搞定。有些公共组件需要彻底重写。我们估算,人工来做要超过 300 个小时。即便我们有一套还算全面的测试套件,这个项目仍然风险不小。

在我们精简的工程团队看来,这几乎是不可能完成的任务。直到编码代理出现。2025 年年中的时候,我兴奋地把 Claude Sonnet 4 派上去干活,结果只看到它和我们的测试套件玩起了打地鼠:修了同一个 bug,又弄出同一个 bug,反反复复没完没了。当时需要投入的盯梢精力太多,换来的却是平庸的结果,实在不划算。于是这个迁移任务就被搁置了。

今年早些时候,我们觉得是时候再试一次了。Codex 和 Claude 这些模型在规划、编码和自主工作方面的能力都有了明显提升。我希望能这样:给它一份详细的迁移计划文档,让某个前沿编码代理自己一步步把迁移做完,我时不时过来检查一下进度。

Codex 总是停下来

那份迁移计划里附带了 Ember 升级资料的链接、跑测试套件的命令,还要求它一次处理 10 到 20 个相关的文件。计划里还维护了一份尚未迁移的文件清单,这样 Codex 万一被打断,也能知道从哪儿接着干。

有一阵子,光靠这个提示词效果意外地好。Codex 能独立工作很长时间。即便我中途重启它,计划里仍然清楚标着:哪些完成了、下一步做什么、剩余工作有哪些约束。

问题出在“让它持续干下去”这件事上。Codex 完成一批任务后,常常无缘无故就停了。我问它:“为什么没继续迁移?” 它经常回答:“我继续了啊。”可实际上它已经闲置了好几个小时。

长时间会话还暴露了第二个问题:Codex 会逐渐偏离之前遵守的指令。我在计划里加了一句“开始处理下一批文件前,先重新读一遍迁移计划”,但光靠这个并不总能奏效。我给这种表现起了个名字:上下文腐化(context rot)。在一次很长的会话中,随着上下文不断膨胀或被压缩,早期给出的指令和决策会渐渐失去约束力。

我试过在 Codex 内部解决这两个问题。我把提示词做成了一个带 Stop 钩子(hook)的 $work-autonomously 技能(skill),用文本标记来表示“这一批文件已完成”或“遇到了阻塞”,并且要求它在每批任务开始前用 /new 重置上下文。这个技能起了一定作用,但 Codex 仍然不是每次都记得开新会话。

我问 Codex:

you don't always follow the instruction to start a new context with every chunk of work. how feasible would it be to build a node app to direct codex to do a chunk of work?

这个问题成了 neal 的前身:一个用 Codex SDK 编写的 node 脚本,以循环方式执行迁移任务。

引入第二个模型做审查

在查看增量迁移提交时,又暴露出另一个问题:代码质量并不总是那么理想。我决定引入自己常用的一套工作流:先让 Codex 实现功能,再让 Claude 审查代码,最后让 Codex 根据 Claude 的反馈做出修改。如此反复,直到两个模型达成一致,然后我再亲自审查改动。

我在 node 脚本里加上了这一步:用 Claude SDK 审查最新一次提交。脚本会把 Claude 的反馈抓取下来,再发给 Codex。到这时候,我决定给这个编排程序起个名字:neal。

名字来源于 anneal(退火)——一种将材料置于受控冷热循环中,直到其内部应力稳定下来的工艺。我去掉了开头的“an”,把这个过程留了下来。

neal 能做什么

neal 是一个本地命令行工具,围绕一份计划文档协调 planner(规划者)、coder(编码者)和 reviewer(审查者)三种角色。你可以为每个角色选择不同的服务商和模型。最近我这边是用 Anthropic 的 Fable 当编码者,OpenAI 的 Sol 当审查者。

neal run 是常规工作流。它先把一份粗略的计划交给规划者和审查者,由他们为计划确定可执行的形态,把较大的工作拆分成若干 scope(任务范围),并补充实现思路、验证方式和成功条件。至于文件级别的探索和本地的具体实现选择,则留给编码阶段去处理。如果你想在动手之前先读一读或者改一改完善后的计划,也可以单独使用 neal planneal execute

neal 的核心循环:一份计划文档为每个 scope 提供全新的 agent 上下文,agent 执行该 scope(卡住时可咨询),提交代码(需经过审查),然后循环进入下一个 scope。所有 scope 完成后,执行最终审查并压缩提交。

执行阶段,编码者每次都会带着全新的上下文进入一个 scope,实现并验证工作,随后提交。只读的审查者则跨 scope 保持自己的上下文,针对每个 scope 检查对应的提交。审查意见会返回给编码者,直到审查者认可为止。

当一次运行卡住时,neal 可以让审查模型进行一轮有边界的顾问式介入。这适用于编码者被阻塞、编码者与审查者循环陷入僵局,或者 scope 拆分不合理等情形。顾问可以诊断问题、给编码者指明具体方向,但它不能改代码,也不能免除验证要求。如果前方没有安全可行的路,neal 就会请求人工介入。此外,如果某个 scope 在实践中被证明过大,也可以把它拆成一个子计划。

每个 scope 都被接受后,编码器和评审者会对完整的实现和计划做最后一轮检查,发现的问题会重新进入同样的实现和评审循环。除非你明确要求不这么做,否则 neal 会把各 scope 的提交压缩(squash)成单个提交。运行状态、执行记录和评审产物都存放在 .neal/ 目录下,因此一旦运行中断,可以从最后记录的阶段继续。

neal 为 Codex 和 Claude 提供了原生适配器,同时也支持 OpenAI 兼容接口,可用于 OpenRouter 等服务,以及 Ollama 或 vLLM 这类本地端点。原生适配器可以直接复用你现有的 Codex 和 Claude 订阅鉴权。我之所以这样设计,是因为一个由规划、编码、评审组成的长时间循环,走订阅套餐要比按 token 付费便宜得多。另外,我很幸运,公司愿意报销我的 Codex 和 Claude Max 订阅费用 ;-)

迁移落地

迁移完成了。neal 完成了这次迁移。迁移分支最终包含 549 个提交,涉及到 3000 多个文件,还新增了大约 13000 行测试代码。在客户的验收测试环境中跑了数周之后,我们于上周日把它发布到了生产环境。

我倒是希望可以说整个迁移只花了几天时间,但实际上,迁移进行的同时我还在开发这个编排器,所以并不清楚项目真正的执行耗时。整个过程大概用了一个月,但其中大部分工作是在写 neal。可以这么说,开发 neal 比迁移本身有意思多了。

SWE-bench Pro

我还好奇,Codex + Claude 这种组合在标准编码基准测试上表现如何。为了找到一个公认的、仍有区分度、又能支持自定义编排器的基准测试,我着实钻研了一番,最后选定了 SWE-bench Pro。

一开始有105个Codex(GPT-5.5)自己无法解决的案例。我用Codex同时担任三个角色来运行neal,结果有8个之前失败的案例通过了。接着我用Codex负责规划和编码、Claude(Opus 4.8)负责审查,再次运行同一批案例,这次有15个通过。我本来希望效果更好,但这个实验确实体现了我个人认为neal有价值的部分:一个能生成有针对性的、经过审查的规划循环;编码者和审查者之间的实现循环;以及让不同模型担任审查者的做法。测试框架、方法和每个案例的结果都在neal-swebench仓库里。

在neal上做了这么多轮迭代(所有基准测试和Ember迁移)的一个意外收获,就是积累了大量运行产物。我有LLM聊天记录、计划修订记录、每个阶段需要多少轮循环的记录等等。我可以定期(当然是用编码代理)分析最近的运行结果,找出失败和低效的地方。这些发现反馈给neal,用来改进它自己的设计。

模型兼容性

一个模型能回答问题,不代表它能胜任neal的某个角色。neal要求结构化的输出、工具调用、文件检查,以及特定的审查结论。很多在其他方面很优秀的模型都会在这些协议中的某一项上失败。

neal compat 会针对编码、审查和规划三个角色运行小型确定性任务,并报告通过或不通过。通过只表示模型能说neal的“协议语言”,并不代表它擅长规划、编码或审查。

作为这个项目的一部分,我用90个OpenRouter模型运行了兼容性检查,以Codex(GPT-5.5)作为每个候选模型的已知合格搭档。44个模型在所有角色上都通过了。带日期的结果和具体的失败次数都在仓库的compatibility列表里。

neal compat 还发现了neal自身的bug。五个旗舰模型以完全相同的方式失败,原因是我的验证器里有一条过于严格的规则。另一组审查失败则源于Ubuntu 24.04的AppArmor阻止了Codex的只读沙箱。这两种情况下,问题都在测试框架而不是模型本身。

试试看

查看原文