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-cli10 次5s 起,30s 封顶,±30% 抖动
qwen-code7 次(另有持久模式)1.5s 起,30s 封顶;持久模式单次等待最长 6 小时
opencode5 次2s 起,30s 封顶,+0~25% 单向抖动
kimi-code10 次0.5s 起,32s 封顶,+0~25% 单向抖动
dsh2 次(另有无限档)0.5s 起,10s 封顶,±10% 抖动
omponeshot 默认 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-cli9 类:无 finish_reason、空响应、畸形工具调用、只有思考无正文等
qwen-code7 类,含协议标签泄漏、上游降级响应等国内工况特有项

纯传输层重试看不到这类错误,需要在流消费层做判定。

第二类:严格短重试型

codex、dsh、opencode 假设后端是稳定的自建或付费服务,重试预算小(2~5 次),失败快速上抛。但各有一个逃生阀。

codex 的逃生阀是连接级无限重试。 错误是连接失败、请求类型是采样(压缩请求不算)时,进入无限重连分支:5 秒起步、翻倍、60 秒封顶,不消耗 5 次重试预算(responses_retry.rs:44-83)。流式重试耗尽 5 次后还有一层:尝试从 WebSocket 切到 HTTPS 传输,切换成功后重试计数清零再试一轮,降级是会话级粘滞的(:85-100)。

流错误

连接失败且是采样请求?
(压缩请求不算)

连接级无限重试:5s 起步翻倍、60s 封顶
不消耗常规重试预算

流式重试预算未耗尽(5 次)?

普通重试
Retry-After 优先,否则指数退避 + ±10% 抖动

可切换传输?

WebSocket → HTTPS 降级
重试计数清零再试一轮,会话级粘滞

显式失败

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 开关是硬闸:即便错误本身可重试,只要处于已产出内容的场景就返回不可重试。

流结束 / 出错

committed?
已产出过可见内容

禁止重试
半截输出已外抛,重发会重复副作用

无可见内容、停止原因正常、
输出 token ≈ 0?

正常结束 / 上抛错误

重试预算未耗尽?

丢弃缓冲的 start 事件
指数退避后重试

best-effort 返回空结果

omp 还有凭据轮换重试:403 通常意味着 token 缺权限,作废当前凭据轮换到同组兄弟账号,每次轮换是全新的重试预算(上限 64 次);但「并发上限 403」是瞬时的,不轮换(auth-retry.ts:98-100)。模型层另有服务端 fallback:Anthropic provider 支持请求体带 fallbacks 数组,内容安全拦截时服务端自动切备用模型。

流中断与超时处理

项目流中断处理超时设计
codex流关闭而未收到 completed 即显式失败流空闲 300s,WS 连接 15s
gemini-climid-stream 重试 4 次,重试前发信号让 UI 丢弃半截渲染shell 不活动超时
qwen-code内容级重试 7 类流空闲看门狗 4 分钟,合成超时错误走既有重试栈
opencode中断时收尾 + 清理孤儿工具MCP 连接 30s,bash 按参数设超时
kimi-code溢出→缩窗→压缩→重试循环,3 轮熔断子代理 2 小时,hook 30 秒
dsh空响应当错误流空闲看门狗 5 分钟映射为 TIMEOUT
ompreplay 安全判定,已产出内容禁盲目重试按工具类型独立配置超时

两个值得细说的设计:

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 开发者的借鉴清单

  1. 先想清楚你的后端工况。 面向免费配额服务就学第一类(大预算、长封顶、心跳保活、模型降级);面向稳定付费服务就学第二类(短重试、快速失败、逃生阀)。
  2. 尊重 Retry-After。 七家都做了,opencode 做得最彻底:有响应头时不受本地 30 秒封顶约束,完整尊重服务端指示。
  3. 长等待要输出心跳。 30 秒一条,否则无人值守环境会杀进程(qwen 的教训)。
  4. 内容级无效需要独立检测。 HTTP 200 不等于响应可用,畸形工具调用、空响应、协议泄漏都该进重试判定。
  5. 降级链是逃生阀。 传输降级(codex WS 切 HTTPS)、模型降级(gemini Flash)、凭据轮换(omp),至少配一条。
  6. 已产出可见内容的流禁止整请求重发。 半截文本重发会重复副作用,这是被普遍忽视的问题,omp 的 committed 标记是参考实现。
  7. 重试对用户可以透明也可以可见。 opencode 的倒计时让用户知道系统在等什么,比静默挂起体验好。
  8. 补齐 turn 内断点续跑。 七家都没做,崩溃只能会话级恢复,进行中的采样全部重来。对超长 step 这是真实痛点,自研时值得优先考虑。

文档地图

文章内容速览
总述拆了七大开源 Agent 的源码,最高分竟然不是 Codex总体结论、评估方法与评分卡
01架构对比四种流派与分化根源、主循环与事件机制、插件化与耦合风险
02上下文管理压缩触发阈值、摘要方式、token 计数口径、跨会话记忆
03会话管理持久化模型三层次、并发控制、崩溃恢复与 resume / fork
04工具调用注册与可见性、并行调度四种语义、审批门控、错误处理
05重连与容错重试预算、流中断处理、降级链、副作用安全
06系统提示词与指令遵循四种组装范式、动态注入、注入防御三层与共同敞口
07思维链与工作流编排思维链接入、plan 模式语义、子代理治理、工作流引擎
08性能设计前缀缓存三档分化、启动优化、成本核算
09可扩展性MCP 接入、自定义工具、多 provider、SDK 与 API
10安全与权限控制沙箱两种语义、审批默认姿态、企业管控、敏感数据保护
Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。

更多推荐