干这行最怕听到的一句话是:“agent 又挂了,肯定是模型不行。”说这话的人多半刚看完一行错误日志——有时只是一句“agent execution terminated due to error”,或者一个孤零零的 HTTP 422——就急着去骂模型服务商。但我拉着他们往深里挖过几次之后发现,真正因为模型本身发挥失常导致 agent 失败的,占比远没有想象中那么高。多数情况下,问题出在工具调用超时、上下文被截断、沙箱环境跑偏、甚至网络配置冲突这些“周边环节”上。
这篇东西想聊的,就是怎么把一行粗错误码,顺着失败链一点点挖到真正的根因。顺着这个思路走一遍,你会发现很多“模型废物论”其实都是冤案。
1. 为什么 Agent 一挂,锅总落在模型头上
1.1 粗错误码给了所有人一个“合理”的借口
先说个最常见的场景:Agent 项目在跑,某一步突然中断,控制台里永远只有那种“信息量约等于零”的报错——model request failed、agent execution terminated due to error,或者干脆就是 HTTP 422。这类粗错误码有个共同特点:它只告诉你“失败了”,不告诉你“哪一环失败”。
我见过不少团队,手里拿着一个 422 就去找模型服务商,对方查了半天,说请求根本没到他们那边,两边来回扯皮。真正的问题可能一直静静躺在你自己的服务里:鉴权 token 过期了,工具返回的参数格式不对,或者 API 网关把响应截断了。粗错误码的问题就在于此——它把一条完整的失败过程抽象成了一个点,而实际排查时,失败从来都是一条链。
一旦习惯了这种“看到错误码 → 甩锅给模型”的路径,你就会跳过所有中间环节:工具层报错、编排层逻辑错误、网络层超时、基础设施资源不足。这些环节的日志你可能从来没打开过。
1.2 真实失败现场的三种归因误区
第一个误区是“看见 model 字样就怪模型”。很多错误消息里带着 model 这个词,但它描述的可能是“模型请求转发失败”,而不是“模型生成内容失败”。前者是链路问题,后者才是模型问题。一字之差,差出十万八千里。
第二个误区是“重试解决一切”。超时重试当然有用,但如果是因为工具返回了畸形 JSON 导致 Agent 解析失败,你重试一百次也一样失败。更麻烦的是,某些场景下重试还会放大问题——比如幂等性没做好,工具被重复调用,订单下了两遍,这种事故我见过不止一次。
第三个误区是“只看应用层日志,不看系统层信息”。Agent 跑在容器里,容器跑在物理机上,物理机连着网络。应用日志里什么都没写,不代表系统层没发生事情——磁盘满了、内存挤爆、端口被占、IP 冲突,这些都不会出现在 agent 框架的日志里,但都会表现为“莫名其妙失败”。
1.3 模型、工具、基础设施:先把责任边界划清楚
要快速定位问题,我习惯先把 Agent 的运行链路拆成三层,每一层都有各自的失败特征:
| 层级 | 典型组件 | 失败特征 | 常见错误 |
|---|---|---|---|
| 模型层 | 大模型API、推理服务 | 生成缓慢、内容格式不符、拒绝回答 | 超时、离谱输出、上下文溢出 |
| 工具层 | 函数调用、API网关、沙箱 | 参数错误、鉴权失败、返回结构异常 | 422、401、500 |
| 基础设施层 | 容器、网络、存储、DNS | 连接中断、资源耗尽、配置冲突 | timeout、connection refused、OOM |
模型层出问题,往往表现为“响应内容质量差”或“生成时间异常长”,而不是“请求根本没到达”。工具层出问题,通常发生在 Agent 拿着模型给的参数去调用外部 API 时,参数校验不过、鉴权失败、返回格式和预期不符。基础设施层出问题,则会影响链路中的任何一环——网络一抖,模型请求和工具调用一起完蛋。
这三层的排查优先级,我建议永远是“先保链路通,再看质量差”。也就是说,先确认基础设施正常,再确认工具层正常,最后才去怀疑模型。顺序反了,大概率白忙一场。
2. 把粗错误码拆成失败链:排查的核心方法论
2.1 失败链是什么:一条从现象到根因的证据路径
你可能听过“调用链”“trace链路”这些词,它们说的是“请求经过了哪些服务”。而失败链说的是另一回事:从一个粗错误码出发,沿着“现象 → 现场 → 链路 → 根因”的顺序,把失败发生前后的所有证据串起来。
举个例子。假设一个 Agent 在执行“查询订单并发送通知”的任务,最后输出一句“agent execution terminated due to error”。这行报错就是现象。现场是什么?是工具调用返回了 503。链路是什么?是 agent → API 网关 → 订单服务 → 消息服务。根因呢?可能是消息服务所在主机的磁盘满了,导致它在处理请求时直接失败。
没有失败链思维的人会怎么做?看到报错里有 error 就重新跑一次,或者换一个更大的模型。有失败链思维的人会怎么做?先抓现场,再查链路,一层层排除,最后在消息服务的机器上敲一句df -h,5分钟解决问题。
2.2 从错误码到失败链的拆解步骤
我总结了一套六步法,不敢说放之四海而皆准,但至少在我经手的排查里,绝大部分都能走通。
第一步,还原现场。把报错信息、时间点、操作序列原样记录下来。这一步不要省略,很多线索就在“失败前那一步做了什么”里面。如果 agent 刚调用完某个工具就失败了,问题大概率不在模型,而在工具返回的数据。
第二步,确认链路。画出一条“这次请求实际经过了哪些组件”的清单,不是想当然那种,而是看实际配置。比如网关配了什么路由,默认超时设的多少,鉴权在哪一层做。这些配置本身往往是问题所在。
第三步,分层验证。按前面说的三层——基础设施、工具、模型——用最小的动作逐层探测。基础设施层会不会网络不通?工具层能不能单独调通?模型层裸调用是否正常?每一层都做一次“最小可复现实验”。
第四步,抓取证据。不要靠猜,靠日志、抓包、监控指标说话。应用日志不够就看系统日志,系统日志不够就抓包。证据链越完整,根因定位越准。
第五步,复现实验。在确认根因后,小范围复现一次,验证你的判断。这一步的价值在于区分“真正的根因”和“碰巧一起出现的异常”。
第六步,修复并验证。修复之后,不只跑一次这个 case,还要把同类链路都回归一遍,确认没有殃及池鱼。
2.3 根据错误码定位路径的实际操作
很多人不知道粗错误码怎么“查路径”。我常用的方法有三个。
第一个是把错误码直接扔进代码仓库里搜。以 422 为例,去搜后端服务里所有返回 422 的地方,你会发现它通常不是框架自动生成的,而是某个校验逻辑主动返回的。找到那段代码,你就拿到了具体的校验条件,错误码就有了上下文。
第二个是看网关或框架的链路日志。现在绝大多数 agent 框架和 API 网关都支持 trace,请求经过的每个节点都会有记录。报错说 422,你要找的是这个 422 是哪个节点写的响应。是模型网关?还是你自己的 tool server?还是最上游的第三方服务?节点定位对了,排查就完成了一半。
第三个是直接实验。拿到一个错误码,别急着查文档,先把请求原样复现一遍,用curl -v看完整的请求和响应头。很多时候响应头里的信息比响应体更有价值,比如x-request-id可以拿去查日志,retry-after告诉你触发了限流,content-length: 0暗示连接被提前断开。这些都是粗错误码之外的真实细节。
3. 实操复盘:一次 422 通信故障的完整排查
3.1 第一现场:报错只有一行,日志全在另一个容器里
上个月帮一个朋友看问题,他们的 agent 在调用一个内部工具时稳定失败,错误码是 422,附带一句“通信故障”。注意,是 agent 框架这边报 422,而不是模型那边。朋友的第一反应是“是不是提示词写得太复杂,模型不会调用工具了”,差点就要去调 prompt。
我让他先把 agent 引擎的日志拉出来,发现这个 422 根本不是模型返回的,而是 agent 引擎在调用内部工具服务时,工具服务返回的。引擎只是把错误透传过来,在页面上展示了粗错误码。真正要看的,是工具服务自己的日志。问题在于,工具服务跑在另一个容器里,日志默认只输出到容器内部,团队平时根本没人去看。
我让他执行了一句docker logs <工具服务容器名> --since 10m,立刻看到一串 422 的完整响应体——原来是工具服务要求请求头里必须带X-Client-Version,而 agent 引擎在升级后换了新的请求头字段名,旧字段被网关拦截了。
3.2 逐层排查:网关、鉴权、沙箱、模型服务
这个 case 的排查路径其实挺典型的,我按顺序记录一下。
第一步查网关。工具服务的请求要经过内部网关,我在网关的 access log 里看到了那条 422 请求,确认它确实命中了路由,没有被拦截在网关之外。同时确认网关的响应头里没有异常,说明网关本身转发逻辑正常。
第二步查鉴权。422 不是 401 也不是 403,说明请求已经过了鉴权层。但仍然值得看一眼——有些网关会在鉴权失败时统一返回 422 以隐藏内部结构。我检查了 token 的过期时间,确认 token 有效。
第三步查沙箱和容器网络。agent 引擎和工具服务各自跑在独立容器里,通过内部网络通信。我看了容器网络配置,确认两个容器在同一网络段,DNS 解析正常,端口映射没有冲突。这一步如果网络有问题,通常不是 422,而是 timeout 或 connection refused,所以基本排除。
第四步,真正定位。回到容器日志,看到了响应体里的字段校验错误。顺着代码找到了工具服务里的参数校验逻辑:if client_version not in allowed_versions: return 422。而 agent 引擎侧配置里写的是另一个版本号。两边定义不一致,校验不通过,就这么件事。
3.3 根因确认与修复方案
根因确认方式很简单:我在本地用curl -x POST -H "X-Client-Version: v2"调用工具服务,返回 200;用旧的v1再调,返回 422。复现实验一次通过。修复方案也简单,把 agent 引擎的请求头配置改成 v2,重新部署,任务跑通。
但这里我想多说一句:这类问题的修复从来不只是改个版本号。这背后反映的是两个服务之间“接口契约没有版本化管理”的问题。422 是校验层在提醒你“请求不符合协议”,而协议不一致往往是因为变更流程里缺少同步机制。我把他们的接口文档整理了一遍,加了一个简单的契约测试,保证每次改动后自动校验字段名和版本号是否匹配。后面的排查成本一下就降下来了。
4. 高频失败场景与 Linux 排查指令实录
4.1 连接超时与 IP 冲突:网络层怎么查
Agent 项目里最磨人的不是逻辑错误,而是网络问题。尤其是连接超时,它在应用层表现为“请求发出去就没下文了”,在 agent 框架里可能被包装成model request failed或者tool call timeout,很容易让你误判成模型或工具的问题。
遇到超时,我习惯按下面的顺序查。
先看本机到达目标主机的可达性,最简单的就是ping和traceroute。ping 不通,可能是网络不通;ping 通但丢包严重,可能是链路质量差。traceroute 可以看到路径上哪一跳延迟异常。
再看端口和连接状态。ss -tunap能看到本机所有监听端口和活动连接,排查“端口被占”和“连接数打满”都很管用。如果发现大量SYN_SENT状态的连接,说明本机发出去的连接根本没有得到响应,要么目标没监听,要么被防火墙挡了。
IP 冲突是个特别容易忽略的坑。我排查过一个 case:一个 agent 服务突然间歇性不可用,应用日志完全正常,但请求就是时好时坏。最后发现是另一台设备占用了同一个 IP,导致部分请求到了错误的机器。查 IP 冲突,可以用arp-scan -l扫描局域网内的 IP 和 MAC 对应关系,如果发现同一个 IP 对应了两个不同的 MAC,基本就是冲突了。再配合ip addr看本机 IP 配置,确认是不是 DHCP 分配重复。
网络排查还有一个容易被忽略的角落:DNS。域名解析超时导致的“假死”非常常见。用dig或nslookup检查目标域名的解析时间,如果解析耗时异常,就得看看是不是本地 DNS 配置有问题,或者/etc/resolv.conf里的 nameserver 不可达。
4.2 模型请求失败的三个检查点
模型请求失败是 agent 排查里的重灾区。这里说的不是“模型答得不好”,而是“请求根本没成功”。我总结出三个必查的检查点。
第一个检查点是超时设置。模型推理本来就慢,长上下文场景下更慢。很多默认超时是 30 秒或 60 秒,但在复杂任务里,模型思考加上工具调用,一轮完整交互可能超过两分钟。如果超时设置得太短,你看到的失败其实是“客户端自己放弃等待”,跟模型性能没有关系。排查方法是看请求发出到异常抛出的时间差,如果每次都在同一个固定阈值附近失败,就说明是超时问题。
第二个检查点是上下文长度。我踩过最典型的坑是:任务执行到一半,上下文接近模型的上限,请求直接返回 400。这种情况在日志里往往能看到“maximum context length exceeded”之类的字样,但 agent 框架可能只向上层展示一个model request failed。排查方法是统计每次请求的 token 数,看是不是在接近上限时失败,然后考虑做上下文压缩或历史裁剪。
第三个检查点是鉴权和配额。token 过期、欠费停服、并发配额打满,这些都会导致模型请求失败,而且错误码五花八门。我见过 401、429、甚至 200 但响应体是空的情况。排查方法是直接裸调模型 API,绕过 agent 框架。如果裸调用正常,框架调用失败,问题就在框架侧的鉴权配置或请求构造上。这一步之后,你才能理直气壮地说“这次真的是模型的问题”。
4.3 Agent 沙箱执行异常的排查路径
Agent 执行代码或命令时,通常会在沙箱里跑。沙箱执行失败的排查和普通服务不太一样,因为失败原因经常藏在沙箱内部。
首先要确认沙箱本身有没有被创建出来。看容器状态,用docker ps -a看沙箱容器是不是处于exited状态。很多 agent 框架会为每个任务创建临时容器,如果镜像拉取失败、存储空间不足,容器根本起不来。dmesg | tail可以看到内核层面的错误,比如 OOM 掉进沙箱里的进程,或者/dev/shm空间不够。
其次要看沙箱里的执行环境。同样的代码,在不同镜像里结果不一样。缺依赖、缺环境变量、时区不对、Python 版本不一致,都会导致执行失败。排查时进入沙箱容器,手动跑一遍同一段命令,多看一步少猜一步。
最后要看文件系统。沙箱通常是临时目录,任务执行过程会写大量中间文件。如果宿主机的/tmp或者 docker 的存储驱动目录满了,沙箱写入会报no space left on device,这类错误应用层经常表现得非常诡异——明明逻辑没错,但文件写不进去、任务就断在那里。遇到“莫名失败”,先敲一句df -h看看,成本最低,收益最高。
5. 常见问题速查与实操心得
5.1 高频报错速查表
把最近一年在 agent 项目里遇到的高频报错整理成了一张表,排查时可以直接对照。
| 报错表现 | 常见根因 | 首选排查动作 |
|---|---|---|
| HTTP 422 | 请求参数或头信息不符合服务端契约 | 查看工具服务的原始响应体,搜代码里返回 422 的校验逻辑 |
| agent execution terminated due to error | 任务执行到某一步被异常中断 | 拉取完整 trace,确认中断发生在哪个节点 |
| model request failed | 超时、上下文溢出、鉴权失败、网络异常 | 裸调模型 API,对比框架调用差异 |
| connection timeout | 网络不通、防火墙拦截、DNS 解析慢 | ping 目标主机,ss 看连接状态,dig 检查解析耗时 |
| no space left on device | 磁盘满了 | df -h 检查挂载点使用率 |
| 沙箱容器 exited | 内存不足、镜像异常、启动命令错误 | docker logs 看沙箱启动日志,dmesg 看 OOM 记录 |
| 间歇性不可用 | IP 冲突、负载不均、连接池耗尽 | arp-scan 查 IP 冲突,ss 看连接数,检查连接池配置 |
| 429 Too Many Requests | 配额或限流触发 | 检查服务商的配额用量,确认 retry-after 响应头 |
这张表不是一个万能答案,只是一个起点。关键是你拿到一张表之后,第一件事不是照做,而是想清楚:这个报错到底是在哪一层出现的?这一层前面还有没有其他层?
5.2 一点实在的个人经验
排查 agent 失败这件事,到最后拼的不是对某个框架的熟练度,而是对“链路”的敏感度。模型只是链路里的一环,但因为它最耀眼,所以最容易背锅。我的习惯是:每次排查都写一个简单的失败链记录——现象是什么,哪一层出的错,证据是什么,根因是什么,修复是什么。积累上十几个 case 之后,你会发现大部分问题都是重复的模式,真正的“新问题”少之又少。
另外一个最实用的习惯,是永远保留“最小复现”的能力。不管问题是模型、工具还是基础设施,能用一个最简单的手段复现出来,排查就成功了一半。比如直接 curl 调用那个 API,直接进沙箱跑那行命令,直接写个最小脚本调用模型接口。复杂系统的真相,往往藏在最简单的实验里。
最后再分享一个小技巧:别只盯着错误码本身,把时间戳、请求 ID、前后操作的顺序都记录下来。工具调用的前一步、后一步经常才是真正值得看的现场——很多 agent 失败问题,不是模型不够聪明,而是它在上一步拿到了错误的数据,然后在这个错误数据上做出了看起来离谱的决策。这种问题,换什么模型都救不了,得从数据接入和工具返回的校验下手。这是我的经验,也是为什么我一直说:别再把 agent 的失败都算给模型了。