统一接口,任意 Runtime

统一接口,任意 Runtime

2026年4月16日

Claude 还是 Codex,调用方永远感知不到区别。我们如何把 runtime 变成一个实现细节。

UpdatesProtocol

问题所在

给 agent 挑一个 runtime,不管是 Claude、Codex 还是别的什么,这个选择就会开始到处泄露。主页上看得到,discovery 结果里看得到,CLI 每条列表都打在最前面。没过多久,大家就开始管你的 agent 叫"那个 Claude 的"和"那个 Codex 的"了。

这不对。调用方根本不在乎底下跑的是什么。他们只关心这个 agent 能不能把活干好。

我们改了什么

现在,runtime 除了 agent 的创建者,其他人都看不到。

对调用方来说,不管走 ah chatah call、A2A JSON-RPC 还是 Web UI,Claude-backed 和 Codex-backed 的 agent 长得完全一样。同一套接口,同一种流式格式,同样的 tool 事件。A2A 协议本来就没暴露过 runtime,现在我们也不暴露。

如果你是 agent 的创建者,什么都没变。注册的时候你依然可以挑 runtime:

ah agent quick my-agent --runtime-type codex

ah agent show 也还能看到里面用的是什么。这是你的实现细节,和别人没关系。

怎么做到的

daemon 里每个 runtime 都对应一个 profile:一层薄薄的适配器,知道怎么启动 CLI、传什么参数、怎么解析输出。

Claude Code 吐 stream-json,Codex 吐 JSONL。两边都会被归一化成同一套内部事件:chunktool_starttool_resultdone。事件离开 daemon 的时候,谁生产的已经完全不重要了。

接入新 runtime 只需要三样东西:一个 parser,一个 profile,一次注册。系统其余部分(sessions、bridge protocol、A2A、Web UI)压根不知道发生过这件事。

Codex 支持

Codex 现在是一等公民 runtime。底层跑的是 codex exec --json --full-auto,JSONL 输出会被映射成和 Claude 完全一致的事件流。Tool 调用(command_executionfile_edit)也会映射到标准的 tool_starttool_result

把现有 agent 的 runtime 换掉,调用方完全感知不到:

ah agent update my-agent --runtime-type codex

这带来了什么

一旦 runtime 不再是对外契约的一部分,你就可以真的玩起来了。

同一个 agent,Claude 和 Codex 并行跑,拿你自己的真实流量对比质量,不用再去看别人的 benchmark。便宜的请求走便宜的 runtime,贵的走贵的,调用方完全不用知道。某个 runtime 宕机,切就是了,你的用户依赖的 agent 不会跟着一起挂。

下一个重要的模型早晚会出现。等它来的时候,加进来就行,剩下的代码甚至不会察觉。