上周二上午 10 点 07 分我的终端像开了锅一样往外吐错误。跑着代码重构任务的 Claude Code 连续三次 APIConnectionError负责批量生成模板代码的 Codex 在 /responses 请求上全面超时连平时只做快速思路验证的 Grok 接口都开始返回 503。我那套已经正常运行了两三个月的 Agent 工作流一分钟之内从“多模型并行”变成“集体哑火”。这不是我第一次遇到单个模型服务抽风但还真是头一回见到 Claude、Codex、Grok 三家在同一个时间窗口集体翻车。更麻烦的是由于整条自动化流水线里每一步都依赖前一步的模型输出上游一断后面的任务就像多米诺骨牌一样全部卡死。这篇文章我想把当时的现场、排查链路、抢救方案和事后改造完整记录下来给同样在用 AI Agent 和多模型协作做开发工作的朋友一个参考。尤其是那些和我一样习惯把“多AI协作”当成“高可用”的人这篇应该能帮你避掉几个大坑。1. 崩溃现场三个模型服务在同一分钟集体失联1.1 从单点报错到全队列冻结的十五分钟我日常的开发工作流跑在一套自建的 Agent 编排器上SQLite 队列做任务持久化调度器把不同类型的任务分发给 Claude Code、Codex 和 Grok。平时这套流水线很省心我只需要把需求拆成一个个子任务丢进队列剩下的交给模型们去跑。但那天上午事情的发展完全超出预期。时间正在执行的任务故障表现我的第一反应10:07Claude Code 做代码重构评审APIConnectionError连续重试三次仍失败以为是单个会话超限杀掉重跑10:09Codex 批量生成模板代码请求在本地请求转发层就报错/responses 一直拿不到回包怀疑本地配置坏了10:11Grok 做需求拆解和快速问答接口直接返回 HTTP 503觉得事情不太对10:14整个编排队列彻底卡住Agent RPC 返回 empty sid and service name确认整个工作流停摆最后一个错误最开始让我很困惑。agent rpc error (-1): empty sid and service name这种报错意味着编排器和执行器之间的 RPC 通道虽然还连着但协调服务已经拿不到有效会话 ID无法锁定任何任务上下文。说人话就是链路没有完全断但服务端已经处于半死状态既不能新建任务也不能恢复旧任务。1.2 为什么这次比单个模型宕机更致命如果只是 Claude 挂了我可以把任务切到 Codex 顶上如果只是 Grok 挂了我甚至不会太在意。但三家同时失联就暴露出一个问题我那套看起来“多AI协作”的架构在故障面前其实根本没有形成真正的备份关系。更麻烦的是自动重试机制。编排器发现任务失败后默认会重新入队于是所有子任务都在疯狂地反复调用同一个已经失去响应的接口。这不仅没有解决问题还白白消耗了 API 额度把后续真正需要的重试窗口都堵死了。事后我估算了一下那十五分钟里至少有四十多次无效请求打到了各个供应商的端点上。这种“自有系统帮倒忙”的感觉比服务宕机本身更让人抓狂。2. 我那套自认为“多保险”的 Agent 工作流实际攥着哪些命门2.1 每层组件到底承担了什么职责先把我这套工作流的真实分工摊开来讲。很多人一听到 Agent 工作流就以为是某个大模型全权接管一切实际上我这边是分层配合的Claude Code充当“大脑”。负责需求拆解、代码重构规划、复杂上下文理解。它通过 MCP 协议读取项目文件、调用搜索工具、执行命令整个流程里最重要的判断基本都由它来做。Codex CLI充当“执行层”。承担大量机械性的代码生成、批量重命名、单元测试补全。为了控制成本日常默认接入的是 DeepSeek 的兼容接口配置文件里同时保留了 OpenAI 官方端点作为备用。Grok充当“外援”。适合快速问答、思路验证、生成样例数据这些对准确性要求不那么苛刻的场景。本地编排器用队列把任务分配给上面的三个执行体并负责记录状态、重试和结果汇总。MCP 服务通过 npx 启动一系列工具服务比如文件系统访问、代码检索、命令行执行让 Claude Code 能够真正“动手”。这个结构在正常情况下跑得很顺。比如我给 Claude Code 一个“重构这个模块并补充测试”的任务它会先读取代码结构规划改动方案生成修改后的代码再调用 MCP 工具执行测试最后把结果写回队列。Codex 则在旁边处理大量模板代码Grok 时不时被拉进来做快速论证。2.2 多AI协作听起来分散了风险实际上依赖的都是同一批隐形单点我踩过这个坑之后才想明白多 AI 协作只是让“模型能力”这一层有了冗余但底下还叠着好几个被忽略的共享组件。共享组件故障影响同一个本地请求转发进程一个组件挂了所有供应商请求都出不去同一台开发机的网络出口出口抖动Claude、Codex、Grok 一起超时同一个 key 管理脚本key 轮换配置出错全部鉴权失败同一个编排器编排器状态丢失再健康的模型也接不到任务MCP 公共服务工具层崩溃模型即使在线也动不了手所以当那天我看到三家服务同时失联第一反应其实是“不可能吧”。但冷静下来就明白了很可能不是三家供应商约好了集体宕机而是我自己的某个共享层先出了问题把所有请求都堵在了门口。这也就解释了为什么排查方向一开始会被带偏。3. 排查链路从本地转发报错一路追到上游服务状态页3.1 第一步差点被本地配置故障带偏Codex 的那条报错日志里有一串“cc switch local failed while handling codex endpoint /responses”的字样。这里说的 local 是我在本地跑的一个请求转发进程主要用来统一记录各个模型接口的出入参、做简单的日志审计。看到这个报错我第一反应就是我的转发进程挂了。所以我做了所有“常规操作”检查配置文件、重启本地转发进程、确认端口能被正常访问。进程重启了三次端口是通的配置文件语法也没问题但 Codex 的请求依然发不出去。这个阶段的教训是当一个请求链路里既有本地组件又有远端服务时报错信息往往会指向离请求最近的那个环节但它未必是根因。3.2 用最朴素的方式确认直接打 API 探活被本地配置绕了差不多十分钟后我决定抛开所有中间层直接拿 curl 打最原始的 API 端点。探活命令其实很简单不需要用任何监控平台# 探 OpenAI 系端点 curl -sS -o /dev/null -w %{http_code} https://api.openai.com/v1/models \ -H Authorization: Bearer $OPENAI_API_KEY # 探 Anthropic 系端点 curl -sS -o /dev/null -w %{http_code} https://api.anthropic.com/v1/messages \ -H x-api-key: $ANTHROPIC_API_KEY \ -H anthropic-version: 2023-06-01 \ -d {model:claude-3-5-sonnet-latest,max_tokens:1,messages:[{role:user,content:hi}]}看到 curl 直接返回 5xx 的那一刻我才确认问题大概率出在上游服务端而不是我本地的转发进程。随后我又用另一台设备、切到另一个网络出口做了交叉验证结果完全一致。这一步很重要如果只在一台设备、一个网络环境里测永远分不清是本机问题还是远端问题。3.3 状态页、社区反馈和自建探针交叉验证接下来就是去翻各家的状态页。三家平台的状态页确实都亮了黄灯或红灯社区里也有大量同样报错的反馈基本可以确认是集体性故障。不过我想多说一句状态页可以作为辅助参考但绝对不能当成唯一依据。状态页有滞后性而且有些平台会把某个区域的故障描述得比较模糊。我更推荐的做法是长期维护一份自己的探活脚本定时用 curl 或上层网关发最小请求把结果记录到本地。这样一旦出问题你能立刻知道“上游挂了还是状态页还没更新”不用干等。3.4 快速判断故障类型的排查表这次排错之后我整理了一张对照表后面每次遇到模型服务异常都会先按这个思路过一遍错误特征大概率原因下一步动作401 / 403key 失效、账户权限问题检查 key 和账户状态429限流、额度不足停止重试等配额或调 provider5xx / 超时上游服务故障切换备用模型池连接被重置本地转发层或网络出口异常排查本地组件切网络验证这张表最大的价值在于帮你快速排除“自己吓自己”的情况。像 5xx 这种错误如果反复重试只会加重问题正确做法是直接切换到备用通道。4. 止血抢救没有大模型可用的时候怎么把任务继续推下去4.1 第一步先把自动重试全部停掉确认是上游集体故障之后我做的第一件事不是“等恢复”而是把编排器的自动重试全部关掉。我当时心里很清楚如果继续让任务队列反复打那几个已经故障的服务唯一的结果就是把 API 额度和我的耐心一起烧光。怎么做到的呢我在编排器里加了一个全局熔断开关当 3 分钟内失败任务数超过 10 个自动暂停整个 Agent 调度改由人工确认。这次正好触发。把队列停掉之后至少不会再产生无效的外部请求系统从“疯狂空转”变成了“安静等待”。4.2 第二步非模型任务全部转本地执行接下来我梳理了一遍队列里的任务发现并不是所有任务都必须依赖大模型。有些子任务只是格式校验、静态分析、依赖检查这些完全可以退回到本地 Git hooks 和命令行工具来做。我把这部分任务从 Agent 队列里抽出来直接交给本地脚本执行至少让关键路径上的质量检查没有被阻断。这里有个容易被忽略的点很多 Agent 工作流的问题不是“模型能力不够”而是把本可以本地完成的简单步骤也硬塞给了大模型白白增加链路风险和成本。故障时刻重新审视任务列表往往会发现至少三分之一的工作根本不需要模型参与。4.3 第三步切换备用 provider哪怕能力降级也要保住流程大约持续了近一个小时后部分服务开始恢复但不是完整恢复接口时好时坏。这时候我做了两件事把 Codex 的当前 provider 从默认配置切到 OpenAI 官方端点通过修改~/.codex/config.toml里的生效配置完成切换。虽然 OpenAI 那边也不是完全健康但至少有一部分请求能通了。把 Claude Code 里不太依赖长上下文的任务改走本地小模型用 Ollama 起了一个相对轻量的模型做补全和格式化能力确实不如 Claude但足够撑住基础工作。这个操作让我意识到一个事实故障恢复从来不是“等了半天一切变好”而是“能跑的先跑起来跑不动的等一下再跑”。4.4 为下一个任务队列恢复做铺垫在上游服务完全恢复之前我还做了一项重要工作把任务队列的断点标记好。因为我的队列是 SQLite 持久化的每个任务都有唯一 ID 和状态字段所以我只需要重新标记失败任务为 pending等服务恢复后一键重放即可不需要重新规划所有任务。这也是我想重点提醒大家的Agent 工作流的上游可以完全依赖外部供应商但任务状态、执行轨迹这些元数据一定要本地持久化。没有这一层每次故障后你都只能从头再来而不是从断点续跑。5. 灾后加固为下次大规模故障做的七处改造5.1 统一模型网关把供应商池化故障结束后我做的第一项改造就是在所有模型调用前面加了一层统一网关。网关的主要职责有三个维护多个供应商的配置、定时探活、自动摘除不健康节点。以前 Claude Code、Codex、Grok 各连各的接口互不知晓现在它们统一走网关网关维护一个健康列表任何一个供应商连续探活失败 3 次就自动被临时摘除后续请求只会转发到健康的供应商。这个思路在传统运维里叫负载均衡放在 AI 工作流里同样适用。麻烦点在于需要比较仔细地管理各家 API 的鉴权和计费但这点成本比起故障时整个流程瘫痪来说非常值得。5.2 Codex 配置文件里的多供应商方案很多朋友在搜 codex 接入 deepseek 或者 codex 安装教程其实安装本身不复杂真正值得研究的是它的配置文件结构。我在~/.codex/config.toml里维护了多个 provider日常默认走成本较低的 DeepSeek 兼容接口故障时可以快速切到官方端点[model_providers.deepseek] name deepseek base_url https://api.deepseek.com/v1 env_key DEEPSEEK_API_KEY [model_providers.openai] name openai base_url https://api.openai.com/v1 env_key OPENAI_API_KEY model_provider deepseek model deepseek-chat切换时只需要改变model_provider和model字段再重启 Codex 会话即可。不同版本的 Codex 命令可能略有差异但配置结构的思路是通用的。这不是什么高深技巧但在故障时刻能帮你省下大量搜索文档的时间。5.3 MCP 服务从“中心化”改成“本地优先”我之前把文件系统访问、代码检索这些能力都挂在 npx 启动的 MCP 服务上。问题在于如果这个 NPM 包所在的环境或注册表出现问题所有依赖它的 Agent 都会失去动手能力。现在我把 MCP 服务拆成了两层第一层是本地脚本用 shell 直接完成文件读取、代码搜索、测试执行第二层才是各种 npx server提供更丰富的上下文能力。Claude Code 的 MCP 配置里保存了一段类似这样的结构{ mcpServers: { filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, ./project ] } } }重点是本地脚本是兜底MCP 是增强。平时用增强故障时切兜底不至于一个字都跑不动。5.4 Agent 任务队列的熔断、幂等和重放机制这次最大的危机不是模型挂了而是我的编排器不知道“什么时候该停”。所以我给整个工作流补充了三个机制机制具体做法熔断连续 10 个任务失败时暂停自动调度人工确认后再继续幂等每个任务都有唯一 ID执行结果可安全重放不会产生重复副作用重放任务状态持久化到 SQLite服务恢复后只需把 failed 状态改回 pending有了这三层故障时段就变成“可管理的等待”而不是一团乱麻。我很推荐所有做 Agent 自动化的朋友先检查这三块是否齐备再考虑模型能力怎么提升。流程韧性永远是自动化系统的底座。6. 这次集体宕机最值钱的是这四条教训回头来看技术上的改造都在预期之内真正让我观念转变的是几件事。第一多 AI 协作不等于高可用。供应商再多只要它们共享同一个网络出口、同一个请求转发进程、同一个编排器出故障时依然是全军覆没。真正的高可用来自故障域隔离请求路径、鉴权方式、任务状态最好都拆开至少要让两个模型走完全不同的链路。第二状态页只能当参考不能当决策依据。状态页更新往往滞后几十分钟而 Agent 自动调度可能在这几十分钟内产生几十次无效调用。自建一个最小探活脚本定时打一个 token 级别的请求才能在第一时间知道到底该切还是该等。第三自动化的系统必须保留手动逃生通道。故障时最可靠的往往还是自己打开编辑器直接改代码、敲命令跑测试。给我最多安全感的从来不是某个特别聪明的 Agent而是我的终端里随时能用的 Git、coverage、lint 这些基础工具。第四配置基本功决定抢修速度。这次我能快速切 Codex 的 provider、调整 Claude Code 的 MCP 配置是因为平时就理解这些配置文件的结构而不是故障时刻临时查文档。包括很多人关心的 claude code 安装、vscode 配置 claude code本质上都要回到“先看懂配置再谈自动化”这一点上。把配置文件里的字段含义吃透比收藏一百条安装教程有用得多。那天下午服务全面恢复后我把整条 Agent 流水线重新跑了一遍之前卡住的几十个任务通过重放机制顺利执行完。说实话这次事故给我的感受不是“技术不行”而是“自动化越深越要对底层链路心存敬畏”。以后每次往里加新模型、新工具我都会先问自己一句要是它挂了我能在一分钟内把它踢出主链路吗