将bash工具从Claude Code Style迁移到Codex Style #3342
Replies: 3 comments 3 replies
|
我也经常遇到命令意外卡死和超时影响 Cache 的情况,这个痛点提得非常及时! 不过关于把默认工具切到 Codex 风格的 Yield + Poll 模式,结合 Maka 的使用体验,我个人有一点顾虑,想和大家探讨一下:
个人感觉更轻量的思路: |
|
我觉得这里有一个概念最好明确区分:yield 和 background 不是一回事。 Maka 之前其实已经尝试过比较接近“长时间运行后自动转后台”的方案。
所以基于这些历史,我会比较谨慎地把下面这种语义重新作为默认行为: 我觉得 Codex-style 方案真正值得借鉴的地方稍微不一样:
例如: 这里它仍然是同一个 foreground process,并没有因为超过 10 秒就被悄悄地变成一个“background task”。 变化的只是:这一次 tool call 不再继续阻塞 model loop。 我觉得这个区分很重要,因为它可以同时得到:
completion event 当然仍然有价值,但我更倾向于把它看成建立在 session/process-handle 模型之上的一种优化,而不是用它替代 session 模型。 例如: 这样也不会重新引入 #764 当时要解决的问题:因为运行时间过去了多久,Runtime 就隐式改变了命令原本的执行语义。 至于 polling 会产生更多 Events / Turns 的问题,我觉得这个问题确实存在,但它可能更适合在 Runtime / Event Log 的表示层解决。 例如连续的空 poll,并不一定每一次都需要作为同等级的 first-class timeline event 暴露出来。 所以我个人更倾向于: foreground = short yield + persistent process handle background = explicit intent completion notification = optional optimization 而不是根据命令运行了多久,自动把 foreground execution 转成 background execution。 |
|
TL;DR: I recommend against a wholesale migration to the Codex-style tools. After a source-level survey of 10 mainstream agent projects, I believe both the Claude Code style and the Codex style each got half of the problem right, and that there is a third form — park (the tool call simply doesn't return) + debounce-based output chunking + durable process handles — that is conceptually smaller than either and lands almost directly on Maka's existing ShellRun skeleton. Below: the full comparison, the reasoning, point-by-point answers to the 5 discussion questions, and an honest list of this proposal's known weaknesses (including a caveat about my own benchmark results). 1. The survey: 10 projects fall into three and a half schoolsI shallow-cloned and read the current source (2026-08) of codex, pi, grok-build, kimi-code, opencode (v2), hermes-agent, deepseek-harness, prime-agent, lobehub, and deer-flow. All claims are backed by file:line-level evidence:
Three and a half schools: model-driven polling (Codex, lobehub), runtime wake-up (grok, kimi, opencode, hermes, DSH), pure blocking (Pi, deer-flow), plus the PTC half-school (prime-agent's persistent kernel, Codex's code mode, DSH's run_code — these three have not converged, so I set PTC aside for now). Two widely repeated claims need correcting:
2. Reframing: this is not about tool shapeThe original framing of #3342 is "what should the tool look like." With 10 implementations side by side, the essential question emerges: processes live in wall-clock time; the model lives in discrete steps — who absorbs the waiting in between? There are only three candidates:
The model is the only actor in the system that pays to check the time. So the default path should never have the model doing the waiting — both camps agree this far. The disagreement is how the runtime hands control back. 3. Diagnosis of both routes (including a correction to my own initial conclusion)The Codex route (polling): for the same wait duration, polling strictly costs more requests than any runtime-owned scheme — it is a dominated strategy. The only thing it buys is intervenability, and intervenability comes from the handle, not from making polling the default. What is genuinely worth absorbing from Codex is yield does not change process semantics (@me2seeks's distinction in this thread is exactly right): yielding only returns control to the agent; the process is still the same process, not implicitly converted into something else by elapsed time. That property must be preserved. The wake-up route (grok/kimi/opencode, and @cat0825's lightweight variant): I initially believed this was optimal, and have since corrected myself. Note one survey fact: every project that ships wake-ups was forced to build concurrency machinery — grok has admission control against wake storms, kimi has three-set exactly-once dedup, DSH has maxConsecutiveWakes against self-excitation. This is not a coincidence; it is evidence that wake-up is inherently a concurrency problem: the arrival time is decided by an external process, arrival while the agent is busy forces queueing, queueing makes it late, and lateness destroys the value of waking. Wake-up as the default continuation costs an entire admission/dedup/synthetic-message apparatus as its entry fee. (I previously argued wake-ups are expensive because of cold-cache re-encoding; I withdraw that argument: cache renewal is a problem every design faces and does not differentiate them. Noting this for honesty.) 4. My proposal: park + debounce chunking + handlePark: the tool call does not return; the runtime suspends the agent loop; when a boundary is reached, the result is filled into the same tool call. From the model's perspective this is pure blocking — but it is not a new mechanism: "the tool call hasn't returned yet" is already a native concept of the agent loop. Zero new abstractions. Compare: polling teaches the model to be a scheduler; wake-up requires a concurrency apparatus; park builds nothing. Debounce chunking: park is not unbounded. The tool call returns at Handle: a ref plus three verbs. The key one: calling read on a running ref parks until the next output chunk — this is the embodiment of "keep waiting," and the entry point for an "explicit waiter" (wake-up can therefore safely respond only to explicit subscriptions, never by default). There is no background concept: every process has a durable ref from birth; early return does not change process semantics, only who is watching. The tool surface converges to: 5 core concepts (command/cwd/ref/read/stop) + 2 pluggable ones (pty/write can live in a profile/extension — Pi's interactive-shell extension proves this path). Each surviving concept earns its place by naming something the command string cannot express, or something whose expression would blind the permission/audit system. This is closer to Maka's current state than it looks: the existing Bash + WriteStdin + StopBackgroundTask + Read(ref) skeleton is all there. The changes are semantic tightening, not additions — replace timeout_ms with debounce chunking (without killing the process), decouple pty from run_in_background, unify the return shape. 5. Point-by-point answers to the 5 discussion questions
6. Honest cost list (known weaknesses of this proposal)To avoid this reading like armchair design, here are the real costs I verified against Maka's source:
7. Open question: I doubt my own benchmark attributionMy internal comparison shows Codex mode performing better with GLM-5.2 and DSv4F — but I cannot rule out post-training familiarity (these models' tool-call traces have likely seen Codex-style interaction extensively) rather than an architectural advantage. The ablation I plan: keep the schema in Codex form, change only the runtime behavior (block-to-completion vs. debounced early return), single model (DSv4F), long-command task subset. If identical schemas with different runtimes still diverge, the yield checkpoint itself carries value. Challenges to this experiment design are welcome. One final note: everything above is a position, not a verdict — I may well be wrong. If you see a formulation that is more Occam-lean or closer to the fundamentals, I genuinely want to hear it. Pushback and challenges are very welcome. Methodology disclosure: this reply is based on AI-assisted source-level research across the 10 repositories above (multiple read-only research agents collecting file:line evidence in parallel), and the proposal was revised through two adversarial review rounds with external models (Claude, Codex CLI). 简体中文TL;DR:我不建议整体迁移到 Codex 风格。我调研了 10 个主流 agent 项目的 shell 工具源码后认为,CC 风格和 Codex 风格各自只答对了一半,还存在第三种形态——park(tool call 停着不返回)+ 防抖切段 + 持久 handle——它在概念数上比两者都少,并且正好落在 Maka 已有的 ShellRun 骨架上。下面附完整对比、推理过程、对 5 个讨论点的逐条回答,以及这套方案已知的坑(含对我自己 benchmark 结果的诚实存疑)。 1. 先放调研:10 家其实分成了三个半流派我浅克隆并源码调研了 codex、pi、grok-build、kimi-code、opencode(v2)、hermes-agent、deepseek-harness、prime-agent、lobehub、deer-flow 的最新版本(2026-08),所有结论均有 file:line 级证据:
三个半流派:模型轮询(Codex、lobehub)、runtime 唤醒(grok、kimi、opencode、hermes、DSH)、纯阻塞(Pi、deer-flow),外加 PTC 那半个(prime-agent 的常驻内核、Codex 的 code mode、DSH 的 run_code——这三家形态还没收敛,先不展开)。 两个流传较广的说法需要纠正:
2. 问题的重新表述:这不是工具形状之争#3342 的原始 framing 是"工具长什么样"。但把 10 家摆在一起后,本质问题浮现出来:**进程跑在墙钟时间里,模型活在离散的步里,中间这段等待由谁消化?**候选只有三个:
模型是系统里唯一"看表要花钱"的角色。所以默认路径上不该由模型来等待——这一点两家的共同结论是一致的,分歧在 runtime 等完之后怎么交棒。 3. 对两条路线的诊断(含对我自己初步结论的修正)Codex 风格(轮询):在相同等待时长下,轮询的请求数严格多于任何 runtime 方案——它是被支配策略。它唯一买到的是"可干预性",而可干预性用 handle 就能获得,不需要把轮询设为默认。Codex 真正值得吸收的不是轮询,是 yield 不改变进程语义(@me2seeks 在楼里的区分完全成立):yield 只是把控制权还给 agent,进程还是那个进程,没有因为"超时"被隐式转成别的东西——这条必须保留。 唤醒路线(grok/kimi/opencode,以及 @cat0825 的轻量方案):我一度也认为这是最优解,后来修正了。注意一个调研事实:每一家做唤醒的都被迫配了并发机器——grok 有 admission 控制防唤醒风暴、kimi 有三集合 exactly-once 去重、DSH 有 maxConsecutiveWakes 防自激励。这不是巧合,是"唤醒本质是个并发问题"的证据:唤醒到达时刻由外部进程决定,到达时 agent 正在做别的事就要排队,排队就不及时,不及时唤醒的价值就塌了。唤醒作为默认 continuation,入场费是一整套 admission/dedup/合成消息机器。 (我曾用"冷缓存重编码"论证唤醒贵,后来自己撤回了:缓存续期是所有方案共同面对的问题,不构成方案间的区分度。诚实起见注明这一点。) 4. 我的提案:park + 防抖切段 + handlepark:tool call 不返回,runtime 挂起 agent loop,边界到达时把结果填进同一个 tool call。模型视角 = 纯阻塞,但它不是新机制——"工具调用还没返回"本来就是 agent loop 的原生概念,零新抽象。对比:轮询要教模型当调度器,唤醒要造一整套并发机器,park 什么都不造。 防抖切段(debounce):park 不是无界的。tool call 的返回边界是 handle:ref + 三个动词。关键一条:对 running 的 ref 调 read 时默认 park 到下一个输出段——这就是"我继续等"这句话的实体,也是"显式 waiter"的入口(唤醒因此可以安全地只响应显式订阅,不当默认)。没有 background 概念:进程从出生就有 durable ref,提前返回不改变进程语义,只改变谁在看。 工具面收敛为: 5 个核心概念(command/cwd/ref/read/stop)+ 2 个可插拔(pty/write 可作为 profile/扩展承载,Pi 的 interactive-shell 扩展证明这条路可行)。每个概念留下的理由是它命名了命令字符串表达不了、或表达出来会让权限/审计系统变瞎的东西。 这套方案与 Maka 现状的距离比想象近:现有 Bash + WriteStdin + StopBackgroundTask + Read(ref) 骨架都在,改动是语义收紧而非新增——timeout_ms 换成防抖切段(进程不杀)、拆掉 pty⇒run_in_background 耦合、统一返回形状。 5. 对原帖 5 个讨论点的逐条回答
6. 诚实的代价清单(这套方案已知的坑)为避免这看起来像纸上谈兵,列出我拿 Maka 源码核实过的真实代价:
7. 开放问题:我对自己的 benchmark 归因存疑我内部的对比实验显示 Codex 模式在 GLM-5.2 和 DSv4F 上表现更好——但我不能排除这是后训练熟悉度(这两个模型的 tool-call 轨迹大概率大量见过 Codex 风格交互),而非架构本身的优势。我计划的消融实验:schema 保持 Codex 形态不变,只改 runtime 行为(阻塞到底 vs 防抖提前返回),DSv4F 单模型、长命令任务子集。如果 schema 相同而 runtime 不同仍产生差异,才能说明 yield 检查点本身有价值。欢迎对这个实验设计的挑战。 最后说明:以上只是一家之言,未必对。如果你看到更符合奥卡姆剃刀、更贴近问题本质的方案,我非常希望看到——欢迎 push back 和挑战。 调研方法披露:本回复基于对上述 10 个仓库最新源码的 AI 辅助调研(多个只读研究 agent 并行取证,所有论断带 file:line 引用),方案经两轮外部模型对抗评审(Claude、Codex CLI)修正。 |
Uh oh!
There was an error while loading. Please reload this page.
当前默认使用的是 Claude Code 风格的 bash 命令工具(支持
write_stdin),但background=false的问题仍然非常值得关注。Current Approach & Pain Points
background=false时,如果命令进入自循环 / 长时间阻塞(例如交互式 CLI、REPL、等待输入的进程、或意外的死循环),会直接卡住整个 agent 运行。Why Codex-style is better
Codex 的
exec_command+write_stdin方案采用 yield + poll 模式:exec_command启动命令,最多等待yield_time_ms(默认 10s),如果还在运行就返回session_id。write_stdin(可带chars或空字符串)继续发送输入 / 轮询输出。优点:
结论:从 cache 友好性和 agent 可控性来看,Codex 方案实际上更优。
Existing Implementation
我已经有一个可用的 DeepSeek / DSH 插件实现了这套方案,并且做了参数简化:
exec_command(cmd, workdir?, yield_time_ms?, max_output_tokens?)write_stdin(session_id, chars?, yield_time_ms?, max_output_tokens?)这个插件可以直接作为参考 / 默认实现。
Discussion Points
exec_command+write_stdin)?yield_time_ms默认值、max_output_tokens、session 生命周期策略是否需要再调优?欢迎大家分享实际使用体验、对比测试结果,或者直接反对意见。
如果大家倾向切换,我们可以进一步讨论落地计划(preset、文档、默认配置等)。
All reactions