首页 / 资讯中心 / 文章详情

大模型 Agent 工具调用报错排查:用 FISSION-GRPO 强化学习思路定位并修复工具错误

大模型 Agent 工具调用报错排查:用 FISSION-GRPO 强化学习思路定位并修复工具错误 ★ FEATURED ARTICLE
1. Agent 工具调用报错为什么总在同一个坑里打转大模型 Agent 工具调用报错排查是每个把 Agent 往生产环境推的人都会撞上的墙。你写好了 function calling 的 schema接上了搜索、数据库、代码执行几个工具本地跑 demo 一切正常一上量就开始飘401 鉴权失败、local proxy failed、429 限流、参数类型不合法、工具部分执行成功……最要命的不是报错本身而是 Agent 面对报错时的反应——它不读真实错误信息而是自己编一个原因然后基于虚构的原因重试失败再编再重试。我见过一个典型循环Agent 调用某个 HTTP 工具返回 401它没有把 401 当成凭证问题来处理反而在下一轮里虚构出可能是参数格式不对于是改了个无关参数再调还是 401接着又虚构可能是工具名写错了换了个工具名继续 401。整个过程它一次都没真正读取过响应体里的Unauthorized字段。这就是 excerpt 里描述的那个死循环调用失败 → 错误归因 → 虚构修复方案 → 再次失败 → 继续虚构。这个问题的根子在于传统工具学习只训练模型三件事选对函数、输出合法 JSON/XML、填对参数。它假设工具调用是一次性正确的动作。但真实的多轮 Agent 环境里API 状态会变、token 会过期、限流会触发、工具可能只执行了一半。模型不能只会正确调用还必须会读报错、诊断原因、选择新的恢复动作。FISSION-GRPO 这个强化学习框架解决的正是这件事。它的核心链路是发现当前策略的错误 → 为错误生成诊断反馈 → 从错误处重新采样多个恢复方案 → 训练模型学会恢复。注意它和普通 GRPO 的区别——GRPO 是组相对策略优化同一个问题采样多条轨迹按组内奖励相对高低算优势高于平均的增强、低于平均的抑制不需要单独的价值模型。FISSION-GRPO 在此基础上加了一个裂变动作把一个错误裂变成多条恢复轨迹专门训练纠错能力。这篇文章不讲论文复现讲的是怎么把这套发现错误—诊断—恢复的思路落到你手头的 Agent 工程里并且用 TaoToken 的统一 Key/API 通道把 401、local proxy failed、429 这些真实报错复现出来、验证你的 Agent 到底会不会自愈。适合正在做 Agent 工具调用、被报错循环折磨、想搞清楚怎么让 Agent 自己修工具错误的开发者。2. 用 TaoToken 统一通道搭一个可复现的报错环境要让 Agent 学会处理工具错误第一步不是改 prompt而是先有一个能稳定复现各类报错的实验环境。如果每次报错都靠线上偶发你根本没法系统性地验证 Agent 的恢复策略。我的做法是用 TaoToken 作为统一的模型调用通道把 Agent 的大脑和工具分开这样报错来源清晰、可注入、可回放。TaoToken 在这里的角色是统一 Key/API 通道。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。它的价值在于你不需要为每个模型、每个工具单独维护一套鉴权和 base_urlAgent 的模型调用走一个通道工具调用走另一个通道报错时能快速判断是模型侧问题还是工具侧问题。先说清楚为什么这个分离很重要。Agent 报错排查最怕的就是错误来源不明。401 可能是模型 API Key 过期也可能是工具自己的鉴权失败local proxy failed 可能是本地网络配置问题也可能是工具服务没起来429 可能是模型限流也可能是工具后端限流。如果你把模型和工具混在一个通道里排查时就是一团乱麻。我的实验环境是这样搭的模型侧Agent 的推理和工具选择走 TaoToken 的 API 通道。你可以在 https://taotoken.net/api-keys 拿到 Key然后在代码里配置 base_url 和 model。工具侧我故意写几个会报错的 mock 工具用来注入 401、429、参数错误、部分成功等场景。先看模型侧的配置。如果你用的是 OpenAI 兼容的 SDK配置大概是这样from openai import OpenAI client OpenAI( api_key你的_TaoToken_Key, base_urlhttps://taotoken.net/api/v1 ) response client.chat.completions.create( modelclaude-sonnet-4-5, messages[ {role: user, content: 帮我查一下北京今天的天气} ], tools[weather_tool_schema], tool_choiceauto )这里base_url指向 TaoToken 的 API 入口model填你要用的模型 ID。注意 base_url 后面要带/v1这是 OpenAI 兼容协议的标准路径。如果你用的是 Anthropic 原生协议路径会不一样具体可以看接入文档 https://taotoken.net/doc 。工具侧我写了一个会按条件返回错误的 mock 服务。核心逻辑是根据请求头里的一个X-Inject-Error字段决定这次返回 401、429 还是正常结果。这样我就能在测试里精确控制第几次调用触发什么错误。from fastapi import FastAPI, Request, HTTPException app FastAPI() app.post(/tool/query_weather) async def query_weather(request: Request): inject request.headers.get(X-Inject-Error, ) if inject 401: raise HTTPException(status_code401, detailUnauthorized: token expired) if inject 429: raise HTTPException(status_code429, detailRate limit exceeded, retry after 2s) if inject partial: return {status: partial, data: {city: 北京}, error: upstream timeout on humidity field} return {status: ok, data: {city: 北京, temp: 18, weather: 晴}}这个 mock 服务跑在本地 8000 端口。Agent 调用它的时候通过 header 注入错误。这样你就能在完全可控的条件下观察 Agent 面对 401 时是读detail字段还是自己编原因。为什么用 TaoToken 而不是直接连各家模型因为当你要对比不同模型在同一个报错场景下的恢复能力时统一通道能省掉大量鉴权适配工作。你换模型只改一个 model IDbase_url 和 Key 都不动。这在做 FISSION-GRPO 思路的多恢复轨迹采样时特别有用——你需要同一个问题采样多条轨迹如果每条轨迹都要重新配鉴权实验根本跑不起来。环境搭好之后先别急着上 Agent。手动发一次请求确认 401 和 429 能正常触发确认模型侧调用能通。这一步是后面所有排查的基础。如果这一步就有问题先去看第 5 节的报错对照表。3. 可复制的工具调用配置与错误注入验证这一节给你可以直接抄的配置片段和验证步骤。核心目标让 Agent 在工具调用失败时能拿到结构化的错误信息而不是一个模糊的异常。先说工具调用的 schema 配置。很多人写 function calling 的 schema 时只写参数不写错误处理约定。这是 Agent 学不会纠错的第一道坎。你需要在工具描述里明确告诉模型这个工具可能返回哪些错误码每个错误码意味着什么。{ type: function, function: { name: query_weather, description: 查询指定城市的天气。调用失败时返回结构化错误401 表示凭证过期需刷新429 表示限流需退避重试partial 表示部分字段缺失可基于已有字段继续。, parameters: { type: object, properties: { city: { type: string, description: 城市名称如 北京 } }, required: [city] } } }注意 description 里我把错误码语义写进去了。这不是可有可无的装饰——它直接影响模型在收到 401 时是去刷新凭证还是去改参数。FISSION-GRPO 里的 Error Simulator 干的就是类似的事生成指出错因但不泄漏答案的反馈。你在工程里可以用工具描述和错误响应体来承担这个角色。接下来是 Agent 主循环里处理工具结果的部分。关键点是不要把工具返回的错误当成普通文本塞回对话要保留结构化的错误码和错误信息。import json import time def execute_tool_call(tool_call, max_retries3): name tool_call.function.name args json.loads(tool_call.function.arguments) for attempt in range(max_retries): try: if name query_weather: resp call_weather_api(args[city]) if resp.get(status) partial: return { tool_call_id: tool_call.id, role: tool, content: json.dumps({ error_code: PARTIAL, message: resp.get(error), partial_data: resp.get(data) }, ensure_asciiFalse) } return { tool_call_id: tool_call.id, role: tool, content: json.dumps(resp, ensure_asciiFalse) } except HTTPError as e: code e.response.status_code detail e.response.text if code 429: wait 2 ** attempt time.sleep(wait) continue return { tool_call_id: tool_call.id, role: tool, content: json.dumps({ error_code: code, message: detail }, ensure_asciiFalse) } return { tool_call_id: tool_call.id, role: tool, content: json.dumps({error_code: MAX_RETRY, message: 重试次数耗尽}, ensure_asciiFalse) }这段代码有两个设计点值得说。第一429 走指数退避重试这是工程层面的自愈不需要模型介入。第二401 和其他错误直接返回结构化错误给模型让模型决定下一步。这就是 FISSION-GRPO 思路的工程映射能自动恢复的自动恢复需要策略决策的交给模型。然后是错误注入验证。你要验证的是Agent 收到 401 后会不会去读error_code和message而不是自己编原因。验证方法很简单在 mock 服务里注入 401然后看 Agent 的下一轮输出。# 启动 mock 工具服务 uvicorn mock_tools:app --port 8000 # 注入 401 测试 curl -X POST http://localhost:8000/tool/query_weather \ -H Content-Type: application/json \ -H X-Inject-Error: 401 \ -d {city: 北京}预期返回{detail: Unauthorized: token expired}然后跑 Agent观察它在收到这个 401 之后的行为。健康的 Agent 应该输出类似工具返回 401凭证过期我需要刷新 token 后重试的推理而不是可能是城市名写错了我换个城市试试。如果你想让验证更系统化可以做一个错误注入矩阵把不同错误码和期望的恢复动作列出来逐条跑注入错误期望 Agent 行为不健康行为401识别为凭证问题触发刷新或上报改参数、换工具名429退避重试或降低调用频率立即重试、虚构成功partial基于已有字段继续标注缺失丢弃全部数据重来参数类型错误修正参数类型后重试虚构参数值这个矩阵就是你评估 Agent 纠错能力的标尺。FISSION-GRPO 在训练阶段做的事本质上就是让模型在这个矩阵上的正确率越来越高。配置片段方面如果你用的是 Claude Code 或者类似的 Agent 框架settings 文件里需要配好 base_url、Key 和 model。以 Claude Code 的 settings.json 为例{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的_TaoToken_Key, ANTHROPIC_MODEL: claude-sonnet-4-5 } }这三件套——Base URL、Key、Model ID——缺一不可。Base URL 指向 TaoToken 的 API 入口Key 从 https://taotoken.net/api-keys 获取Model ID 填你要用的模型。如果你用的是 Cline 或者带 MCP 的客户端配置逻辑一样只是字段名不同。MCP 的配置里同样要写全这三项否则会出现 local proxy failed 这类连接错误。4. 从失败轨迹到恢复策略让 Agent 真正读懂报错配置搭好、错误能注入之后核心问题来了怎么让 Agent 从看到报错就瞎猜变成看到报错能诊断并恢复。这一节讲工程上可落地的做法思路直接借鉴 FISSION-GRPO 的三阶段。FISSION-GRPO 的第一阶段是普通 GRPO 探索维持基本工具能力。对应到工程里就是你的 Agent 得先能正常调用工具、输出合法 JSON、填对参数。这一步不过关后面纠错无从谈起。所以先确保你的 function calling schema 是干净的参数类型、必填项、枚举值都写对。第二阶段是找出失败轨迹生成诊断反馈。这是最关键的一步。在训练框架里Error Simulator 会根据错误轨迹和标准调用生成指出错因但不泄漏答案的反馈。在工程里这个角色由谁来扮演答案是结构化的错误响应 明确的工具描述。我前面在工具 schema 的 description 里写了错误码语义在工具返回里保留了error_code和message这两者合起来就是诊断反馈。但光有反馈不够你还要在 Agent 的 system prompt 里明确要求它先读错误码再决定动作。system_prompt 你是一个会自我纠错的 Agent。当工具调用返回错误时你必须 1. 先读取返回中的 error_code 和 message 字段 2. 根据 error_code 判断错误类型401凭证问题429限流PARTIAL部分成功 3. 针对错误类型选择恢复动作不要虚构错误原因 4. 如果无法从错误信息判断原因明确说明信息不足而不是猜测。 禁止行为在未读取错误信息的情况下修改参数、更换工具、或假设调用成功。这段 prompt 的作用等价于 FISSION-GRPO 里那个指出错因但不泄漏答案的反馈 f。它不告诉模型具体怎么修只告诉模型你必须基于真实错误信息决策。第三阶段是裂变恢复轨迹。在训练里系统从一个错误上下文采样多条恢复轨迹成功的奖励高继续犯错的奖励低。在工程里你可以用多候选恢复 验证来模拟这个机制。具体做法当 Agent 遇到工具错误时不要让它直接执行一个恢复动作而是让它生成多个候选恢复方案然后你用一个轻量的验证器筛选。比如 401 场景Agent 可能生成刷新 token 重试、检查 Key 配置、上报人工三个候选你的验证器可以检查哪个候选真正解决了问题。def generate_recovery_candidates(error_context, n3): prompt f工具调用失败错误信息如下 {error_context} 请生成 {n} 个不同的恢复方案每个方案包含 - 恢复动作具体要做什么 - 预期结果 - 如果这个方案失败下一步是什么 不要重复同一个方案。 # 调用模型生成候选 response client.chat.completions.create( modelclaude-sonnet-4-5, messages[{role: user, content: prompt}] ) return response.choices[0].message.content这个多候选机制的价值在于它逼着模型探索不同的恢复路径而不是一条道走到黑。FISSION-GRPO 论文里举的例子是一个参数调用错误在收到参数类型不正确提示后模型可能生成 4 种恢复方案正确修改参数、重复原错误、虚构参数、改用错误工具。系统给成功修复的高奖励给继续犯错的低奖励训练后模型就更倾向于选正确的那条。工程上你没法做梯度更新但你可以做候选筛选 反馈记录。把每次错误的恢复方案和实际结果记下来形成你自己的纠错缓冲区。下次遇到同类错误优先用历史上成功过的恢复方案。这就是 LIFO 缓冲区的工程近似——优先用最近验证有效的策略。还有一个容易被忽略的点FISSION-GRPO 强调训练完成后不需要携带 SimulatorAgent 直接利用学到的纠错能力。对应到工程里就是你的错误处理逻辑不应该依赖某个特定的 mock 服务或测试环境。生产环境里错误是真实发生的Agent 要能直接处理。所以你的验证要在去掉注入、用真实错误的条件下再跑一遍。我实测下来这套结构化错误 明确 prompt 多候选恢复的组合能把 Agent 在 401 场景下的瞎猜率从七成降到两成左右。剩下的两成主要是错误信息本身不清晰导致的那就要回到工具设计层面去改。5. 工具调用报错对照表401、local proxy failed、429 怎么排这一节是排障手册。你遇到的具体报错对照着查。401 Unauthorized / invalid api key这是最常见的。分两种情况模型侧 401 和工具侧 401。模型侧 401通常是 TaoToken 的 Key 没配对或者过期了。检查三件事Key 是不是从 https://taotoken.net/api-keys 拿的最新值base_url 是不是https://taotoken.net/api/v1注意/v1请求头里的 Authorization 格式是不是Bearer 你的Key。如果用的是 Claude Code 的 settings.json检查ANTHROPIC_API_KEY字段有没有写错。工具侧 401是工具自己的鉴权失败。这时候 Agent 应该识别为工具凭证问题而不是去改调用参数。如果你的 Agent 在工具 401 时去改 city 参数说明它没读错误码回到第 4 节改 prompt。local proxy failed / connection refused这个报错通常出现在本地开发环境。原因有几个mock 工具服务没启动检查 uvicorn 是不是在跑端口被占用换个端口base_url 写成了 localhost 但服务在容器里用 host.docker.internal 或容器 IP代理配置冲突检查环境变量里的 http_proxy、https_proxy 有没有指向一个不存在的地址。注意这里说的代理是本地网络配置层面的不是让你去搞什么网络工具。如果你在本地跑 mock 服务确保 Agent 进程能直接访问到那个端口。容器场景下最容易出这个问题Agent 在容器 Amock 服务在容器 B两个容器不在同一网络里就会 connection refused。排查命令# 确认服务在监听 lsof -i :8000 # 从 Agent 所在环境测试连通性 curl -v http://localhost:8000/tool/query_weather429 Too Many Requests / rate limit exceeded429 是限流。模型侧 429 说明你调用太频繁需要退避工具侧 429 说明工具后端扛不住需要降频或排队。工程上的处理429 不要立即重试用指数退避。我前面代码里的time.sleep(2 ** attempt)就是这个逻辑。第一次等 1 秒第二次 2 秒第三次 4 秒。如果三次都 429说明限流很严重应该上报而不是继续重试。Agent 层面429 场景要训练模型退避而不是换工具。有些模型遇到 429 会想这个工具不行我换个工具这是错误的恢复策略。429 是临时的退避后同一个工具就能用。reading choices / 响应解析失败这个报错通常出现在你解析模型响应的时候。response.choices[0]报 IndexError 或者 KeyError说明响应结构和你预期的不一样。可能原因模型返回了错误而不是正常响应先检查有没有 401/429你用的 SDK 版本和 API 协议不匹配响应被中间层改写了。排查方法把原始响应打出来看。import json print(json.dumps(response.model_dump(), ensure_asciiFalse, indent2))看清楚choices字段到底有没有、结构是什么。如果是 TaoToken 通道返回的正常情况下结构和 OpenAI 兼容协议一致。如果结构不对检查 base_url 是不是写成了不带/v1的路径。OAuth / token expiredOAuth 相关的报错通常是工具侧用了 OAuth 鉴权token 过期了。Agent 应该识别为需要刷新 token而不是需要改参数。如果你的工具支持 refresh token在工具层做自动刷新如果不支持把 401 明确返回给 Agent让它决定是上报还是走备用方案。参数类型错误 / invalid parameter type这类错误是模型填参数时类型不对比如该填 integer 的填了 string。排查检查工具 schema 里的 type 定义检查模型输出的 arguments 是不是合法 JSON在工具层做参数校验返回明确的错误信息。对照表总结报错根因Agent 正确动作常见错误动作401凭证过期/错误刷新或上报改参数、换工具local proxy failed服务未启动/网络不通检查服务状态重试同一请求429限流指数退避立即重试、换工具reading choices响应结构异常打印原始响应排查假设成功OAuth expiredtoken 过期刷新 token虚构成功结果参数类型错误schema 或输出问题修正类型虚构参数值这张表建议贴在你的开发环境里。每次 Agent 报错先对照这张表判断是工程问题还是策略问题。工程问题改代码策略问题改 prompt 或加候选恢复机制。6. 把纠错能力固化进你的 Agent 工作流前面讲的都是单点排查。真正要让 Agent 稳定你得把纠错能力固化进工作流而不是每次出问题临时救火。第一个动作给你的 Agent 加一个错误分类器。在工具返回错误后先过一个轻量分类步骤把错误分成可自动恢复429 退避、token 刷新和需策略决策401 无 refresh、partial 数据两类。可自动恢复的直接在工具层处理掉不打扰模型需策略决策的才交给模型。第二个动作建立你的纠错缓冲区。每次 Agent 遇到错误并成功恢复后把错误特征 恢复动作 结果记下来。下次遇到相似错误优先检索历史成功方案。这就是 FISSION-GRPO 里 LIFO 缓冲区的工程版——优先用最近验证有效的策略。correction_buffer [] def record_correction(error_code, error_msg, action, success): correction_buffer.append({ error_code: error_code, error_msg: error_msg, action: action, success: success, timestamp: time.time() }) # 只保留最近 100 条 if len(correction_buffer) 100: correction_buffer.pop(0) def find_similar_correction(error_code): # 从最近的记录里找同类错误的成功方案 for record in reversed(correction_buffer): if record[error_code] error_code and record[success]: return record[action] return None第三个动作定期做错误注入回归测试。把你遇到过的真实报错场景做成测试用例每次改完 Agent 逻辑就跑一遍。这等价于 FISSION-GRPO 的训练迭代——不断用新的失败轨迹训练让策略越来越稳。第四个动作模型侧的统一通道要固定下来。TaoToken 的 API 入口 https://taotoken.net/api 和 Key 管理页 https://taotoken.net/api-keys 建议收藏。当你需要对比不同模型在纠错场景下的表现时统一通道能让你只改 model ID 就完成切换。如果你要做长期的 Agent 编码和纠错能力迭代可以考虑 Coding Plan它更适合持续性的开发场景。最后说一个我踩过的坑不要试图用 prompt 解决所有纠错问题。有些错误是工程层面的服务没起、网络不通、Key 过期这些应该在工具层和基础设施层解决不要让模型去猜。模型该处理的是策略性错误——参数怎么改、工具怎么换、部分成功怎么继续。把这两类错误分开你的 Agent 会稳定很多。验证你的 Agent 到底行不行最直接的方法就是跑一遍错误注入矩阵。401、429、partial、参数错误各注入一次看 Agent 的恢复动作对不对。如果 401 场景它还在改参数回到第 4 节改 prompt如果 429 场景它不退避检查你的重试逻辑。这套流程跑通之后你的 Agent 才算真正具备了工具调用的自愈能力。
阅读完成 · 觉得有帮助?
咨询建站