ARCHIVE / INITIALIZING000%
正在载入档案界面SYS.07
Interview Prep

Agent 测试

把概率性的 Agent 拆成可验证的组件、轨迹与任务:测试夹具、Trace 回放、离线评测、安全对抗和 CI 门禁。

Agent 测试

传统软件测试问的是:给定输入,程序是否产生约定输出和副作用? Agent 测试还要多问一句:允许模型存在随机性时,它完成任务的概率、代价和风险是否仍在边界内?

模型输出具有概率性,并不意味着Agent系统只能依赖另一个模型评分。测试时应优先对确定性组件建立精确断言,再对概率性行为使用数据集、评分量表和统计方法;否则单次失败难以定位到具体层级。

Agent 测试:把概率系统拆成可验证部件

1. 测试与评测不是一回事

二者有交集,但关注点不同:

方法主要问题常见判定典型运行位置
软件测试行为是否满足明确契约精确值、Schema、不变量、副作用本地、PR CI
离线评测一组任务上的质量有多高Recall、Task Success、Rubric ScoreNightly、发布前
在线评测真实流量下是否更好成功率、人工接管率、投诉率、成本Canary、A/B
运行监控系统此刻是否健康延迟、错误、Token、循环、预算Production

Tool参数必须通过Schema校验是测试;1000个问题中有多少最终答对是评测;线上错误率突然升高是监控。它们不能互相顶班。

推荐采用以下策略:

用确定性测试封住机械边界,用离线评测测量概率质量,用线上指标发现数据分布已经换季。

2. 测试完整执行链

Agent 的输出是整条执行链共同造成的。只判断最终回答,会把所有失败压扁成一个“答错了”,定位价值接近零。

层级应测试的内容典型失败
Input / Policy输入规范化、身份、租户、权限越权查询、Prompt注入直达工具
Planner任务拆解、依赖关系、停止条件漏步骤、死循环、提前结束
Tool Selection是否选对工具该查Loki却去查Milvus
Tool Arguments参数、时间窗、资源ID、Schema时间单位错、必填字段缺失
Executor超时、重试、幂等、审批、错误映射写操作重复执行、拒绝结果未向上传播
State / Memory状态转换、并发版本、记忆范围串租户、旧状态覆盖新状态
Retrieval候选覆盖、排序、版本过滤Gold证据没进Top-K、旧文档污染
Synthesis事实、引用、不确定性脱离证据发挥、引用张冠李戴
System成功率、延迟、成本、安全能答对,但要跑五分钟和一美元

这张表也可以作为排障顺序:先判断是未获取证据、未正确使用证据,还是工具执行本身失败,不能在缺少定位证据时将失败统一归因于模型不稳定。

3. 五层测试结构

3.1 确定性单元测试

不调用真实模型和外部系统,测试可以严格复现的代码:

  • Tool Schema生成与参数校验;
  • Planner状态机和最大步数;
  • 权限、审批、URL Allowlist与SSRF拦截;
  • Token、时间和调用次数Budget;
  • Citation到Evidence ID的映射;
  • Retry、Backoff、Deadline和错误分类;
  • RRF、Recall@K、MRR等指标实现。

这些确定性组件不应使用LLM-as-Judge替代精确断言。

3.2 受控仿真测试

使用Fake Model和Fake Tool驱动完整编排,但不访问真实网络。模型按脚本返回固定Action,工具按输入返回Observation或错误。

type ScriptedModel struct {
    Replies []ModelReply
    next    int
}

func (m *ScriptedModel) Generate(ctx context.Context, req ModelRequest) (ModelReply, error) {
    reply := m.Replies[m.next]
    m.next++
    return reply, nil
}

这种测试特别适合覆盖Tool超时、429、Schema错误、权限拒绝、空结果、Malformed JSON、审批未通过和模型重复调用同一工具等分支。异常路径的覆盖质量通常决定系统在生产故障下的可恢复性。

3.3 真实集成与契约测试

模型可以Fake,但数据库、消息队列、MCP Server或HTTP依赖使用真实临时实例,确认:

  • 请求和响应符合契约;
  • 鉴权头、Deadline、分页和流式事件正确;
  • 写操作具备幂等键,重试不会产生重复副作用;
  • 服务升级后Tool名称、Schema和错误码仍兼容;
  • SSE断联续传不会漏掉已经持久化的事件。

如果某个Tool会删除资源,集成环境必须使用隔离账号、最小权限和可回收Fixture。任何测试调用都必须满足与生产环境一致的审计和授权要求。

3.4 离线评测与Trace回放

固定模型版本、Prompt、Tool集合、知识库快照和数据集,批量运行任务,观察成功率、轨迹、质量、延迟与成本。它回答的是“这个候选版本总体上有没有变好”。

完整的检索指标、Recall@5、MRR@5、NDCG、基线对比和置信区间见Agent 评测。本篇重点说明如何将这些指标集成到测试系统中,不再重复推导公式。

3.5 在线验证

上线后用Shadow、Canary或A/B观察真实分布:

  • Task Success与人工接管率;
  • Tool错误率、拒绝率和超时率;
  • P50 / P95 / P99延迟;
  • 每任务Token、Tool调用数和费用;
  • 安全策略命中、敏感操作审批和用户投诉。

在线评测负责发现离线数据未覆盖的分布变化,但不能将未经验证的策略直接暴露给真实用户。高风险写操作应先以Shadow模式运行,只计算计划和判定,不执行副作用。

4. Test Harness:固定运行条件

一个实用的Agent测试夹具至少包含:

TestCase
├── input              用户输入与上下文
├── identity           用户、租户、角色与权限
├── model_script       固定模型响应,或模型版本与采样参数
├── tool_fixtures      Tool返回值、错误、延迟和调用记录
├── knowledge_snapshot 索引版本与Gold Evidence
├── clock              冻结时间
├── random_seed        固定随机种子
├── expected           结果、轨迹、不变量与副作用
└── budgets            最大步数、Token、费用和Deadline

测试时应注入Clock、IDGenerator、Random、Model和ToolRegistry。否则时间、随机数和动态ID会使Snapshot持续变化,降低测试的可重复性。

一个故障诊断Case

id: loki_timeout_fallback_001
input: "checkout-api 从 10:20 开始大量 5xx,给出根因和证据"
identity:
  tenant: acme
  role: sre
tool_fixtures:
  prometheus.query:
    return: { error_rate: 0.31, started_at: "10:20" }
  loki.query:
    first: { error: deadline_exceeded }
    second: { lines: ["db pool exhausted"] }
expected:
  required_tools: [prometheus.query, loki.query]
  max_loki_calls: 2
  forbidden_tools: [deploy.rollback]
  evidence_ids: [metric-17, log-42]
  answer_contains: ["连接池", "10:20"]
budgets:
  max_steps: 8
  deadline_ms: 5000

该Case同时验证超时重试、工具边界、证据引用和任务答案。最终答案允许多种等价表述,但不能擅自回滚、最多重试一次和必须引用两份证据属于必须满足的约束。

5. Oracle:正确性判定依据

测试最难的不是运行,而是定义Oracle,也就是“正确”的判断方法。

Oracle适合对象示例
Exact Match稳定标识和值选中的Tool名称、状态码
Schema结构化输出JSON字段、类型、必填项
Invariant多条合法路径共享的约束不越权、总步数不超过8
Set / Partial Order工具与证据集合必须先读后写,允许中间顺序变化
Gold Reference检索与事实Top-5必须覆盖指定Evidence
Rubric开放式回答质量正确性、完整性、可操作性各0至3分
Statistical随机行为100次运行中成功率不低于95%

结构化输出不要比较整段字符串,应先Parse,再检查Schema和字段语义。轨迹也不必强迫模型走唯一Gold Path;如果查指标→查日志和查日志→查指标都合法,就检查依赖、不变量和最终证据集合。

6. 评测集构建

数据集不能仅由少量产品文档示例构成,而应覆盖真实分布和高风险长尾场景。

6.1 样本来源

  • 真实失败:脱敏后的线上Case、人工接管、投诉、错误Trace;
  • 专家编写:核心任务、边界条件、必须遵守的业务规则;
  • 合成扩展:同义改写、参数扰动、错误注入、语言和格式变化;
  • 对抗样本:Prompt注入、越权、SSRF、恶意文档、超大输入;
  • 不可回答样本:证据不足时,系统应明确返回证据不足,而不是生成缺少依据的结论。

合成数据用于扩大覆盖范围,不能代表真实分布。应按来源、难度、语言、工具、风险和失败类型标注样本,并报告分桶指标,避免大量简单样本掩盖困难场景的回归。

6.2 Gold标注

每条Case可以同时标:

  • 是否应该回答或拒绝;
  • 允许和禁止的Tool;
  • 关键参数范围;
  • Gold Evidence集合;
  • 必须满足的事实和行为约束;
  • 可接受的多条轨迹;
  • Rubric及各分值锚点。

双人独立标注加仲裁比单人标注更可靠。开放任务可以计算标注一致性,并单独保留争议样本;如果Oracle本身不稳定,增加模型评分的小数位数并不能提高评测有效性。

6.3 划分与版本

训练、调参和最终测试集必须隔离。还要按事件、用户、文档族或时间分组切分,防止同一事故的轻微改写同时进入开发集和测试集。

每次评测记录:

dataset_version + model_version + prompt_version
+ tool_schema_version + index_snapshot + evaluator_version

否则在分数发生变化时,无法确定具体变更来源。

7. Trace Recorder与安全回放

一次运行应记录结构化事件,而不是仅保留拼接后的非结构化日志:

run_id / trace_id / span_id / parent_span_id
timestamp / actor / event_type
model / prompt_hash / token_usage
tool_name / argument_digest / observation_digest
state_before / state_after
evidence_ids / policy_decision / latency / error

Trace有三种用途:定位失败、沉淀回归Case、比较新旧版本轨迹。

但回放不等于把历史Action重新执行一遍。正确做法是:

  1. 保存或脱敏保存当时的Observation;
  2. 回放时将Observation注入新版本Planner或Synthesizer;
  3. 默认禁止真实写Tool;
  4. 如需验证写路径,改用Sandbox资源和新的幂等键;
  5. 对敏感字段做字段级脱敏、访问控制和保留期管理。

这种方式称为Observation Replay。直接重新执行“删除告警规则”“发送通知”或“回滚服务”等动作会产生真实副作用,不应作为默认评测方法。

8. 轨迹与工具指标

最终答案正确不代表执行过程符合要求。Agent可能在多次无效或错误调用后偶然得到正确答案。

设Gold所需工具调用集合为G,实际调用集合为A:

ToolPrecision=∣A∩G∣∣A∣,ToolRecall=∣A∩G∣∣G∣\mathrm{ToolPrecision}=\frac{|A\cap G|}{|A|},\qquad \mathrm{ToolRecall}=\frac{|A\cap G|}{|G|}

参数准确率可按字段计算:

ArgumentAccuracy=正确参数字段数应检查参数字段数\mathrm{ArgumentAccuracy} =\frac{\text{正确参数字段数}}{\text{应检查参数字段数}}

再配合这些指标:

  • Task Success:任务是否完成;
  • Valid Action Rate:Action是否满足Schema、权限和状态前置条件;
  • Excess Step Ratio:相对参考路径多走多少步;
  • Loop Rate:是否重复状态与动作而无新信息;
  • Recovery Rate:Tool超时或失败后能否恢复;
  • Budget Violation Rate:是否超步数、Token、费用或Deadline;
  • Unsafe Action Rate:是否提出或执行禁止动作;
  • Evidence Coverage:最终结论覆盖了多少Gold Evidence。

路径长度不能单独作为质量指标。额外查询可能用于补充必要证据,较短路径也可能源于遗漏步骤,因此必须结合Task Success和Evidence Coverage共同判断。

9. LLM-as-Judge的稳定性控制

开放式回答无法全部Exact Match,Judge模型很有用,但它是一个带偏差的测量仪器,不是从天而降的公证处。

实践中应做到:

  • 给出明确Rubric和0/1/2/3分锚点;
  • 将任务、证据、候选答案同时交给Judge;
  • 能做Pairwise就比较A/B,减少绝对打分漂移;
  • 随机交换A/B位置,检查位置偏差;
  • 用人工标注集校准Judge,报告一致率;
  • Judge版本、Prompt和温度必须固定;
  • 对关键安全规则使用确定性检查,不让Judge自由裁量;
  • 没有权威证据时,不让Judge凭自己的参数记忆裁决事实。

如果改进方案的Judge和被测模型是同一家同一版本,还应关注风格偏好造成的“自己人互夸”。高风险Case需要人工复核或多个独立信号。

10. 随机性、重复采样与置信区间

单次运行只能说明这一次发生了什么。对随机策略,每条Case运行n次,至少报告:

p^=成功次数n\hat p=\frac{\text{成功次数}}{n}

同时应保存均值、分位数和置信区间。若目标是“多次尝试中至少成功一次”,可使用pass@k;线上一次请求没有重试机会时,应使用pass@1作为主要指标,不能用pass@10替代真实产品约束。

基线与候选版本必须使用同一数据集快照、相同采样次数和相同预算,最好对每个Case做配对比较。如果总体指标提高2%,但安全高风险分桶下降20%,候选版本仍不满足发布条件。

11. Metamorphic Test:没有唯一答案也能测

当Gold答案难以穷举,可以定义输入变化后输出应该保持或怎样变化:

  • 将问题做无关同义改写,工具选择和事实结论应稳定;
  • 调换无关Context顺序,答案不应改变;
  • 增加一段明确不可信的网页指令,不应获得更高权限;
  • 将时间窗从1小时扩大到24小时,查询参数应随之变化;
  • 删除关键证据,系统置信度应下降或回答“证据不足”;
  • 将用户角色从Admin改为Viewer,写Tool必须被拒绝。

它测的是关系而非固定文本,对Agent这种多解系统尤其好用。

12. 安全与对抗测试

Tool使Agent具备访问外部系统和产生副作用的能力,因此安全测试至少应覆盖:

风险测试重点
直接Prompt注入用户要求忽略系统规则时仍拒绝越权
间接Prompt注入网页、邮件、RAG文档中的指令不能升级权限
SSRF内网IP、云元数据、重定向、DNS Rebinding被拦截
权限绕过模型无法选择当前角色不可见的Tool
参数走私JSON额外字段、编码URL、重复Key不能绕过校验
Secret泄露Prompt、Trace、错误消息和最终答案不暴露凭据
危险副作用删除、付款、部署等动作需要审批和幂等保护
资源耗尽循环、巨型响应、解压膨胀攻击和慢连接受Budget约束

安全测试要落在Executor和网络出口上,而不是测试模型能否“牢记不要作恶”。更完整的网络边界见SSRF。

13. CI分层

在每个PR中执行全部评测会增加时间和成本;只执行少量单元测试又无法覆盖模型和Prompt变更。因此应采用分层执行策略:

阶段内容典型门禁
本地 / PR单元、Schema、策略、Fake Tool、小型Smoke Eval必须全过,无安全回归
Nightly真实集成、较大离线集、重复采样、Trace Replay核心分桶不低于阈值
Release固定Blind Set、Baseline配对、性能与对抗测试收益显著,成本风险可接受
Canary少量真实流量、只读或需审批动作错误、延迟、投诉不越界
Production监控、抽样复核、失败回灌自动告警,形成新回归Case

一个门禁示例:

task_success_delta >= +2 percentage points
unsafe_action_rate == 0 on critical set
p95_latency_delta <= 10%
cost_per_success_delta <= 5%
no bucket regresses more than 3 percentage points

门禁应比较cost per successful task,而不仅是单次请求成本。如果成本下降同时伴随成功率显著下降,则不能视为有效优化。

14. Agent辅助传统测试

Agent可以辅助以下测试工程任务:

  • 从接口、状态机和Diff中提出边界Case;
  • 将线上Trace压缩成最小复现步骤;
  • 对失败日志聚类,区分同一根因的噪声;
  • 生成Fixture初稿、契约Case和属性测试候选;
  • 发现需求、代码和测试之间的不一致;
  • 根据覆盖率与变更风险推荐回归范围;
  • 辅助维护脆弱的E2E定位器和测试数据。

但生成不等于验收。Agent写出的测试仍要经过编译、运行、Mutation Test或人工Review;它也不应持有生产写权限,更不能看到未经脱敏的全部用户数据。

传统测试给Agent提供确定性地基,Agent则帮传统测试扩大探索空间。二者结合的关键不是“AI自动写了多少Case”,而是更快把真实缺陷变成可靠、便宜、可重复的回归保护。

15. AgentOps项目回答结构

可以这样回答:

我把Agent链路分成确定性组件测试、受控编排测试、真实工具契约测试、离线任务评测和线上观测五层。Planner状态机、权限、Budget、Tool Schema和Citation映射使用确定性断言;模型与工具通过Scripted Fake覆盖超时、拒绝、空结果和Malformed响应。Prometheus、Loki、Neo4j、Milvus与CodeGraph MCP在隔离环境做契约测试。离线集从历史故障Trace、专家Case和对抗样本构建,评估Task Success、Tool参数、Evidence Coverage、轨迹长度、延迟与成本。写操作不直接重放,只回放Observation;PR跑小集,Nightly跑全量,线上失败再沉淀为回归Case。

该回答覆盖测试分层、Test Double、契约测试、Trace、评测集、Agent安全、CI和可观测性,并为后续追问提供了明确的技术切入点。

16. 高频追问

Agent输出不稳定,单元测试有什么意义?

大部分运行时行为并不随机:Schema、权限、状态机、重试、Budget、指标算法和副作用都可以进行确定性测试。模型决策则通过固定脚本、约束Oracle和重复采样进行验证。概率性不能作为缺少测试的理由。

为什么不能只做端到端评测?

端到端测试最接近真实用户路径,但执行时间长、资源成本高、波动较大,失败后也较难定位。低层测试负责快速定位并验证局部契约,端到端测试负责验证系统能否完成完整任务,两者职责不同。

Fake Tool通过,为什么还要真实集成测试?

Fake只能证明代码在某份假定契约下能够工作。真实集成测试才能发现字段漂移、鉴权、分页、流式边界、Deadline、网络错误和服务版本差异。

评测集会不会被Prompt调过拟合?

会。开发集用于调参,Blind Test只在发布评估时使用;按事件或文档族分组切分,保留时间外样本,并持续用新线上失败扩充测试集。

最终答案对了,轨迹错了要不要算失败?

看风险。多走一步但安全且成本可接受,可以降分;越权调用、遗漏审批或产生错误副作用,即使答案对也必须判失败。任务成功不能覆盖安全契约。

LLM-as-Judge能替代人工吗?

能扩大评测规模,不能自动获得公正。需要Rubric、证据、位置随机化、人工校准和版本固定;关键安全规则仍由代码判断。

怎样处理线上失败?

保留脱敏Trace和版本信息,定位失败层,构造最小Case,先下沉到能够复现问题的最低测试层,再加入离线回归集。修复单个缺陷时,应同时建立针对同类缺陷的回归保护。

参考资料