第 16 章 评测基建:怎么系统地检验 Agent
单测通过,不代表工作流能用
第 15 章讲的是"单次验证"。但一个残酷的事实是:单测、linter、类型检查器都过了,Agent 工作流还是可能一跑就废。 因为这些工具验证的是单个组件,不验证组件之间的配合。你可能写出一个底层都对、但整体根本跑不起来的提示词组合。
评测(evals)要解决的就是这个:把"提示词 + 工具 + 编排"当作一个系统来检验。这一章讲四个模式,回答"评测基建怎么搭":
- Workflow Evals with Mocked Tools(Mock 工具评测):用假工具端到端跑工作流。
- Incident-to-Eval Synthesis(事故转评测):把生产事故变成回归测试。
- Orchestration Prompt-Writing Benchmark(编排器基准):单独评测编排器的"派活"质量。
- Action Caching & Replay(动作缓存回放):缓存动作,免 LLM 重放回归测试。
模式一:Mock 工具评测(Workflow Evals with Mocked Tools)
问题
单测和 linter 验证单个组件,但不做端到端。很容易造出"底层都对、整体不跑"的提示词。 你需要验证的是提示词和工具作为一个系统能不能配合。
方案
实现工作流评测(模拟):用 mock 工具测试完整的 Agent 工作流。
核心组件(Sierra 思路):
1. 双工具实现:每个工具都有 true 和 mock 两个版本。
# true 实现,调真实 API
def search_knowledge_base_true(query: str) -> str:
return kb_api.search(query)
# mock 实现,返回静态/测试数据
def search_knowledge_base_mock(query: str) -> str:
return TEST_KB_RESULTS.get(query, DEFAULT_RESULT)2. 模拟配置:每个 eval 定义初始提示词、情境元数据、评测标准。
evals:
- name: slack_reaction_jira_workflow
initial_prompt: "给这条 Slack 消息里的 JIRA 工单加个笑脸反应"
metadata:
situation: "slack_message_with_jira_link"
expected_tools:
- slack_get_message
- jira_get_ticket
- slack_add_reaction
evaluation_criteria:
objective:
- tools_called: ["slack_get_message", "jira_get_ticket", "slack_add_reaction"]
- tools_not_called: ["slack_send_message"]
subjective:
- agent_judge: "回复是有帮助且准确的"3. 双评测标准:
- 客观标准:调了哪些工具、没调哪些工具、给会话加了哪些标签/状态。
- 主观标准:Agent-as-judge 评估("回复友好吗")、LLM 对定性结果的评价。
4. CI/CD 集成:每个 PR 自动跑评测。
执行流程:
1. 加载 eval 配置
2. 把所有工具换成 mock 实现
3. 用初始提示词 + 元数据跑 Agent
4. 记录 Agent 调了哪些工具
5. 对照客观标准(工具使用)评估
6. 用 agent-as-judge 跑主观标准
7. 报告通过/失败及细节证据
- 证据等级:新兴(
emerging),以行业驱动为主。 - 关键发现:单测和 linter 不能有效验证提示词和工具的集成;客观 + 主观的双重评测是所有实现的标准做法;非确定性仍是主要挑战,最适合做方向性指引而不是硬门禁。
- 不清楚:有效评测需要多高保真的 mock。
怎么用
最适合:
- 工具带副作用(API、数据库)的 Agent 工作流。
- 需要工作流验证的 CI/CD 流水线。
- 提示词工程和优化。
- Agent 行为变化的回归测试。
实施:
- 建 mock 层:一个
MockToolRegistry,mode切 mock/real。 - 定义 eval 用例:prompt、expected_tools、forbidden_tools、subjective_criteria。
- 跑 + 评:跑 Agent(用 mock registry)→ 检查客观标准(该调的调了、不该调的没调)→ 过了再跑主观 LLM 判断。
- 接 CI/CD:PR 触发,结果发成 PR 评论。
处理非确定性(Will Larson 的原话很直白):评测"没有我想象的那么好"。全过或全挂是强信号,混合结果是弱信号。缓解:失败的重试("三次里至少过一次")、调提示词和 mock、把复杂工作流从提示词驱动改成代码驱动、当方向性参考而非 CI 门禁。
取舍
- 好处:端到端验证提示词 + 工具的系统配合;回归在进生产前被抓到;mock 工具避免测试时的副作用;标准清晰(工具调用 + 质量双维度);每个 PR 自动验证。
- 代价:LLM 可变性让测试不稳定;mock 要跟上真实工具行为;提示词驱动的工作流更脆弱;难当 CI 硬门禁;要持续调提示词和 mock 响应;混合通过/失败的结果给的是模糊信号。
模式二:事故转评测(Incident-to-Eval Synthesis)
问题
很多团队跑评测,但评测集和生产里的真实失败越漂越远。事故被运维处理掉了,但确切的失败模式很少被转成持久的回归测试。结果就是:重复事故,加上过时基准集带来的虚假信心。
方案
把每次生产事故转成一个或多个可执行的评测用例,然后用这些用例门禁未来的改动。
机制:
- 抓取事故产物:输入、上下文、工具轨迹、输出、影响。
- 脱敏 + 提炼:规范化敏感数据,得出最小可复现场景。
- 编码预期行为:转成客观的通过/失败标准。
- 加入评测语料:带上严重级别和负责人元数据。
- 跑在 CI 和发布门禁里:事故派生的评测进 CI 和发布检查。
incident = ingest_incident(ticket_id)
case = build_eval_case(
prompt=redact(incident.prompt), # 脱敏后的提示词
tools=incident.tool_trace, # 事故时的工具轨迹
expected=define_acceptance_criteria(incident) # 验收标准
)
suite.add(case, labels=["incident", incident.severity])
if not suite.run(candidate_policy).pass(case.id):
block_release(candidate_policy) # 新改动没通过这个事故用例就拦发布证据
- 证据等级:中(
medium)。 - 有价值发现:学术研究显示从失败报告自动生成测试的成功率 60%-80%;只有 30% 的组织系统化复用事故数据,而这么做的组织重复事故更少;OpenAI、Anthropic、Meta 在 ML 系统上用生产派生的评测验证了这条路。
- 未验证:专门针对 AI Agent 的事故转评测研究还不多,大部分工作聚焦传统软件或模型评测。
怎么用
- 只从 P0(严重)事故开始,分层门禁:一开始只有 P0 评测拦发布,P1/P2 只警告。
- 事故关闭标准里,要求关联一个评测用例。
- 追踪两个指标:事故复发率和发布前评测抓到率。
- 定期裁剪或合并冗余的事故派生测试,保持运行时可控。
取舍
- 好处:评测对齐真实风险;运营经验随时间复利积累。
- 代价:加分流(triage)开销;事故数据抓取要有纪律。
模式三:编排器基准(Orchestration Prompt-Writing Benchmark)
问题
多 Agent 系统依赖编排器正确地把任务信息拆分给各子 Agent 角色:哪个片段给哪个角色、每个子 Agent 被告知了什么、没被告知什么、这些如何变成每个子 Agent 收到的实际提示词文本。端到端评测(执行成功、工具调用正确)和拓扑定义(谁和谁说话)都假设这个交接步骤做对了,但两者都不直接测它。
一个子 Agent 拿着不完整或范围错误的提示词,也可能勉强把任务做完,把编排 bug 掩盖到系统被放大到更难的任务或陌生的拓扑时才暴露,那时信息错配成了主导失败模式。
方案
把"写对子 Agent 提示词"当成一个独立的技能来评测,和最终任务是否成功分开。对一组覆盖不同通信拓扑的场景:
- 定义基准真相:任务需要的信息片段,以及每个片段合法归属于哪个角色。
- 让编排器 LLM 分解任务,为该拓扑里的每个子 Agent 角色写出实际提示词文本。
- 对照基准真相打分,三个维度:
- 召回(recall):该角色需要的每个片段都收到了吗?
- 泄漏(leakage):该角色不该看的片段,有没有被隐瞒?
- 分配准确率(assignment accuracy):每个片段都路由到了正确角色,而不是邻近角色?
- 按拓扑聚合得分,看编排质量在哪退化:链式和星型拓扑一般压力不大,更深的多对多交接的树型、网格拓扑才是重灾区。
任务 + 拓扑规格 → 编排器 LLM → 各角色提示词(角色1/角色2/角色N)
→ 片段分配评分器 → 按拓扑的准确率/泄漏报告这个模式刻意和 Mock 工具评测正交(那个评"该调的工具有没有调对"),也和声明式拓扑定义正交(那个定义 Agent 图,但不验证提示词是否正确填充了图)。它更接近交接步骤本身的单元测试。
证据
- 证据等级:低-中。目前只是一篇基准论文,还没有独立复现。
- 最有价值的发现:把评测建立在拓扑变化上(而不是固定一种拓扑),能暴露固定拓扑评测套件会漏掉的失败模式:片段分配质量在不同拓扑间不均匀地变化。
- 未验证:基准表现对生产系统端到端任务成功的预测能力;泛化性未经独立确认。
怎么用
- 在构建或加固多 Agent 系统、想要编排器"派活质量"的回归套件时用,而不只是最终输出质量。
- 每个实际用的拓扑(链式、星型、树型、扇出/扇入、辩论、网格)写几个场景,不需要全拓扑。
- 每个生成的子 Agent 提示词打三个分:必需片段召回、禁用片段泄漏、角色分配准确率。
- 和端到端工作流评测并行跑,而不是替代。它抓的是一类不同的 bug(错派活),工具调用和输出格式评测抓不到。
- 改了拓扑或换了编排器模型就重跑。
取舍
- 好处:隔离出"派活质量"的 bug,否则端到端评测会把锅甩给错的子 Agent;按拓扑索引的评分精确显示系统超过简单链/星后编排在哪退化;扩展便宜,新场景就是(拓扑、片段、基准分配)三元组。
- 代价:每个场景要手工编写基准真相的片段/角色分配,没法自动扩展到新领域;只有一篇基准,绝对分数当方向性参考,等独立复现;不替代端到端评测,系统在这里得分高,下游仍可能因为无关原因(坏工具、坏模型)失败。
模式四:动作缓存回放(Action Caching & Replay)
问题
基于 LLM 的 Agent 执行又贵又不确定。同一工作流跑多次,结果不同,还反复烧 LLM 成本。带来的问题:
- 成本爆炸:相同的任务,每次跑都烧 token。
- 非确定性:同样的输入,不同次跑输出不同。
- 没法做回归测试:无法验证修复没有破坏现有工作流。
- 迭代慢:不付 LLM 成本就没法快速测改动。
- 没法接 CI/CD:Agent 工作流的自动化测试不现实。
方案
执行时记录每个动作,带精确元数据(XPath、frame 索引、执行细节),实现不调用 LLM 的确定性重放。缓存捕获的信息足够在页面结构轻微变化时也能重放动作。
这个模式建立在强化学习的 experience replay 之上:Agent 通过复用过去的成功动作学习,而不是每次重新探索。
动作缓存条目存储完整执行元数据:
interface ActionCacheEntry {
stepIndex: number; // 工作流中的顺序
instruction: string; // 自然语言描述
elementId: string; // 编码的 frameIndex-backendNodeId
method: string; // click, fill, type 等
arguments: string[]; // 方法参数
frameIndex: number; // iframe 的 frame 上下文
xpath: string; // 归一化的元素 XPath
actionType: string; // 动作类别
success: boolean; // 执行结果
message: string; // 输出或错误消息
}智能回退重放:
重放流程:
1. 直接用缓存的 XPath 试
2. 失败就重试归一化的 XPath 变体(最多 3 次)
3. 还失败就调用 LLM 解析元素
4. 用成功的解析更新缓存生成独立脚本:把缓存的工作流导出成可运行的 TypeScript 脚本(Playwright 代码),用于手动调试或 CI。
证据
- 证据等级:新兴(
emerging),来自 HyperAgent(Hyperbrowser 团队)。 - 学术基础:Zhang 等人(2025)的 test-time plan caching 显示平均成本降低 46.62%。
- 生产数据:成本降低 43%-97%,缓存命中率 85%+。
怎么用
- 开动作缓存:执行时
enableActionCache: true。 - 持久化:缓存存成 JSON 文件,最好和版本控制里的工作流定义放一起。
- 重放:
page.runFromActionCache(savedCache, { maxXPathRetries: 3, fallbackToLLM: true })。 - 接 CI/CD:写个测试,读缓存、重放、断言
finalState.success === true。 - 生成脚本:
npx hyperagent script workflows/login-cache.json > login.test.ts。
缓解策略: 缓存版本化 + 自动过期;重放失败用 LLM 回退并更新缓存;缓存和工作流定义一起进版本控制;CI 里设自动化缓存校验。
取舍
- 好处:成本大降(XPath 正常时重放接近零 LLM 调用,43%-97% 的成本降幅);确定性的回归测试,验证修复没破坏工作流;缓存重放比 LLM 执行快 10-100 倍;缓存是完整执行历史,方便调试;能导出独立自动化脚本;页面结构变化有 LLM 回退兜底。
- 代价:缓存要存储、版本化、失效管理;重大 UI 改版会弄坏 XPath;第一次跑还是要完整 LLM 执行;缓存累积要清理;只适用于确定性工作流。
四个模式怎么选
| 场景 | 推荐模式 |
|---|---|
| 想端到端测提示词 + 工具配合 | Mock 工具评测 |
| 生产老出事故,评测集却越来越假 | 事故转评测 |
| 多 Agent 系统,派活质量心里没底 | 编排器基准 |
| 回归测试太贵,想免 LLM 重放 | 动作缓存回放 |
四个模式是评测基建的四块砖:Mock 评测测"工作流能不能跑通",事故转评测让评测集跟得上真实风险,编排器基准测"派活有没有派对",缓存回放让回归测试变得便宜又确定。组合起来,就是一条从"预防"到"兜底"的评测链路。
实践清单
- 工作流评测:工具做 mock 版,跑初始提示词,客观(该调/不该调)+ 主观(judge)双重标准
- Mock 评测接 CI/CD,结果发 PR 评论;非确定性用"三次过一"缓解
- 每次生产事故转评测用例:抓产物、脱敏、编码验收标准、进发布门禁
- 先只让 P0 事故评测拦发布,P1/P2 只警告;追踪事故复发率和评测抓到率
- 多 Agent 系统:按拓扑写编排器基准场景,打召回/泄漏/分配准确率三分
- 编排器基准和端到端评测并行跑,改拓扑或换模型就重跑
- 浏览器类工作流开动作缓存,重放免 LLM,XPath 失败走 LLM 回退
- 缓存版本化 + 自动过期,和版本控制里的工作流定义放一起
- 定期裁剪冗余评测用例,保持运行时可控
本章小结
- Mock 工具评测:假工具端到端跑工作流,客观 + 主观双标准,每个 PR 自动验。
- 事故转评测:生产事故变回归测试,评测集对齐真实风险。
- 编排器基准:单独测编排器的派活质量,召回/泄漏/分配准确率三维打分。
- 动作缓存回放:缓存动作免 LLM 重放,回归测试便宜又确定,成本降 43%-97%。
- 评测基建搭起来,Agent 才能持续可靠。
下一章讲可观测性:评测是对着已知用例验,生产里还有看不见的失败。LLM 可观测、证据分层评估、推理 token 防火墙、CoT 监控。