Home
avatar

.Sam

【智能体测评-番外】测评之前:让智能体可测的工程准备

这是“智能体测评”系列的一篇番外。前面我们讲完了测评集方法论(第 7 篇),下一篇就要动手搭流水线了。但很多人照着流水线代码搭起来之后会发现一个尴尬的问题:任务能发出去,结果也能收回来,但中间的过程数据一律拿不到,想 mock 工具也无处下手——流水线跑成了黑盒。这篇番外补上缺失的一环:在测评之前,被测的智能体需要具备什么条件、怎么获取数据、这些工程设施应该怎么埋。

一个常见的翻车现场

假设你接手了一个测评任务:评估团队刚买的某款 Agent 平台。你打开它的管理后台,找到 API 文档,写了个脚本批量发任务,拿到回复,用 LLM-as-Judge 打分——然后你就会发现:

  • 它做一个任务调了十几次工具,但你只看到了最终的那段回复文本;
  • 你怀疑它中间访问了错误的网页,但没有任何日志能证实;
  • 你想让它在沙箱里跑,它的工具却直连了生产环境的数据库;
  • 同一个任务跑两次,结果一次成功一次失败,你说不清这是模型波动还是环境变了;
  • 你想算它做一个任务花了多少 token,账单是按月给的。

这时候你手里有一套精心设计的测评集、一套漂亮的评分流水线,但你测的东西根本喂不进去,过程也看不见。这不是测评方法的问题,是被测系统不“可测”的问题。

软件测试领域有个老概念叫 testability(可测性),定义很简单:软件在给定测试条件下,能够被测试、被观测、被诊断的容易程度。Agent 测评之前,先要做可测性检查。

可测性的两个维度:可控 + 可观测

Agent 的可测性可以拆成两个正交的要求:

  • 可控(Controllability):你能把任务标准化地喂进去,能把环境固定成你想要的样子;
  • 可观测(Observability):它干活的过程和结果,你能完整地拿出来。
graph LR
    subgraph 可控["你能喂什么"]
        A1["任务注入"] --> A2["状态重置"]
        A2 --> A3["工具/环境控制"]
        A3 --> A4["随机性控制"]
    end
    subgraph 可观测["你能拿什么"]
        B1["最终输出"] --> B2["完整轨迹"]
        B2 --> B3["工具调用"]
        B3 --> B4["环境副作用"]
        B4 --> B5["资源指标"]
    end
    可控 ==> AGENT["被测智能体"]
    AGENT ==> 可观测

这两个要求听起来朴素,但市面上大量的 Agent 产品和内部系统,在其中至少一个维度上是残缺的。下面分别展开。

一、需要从智能体拿到的五类数据

按“从结果往过程挖”的顺序,每一类数据对应你能测到的层次(层次定义见第 3 篇):

1. 最终交付物(L3 的最低要求)

  • 给用户的最终回复文本、生成的代码 patch、产出的文件或工单;
  • 任务的终止状态和终止原因:完成 / 放弃 / 报错 / 超时。

没有它,连成功率都算不出来。但注意:只有它,你只能做最粗糙的 L3 端到端测评。

2. 完整轨迹 trace(L2 的核心)

每一步循环的完整记录:

  • 本步输入给模型的上下文(或其摘要);
  • 模型输出:思考过程 + 动作决策;
  • 环境观察到了什么;
  • 步骤序号、时间戳、本步 token 消耗。

第 6 篇讲的步数、回溯率、无效动作率、恢复质量,全部以 trace 为数据源。没有 trace,过程测评免谈。

3. 工具调用记录(工具维度)

每次工具调用的四要素:工具名、入参、返回值(或报错)、耗时,外加一个成功/失败标记。

这是计算工具选择准确率、参数构造错误率、幻觉工具率的依据,也是失败归因时最先要翻的东西——大多数 Agent 的失败,日志一看就知道是“工具选错了”还是“工具返回了它读不懂”。

4. 环境副作用(验证“真做成了”)

Agent 对外部世界造成的状态变更:写了什么文件、发了什么请求、改了哪条记录、点了哪个按钮、提交了什么表单。

这类数据最容易被忽略,但它是防“幻觉式成功”的唯一手段。Agent 回复“我已经帮你订好了”和它真的下单了,是两回事——SWE-bench 之所以可信,就是因为它不看 Agent 说了什么,而是跑测试用例看代码副作用;客服测评要看工单系统里是否真的建了单。凡是能查副作用的任务,就不要只看回复文本判成败。

5. 资源与运行指标(效率层)

  • token 用量,最好能区分模型调用消耗 vs 工具返回内容占用的 token;
  • 端到端时延、各阶段时延(模型推理多久、工具执行多久);
  • 成本、重试次数、并发行为。

别忘归因字段

上面五类数据,每条 trace 都必须带上一组归因字段:

run_id        本次运行的唯一 ID
session_id    会话 ID(多轮任务关联用)
task_id       对应的测评任务 ID
model         模型名 + 版本号
agent_version Agent 代码 / prompt 版本
timestamp     时间戳

没有版本信息的 trace 是考古废料——你发现成功率掉了 5%,却分不清是换了模型、改了 prompt 还是上游工具改版了,测评结果无法归因,等于白跑。

二、智能体必须具备的四种可控能力

数据拿得到的前提是环境你控得住。

1. 可编程的调用入口。 有 API 或 SDK 能批量、并发地发起任务,而不是只能在网页聊天框里手动一句句问。这是自动化测评的入场券——测评集动辄几百上千条任务,手动触发不可想象。

2. 状态可重置。 每个 case 必须能从干净的初始状态开始:新会话、清空记忆、环境回到快照。否则 case 之间互相污染(第 3 个 case 用到了第 1 个 case 建的文件),结果不可复现。WebArena 之类的 benchmark 都有专门的环境重置机制。

3. 工具与环境可控。 这是工程量最大但最关键的一条:

  • 外部工具能 mock:调第三方 API 又贵又慢又不可复现,测评时必须能替换成可控的 mock 返回;
  • 危险操作有沙箱:写文件、发消息、下单、改数据库,都必须在隔离环境里执行,任务结束能回滚;
  • 工具可白名单化:能指定这轮测评允许它用哪些工具。

4. 随机性可固定。 temperature 能设 0(或支持 seed)、模型版本能锁定。Agent 天然有方差——同一个任务跑两次结果可能不同,不控制变量,你看到的“提升 3%”可能只是噪声。严肃测评通常同一任务跑 3-5 次取分布,而不是看单次结果。

三、开放程度决定你能测到哪一层

把可控和可观测拼起来,被测系统大致分四档:

开放程度能拿到什么能做的测评
纯黑盒:只有产品界面屏幕上的最终回复L3 粗测:成功率 + LLM judge,多跑取分布,过程诊断免谈
有 API 但无过程最终输出 + 资源用量L3 自动化,可批量,但看不到轨迹
有 trace 但环境不可控全部过程数据L1-L3 可分析,但副作用判定靠人工、结果难复现
trace + 环境全可控五类数据 + 沙箱/mockL1-L3 全自动流水线,即第 8 篇的方案
再加生产日志线上真实轨迹L4 持续测评闭环,即第 10 篇的方案

测评立项的第一件事,就是对着这张表确认被测对象在哪一档——档位决定了测评方案的上限,别在纯黑盒系统上硬做轨迹分析,那是浪费时间。

四、数据怎么获取:四种接入方式

根据被测对象的开放程度,从白盒到黑盒有四种接法:

1. SDK / 源码直连埋点(最理想)。 自研或开源 Agent,直接在代码里埋点,五类数据按结构化格式落库,想记什么记什么。

2. Observability 平台集成。 第三方但允许配置的系统,让它接 Langfuse、LangSmith 或 OpenTelemetry(GenAI semantic conventions 已经覆盖了 LLM/Agent 场景),trace 自动导出。

3. LLM 网关代理。 你只能拿到它的 API key(比如它是个基于 LangChain 搭的服务、或直接调闭包模型的 Agent),就在模型调用链路上架一层代理网关(如 LiteLLM Proxy),拦截全部请求和响应。这样能拿到完整的模型交互轨迹和 token 消耗,但拿不到框架内部的规划状态。

4. UI 自动化(黑盒兜底)。 只能用产品界面(比如测评别家发布的 Agent 产品),用 Playwright 之类的工具喂任务、截图录屏、抓 DOM。能做 L3 粗测,过程指标基本拿不到,成本高、脆弱,但这是黑盒场景的唯一选择。

五、Hook:可观测性的工程落点

前四节讲的是“要什么”,这一节讲“在自研系统里怎么落地”。答案很简单:埋 hook。

如果你自己写 Agent 循环,可观测性的标准做法是在循环的关键节点上埋回调点(callback / handler / middleware,不同框架叫法不同)。这不是测评专用的额外工程——正经做一个要长期迭代的 Agent,hook 本来就是标配:

graph TB
    H0["H0 会话生命周期<br/>start / reset / end"]
    N1["① 会话:任务输入"]
    H1["H1 LLM 调用前后<br/>prompt / 响应 / token"]
    N2["② LLM 推理:思考与规划"]
    H2["H2 决策拦截<br/>审批 / 改写 / 拦截"]
    N3["③ 动作决策:选工具或回复"]
    H3["H3 工具调用前后<br/>mock / 沙箱 / 录制"]
    N4["④ 工具执行:副作用"]
    H4["H4 上下文写回<br/>记忆 / 检索内容"]
    N5["⑤ 观察写回上下文"]
    H5["H5 错误与终止事件<br/>异常 / 超时 / 放弃 / 完成"]

    H0 -.-> N1
    N1 --> N2
    H1 -.-> N2
    N2 --> N3
    H2 -.-> N3
    N3 --> N4
    H3 -.-> N4
    N4 --> N5
    H4 -.-> N5
    N5 -.->|"循环"| N2
    H5 -.-> N5

Hook 分两类,用途完全不同:

记录型 hook(被动留痕):H0、H1、H4、H5。只看不改,是 trace、监控、测评的数据源。成熟框架全都内置了——LangChain 的 CallbackHandler 有 on_llm_start/endon_tool_start/end;OpenAI Agents SDK 有 RunHookson_tool_start/endon_handoff;LlamaIndex 有 CallbackManager。自研循环就是在 call_llm()call_tool() 外面包一层装饰器。

拦截型 hook(能介入流程):H2、H3。可以阻断或改写流程,生产环境用于护栏和人工审批(高风险动作先过 H2 等人点确认),测评环境则用于挂 mock 和沙箱——这就是“工具与环境可控”在代码层面的落点:测评模式下,H3 把真实工具调用拦截掉,返回预置的 mock 数据。

这里有个关键认知:测评数据是 hook 的副产品,而不是单独造的采集系统。

  • 生产环境:hook → trace 落库 → 监控告警 + 失败回采(L4);
  • 测评环境:同一批 hook → 同结构 trace 落库 → 自动评分 + 指标计算(L1-L3)。

两边 schema 一致,你才能回答最要命的那个问题:“为什么测评全过了,线上还是翻车?”

六、最小改造清单

如果你的自研 Agent 现在一行埋点都没有,按这个清单做,一天之内能完成:

  1. call_llm():前后各一个 hook,记录完整 prompt、响应、token 数、时延;支持注入 mock 响应;
  2. call_tool():前后各一个 hook,记录工具名、参数、返回、耗时;支持拦截(返回 mock)和危险操作审批;
  3. 循环层发三个事件run_start(带 run_id 和版本信息)、step_end(每步的思考 + 动作 + 观察)、run_end(终止状态 + 原因);
  4. 所有事件统一字段run_id / session_id / agent_version / model / timestamp,异步落库(别阻塞主循环);
  5. 提供一个 run(task, session_id, mode) 入口mode=prod 正常执行,mode=eval 时工具走 mock、随机性归零、环境强制沙箱。

两个坑提前说:

  • hook 里别塞业务逻辑。它是切面,不是流程的一部分——把审批规则写进 hook 里的人,三个月后会收获一个没人敢动的意大利面系统。
  • trace 落库前脱敏。prompt 和工具参数里满是用户隐私、密钥、业务数据,按合规要求做字段级脱敏再入库。

小结

回到这篇番外开头的翻车现场:测评做不下去,往往不是方法问题,而是被测系统不具备可测性。记住三个递进的判断:

  1. 测评之前先做可测性检查:任务能不能批量喂?环境能不能重置?工具能不能 mock?过程数据能不能拿到?
  2. 可测性的工程落点是 hook:记录型 hook 解决“看得见”,拦截型 hook 解决“控得住”;
  3. 生产埋点和测评采集是同一套设施:今天为排查线上问题埋的 hook,就是明天测评流水线的数据源。

这也是为什么我们反复说测评能力要在 Agent 开发第一天就开始建——等到要测评了才补埋点,往往要把整个循环重构一遍。

可测性就绪之后,下一步就是把这套数据接入自动化流水线了——第 8 篇给了一套可以直接跑的开源工具链实现。


系列番外 · 下接 【智能体测评-08】动手搭一套Agent测评流水线

AI Agent 可观测性 Hook 测评工程