Agent 报错别急着换模型:从错误码到失败链的逐层排查指南

发布时间:2026/11/4 9:04:27
Agent 报错别急着换模型:从错误码到失败链的逐层排查指南 1. 别急着换模型先看看错误码在说什么Agent 项目跑崩了很多人的第一反应是“模型不行”然后换模型、调温度、加提示词折腾一圈发现该崩还是崩。我踩过太多次这种坑了后来才慢慢意识到大部分 Agent 失败根本不是模型能力问题而是失败链路上游某个环节先出了问题模型只是最后一个背锅的。举个很典型的场景。你写了一个 Agent让它调用工具查数据、做分析、生成报告。跑着跑着突然报错日志里只有一行agent execution terminated due to error或者更粗暴的model request failed。这时候你去看模型服务商的错误信息可能显示的是 422、超时、连接中断。但如果你只盯着模型这一层排查永远找不到根因。我后来总结了一套方法把 Agent 的失败当成一条链来看从最外层的粗错误码开始一层一层往里剥直到定位到真正断掉的那个环节。这条链大致是这样的用户输入 → Agent 编排层 → 工具调用层 → 外部服务/API 层 → 模型请求层 → 结果解析层 → 输出层任何一层出问题最终表现出来的都可能是“Agent 失败了”。但错误码的粒度和位置能告诉你该往哪个方向查。这篇文章适合两类人一是刚开始做 Agent 开发、遇到报错就懵的新手二是已经跑过几个 Agent 项目、但排查效率一直不高的开发者。我会用实际案例拆解从错误码怎么读、失败链怎么建、每一层怎么排查到常见坑和速查表尽量把这件事讲透。1.1 为什么“换模型”是最差的排查策略先说说为什么不能一上来就换模型。原因很简单换模型会掩盖问题而不是解决问题。你换了一个模型可能因为它的超时阈值更宽、对畸形输入的容忍度更高、或者恰好那次请求没触发某个边界条件于是“看起来好了”。但失败链上游的问题还在过几天换个场景又崩了。更糟糕的是你换模型之后原来的错误码和日志对不上了排查难度反而更大。我自己的经验是除非确认是模型本身的能力边界问题比如上下文长度不够、不支持某种工具调用格式否则不要动模型。先把错误码读明白把失败链建起来找到真正断掉的那一环。1.2 粗错误码到底粗在哪里大部分 Agent 框架和平台返回的错误码粒度都很粗。常见的几类错误码/信息实际含义可能根因agent execution terminated due to errorAgent 执行被终止工具调用失败、超时、解析异常、模型返回异常model request failed模型请求失败网络问题、鉴权失败、请求格式错误、限流422请求实体无法处理参数格式错误、必填字段缺失、类型不匹配timeout超时外部服务慢、模型响应慢、死锁connection refused连接被拒目标服务没启动、端口不对、防火墙out of memory内存不足容器内存限制、大对象未释放、并发过高你看这些错误码没有一个能直接告诉你“是哪一行代码、哪个工具、哪个参数出了问题”。所以我们需要把粗错误码展开成失败链逐层缩小范围。2. 失败链的搭建从错误码到根因的映射方法失败链的核心思想是每一个错误码都不是孤立的它一定对应链路上某个节点的异常状态。我们要做的就是把这个对应关系建起来。2.1 先给 Agent 执行过程打上“层标签”我在自己的项目里会给 Agent 的每个执行阶段打上标签日志里必须带这些标签。这样一旦出错我能立刻知道是哪一层的问题。# 示例给 Agent 执行阶段打标签 import logging logger logging.getLogger(agent) def execute_agent(user_input): logger.info([LAYERINPUT] user_input%s, user_input) try: plan planner.plan(user_input) logger.info([LAYERPLAN] plan%s, plan) except Exception as e: logger.error([LAYERPLAN] error%s, e) raise for step in plan.steps: try: logger.info([LAYERTOOL] tool%s args%s, step.tool, step.args) result tool_registry.call(step.tool, step.args) logger.info([LAYERTOOL] result%s, result) except Exception as e: logger.error([LAYERTOOL] tool%s error%s, step.tool, e) raise try: logger.info([LAYERMODEL] prompt%s, prompt) response model.call(prompt) logger.info([LAYERMODEL] response%s, response) except Exception as e: logger.error([LAYERMODEL] error%s, e) raise return response这样做的目的是当粗错误码出现时你能通过日志里的 LAYER 标签快速定位到是哪一层先出的问题。没有这个标签你只能看到“Agent 失败了”然后从头开始猜。2.2 错误码到失败链的映射表基于我自己的项目经验整理了一张映射表。这张表不是绝对的但能覆盖大部分常见情况。粗错误码优先排查层次要排查层常见根因agent execution terminated due to errorTOOL 层PLAN 层工具参数错误、工具内部异常、工具超时model request failedMODEL 层网络层鉴权、限流、请求格式、网络不通422TOOL 层 / MODEL 层输入层参数类型错误、必填缺失、JSON 格式错误timeoutTOOL 层MODEL 层外部 API 慢、模型响应慢、死锁connection refused外部服务层网络层服务未启动、端口错误、防火墙out of memory运行时层TOOL 层容器内存限制、大对象、并发过高permission denied鉴权层TOOL 层API Key 错误、权限不足、角色配置错误这张表的使用方法是先看错误码确定优先排查层然后去日志里找对应 LAYER 标签的最后一条记录。最后一条记录的位置就是断点所在。2.3 一个真实案例422 错误背后的三层问题我之前遇到过一个 422 错误表面上看是模型请求失败但实际排查下来根因在工具层。现象Agent 执行到某一步时报422 Unprocessable Entity模型服务商返回的错误信息是“请求体格式错误”。第一层排查MODEL 层检查发给模型的请求体发现 JSON 格式没问题字段也齐全。但注意到一个细节请求体里有一个字段tool_result它的值是一个很长的字符串里面包含了换行符和特殊字符。第二层排查TOOL 层去看工具返回的结果发现工具返回的是一个未经过滤的原始文本里面包含了\n、\t、引号等字符。这些字符在拼接进 JSON 时没有被正确转义导致模型服务端解析失败。第三层排查PLAN 层为什么工具会返回这种未过滤的文本因为规划层在调用工具时没有指定输出格式工具就按默认方式返回了原始数据。根因规划层缺少输出格式约束 → 工具层返回未转义文本 → 模型层 JSON 解析失败 → 422。修复在规划层给工具调用加上output_formatjson约束工具层统一做转义处理模型层增加请求体校验。这个案例说明422 不一定真的是模型的问题它可能是上游工具层和规划层的问题只是最终在模型层暴露出来。3. 逐层排查实操从 terminalBlocker 到根因定位这一章我按失败链的顺序逐层讲怎么排查。每一层我都会给出具体的操作步骤、命令、代码片段和注意事项。3.1 输入层最容易被忽略的起点输入层的问题往往最隐蔽因为用户输入看起来“很正常”但可能包含一些边界情况。排查要点输入长度是否超过限制是否包含特殊字符换行、引号、反斜杠、Unicode 控制字符是否包含注入类内容比如让 Agent 忽略之前指令的文本编码是否统一UTF-8 vs GBK实操命令# 检查输入文件的编码 file -i input.txt # 查看输入中的特殊字符 cat -A input.txt | head -50 # 统计输入长度 wc -c input.txt注意事项我遇到过好几次因为输入里有一个不可见的 Unicode 字符比如零宽空格导致工具调用参数解析失败。这种问题用肉眼根本看不出来必须用cat -A或者十六进制查看器才能发现。3.2 规划层Agent 的“大脑”怎么排查规划层负责把用户输入拆解成可执行的步骤。这一层出问题通常表现为步骤缺失、步骤顺序错误、工具选择错误、参数生成错误。排查方法把规划层的输出完整打印出来包括每一步的工具名、参数、预期输出。检查每一步的参数是否满足工具的输入要求。检查步骤之间是否有依赖关系被遗漏。# 打印规划结果 plan planner.plan(user_input) for i, step in enumerate(plan.steps): print(fStep {i}: tool{step.tool}, args{step.args}, expected{step.expected})常见问题问题表现修复步骤缺失最终结果不完整优化规划提示词增加步骤校验工具选择错误调用了不存在的工具限制工具列表增加工具描述参数类型错误工具调用报 422在规划层做参数类型校验循环依赖Agent 卡死增加最大步骤数限制3.3 工具层terminalBlocker 和外部服务的排查工具层是失败链上最容易出问题的一层因为它涉及外部服务、网络、鉴权、数据格式等多个变量。terminalBlocker 是什么在我的项目里terminalBlocker 指的是工具调用过程中导致整个 Agent 执行被阻塞或终止的异常状态。它可能是一个超时、一个连接拒绝、一个未捕获的异常或者一个死循环。排查步骤确认工具是否被正确注册检查工具注册表里有没有这个工具名称是否匹配。确认工具参数是否正确把参数打印出来和工具文档对比。确认外部服务是否可达用 curl 或 telnet 测试。确认鉴权是否有效检查 API Key、Token 是否过期。确认超时设置是否合理工具调用超时、外部服务超时、模型超时。# 测试外部服务可达性 curl -v -X POST https://api.example.com/endpoint \ -H Authorization: Bearer $TOKEN \ -H Content-Type: application/json \ -d {key: value} \ --max-time 10 # 检查端口是否开放 telnet api.example.com 443 # 查看 DNS 解析 nslookup api.example.com注意事项工具层的超时设置一定要比外部服务的超时设置长。比如外部 API 超时是 10 秒工具层超时至少设 15 秒。否则外部服务还没返回工具层就先超时了你会误以为是外部服务的问题。3.4 模型层请求失败的真实原因模型层的错误码通常比较明确但“明确”不等于“好排查”。因为模型层的错误可能是上游传过来的也可能是模型服务本身的问题。排查清单请求体是否符合模型 API 的格式要求请求体大小是否超过限制是否触发了限流429鉴权是否有效401/403模型名称是否正确是否支持请求中的参数比如某些模型不支持tools参数# 模型请求排查示例 import requests payload { model: gpt-4, messages: [{role: user, content: prompt}], temperature: 0.7, max_tokens: 1000 } try: resp requests.post( https://api.example.com/v1/chat/completions, headers{Authorization: fBearer {api_key}}, jsonpayload, timeout30 ) print(Status:, resp.status_code) print(Body:, resp.text) except requests.exceptions.Timeout: print(请求超时) except requests.exceptions.ConnectionError: print(连接失败)常见问题速查错误码含义排查方向401未授权API Key 是否正确、是否过期403禁止访问权限是否足够、IP 是否在白名单429限流降低请求频率、申请提额500服务端错误模型服务商问题重试或联系支持502/503网关错误服务暂时不可用重试504网关超时请求处理时间过长优化请求3.5 结果解析层模型返回了但解析失败这一层经常被忽略。模型返回了 200你以为成功了结果解析的时候报错。常见问题模型返回的不是合法 JSON模型返回的 JSON 结构和你预期的不一样模型返回了多余的解释文字导致 JSON 解析失败模型返回了空内容排查方法import json response_text model.call(prompt) print(Raw response:, repr(response_text)) try: data json.loads(response_text) except json.JSONDecodeError as e: print(JSON 解析失败:, e) # 尝试提取 JSON 部分 import re match re.search(r\{.*\}, response_text, re.DOTALL) if match: data json.loads(match.group()) else: raise注意事项不要假设模型一定会返回合法 JSON。即使你在提示词里明确要求了模型也可能返回带 markdown 代码块标记的 JSON或者前后加了解释文字。解析层一定要做容错处理。4. 常见问题与排查技巧实录这一章我把实际项目中遇到的高频问题整理成速查表并补充一些独家避坑技巧。4.1 高频问题速查表现象可能根因排查动作修复方案Agent 突然终止工具层未捕获异常查看 TOOL 层最后一条日志增加 try-catch返回结构化错误模型请求 422请求体格式错误打印请求体检查特殊字符转义特殊字符校验 JSON工具调用超时外部服务慢curl 测试外部服务增加超时增加重试内存持续增长大对象未释放查看容器内存监控限制并发及时释放对象Agent 卡死循环依赖或死锁查看步骤日志增加最大步骤数增加超时结果不完整规划层步骤缺失打印规划结果优化提示词增加校验鉴权失败Token 过期检查 Token 有效期自动刷新 Token连接被拒服务未启动telnet 测试端口启动服务检查端口4.2 独家避坑技巧技巧一给每个工具调用加唯一 ID这样在日志里可以追踪一次工具调用的完整生命周期开始、参数、结果、耗时、异常。import uuid call_id str(uuid.uuid4())[:8] logger.info([TOOL_CALL%s] start tool%s args%s, call_id, tool_name, args) try: result tool.call(args) logger.info([TOOL_CALL%s] success result%s, call_id, result) except Exception as e: logger.error([TOOL_CALL%s] failed error%s, call_id, e)技巧二失败链日志要包含上下文不要只记录错误信息还要记录当时的输入、规划结果、工具参数、模型请求体。这样你才能复现问题。技巧三给 Agent 执行加“黑匣子”把每次 Agent 执行的完整日志输入、规划、工具调用、模型请求、模型响应、解析结果写入一个单独的文件。出问题时直接看这个文件不用在大量日志里翻找。import json from datetime import datetime def save_blackbox(execution_id, data): filename fblackbox/{execution_id}_{datetime.now().isoformat()}.json with open(filename, w) as f: json.dump(data, f, ensure_asciiFalse, indent2)技巧四区分“可重试”和“不可重试”错误不是所有错误都值得重试。网络超时、限流可以重试参数错误、鉴权失败重试也没用。RETRYABLE_ERRORS [timeout, 429, 502, 503, 504] def should_retry(error_code): return any(code in str(error_code) for code in RETRYABLE_ERRORS)技巧五用最小复现法定位问题当失败链很长时不要从头到尾查。把问题拆成最小可复现单元单独调用工具、单独调用模型、单独解析结果。哪个单元失败问题就在哪里。4.3 一个完整的排查实录最后分享一个我最近排查的案例把上面的方法串起来。现象Agent 在执行到第 3 步时报agent execution terminated due to error日志里只有这一行。第一步看错误码。粗错误码没有更多信息。需要看 LAYER 标签。第二步看日志。发现最后一条日志是[LAYERTOOL] toolsearch_api args{...}没有对应的 result 或 error。说明工具调用开始了但没有返回。第三步查工具层。单独调用 search_api发现它依赖一个外部服务。用 curl 测试外部服务返回 504 网关超时。第四步查外部服务。联系外部服务方确认是他们那边的一个数据库查询慢导致接口超时。第五步修复。在工具层增加超时设置15 秒增加重试机制最多 2 次增加降级方案超时后返回缓存数据。结果Agent 不再因为外部服务慢而整体失败而是能在超时后走降级路径继续执行。这个案例的关键点是粗错误码只是入口真正的根因在失败链的最末端。如果没有 LAYER 标签和工具调用日志我可能还在怀疑模型。4.4 工具选型排查辅助工具推荐工具用途备注curl -v测试 HTTP 接口看请求头、响应头、状态码telnet测试端口连通性快速判断服务是否可达jq格式化 JSON方便查看请求体和响应体htop查看进程资源排查内存、CPU 问题docker stats查看容器资源排查容器内存、CPU 限制tcpdump抓包排查网络层问题strace跟踪系统调用排查文件、网络、信号问题提示tcpdump和strace需要一定权限生产环境慎用。优先用日志和监控排查。5. 把失败链变成排查习惯排查 Agent 失败最怕的就是“猜”。猜模型不行、猜网络不好、猜工具挂了。猜来猜去时间浪费了问题还在。我现在的习惯是每次 Agent 失败先建失败链再逐层排查。失败链不是画给别人看的是画给自己看的。把链路写下来把每一层的状态标出来断点自然就出现了。具体来说我会做三件事第一日志必须带层标签。没有层标签的日志在排查时等于没有日志。第二每个工具调用必须有唯一 ID 和完整上下文。这样我才能追踪一次调用的完整生命周期。第三每次排查完把根因和修复方案记下来。下次遇到类似错误码直接查记录不用重新排查。这套方法我用了大半年Agent 项目的平均排查时间从原来的几个小时降到了十几分钟。不是因为我变聪明了而是因为我不再瞎猜了。最后再分享一个小技巧如果你的 Agent 框架支持给每个执行阶段加一个“健康检查”钩子。比如工具调用前检查外部服务是否可达模型请求前检查 Token 是否有效结果解析前检查 JSON 是否合法。这样很多问题在发生之前就能被拦截而不是等到 Agent 整体失败才发现。这个内容后续还可以这样扩展把失败链和监控系统对接每个层的异常自动上报形成告警。这样你甚至不用等用户反馈系统自己就能告诉你哪里快断了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询