7大开源Agent源码对比解读——重连与容错
7大开源Agent源码对比解读——重连与容错
本文是「7大开源Agent源码对比解读」系列第 5 篇。评估对象:codex、gemini-cli、qwen-code、opencode、kimi-code、deepseek-harness(下称 dsh)、oh-my-pi(下称 omp)。重试参数均来自源码常量,可复核。
| 篇 | 文章 | 内容速览 |
|---|---|---|
| 总述 | 拆了七大开源 Agent 的源码,最高分竟然不是 Codex | 总体结论、评估方法与评分卡 |
| 01 | 架构对比 | 四种流派与分化根源、主循环与事件机制、插件化与耦合风险 |
| 02 | 上下文管理 | 压缩触发阈值、摘要方式、token 计数口径、跨会话记忆 |
| 03 | 会话管理 | 持久化模型三层次、并发控制、崩溃恢复与 resume / fork |
| 04 | 工具调用 | 注册与可见性、并行调度四种语义、审批门控、错误处理 |
| 05 | 重连与容错 | 重试预算、流中断处理、降级链、副作用安全 |
| 06 | 系统提示词与指令遵循 | 四种组装范式、动态注入、注入防御三层与共同敞口 |
| 07 | 思维链与工作流编排 | 思维链接入、plan 模式语义、子代理治理、工作流引擎 |
| 08 | 性能设计 | 前缀缓存三档分化、启动优化、成本核算 |
| 09 | 可扩展性 | MCP 接入、自定义工具、多 provider、SDK 与 API |
| 10 | 安全与权限控制 | 沙箱两种语义、审批默认姿态、企业管控、敏感数据保护 |
七个项目的重连容错策略可以归为三类,差异在为谁优化。先说共性:没有项目实现 turn 内断点续跑,所有重试都是整请求重发。所以「重试是否安全」完全取决于「重发会不会产生重复副作用」,这正是各家处理深浅不同的地方。
重试预算总览
| 项目 | 重试次数 | 退避与抖动 |
|---|---|---|
| codex | 请求 4 次 / 流式 5 次 | 200ms 起指数翻倍,±10% 抖动,无延迟封顶 |
| gemini-cli | 10 次 | 5s 起,30s 封顶,±30% 抖动 |
| qwen-code | 7 次(另有持久模式) | 1.5s 起,30s 封顶;持久模式单次等待最长 6 小时 |
| opencode | 5 次 | 2s 起,30s 封顶,+0~25% 单向抖动 |
| kimi-code | 10 次 | 0.5s 起,32s 封顶,+0~25% 单向抖动 |
| dsh | 2 次(另有无限档) | 0.5s 起,10s 封顶,±10% 抖动 |
| omp | oneshot 默认 3 次 | 8s 封顶 × 75~100% 缩减抖动 |
第一类:配额毛刺友好型
qwen-code、gemini-cli、kimi-code 服务的是免费和配额型 API 的真实工况:后端可能持续 429 若干分钟。它们的共同特征是大预算、长封顶,宁可等也不让用户看到失败。
qwen-code 的持久模式是无人值守场景的专门档位(需显式开启,不会在 CI 环境自动启用,避免把快速失败变成无限等待):429/529 错误触发,退避单次封顶 5 分钟,单次等待绝对上限 6 小时,期间每 30 秒向 stderr 打一条心跳(Waiting for API capacity... attempt N, retry in Ms),目的是让 CI runner 不因长时间无输出被杀(utils/retry.ts:24-26、192-221)。注意 HTTP 500 被排除在持久重试之外,注释的理由是 500 可能是永久性服务端 bug。Qwen OAuth 配额耗尽则立即快速失败,抛出带迁移指引的错误,故意不带 status 字段,防止被流侧重试循环重新捕获。
gemini-cli 的模型降级:持续 429 时按策略链选候选模型,降级到 Flash 系小模型(fallback/handler.ts:26)。交互模式先允许 3 次静默重试再弹降级对话框,无人值守模式直接走标准预算。
kimi-code 的预算是算出来的:0.5s 起、每次翻倍、32s 封顶,10 次重试的总等待约 2~3 分钟,注释明说是为「扛过 provider 持续 429 的典型过载窗口」设计(loop/retry.ts:11-15)。
这一类还有一个共同创新:内容级错误重试。模型流式完成(HTTP 200)但产出无效,也被当成可重试错误。
| 项目 | 内容级错误类别 |
|---|---|
| gemini-cli | 9 类:无 finish_reason、空响应、畸形工具调用、只有思考无正文等 |
| qwen-code | 7 类,含协议标签泄漏、上游降级响应等国内工况特有项 |
纯传输层重试看不到这类错误,需要在流消费层做判定。
第二类:严格短重试型
codex、dsh、opencode 假设后端是稳定的自建或付费服务,重试预算小(2~5 次),失败快速上抛。但各有一个逃生阀。
codex 的逃生阀是连接级无限重试。 错误是连接失败、请求类型是采样(压缩请求不算)时,进入无限重连分支:5 秒起步、翻倍、60 秒封顶,不消耗 5 次重试预算(responses_retry.rs:44-83)。流式重试耗尽 5 次后还有一层:尝试从 WebSocket 切到 HTTPS 传输,切换成功后重试计数清零再试一轮,降级是会话级粘滞的(:85-100)。
dsh 把重试写进会话日志。 llm/retry 是原生会话事件类型,携带失败信息、第几次重试、延迟、策略键(llm-retry/src/types.ts:9-16),还有不变量校验(重试编号须与 turn 匹配、次数不超上限)。resume 一个会话后能看到「这个 turn 经历过几次重试、每次等了多久、因为什么错误」。这和它的事件溯源哲学一脉相承。
opencode 把重试状态透传给用户。 每次重试前发布状态事件,前端能显示「第 N 次重试,X 秒后重试」的倒计时(processor.ts:660-674)。免费额度耗尽这类错误还会附上「升级」引导动作。
第三类:副作用安全型
omp 是唯一把「流已经产出可见内容时能否盲目重试」当成一等问题处理的项目。
withEmptyCompletionRetry 的机制(ai/src/utils/empty-completion-retry.ts:74-148):先缓冲内容开始事件,不立即外抛;一旦真实内容流式到来,标记 committed = true 并 flush 缓冲;流结束后,只有在「未产出任何可见内容、停止原因正常、输出 token 几乎为零」时才允许重试。一旦 committed,绝不重试,因为半截输出已经外抛,重发会重复副作用。配套的 replayUnsafe 开关是硬闸:即便错误本身可重试,只要处于已产出内容的场景就返回不可重试。
omp 还有凭据轮换重试:403 通常意味着 token 缺权限,作废当前凭据轮换到同组兄弟账号,每次轮换是全新的重试预算(上限 64 次);但「并发上限 403」是瞬时的,不轮换(auth-retry.ts:98-100)。模型层另有服务端 fallback:Anthropic provider 支持请求体带 fallbacks 数组,内容安全拦截时服务端自动切备用模型。
流中断与超时处理
| 项目 | 流中断处理 | 超时设计 |
|---|---|---|
| codex | 流关闭而未收到 completed 即显式失败 | 流空闲 300s,WS 连接 15s |
| gemini-cli | mid-stream 重试 4 次,重试前发信号让 UI 丢弃半截渲染 | shell 不活动超时 |
| qwen-code | 内容级重试 7 类 | 流空闲看门狗 4 分钟,合成超时错误走既有重试栈 |
| opencode | 中断时收尾 + 清理孤儿工具 | MCP 连接 30s,bash 按参数设超时 |
| kimi-code | 溢出→缩窗→压缩→重试循环,3 轮熔断 | 子代理 2 小时,hook 30 秒 |
| dsh | 空响应当错误 | 流空闲看门狗 5 分钟映射为 TIMEOUT |
| omp | replay 安全判定,已产出内容禁盲目重试 | 按工具类型独立配置超时 |
两个值得细说的设计:
gemini-cli 的 RETRY 事件。 mid-stream 重试前先向事件流发一个 RETRY 事件,注释写明「UI 应丢弃已渲染的半截内容」(geminiChat.ts:77-79)。重试不仅是网络行为,还要协调前端渲染状态,这一步很容易被忽略。
qwen-code 的看门狗来自真实事故。 设计文档记录了一次根因:某接口接受请求返回 200 后静默不输出约 595 秒,OpenAI 客户端的超时是请求级的,流建立后分片间空闲无界。修复方案是在管线加看门狗,把静默卡顿合成超时错误,让既有的重试降级栈处理(constants.ts:33)。这个坑对任何流式实现都适用。
kimi-code 的溢出恢复循环独立于网络重试:provider 返回上下文溢出错误后,自动压缩、重试、仍溢出再缩窗再重试,最多 3 轮,成功一轮就重置计数(compaction/strategy.ts:13-30)。它针对的是请求体超窗,和内容级重试是两条不同的恢复链。
给 Agent 开发者的借鉴清单
- 先想清楚你的后端工况。 面向免费配额服务就学第一类(大预算、长封顶、心跳保活、模型降级);面向稳定付费服务就学第二类(短重试、快速失败、逃生阀)。
- 尊重 Retry-After。 七家都做了,opencode 做得最彻底:有响应头时不受本地 30 秒封顶约束,完整尊重服务端指示。
- 长等待要输出心跳。 30 秒一条,否则无人值守环境会杀进程(qwen 的教训)。
- 内容级无效需要独立检测。 HTTP 200 不等于响应可用,畸形工具调用、空响应、协议泄漏都该进重试判定。
- 降级链是逃生阀。 传输降级(codex WS 切 HTTPS)、模型降级(gemini Flash)、凭据轮换(omp),至少配一条。
- 已产出可见内容的流禁止整请求重发。 半截文本重发会重复副作用,这是被普遍忽视的问题,omp 的 committed 标记是参考实现。
- 重试对用户可以透明也可以可见。 opencode 的倒计时让用户知道系统在等什么,比静默挂起体验好。
- 补齐 turn 内断点续跑。 七家都没做,崩溃只能会话级恢复,进行中的采样全部重来。对超长 step 这是真实痛点,自研时值得优先考虑。
文档地图
| 篇 | 文章 | 内容速览 |
|---|---|---|
| 总述 | 拆了七大开源 Agent 的源码,最高分竟然不是 Codex | 总体结论、评估方法与评分卡 |
| 01 | 架构对比 | 四种流派与分化根源、主循环与事件机制、插件化与耦合风险 |
| 02 | 上下文管理 | 压缩触发阈值、摘要方式、token 计数口径、跨会话记忆 |
| 03 | 会话管理 | 持久化模型三层次、并发控制、崩溃恢复与 resume / fork |
| 04 | 工具调用 | 注册与可见性、并行调度四种语义、审批门控、错误处理 |
| 05 | 重连与容错 | 重试预算、流中断处理、降级链、副作用安全 |
| 06 | 系统提示词与指令遵循 | 四种组装范式、动态注入、注入防御三层与共同敞口 |
| 07 | 思维链与工作流编排 | 思维链接入、plan 模式语义、子代理治理、工作流引擎 |
| 08 | 性能设计 | 前缀缓存三档分化、启动优化、成本核算 |
| 09 | 可扩展性 | MCP 接入、自定义工具、多 provider、SDK 与 API |
| 10 | 安全与权限控制 | 沙箱两种语义、审批默认姿态、企业管控、敏感数据保护 |
葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。
更多推荐



所有评论(0)