1. Agent 工具调用失败时为什么不能只靠 try-catchAgent 工具调用失败和普通 HTTP 请求失败看起来都是「报错」但处理逻辑完全不同。普通请求失败你重试一次大概率就好了Agent 工具调用失败如果你只是无脑重试可能会把一次 429 限流放大成连续十几次无效请求甚至让整个任务链路雪崩。我最近在做一个日志分析 Agent它需要调用 bash_exec、search_db、get_order 三个工具。跑了两天发现一个规律失败不是均匀分布的而是集中在三类场景——超时、限流、5xx。超时通常是网络抖动或后端响应慢重试一次就能过限流是 API 配额被打满需要冷却或换通道5xx 是服务端临时故障退避后重试有效。但如果你把这三类混在一起处理用同一个重试策略结果就是该快的慢、该停的还在跑。更麻烦的是Agent 是多轮迭代的。一次工具调用失败后LLM 会拿到错误信息继续推理如果错误信息不结构化模型可能反复调用同一个失败工具几分钟烧掉大量 token。所以异常处理的核心不是「重试」而是「分类 退避 降级」三件事。这篇文章聚焦一个具体问题Agent 工具调用失败后如何用统一 Key/API 通道承接多模型请求实现超时、限流、5xx 三类异常的自动重试与降级切换。我会给出可复制的 config.toml 和 settings.json 骨架以及用日志验证降级是否生效的具体动作。2. TaoToken 统一通道的前置准备在讲重试和降级之前先解决一个基础问题你的 Agent 要能切换模型前提是多个模型走同一个入口。如果每个模型都要单独配 Key、单独改 BaseURL降级逻辑会变得非常臃肿。TaoToken 在这里的角色是统一通道。你只需要一个 API Key就可以在同一个 BaseURL 下请求不同模型。这对 Agent 降级特别有用——当主模型限流时你不需要改代码里的 endpoint只需要换 model 字段。前置准备分三步第一步获取 API Key。访问 https://taotoken.net/api-keys 创建一个 Key复制保存。这个 Key 后面会用在 config.toml 和 settings.json 里。第二步确认 BaseURL。TaoToken 的 API 入口是 https://taotoken.net/api所有模型请求都走这个地址。注意不要加多余的路径后缀OpenAI 兼容接口会自动拼接 /v1/chat/completions。第三步确认你要用的模型名。在模型对话页面可以查看当前支持的模型列表常见的有 claude-sonnet-4-5、gpt-4o 等。降级策略里至少准备两个模型一个主模型一个备用模型。注意API Key 不要硬编码在代码里也不要提交到 Git。建议用环境变量注入config.toml 里引用环境变量名而不是值。如果你还没有 Key可以先到 https://taotoken.net/api-keys 创建。整个准备过程不超过两分钟但它是后面所有重试和降级逻辑的基础。3. 可复制的 config.toml 与 settings.json 骨架这一节给出两个配置文件骨架。config.toml 用于定义模型通道、重试参数和降级链settings.json 用于定义工具调用的超时、重试预算和错误分类规则。两个文件配合使用Agent 启动时加载。3.1 config.toml模型通道与降级链# config.toml # Agent 模型通道配置统一走 TaoToken支持多模型降级 [api] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY # 从环境变量读取不写明文 timeout_seconds 60 # 单次 LLM 调用超时 max_retries 3 # 全局最大重试次数 retry_base_delay 1.0 # 指数退避基础延迟秒 retry_max_delay 30.0 # 单次退避上限 retry_jitter_ratio 0.1 # 随机抖动比例防止惊群 # 降级链按 priority 从小到大尝试 [[models]] name claude-sonnet-4-5 provider anthropic priority 1 max_retries 2 # 主模型最多重试 2 次 timeout_seconds 60 [[models]] name gpt-4o provider openai priority 2 max_retries 1 # 备用模型最多重试 1 次 timeout_seconds 45 [[models]] name gpt-4o-mini provider openai priority 3 max_retries 0 # 保底模型不重试直接返回 timeout_seconds 30 [circuit_breaker] failure_threshold 3 # 连续失败 3 次触发熔断 recovery_timeout 30 # 熔断后 30 秒进入半开 half_open_max_calls 1 # 半开状态放行 1 个试探请求这个配置的关键点降级链按 priority 排序主模型失败后自动切到下一个。每个模型有独立的 max_retries避免主模型重试太多次拖慢整体。熔断器参数控制什么时候停止请求某个模型。3.2 settings.json工具调用超时与错误分类{ tool_call: { default_timeout_seconds: 30, max_retries: 2, retry_budget: { global: 5, per_tool: 2, per_model_switch: 1 }, error_classification: { transient: { patterns: [TimeoutError, ConnectionReset, SSLHandshakeError], action: retry_with_backoff, max_retries: 3 }, rate_limited: { patterns: [429, RateLimitExceeded, QuotaExhausted], action: cooldown_then_switch, cooldown_seconds: 5 }, server_error: { patterns: [500, 502, 503, 504], action: retry_with_backoff, max_retries: 2 }, permanent: { patterns: [400, 401, 403, 404, InvalidParameter], action: fail_fast, max_retries: 0 } } }, fallback: { enabled: true, on_context_overflow: switch_to_long_context_model, on_model_misbehavior: switch_to_next_priority, on_all_failed: return_friendly_error } }settings.json 里最重要的是 error_classification。它把错误分成四类transient 可重试、rate_limited 需冷却后切换、server_error 退避重试、permanent 直接失败。每类对应不同的 actionAgent 在执行工具前先查这张表决定是重试还是降级。提示retry_budget 是防止自杀式重试的关键。global 限制整个 run 的总重试次数per_tool 限制单个工具的重试次数per_model_switch 限制换模型的次数。超过预算直接终止避免无限循环。4. 验证请求与降级是否生效配置文件写好后需要验证两件事一是正常请求能走通二是降级逻辑真的会触发。这一节给出具体的验证命令和日志观察方法。4.1 基础连通性验证先用 curl 确认 TaoToken 通道可用export TAOTOKEN_API_KEY你的Key curl -s -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-5, messages: [{role: user, content: 回复 OK}], max_tokens: 10 } | jq .choices[0].message.content如果返回 OK说明通道正常。接着换模型名再试一次确认多模型可用curl -s -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [{role: user, content: 回复 OK}], max_tokens: 10 } | jq .choices[0].message.content4.2 模拟限流触发降级要验证降级是否生效最直接的方法是模拟 429。你可以临时把主模型的 max_retries 设为 0然后故意用一个不存在的模型名请求观察 Agent 是否自动切到备用模型。更可控的方式是在代码里注入一个 mock 错误。以下是一个 Python 验证脚本import os import time import logging from openai import OpenAI logging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s %(message)s) client OpenAI( base_urlhttps://taotoken.net/api, api_keyos.environ[TAOTOKEN_API_KEY], ) FALLBACK_CHAIN [ {model: claude-sonnet-4-5, max_retries: 2}, {model: gpt-4o, max_retries: 1}, {model: gpt-4o-mini, max_retries: 0}, ] def call_with_fallback(messages): for idx, entry in enumerate(FALLBACK_CHAIN): model entry[model] for attempt in range(entry[max_retries] 1): try: logging.info(f尝试 model{model} attempt{attempt1}) resp client.chat.completions.create( modelmodel, messagesmessages, max_tokens50, ) logging.info(f成功 model{model} content{resp.choices[0].message.content}) return {model: model, content: resp.choices[0].message.content} except Exception as e: err str(e) logging.warning(f失败 model{model} error{err[:80]}) if 429 in err or rate in err.lower(): time.sleep(2 ** attempt 0.1) continue if 500 in err or 502 in err or 503 in err: time.sleep(2 ** attempt 0.1) continue break logging.info(f降级到下一个模型当前 priority{idx1}) return {model: None, content: 所有模型均失败请稍后重试} if __name__ __main__: result call_with_fallback([{role: user, content: 用一句话解释什么是熔断器}]) print(result)运行这个脚本观察日志。如果主模型正常你会看到成功 modelclaude-sonnet-4-5。如果主模型限流你会看到失败 modelclaude-sonnet-4-5 error...429...然后降级到下一个模型接着成功 modelgpt-4o。这就是降级生效的证据。4.3 用日志验证降级链路在生产环境里建议把每次尝试和降级都打到结构化日志里。关键字段包括timestamp、model、attempt、error_type、action、final_model。以下是一个日志片段示例{ts:2026-01-15T10:23:01Z,model:claude-sonnet-4-5,attempt:1,error_type:rate_limited,action:cooldown_then_switch} {ts:2026-01-15T10:23:06Z,model:gpt-4o,attempt:1,error_type:null,action:success,final_model:gpt-4o}如果你看到final_model和请求时的主模型不一致说明降级生效了。如果final_model为 null说明所有模型都失败需要检查 Key 余额或网络。5. 本篇常见错误排查这一节列出配置和验证过程中最容易踩的坑以及对应的排查动作。5.1 429 限流后立即重试导致持续失败现象日志里连续出现 429每次间隔很短重试多次后仍然失败。原因没有冷却时间或者冷却时间太短。限流通常是按时间窗口计算的立即重试只会继续撞墙。排查检查 settings.json 里 rate_limited 的 cooldown_seconds 是否设置。建议至少 5 秒如果限流严重可以设到 10 秒。同时确认 retry_budget 里的 global 是否被耗尽。修复把 cooldown_seconds 调大或者在限流后直接切换到备用模型而不是等待。5.2 5xx 重试次数过多拖慢整体响应现象一次工具调用花了 30 秒以上日志显示 5xx 重试了 4 次。原因max_retries 设置过大或者退避延迟没有上限。排查检查 config.toml 里 retry_max_delay 是否设置。如果没有上限指数退避会变成 1、2、4、8、16、32 秒累计延迟很高。修复设置 retry_max_delay 30并且把 5xx 的 max_retries 控制在 2 次以内。如果 2 次都失败直接降级到备用模型。5.3 降级后模型不支持工具调用导致报错现象主模型失败后切到备用模型但备用模型返回「不支持 function calling」。原因降级链里的模型能力不一致。有些轻量模型不支持工具调用切过去后 Agent 无法执行工具。排查检查降级链里每个模型是否支持 function calling。可以在模型对话页面确认模型能力。修复把不支持工具调用的模型从降级链里移除或者把它放在最后作为纯文本保底。如果必须用需要在降级时移除 tools 参数只保留对话能力。5.4 熔断器误触发健康模型被跳过现象主模型只失败了 2 次但熔断器已经打开后续请求直接跳过主模型。原因failure_threshold 设置过小或者失败统计没有区分错误类型。永久性错误如 400不应该计入熔断统计。排查检查 circuit_breaker 的 failure_threshold 和错误分类逻辑。确认只有 transient、rate_limited、server_error 才计入失败permanent 不计入。修复把 failure_threshold 调到 5 以上并且在熔断统计里排除 permanent 错误。同时设置 half_open_max_calls让半开状态能快速恢复。5.5 API Key 环境变量未生效现象请求返回 401日志显示 api_key 为空。原因环境变量没有导出或者 config.toml 里引用的变量名和实际不一致。排查运行echo $TAOTOKEN_API_KEY确认变量存在。检查 config.toml 里 api_key_env 的值是否和实际变量名一致。修复在启动 Agent 前执行export TAOTOKEN_API_KEY你的Key或者把变量写入 .env 文件并用 dotenv 加载。不要直接把 Key 写在 config.toml 里。6. 接入文档与后续动作配置和验证跑通后下一步是把这套逻辑接入你的实际 Agent 框架。不同框架的接入方式不同但核心思路一致加载 config.toml 和 settings.json在工具调用外层包一层重试和降级逻辑把每次尝试打到结构化日志。如果你在接入过程中遇到报错可以先查接入文档https://taotoken.net/doc 。文档里有各语言的 SDK 示例和常见错误码说明。如果问题出在 Key 或配额上到 API Keys 页面检查https://taotoken.net/api-keys 。对于长期跑编码任务或 Agent 任务的场景建议关注 Coding Planhttps://taotoken.net/coding-plan 。它适合需要稳定通道和较高配额的场景能减少限流触发的频率。最后提醒一点重试和降级是手段不是目的。真正重要的是错误分类要准。分类错了后面所有策略都会失效。我试过把 400 当成 transient 重试结果白白浪费了 3 次请求。所以先把 error_classification 里的 patterns 调准再调重试参数。