ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Agent失败排查实战:从粗错误码到根因,别让模型背锅

Agent失败排查实战:从粗错误码到根因,别让模型背锅

干这行最怕听到的一句话是:“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 的失败都算给模型了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进