AI Agent 回归测试:确定性断言 + LLM-as-Judge 分层门禁,把非确定性关进 CI
AI Agent 是目前唯一一类"改一行 prompt 不敢直接上线"的代码。根因不是代码质量,是非确定性:同样的输入,两次运行可能给出不同的工具调用、不同的参数、不同的答案。prompt 微调、模型升级、工具替换都可能静默降级 agent 行为,多数团队是等用户投诉才发现。Jest 和 Playwright 解决不了这个问题——它们的前提是"输出确定",快照断言在 agent 上必挂。可行的解法是分层:确定性断言(工具调用、token 预算、延迟)每次 PR 全量跑,LLM-as-Judge 质量分(正确性、安全性、忠实度)做发布前门禁。这套方法论在 AgentBench(2026 年新开源的 agent 回归测试框架,TypeScript + Python 双 SDK,Apache 2.0)里落地得比较完整,下面拆开讲实现细节。
传统测试框架为什么测不了 Agent
| 维度 | Jest / Playwright | Agent 测试 |
|---|---|---|
| 断言对象 | 返回值 / DOM | 完整轨迹:工具调用序列、参数、中间状态 |
| 确定性假设 | 强(快照、toEqual) | 必须容忍随机性 |
| 质量判定 | 无(布尔断言) | LLM-as-Judge 打分 + 阈值 |
| 回归检测 | diff 期望值 | replay 快照 + 分数对比 |
| 资源约束 | 一般不关注 | token / 成本 / 延迟都要断言 |
差异的本质:传统测试断言"结果",agent 测试断言"过程 + 结果 + 资源"。
分层测试金字塔:什么在 PR 上跑,什么在发布前跑
L1 确定性断言——每次 PR 全量跑,零 LLM 调用,近零成本:
import { expect } from '@agentbench/core'
const result = await expect(runResult)
.status().toBeCompleted() // agent 正常结束
.tool("search_docs").toBeCalled() // 调了该调的工具
.tool("search_docs").toBeCalledWith({ // 参数正确
query: "refund policy"
})
.tool("hallucinate").not.toBeCalled() // 禁止的工具没调
.output().toContain("30 days") // 输出含关键信息
.output().toMatchRegex(/refund.*policy/i)
.tokens().toBeLessThan(4096) // token 预算
.latency().toBeLessThan(5000) // 延迟预算
.run()
if (!result.allPassed) process.exit(1)
L2 LLM 质量评分——发布前门禁。 LLM-as-Judge 按 8 个维度打分:correctness、faithfulness、safety、relevance、completeness、reasoning、conciseness、tool usage。关键是"分维度 + 阈值"而不是一个笼统总分:
.score("correctness").toBeGreaterThan(7)
.score("safety").toBeGreaterThan(8)
为什么不能只跑 L2?成本。一次多维度 LLM judge 调用的成本约等于一次普通 agent 运行,全量跑不现实。L1 拦掉大部分机械回归(工具没调、参数错、超预算),L2 只处理"答得对不对、安不安全"这类语义问题——这就是金字塔分层存在的经济性理由。
Hybrid Judge:规则和 LLM 怎么投票
纯规则(14 个评估器:exact_match、contains、regex、json_schema、tool_called 等)便宜但答不了语义问题;纯 LLM 全面但有成本和自偏好偏差。工程上常用混合投票,三种策略:
- rule_first:规则先判,命中即定;规则放行才轮到 LLM。成本最低,适合 CI 场景。
- llm_first:LLM 先判,规则兜底。适合语义为主的任务。
- parallel:两者独立跑,按策略合并(任一 fail 即 fail,或加权汇总)。
const judge = hybridJudge({
rules: [
toolCalled({ tool: "search_docs" }),
outputContains({ text: "30 days" })
],
llm: llmJudge({ dimensions: ["correctness", "faithfulness"] }),
voting: "rule_first"
})
Replay 引擎:回归测试的命门
回归测试的前提是"可复现"。确定性代码天然可复现,agent 不行——那就主动制造可复现。Snapshot Manager 保存完整 agent 状态(对话历史、工具返回、中间变量),Replay Engine 基于快照回放,三种模式:
- 确定性回放:固定快照重放,零 LLM 调用、零 flaky,适合塞进每个 PR。
- 跨模型回放:同一快照分别喂给 GPT / Claude / DeepSeek,模型升级或切换时抓行为漂移——"从 GPT 换到 Claude 会不会坏"这类问题不再靠猜。
- 批量回放:数据集(CSV/JSON/JSONL)回归,支持 train/test/validation 切分。
Diff Engine 对文本、指标、轨迹、分数四类对象做对比,自动标记 token / cost / latency / score 回归。配合 A/B 实验(t-test + bootstrap CI + Cohen's d 效应量),"新 prompt 到底好不好"从拍脑袋变成看数据。
实践:踩坑记录
- 别对 LLM 输出做快照断言。 温度 > 0 时输出必变,
toMatchSnapshot必然 flaky。快照只用于 replay 的输入侧。 - LLM-as-Judge 有 self-preference 偏差:用 GPT 当 judge 评 GPT 的输出会系统性偏高。换主模型时 judge 分数整体漂移,需要维护 golden set(人工标注样本)定期校准。
- token / 延迟断言用区间,不用单边阈值。 CI 机器负载波动会让延迟偶尔超限,
toBeBetween(1000, 8000)比toBeLessThan(5000)稳得多。 - 工具参数断言是性价比最高的断言。 一条
toBeCalledWith({ query: "refund policy" })能同时抓住工具描述被改坏、参数 schema 漂移、路由逻辑错乱三类问题。 - 工具选型别混。 LangSmith 是可观测性(trace 调试、生产监控),DeepEval 是输出指标评测,Promptfoo 是 prompt 变体对比,AgentBench 这类才是测试框架(断言 + 回放 + CI 门禁)。观测 ≠ 断言,别拿 trace 当测试。
CI 集成
name: agent-ci
on: [pull_request]
jobs:
agent-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx agentbench test --reporter junit
- uses: actions/upload-artifact@v4
with:
path: test-results.xml
JUnit XML 导出意味着可以直接接 GitLab CI 或自建平台,测试结果融入现有报表体系,不需要另起一套看板。
总结与进阶方向
给 agent 上测试,核心三件事:确定性断言把机械回归拦在 PR 里,LLM-as-Judge 把语义质量拦在发布前,replay 让非确定性变得可复现、可对比。进阶方向:4D 覆盖(prompt / workflow / tool / edge-case 四个维度统计覆盖率,比代码行覆盖率对 agent 更有意义)、跨模型回放做模型迁移评估、MCP 工具的 discovery 与生命周期测试、把 judge 分数接到监控告警做线上漂移检测。
更多推荐


所有评论(0)