找回密码
立即注册
搜索
发回帖 发新帖

4930

积分

0

好友

634

主题
发表于 1 小时前 | 查看: 5| 回复: 0

做一个最小 Coding Agent,看起来并不复杂:接入模型,给它读文件、搜代码、改文件、跑命令的工具,再套一个循环。模型提出动作,系统负责执行,然后把结果送回上下文。Mini-SWE-Agent 这类系统已经证明,一条很短的循环也能跑起一个会修代码的 Agent。真正的麻烦出现在它进入真实项目之后。

模型让你跑测试,测试进程一直不退出,谁来停?工具返回几万行日志,下一轮上下文爆了,谁来裁?用户点了停止,后台命令和子 Agent 还在跑,算停了吗?模型说任务完成,但没有测试、没有 diff、没有验收证据,能信吗?

这些问题不属于“模型会不会写代码”,而是模型外面的运行系统要处理的事。

2026 年 7 月 15 日,arXiv 上出现了一篇 83 页的论文,题目是 Harness Engineering: Anatomy, Architecture, and Evolution of Coding Agents。作者分析了 11 个 Coding Agent 的源码:Claude Code、Codex CLI、Gemini CLI、Mistral Vibe、OpenHands、Aider、Mini-SWE-Agent、Hermes、Pi、OpenCode 和 OpenClaw,并把 Omnigent 作为 meta-harness 对照。

先说明研究边界:论文对 Claude Code 的分析来自 2026 年 3 月流传的源码快照和发行日志,不等于对当前闭源实现的完整审计。

论文整理出 13 个横向观察、29 个复用模式和 18 条设计建议,还附了一个约 90 行的最小 Harness 骨架。数字本身不是重点。更有价值的是,作者没有停在产品介绍页,而是顺着源码里的循环、工具、权限、上下文和扩展点逐层拆解。

本文从 Claude Code 和 Codex 两个入口往外看。它们有不同的产品体验和运行组织,但源码研究真正能回答的,不是谁赢谁输,而是模型外面那层系统究竟承担了什么。

Agent 的很多差异,已经落在模型之外那层 Harness 上。

这里的 Harness,可以先理解成运行时控制面。模型提出下一步,Harness 把这一步放进工程环境:准备上下文,暴露工具,执行动作,限制权限,保存状态,压缩历史,协调子任务,再根据验证信号决定是否继续。

前面几篇关于 Harness 和 Agent 运行链的文章,一直在追同一个问题:模型离开对话框以后,怎样进入工程现场,拿到上下文、执行动作、观察结果,再把状态留下来。

这次论文把这些抽象判断放回源码样本里。它不是在讨论一个概念,而是在看 11 套系统到底把责任放在哪里,哪些责任已经从“提示词写得好不好”变成了运行时设计。

源码研究不是排行榜

读这种论文,很容易先问:Claude Code、Codex、OpenHands 谁更复杂,谁更强。

但这篇论文有一个重要边界:它是源码研究,不是同一个模型、同一批任务、同一套环境下的性能对照实验。论文里引用的一些基准成绩,模型、日期、环境都不一样,不能直接拿来排座次。

更合适的读法,是先看每个系统在替模型承担什么责任。

论文把 Harness 拆成七块:

  • Agent 循环:下一步怎么推进,什么时候停。
  • 模型接入:不同模型协议、流式输出、缓存和错误怎么处理。
  • 工具与动作:能读什么、改什么、执行什么,结果怎么回给模型。
  • 记忆与上下文:本轮看什么,历史太长怎么办,哪些信息能跨会话留下。
  • 安全与权限:动作是否允许,执行环境能碰到哪里。
  • 编排:子 Agent 怎么分工,结果怎么汇总。
  • 扩展:Skills、MCP、Hooks、插件和客户端协议怎么接进来。

这七块不是功能菜单,而是运行时的七个责任边界。任务越长、副作用越多,用户越依赖它交付结果,这些位置就越容易暴露差异。

Harness 在模型与工程环境之间的责任架构

图:模型提出下一步,Harness 用七类责任把它接入真实工程环境。

一个 Coding Agent 能不能长期用,不只看模型强不强、工具多不多,还要看这七个位置能不能接成一条稳定的运行链路。

把这七类责任接起来,运行链路大致如下:

Harness 运行链路

图:模型负责提出下一步,Harness 负责把这一步放进可执行、可观察、可验证的工程现场。

Goal 是用户目标,Context 是本轮模型能看到的信息,Decide 是模型提出下一步;Act 执行动作,Observe 返回结果,Verify 检查是否达成条件;State 留下事件和状态,Next 再决定继续、停止、重试还是交给人。

很多文章喜欢画 Planner、MCP、Memory、Reflection。那些模块都重要,但只有放进这条链里,边界才清楚。

否则很容易变成:每个词都懂,系统还是跑不稳。

循环很短,运行责任却不少

先看最朴素的 Agent 循环。

组装输入
-> 调用模型
-> 执行工具
-> 返回观察
-> 再调用模型
-> 直到结束

这个循环本身不神秘。差异在循环外面:系统怎样处理状态、并发、取消、恢复和验证。

Mini-SWE-Agent 把这件事做得很直观:一个线性循环,一个 shell 工具,少量限制。它适合研究,也适合让人看清 Agent 最小骨架。

OpenHands 往另一端走。它把执行过程写成持久事件日志,围绕会话、分支和回放组织状态;工具调用还能按资源加锁。两个读操作未必互相阻塞,两个都写同一份文件的操作就要排队。

Claude Code 的做法又不同。它按工具是否适合并发来分批,读取和搜索可以更积极地并行,写入和命令执行更保守。

Codex CLI 更像一个异步状态机。会话、模型流式事件、工具执行和对外事件读取分开处理。对于 CLI、TUI、SDK 或服务端入口,这种结构很实用:用户不只等最后一句话,也要知道系统是在请求模型、执行命令,还是等待工具结果。

本文使用的 Claude Code 源码快照里,也能看到类似的分层。远程会话会区分普通 SDK 消息、权限请求、权限取消、用户输入和中断信号;工具运行进度和压缩边界也会作为事件向外传递。这不代表当前闭源实现仍然如此,但说明论文所说的“事件化”和“控制面”有具体实现依据。

Claude Code 与 Codex 的 Harness 入口对比

图:按论文样本和本文使用的 Claude Code 源码快照归纳;比较运行组织,不比较当前产品性能。

这些差异说明:循环不是难点,循环承载的状态才是难点。

一个开发任务不是“模型说、工具做”这么简单。它还会遇到取消、超时、重试、并发、恢复、预算、验证和用户中途改需求。

因此,循环复杂不代表 Agent 一定强,循环简单也不代表系统一定弱。真正要看的是:该记录的有没有留下,该拦住的有没有拦住,该反馈给模型的有没有反馈回去。

工具不是越多越好,关键是契约清楚

Coding Agent 最容易堆功能的地方,就是工具。

读文件、搜文件、跑命令、改文件、查诊断、开浏览器、接数据库、接工单、接 MCP。工具一多,介绍页很好看,但系统不一定更稳。

论文里有一个不显眼、却很影响工程效果的观察:工具定义本身也占上下文。模型每一轮都要读工具名、参数、说明和约束。工具越多,选择负担越重,误选空间也越大。

这和 Anthropic 在 effective agents 里讲的思路一致:工具先少一点、清楚一点、可组合一点。上游系统有 50 个接口,不代表 Agent 要同时看到 50 个工具。很多时候,给模型一个面向任务的工具,比把 SaaS API 原样包一层更稳。

Claude Code 源码快照里也能看到这种压力。上下文建议逻辑会单独检查 Bash、Read、Grep、WebFetch 等工具结果占用了多少 token;结果太大时,会提醒用 head、tail、grep、offset 和 limit 收窄输入。工具接入不是终点,它的输出会反过来影响下一轮判断。

对 Coding Agent 来说,最典型的是编辑工具。

例如修改一个函数,工具可以要求模型同时提供“旧文本”和“新文本”,并要求旧文本在文件中唯一匹配。匹配不到,拒绝;匹配多处,也拒绝。

这个契约很硬,但好处是清楚。模型记错了代码,就要重新读文件;文件状态变了,工具会把问题暴露出来,而不是假装已经改对。对强模型来说,这类精确契约往往够用。模型弱一些、上下文噪声更大时,模糊匹配和修正流程才更有吸引力。

另一类工具会做模糊匹配。空格、缩进、上下文有一点偏差,工具尽量帮模型找位置。这样能提高某些场景的成功率,但也引入另一个问题:两个片段都很像时,工具到底改了哪里?

论文里几个系统在这里选择不同。Aider 有多种编辑格式和检查修正流程;Codex 使用补丁格式;Hermes、OpenCode 更强调匹配容错;Pi 会处理 Unicode 和空白差异,同时检查多处匹配和重叠编辑;Mistral Vibe 在审计窗口里从较宽松的 SEARCH/REPLACE 转向更精确的旧文本唯一匹配。

工具与编辑形式

这背后的架构问题不是“哪种编辑工具最好”,而是工具失败时,模型究竟拿到可恢复的错误,还是一个模糊的坏结果。Harness 至少要分清三组状态:

  • 搜索没有结果,与搜索输出被截断不同。
  • 命令退出失败,与命令仍在运行不同。
  • 编辑找不到唯一位置,与编辑已写入但测试失败不同。

模型下一轮能不能继续,取决于 Harness 能否准确表达刚才发生了什么。

本文检查的一个 MCP 工具实现里,编辑失败会返回“没找到”“匹配多处”“可尝试空白归一化”等信息;命令工具会区分超时、退出码、长任务 session 和找不到进程。这些细节不抢眼,却决定了模型下一步是重读、重试、等待,还是停下来交给人。

上下文压缩不是摘要作文,是状态保存

长任务一定会碰到上下文问题。

用户一开始说:“新增 CSV 导出,沿用当前筛选条件,不影响分页。”

Agent 读页面、查接口、改代码、跑测试、修类型错误。历史越来越长,系统开始压缩。如果压缩后只剩一句“正在实现 CSV 导出”,后面就容易丢掉两个关键约束:筛选条件和分页行为。

所以,上下文压缩不是把聊天记录写短一点,而是回答:下一轮继续执行,最少需要保留哪些状态?

上下文管理方式

Aider 的思路比较直接:把较早历史交给模型总结,保留最近原文。最近原文很重要,因为正在修一个编译错误时,最后那段错误堆栈比一句“编译失败”更有用。

OpenHands 把压缩做成 Condenser,并把压缩请求和结果也放进事件记录。这个细节会直接影响排障:如果 Agent 后面忘了筛选条件,可以追查是模型早就没用上,还是压缩时漏掉了。

Gemini CLI 会生成状态快照并保留近期历史。OpenCode 会按目标、重要细节、当前状态、下一步、相关文件这类结构合并摘要。Hermes 通过父子会话关联保留压缩前后的关系。Pi 的追加式 JSONL 树,则让会话分叉和回看更自然。

这里要分清两件事:上下文服务下一轮推理,事件记录服务恢复、回放和审计。

不能因为模型本轮看不到某段日志,就把它当成没用。完整事件记录不一定进入上下文,但它仍是排障和恢复的依据。

长期记忆也一样。项目测试命令、目录规则、稳定架构约束,可能值得跨会话保存。一次临时推测、一次已经修掉的错误,如果被写进“记忆”,后面的任务就会反复继承一个过期前提。

因此,记忆更适合被定义为“可复用的线索”,而不是“过去说过的话”。成熟的 Harness 要让记忆有来源、有权限,也有纠错路径。否则记忆越多,Agent 越容易背着旧前提继续工作。

不用向量检索代码,反而是一个提醒

这篇论文里有一个很容易引发争论的观察:在它检查的样本中,没有发现这些 Agent 运行时用向量嵌入检索代码。

这是样本观察,不等于向量检索在所有代码任务里都没用。

但它确实提醒我们,Coding Agent 查代码时,不要一上来就把问题想成 RAG。

代码本身有很多结构。路径、文件名、函数名、类型、导入关系、测试名、错误堆栈、调用链,都是非常强的线索。ripgrep、glob、tree-sitter、语言服务、RepoMap,经常已经能把 Agent 带到正确位置。

Aider 的 RepoMap 就是一个例子:它用 tree-sitter 抽符号,再根据当前任务排序,在有限 token 预算内给模型一个仓库地图。

这和全文搜索不是同一件事。搜索帮你找到关键词,RepoMap 帮你看到结构。

更稳妥的顺序是:先用好路径、符号、文本和诊断这些确定性信号。只有当一组真实任务反复证明“用户只有业务描述,没有关键词,结构检索找不到”,再评估语义索引是否值得。

还要分清三种检索:

  • 源码检索:找到当前工作目录里的代码。
  • 历史对话检索:找到过去发生过什么。
  • 长期记忆检索:找到未来任务可能复用的规则和偏好。

OpenClaw 在记忆里用混合检索,不能被拿来证明“Coding Agent 应该用向量检索代码”。问题不同,检索策略也不同。

权限和沙箱不是一回事

很多人用 Agent 时,会把“我同意执行”理解成安全边界。

这不够。

用户同意运行测试,只表示这次测试动作可以发生;测试进程能读哪些文件、访问哪些网络、启动什么子进程,属于另一层边界。

权限与执行边界

论文里对沙箱和权限的比较很具体。Codex 在跨平台沙箱上投入很重;Gemini CLI 也有跨平台隔离;Claude Code 有可选 OS 级沙箱;OpenHands 借助工作空间和不同执行后端;一些系统则主要依赖命令权限、检查点或内容威胁检测。

这说明系统规模和隔离强度不能简单画等号。代码多,不代表沙箱强;功能多,也不代表副作用边界清楚。

这里至少要拆成四个问题:

  • 授权:这次动作是否允许?
  • 隔离:允许以后,它最多能影响哪些资源?
  • 记录:已经发生的副作用有没有留下证据?
  • 恢复:重试时,会不会重复执行有副作用的动作?

用户点停止,也要落到这四个问题上。模型请求停了,工具停了吗?工具停了,子进程停了吗?父 Agent 停了,子 Agent 是否还在消耗 token、改文件、跑命令?

“界面显示停了”和“系统真的停了”之间,不能靠感觉。

多 Agent 先看边界,不要先看数量

多 Agent 很容易被讲成“人多力量大”,但工程任务不是这样。任务长,不一定适合多 Agent;模块多,也不一定适合并行修改。

比如还是 CSV 导出。前端和后端都要改,但接口参数还没确认。如果两个 Agent 同时写,很可能最后对不上。更合理的做法,是先让一个调查任务确认接口边界,再分开实现,最后统一集成验证。

多 Agent 编排方式

论文里提到的多 Agent 形态很多:Agent、Gemini CLI 有 Agent 注册表和远程会话接口;Hermes 对子任务权限取交集;Pi 通过扩展启动独立进程;OpenHands 和 OpenCode 用独立会话或子会话组织工作者。Claude Code 和 Codex 有协调者模式,Mistral Vibe 更偏顺序委派。

这些实现看起来都叫多 Agent,背后的诉求却不一样。并行探索、隔离上下文、复用远程服务、外包一段局部调查,都可能被包装成“子 Agent”。

所以看到“支持子 Agent”,不能马上等价成“更快”。顺序委派也许没有并行收益,但能让上下文更干净。并行工作者也许节省等待时间,但会带来冲突、合并和预算问题。

真正重要的是子任务边界。“帮我看看后端”太散;“检查导出接口支持哪些筛选参数,返回文件位置、参数定义、不支持的字段,暂不修改代码”就清楚得多。

子 Agent 的工具也可以按阶段收窄。调查任务只给读取和搜索,实施任务再给编辑和命令执行。这样主 Agent 能按阶段拿证据,而不是一开始就把环境交给所有工作者。

协调者与工作者

多 Agent 有用的地方,不是把任务拟人化成一个“团队”,而是把上下文、权限、工作目录和返回证据切清楚。

Skills、MCP、ACP:别放在同一个抽屉里

论文里还提到三个容易混在一起的接口:Skills、MCP、ACP。

它们都像“扩展能力”,但接入的对象不同。

Skills 更像过程资产。它把一类任务的步骤、规则、领域知识和可选脚本写下来,供 Agent 按需加载。论文样本里 Skills 的采用数已经到 9/11,比 MCP 的 8/11 还高。很多团队会先让 Agent 学会“按我们这里的方式做事”,再去接更多外部系统。

MCP 是工具和数据服务的连接协议。它让数据库、内部系统、文件系统、浏览器等能力以标准方式暴露给宿主。

ACP 更偏客户端和宿主接口。论文样本里,ACP 已经出现在 6 个系统中,而且不只连接编辑器和 Agent,也开始承载 harness hosting:一个系统可以通过适配器托管另一套 Harness。

这也是论文把 Harness 从工具层推向平台层讨论的原因。

一个 CLI 工具如果只服务自己,边界还比较简单。等它开始提供 SDK、HTTP 服务、会话接口、插件、技能市场、外部 Agent 托管,问题就上移了。

这时,Harness 不再只是“模型外面的一层壳”,而是一个能被嵌入、扩展和治理的平台运行时。论文把这个变化称为从 tool 到 platform。平台化的不是聊天界面,而是每一轮任务怎样被装配、执行、观察和接管。

把 meta-harness、多层反馈循环和 context engineering 放在一起看,指向的是同一个趋势:大家不再只盯着 prompt,而是在问“每一轮工作现场到底由谁维护”。

从头做一个 Harness,先补什么

这 18 条设计建议不必照单全收,第一版也不必追赶 Claude Code 或 Codex 的复杂度。面向代码任务的 Harness,可以先补五件事。

1. 事件记录:模型输入、工具参数、工具结果、文件变更、测试结果和结束原因都要能查。没有事件记录,失败以后只能靠聊天记录猜。

2. 清楚的工具契约:编辑工具要知道怎样定位、拒绝歧义、报告失败;命令工具要区分正在运行、超时、退出失败和输出截断;搜索工具要区分没找到与结果没有返回完整。

3. 上下文压缩:摘要必须保留目标、约束、已改文件、验证结果、当前卡点和下一步。压缩不能删掉唯一证据,完整事件记录要另存。

4. 权限和执行边界:用户授权、工具权限、文件范围、网络范围、子进程和取消语义要分开设计。有副作用的操作尤其不能在恢复时盲目重试。

5. 验证回路:代码写完不等于任务完成。至少要把测试、lint、类型检查、diff、业务验收条件和人工确认中的一部分接回来。没有验证,Agent 只是更快地产出“看起来完成”的东西。

这也呼应了 Martin Fowler 对外层 Harness 的讨论:一部分约束放在动作前,例如 AGENTS.md、Skills、架构规则和项目约定;另一部分放在动作后,例如静态检查、测试、结构规则、审查 Agent 和人工 review。内部运行时与外部团队流程并不孤立,它们最终都影响 Agent 能否自我修正,以及团队能否少做无效复查。

这些做好以后,再谈 Skills、MCP、多 Agent 和远程托管。

原因很简单:扩展会放大已有能力,也会放大已有混乱。一个单 Agent 连状态、权限、验证都没做清楚,拆成多个 Agent 只会让问题更难追。反过来,如果真实失败已经能被记录、被解释、被回放,再加扩展点才有依据。

Harness 最终要承住什么

这篇论文最有用的地方,不是告诉我们“照着哪个系统做”,而是把 Coding Agent 的运行责任拆开:模型接入要处理流式、缓存、错误和模型差异;工具要定义可恢复的失败状态;上下文压缩要保留任务约束和当前工作集。

权限也不能停在用户确认。执行隔离、副作用记录、取消语义和恢复策略,都要进入设计。多 Agent 和扩展接口也是同一个逻辑:先看边界是否清楚,再看要不要加 Skills、MCP、ACP 或子 Agent。

这些问题比“用了哪个框架”“有多少工具”“支持几个 Agent”更接近工程现场。

论文还有两个观察值得冷静看:样本里没有通用 Agent 框架进入运行时,也没有向量嵌入检索代码。这不是在宣布框架和向量检索没用,而是在提醒我们:真正跑起来的 Coding Agent,很多关键能力来自更确定的系统工程——循环、事件、工具契约、上下文、权限和验证。

这些东西没有那么炫,但它们决定 Agent 能不能从“会说、会写”,走到“能接手、能恢复、能交付”。模型越强,这些工程细节越不该被看成外围杂活。模型一旦开始行动,没定义清楚的边界都会落到文件、进程、权限、成本和交付结果里。

模型越强,Harness 越不能只是包装层。它开始承重了。

参考资料




上一篇:Codex 重置监控小程序:微信订阅消息实时提醒,附后端轮询实现
下一篇:科技圈降薪潮里,最先挨刀也最没保障的是外包岗
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-10 05:14 , Processed in 0.071029 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表