Appearance
模型调用失败后怎么处理:Error Taxonomy、Routing 与 Fallback
前置:如何隔离 Provider 差异:Streaming、Adapter 与 Capability
核心问题:同样是一次 Model Call 失败,哪些错误可以重试、哪些应该换模型、哪些必须立即暴露给 Runtime?
429、context overflow、schema rejection、provider outage 和“当前模型不支持所需 capability”虽然都表现为 model call 失败,但处理方式完全不同。只有先把错误归类,Routing / Fallback 才能在不静默改变能力和安全语义的前提下做选择。
Model Error Taxonomy
模型调用失败后,真正重要的不是“报了什么错”,而是 Runtime 下一步应该重试、修改请求、换模型,还是结束当前路径。
| 类型 | 实际含义 | 常见处理 |
|---|---|---|
| transport | 网络连接失败,请求可能没正常完成 | backoff 后有限重试 |
| rate-limit | Provider 暂时拒绝更多请求 | 等待 / 限流后重试 |
| timeout | deadline 内没拿到可接受结果 | 有剩余预算时重试 |
| context overflow | 输入超过可处理范围 | 裁剪 / compaction 后重建请求 |
| invalid output | 返回成功,但不符合 schema/contract | 校验错误反馈给模型,有限 repair |
| provider unavailable | 整个服务暂时不可用 | 尝试兼容 fallback |
| semantic failure | 调用技术上成功,但模型决策明显不对 | replan、验证或升级模型 |
例如 context overflow 不能原样 retry:
text
Context 太大
→ 原样 retry ×
正确:
Context 太大
→ 缩减 history / retrieval / tool result
→ 重建 Context
→ retry因此 Error Taxonomy 的价值是把失败原因映射到恢复动作,而不是多维护几个异常类。
🔍 源码对照:Router 的职责边界:选择 Provider + 串行化 Agent Turn
精确定位
- 文件:
source/node-agent-coordinator/inference-router.ts - Symbol:
createCoordinatorInferenceRouter()→dispatch()→execute() - 深入索引:Code Atlas:08-routing-provider
ts
function dispatchSendPrompt(agentId, request) {
const previous = queues.get(agentId) ?? Promise.resolve()
const next = previous
.catch(() => undefined)
.then(() => execute(currentProvider(), request))
queues.set(agentId, next.finally(() => clearIfCurrent(agentId, next)))
}
async function execute(provider, request) {
const bridge = provider === "claude-code" ? await createMcpBridge() : null
return runRoutedProviderText(provider, messages, bridge ? { mcpServerUrl: bridge.url } : { tools })
}对照理解
- Router 先读取当前 provider,再把同一 agent 的请求串进 per-agent Promise queue,避免 Turn 顺序被并发打乱。
- Claude Code 走本地 MCP bridge,而其他 provider 可以直接获得 Tool definitions;这是 provider capability adaptation,不是通用 fallback 算法。
- Routing 是调用前选择路径;队列、transcript 持久化、stream update 则属于 coordinator/runtime 责任。
Model Routing / Fallback
Routing 和 Fallback 发生在两个不同阶段:
text
Routing = 调用前选择哪个模型
Fallback = 已选模型失败后决定如何恢复Routing 不能只看 token 长度。40K token 的日志摘要可能很简单,而 30 行并发代码的死锁分析可能很难。更合理的顺序是先按 capability 过滤,再比较质量、成本、延迟和风险:
text
任务要求:tool calling + structured output + context >= 64K
↓
排除不满足能力的模型
↓
在剩余候选中比较 quality / cost / latencyFallback 也不是“换一个模型继续调”。备用模型至少要继续满足当前任务依赖的能力和输出契约。否则 Planner 原本返回结构化 Plan,换成只会 plain text 的模型后,Runtime 就无法继续 validate / compile / execute。
还有一类是 quality escalation:模型没有报错,但连续做出差决策,此时可以把任务升级到更强模型。这和 Provider 503 后的故障 fallback 是两种不同恢复路径。
Failure Modes
把所有错误统一 Retry
context overflow、schema 不兼容、权限错误原样重试通常不会自愈,只会消耗 deadline 和 token。RetryPolicy 应先看 normalized error kind。
Fallback 静默降低能力
主模型失败后切到不支持 structured output / required tools 的模型,却继续按原 contract 执行,会让错误从“调用失败”变成更难发现的语义错误。Fallback candidate 必须先做 capability compatibility check,并把切换原因写入 Run Manifest。
源码对照(B 类)
Grok Bot reconstruction 的 inference-router.ts 可作为 provider routing 的案例,但 routing/fallback 的语义仍以本文 error taxonomy 与能力兼容性为准。 - 源码导航:Grok Bot Source Map
自测
- 分别遇到 rate limit、context overflow、invalid structured output 和 semantic failure 时,哪些适合原模型重试,哪些更适合 fallback,哪些应直接交回 Runtime?
- 为什么“所有 ModelError 都指数退避重试”会同时浪费预算并隐藏不可恢复错误?挑一种错误说明。