代理明明在运行,模型调用为何抓不到
作者在agentdrill实测中发现,代理已生成工具调用与最终回答,HTTP捕获记录却显示零次模型调用,误导了适配器排查。测试覆盖8个框架、19个真实代理,强调以请求和响应记录验证调用链路。
AI 深度解读
Agent 已经跑完工具调用、拿到结果并给出最终回答,测试报告却显示 LLM 调用次数为零。作者起初怀疑 llamaindex 适配器坏了,折腾一个下午后才发现:模型正常工作,漏记请求的是自己的采集钩子。
这篇来自 agentdrill v0.1.0 实测的复盘,值得读的不是修复代码有多复杂,而是一个更常见的问题:当日志、计数器和真实行为互相矛盾时,我们凭什么认定日志更可信?
为什么有完整执行轨迹,却抓不到模型请求
agentdrill 是 agentic-suite 中的对抗测试引擎,用版本化攻击包测试已部署的 Agent 防御,也可以在 CI 中运行。这次测试包含 73 个测试单元、来自 8 个框架的 19 个真实 Agent,每个涉及 LLM 的案例都必须提供实际请求和响应作为证据。
作者的采集方案是在测试运行器里给 HTTP 客户端打补丁,把 LLM 流量保存成 JSON 请求与响应对。
问题很有迷惑性:负责评判结果的 judge 请求都能抓到,状态都是 2xx;Agent 的请求却完全消失。没有报错,没有 429,也不是响应体为空,就是零记录。与此同时,Agent 的执行轨迹和最终回答又都存在。
作者因此把排查方向转向客户端适配器,怀疑 llamaindex 调用链出了问题。但真正的差别在 HTTP 客户端内部:
judge 的调用经过 httpx.Client.request(),而 Agent 框架使用的 OpenAI Python SDK 通过 send() 发送请求,绕过了作者打补丁的 request()。
采集器看到零条记录,只能证明它没抓到请求,不能直接证明模型没被调用。
这里的钩子,就是在某个方法上加一层拦截逻辑。问题在于,方法名字看起来像统一入口,不代表所有调用都会经过它。
修复只改了入口,验证却必须跟着升级
作者把拦截位置下移到 send(),同时覆盖 Client 和 AsyncClient。同步和异步都要处理,否则 Agent 使用异步调用时仍可能漏记。
在作者涉及的 httpx 调用路径中,send() 才是请求汇合的位置。这条经验不能脱离客户端实现直接照搬:换成其他语言、SDK 或网络库,仍要确认它们实际经过哪条路径。
修复后,19 个 Track B 案例都拿到了请求与响应对,记录中的状态全部为 2xx,整个测试套件没有非 2xx 的请求响应对。请求体还显示了固定模型:Agent 使用 Qwen3-4B-Instruct-2507-4bit,judge 使用 Qwen3.5-4B-4bit。
原始记录提交在 results/track-b/cases,每个案例一个目录。这让模型选择和请求结果有了可检查的依据。
不过,抓到流量还不等于证据有效。此前采集断言曾接受过一个错误占位响应:响应文件存在,但状态码是 0。检查只看文件有没有生成,于是错误也能过关。
作者随后把成功条件收紧为:存在 N 个请求和 N 个响应,响应必须是真实 2xx,并且响应体非空。统计还要从磁盘文件重新推导,不能只相信采集器自己的计数。
这里要区分两件事:错误响应也有诊断价值;但要证明一次成功调用确实发生,只有一个响应文件远远不够。原文的 2xx 要求,是这套测试对有效成功证据的要求。
怎么确认采集器没有漏掉另一半流量
作者最有用的建议,是引入采集器之外的信号。
服务端看到了请求,执行轨迹也显示 Agent 已经工作,采集结果却为空,这时应该先质疑采集链路。两个互相独立的观察结果,比一个系统反复读取自己的计数更有说服力。
第二个做法是让记录携带身份信息。作者的记录包含 meta.model,每份文件都能说明对应哪个固定模型。后来切换模型时,这个字段还帮助他们识别了旧运行留下的记录,避免把不同配置下的结果混在一起。
第三个做法是先做一次小范围核验:发出一个请求,确认服务端和采集器都看到了它。不过,一次请求只能验证所走的那条路径;同步、异步或不同 SDK 入口是否全部覆盖,还需要分别检查。
原文也提供了安装与演示命令,便于体验这套工具:
pip install agentsec-agentdrill # v0.1.0 is live on PyPI
agentdrill quickstart # first honest result in ~15 minutes, bundled demo
要留意第二行使用的是内置演示,约 15 分钟得到首次结果是作者给出的体验说明,不能据此推断接入自己的 Agent 也需要同样时间。原文报告称 73/73 个测试单元都有依据,但没有展开所有单元的具体覆盖范围。
我的判断
我认同作者把测试基础设施也列为怀疑对象。AI 编程和 Agent 调试里,很容易把日志当成事实本身;实际上,日志只覆盖了我们预先选择观察的路径。入口选错,后续推理再认真,也可能一直查错地方。
观察结果的可信度,取决于采集路径和校验条件是否成立。
相比只数文件或读取内部计数,检查请求响应对、保存模型信息、引入服务端信号,确实提供了更扎实的证据。但原文没有对照实验,也没有量化修复前后的漏采比例,这是一次具体故障的经验复盘,不能扩展成跨框架的完整性保证。
还要留一个问号:2xx 和非空响应体证明了什么?它们能排除这次遇到的错误占位文件,却不能单独证明响应内容正确、任务成功或安全防御有效。模型身份、调用成功和最终判定,仍然需要各自的证据。
对于普通团队,底层钩子也不是唯一选择。原文已经表明,服务端记录可以承担交叉核验的角色。能从现有服务端取得证据时,可以先用它核对;需要客户端补丁时,再沿实际调用链选择入口。直接照抄 request() 或 send(),都不如先验证路径可靠。
能拿来做什么
- 调试日常 AI 编程任务时,如果工具轨迹完整但 LLM 计数为零,先核对服务端记录和采集入口,再决定是否排查 Agent 适配器。
- 给 Node 或其他客户端增加流量采集时,沿 SDK 的实际调用路径检查拦截位置,并分别验证不同入口;不要把 httpx 的
send()结论直接套过去。 - 在 CI 中保存模型请求证据时,检查请求与响应是否成对,并按测试目标校验状态码、响应体和模型信息,避免文件存在就判通过。
- 切换模型后,把模型标识写进每份记录,并从落盘文件重新统计结果,让旧产物和计数偏差更容易暴露。