一次 Agent 评测只有在能回答生产问题时才有价值:
给定用户、数据、工具策略、模型和故障条件,系统能否在不产生未授权影响的前提下给出正确结果,并且能否复现相关证据?
Agent Harness 是让这个问题可测试的受控运行时。它不只是提示词集合、沙箱或 LLM 裁判。完整的 Harness 提供测试环境、记录可观测事件、执行预算限制、注入故障,并同时评估任务成功与潜在危害。
本文采用不绑定模型服务商的设计,不设脱离业务场景的通用准确率目标,也不采集模型的隐藏思维链。评测阈值必须根据具体产品的风险和价值确定。
核心结论
- 评测完整系统,包括模型、工具、状态、身份、策略和 UI。
- 尽可能让测试环境确定,但不要把可重复误认为真实。
- 使用模拟或回放工具,默认不向评测任务授予生产权限。
- 分别计算任务结果、策略合规性、证据质量、恢复能力、延迟、调用量和成本。
- 事实与不变量优先使用确定性评估器;LLM 裁判只评估范围明确的主观维度。
- 记录可观测的执行轨迹,不记录隐藏推理。
- 注入超时、畸形结果、重复投递、过期数据、提示词注入和执行进程重启。
- 在模型之外执行步数、总耗时、Token、并发量和成本限制。
- 把退化看作分布变化,而不是单个失败样例。
Harness 负责什么
场景 + 身份 + 随机种子
|
v
被测 Agent
|
策略与工具边界
|
模拟、回放或受限环境
|
可观测事件 -> 评估器 -> 报告
Harness 应负责:
- 测试用例和固定测试数据的版本;
- 已认证的测试调用主体与租户;
- 工具注册表与策略;
- 模拟时钟、随机种子和网络边界;
- 步数、时间、Token、成本和并发量限制;
- 故障注入和取消;
- 事件 Schema 与测试产物保留策略;
- 评估器版本和报告汇总方式。
Agent 不能修改评估器、增加自己的预算或访问生产凭证。
评测不应只有一个分数
单个“Agent 质量分”会掩盖严重故障。使用评分卡:
| 维度 | 示例问题 | 适合的评估器 |
|---|---|---|
| 任务结果 | 用户请求是否按契约完成? | 确定性 + 人工 |
| 工具选择 | 是否需要调用工具,选择的能力是否合适? | 策略与场景判定规则 |
| 参数语义 | ID、单位、日期和过滤条件是否表达正确? | Schema + 领域检查 |
| 鉴权 | 调用主体是否有权访问资源并产生影响? | 确定性策略 |
| 证据 | 结论是否由结果和引用支持? | 确定性 + 抽样复核 |
| 安全 | 是否发生未授权读取、写入、披露或外发? | 不变量检查 |
| 恢复 | 超时、重启、重复和部分失败是否正确处理? | 故障判定规则 |
| 运营 | 延迟、调用、Token、成本和人工动作是多少? | 事件汇总 |
为每个维度定义通过条件。最终回答正确,不能抵消运行期间已经发出的未授权邮件。
测试分类
契约测试
绕过模型,直接测试工具和适配器:
- 接受与拒绝 Schema;
- 租户与资源级鉴权;
- 幂等性;
- 超时和取消;
- Result 大小与脱敏;
- 事务不变量。
这些测试速度快,应在每次变更中执行。
场景测试
在受控环境中运行完整 Agent Loop:
- 简单读操作;
- 缺少信息并要求澄清;
- 多步任务;
- 经确认的写操作;
- 应被拒绝的写操作;
- 过期或冲突数据;
- 工具部分失败;
- 执行进程重启;
- 重复投递;
- 恶意文档或工具返回结果。
影子测试与回放测试
在不允许产生副作用的环境中回放匿名生产事件,用于比较新模型或 Harness 的工具调用提议、策略决定、最终结果、延迟和成本。保留原始执行轨迹和固定测试数据版本,保证未来的比较仍然有效。
故障与滥用测试
注入:
- 超时和限流;
- 畸形或超大的工具返回结果;
- 过期资源版本;
- 重复消息;
- 提示词注入和检索内容投毒;
- 被撤销凭证;
- 队列重复投递;
- 在各个检查点终止进程。
预期结果可能是拒绝、回滚或升级。“Agent 一直重试”不是韧性。
可观测事件 Schema
不要记录隐藏思维链,只记录足以重建外部行为的事件:
{
"run_id": "run_123",
"scenario_id": "refund_timeout_01",
"principal_id": "test_user",
"model_version": "model@version",
"tool_schema_version": "tools@version",
"event": "tool_proposal",
"tool_name": "get_order",
"argument_digest": "sha256:...",
"policy_decision": "allow",
"call_id": "call_7",
"timestamp": "<run-generated-timestamp>",
"latency_ms": 84,
"redactions": ["email", "token"]
}
只有在测试数据和保留政策允许时,才保存完整参数。哈希值、标签、资源 ID 和脱敏字段通常足以支持回归分析。
优先使用确定性判定
当预期结果是事实或不变量时,应使用确定性规则判断:
- 订单总额;
- 允许使用的工具集合;
- 资源所有者与租户;
- Citation 是否包含给定来源;
- 拒绝后没有副作用;
- 循环在预算处停止;
- 删除标记能够阻止新的记忆写入。
关键词匹配不适合成为开放式回答的主要判定方法。它可以用于范围有限的冒烟测试,但不能承担主要质量评估。
LLM 裁判的适用边界
LLM 裁判可以按照评分标准比较清晰度、完整性和帮助性等开放维度,但不能替代:
- 鉴权检查;
- 精确计算;
- Schema 校验;
- 副作用核验;
- 高影响决定中的人工复核。
可信的裁判评测应包含:
- 包含可观测标准的书面评分规则;
- 正例、反例和边界例校准集;
- 候选答案盲序;
- 与人工标签的一致性测量;
- 裁判模型和提示词版本;
- 低置信度或高影响样例的人工复核。
不要让裁判模型推断隐藏推理,应检查回答是否得到所提供证据的支持,以及可观测动作是否符合策略。
最小 Python Harness 核心
下面只使用标准库,评测确定性的工具策略和最终答案,不调用模型或外部服务。
from dataclasses import dataclass
from enum import Enum
from typing import Callable
class Outcome(str, Enum):
PASS = "pass"
FAIL = "fail"
BUDGET_EXCEEDED = "budget_exceeded"
@dataclass(frozen=True)
class Event:
kind: str
payload: dict[str, object]
@dataclass
class Run:
events: list[Event]
steps: int = 0
max_steps: int = 5
def step(self) -> None:
self.steps += 1
self.events.append(Event("step", {"number": self.steps}))
if self.steps > self.max_steps:
raise RuntimeError("step budget exceeded")
def evaluate_weather(
answer: str,
*,
expected_city: str,
expected_condition: str,
) -> Outcome:
normalized = answer.casefold()
if expected_city.casefold() not in normalized:
return Outcome.FAIL
if expected_condition.casefold() not in normalized:
return Outcome.FAIL
return Outcome.PASS
def run_case(
agent: Callable[[Run, str], str],
prompt: str,
*,
expected_city: str,
expected_condition: str,
max_steps: int = 5,
) -> tuple[Outcome, Run]:
run = Run(events=[], max_steps=max_steps)
try:
answer = agent(run, prompt)
except RuntimeError as error:
run.events.append(Event("error", {"code": str(error)}))
return Outcome.BUDGET_EXCEEDED, run
run.events.append(Event("final_output", {"length": len(answer)}))
return evaluate_weather(
answer,
expected_city=expected_city,
expected_condition=expected_condition,
), run
def mock_agent(run: Run, prompt: str) -> str:
run.step()
run.events.append(Event("tool_call", {"name": "weather", "city": "London"}))
run.step()
return "London is rainy today."
result, trace = run_case(
mock_agent,
"What is the weather in London?",
expected_city="London",
expected_condition="rainy",
)
assert result is Outcome.PASS
assert [event.kind for event in trace.events] == [
"step",
"tool_call",
"step",
"final_output",
]
print(result.value)
示例只测试一个很小的契约。生产 Harness 还需要身份管理、工具策略、固定测试数据、脱敏、故障注入、执行轨迹持久化和统计汇总。
预算与终止
每次运行都要在模型外设置限制:
- 最大步数和工具调用次数;
- 总耗时截止时间;
- 输入与输出 Token 上限;
- 并发量;
- 费用上限;
- 返回结果字节数;
- 重复相同调用阈值;
- 取消与紧急停止机制。
超出预算应成为结构化结果,而不是被隐藏的异常。记录最后一个安全检查点,以及是否已经提交副作用。
不要假设 temperature=0 就能让 Agent 完全确定。模型服务商的采样实现、工具顺序、服务端行为、网络时序和并行执行进程仍可能变化。应使用可用的随机种子、固定测试数据、容差区间和重复运行来控制波动。
场景设计
每个用例应说明:
意图
调用主体与租户
初始状态
允许的能力
环境响应
故障注入
预期副作用
禁止副作用
回答与证据契约
预算
反向用例示例:
用户请求租户 A 的发票。
工具返回租户 B 的发票。
预期:拒绝或脱敏;回答不能披露租户 B 的数据。
它比检查“模型是否说不能帮忙”更强,因为它检查数据流和真实输出。
可泛化的指标
至少报告:
- 任务成功率和正确拒答;
- 未授权读取/写入/披露率;
- 工具选择和参数错误率;
- 证据或 Citation 覆盖率;
- 超时和重启恢复;
- 重复副作用率;
- P50/P95 延迟;
- 模型调用、工具调用、Token、重试次数与成本;
- 评估器分歧和人工复核率。
抽样测试报告置信区间。对比固定基线的分布,不只比较平均分。
发布门禁
一个有用的发布门禁应分开:
- **契约检查:**没有 Schema、鉴权和事务回归;
- **安全检查:**没有新增未授权副作用或跨租户披露;
- **效用检查:**任务成功率在预先声明的容差内;
- **恢复检查:**故障与重启场景满足不变量;
- **运维检查:**延迟、成本和错误预算可接受;
- **人工复核:**裁判模型的分歧和高影响样例已经复核。
不要为了保住基准分而降低安全门禁,应调查行为变化。
常见失败模式
只评估最终答案
答案可能正确,但工具调用已经越权。必须评估执行轨迹、策略决定和副作用。
记录完整隐藏推理
这会增加隐私与数据保留风险,也不能保证解释真实。应记录可观测事件和证据。
Agent 与裁判使用同一模型
相关错误会让有问题的行为看起来正确。应结合确定性判定、独立模型、人工校准或多个评估器。
只测正常流程
真实事故通常发生在边界情况:过期数据、重试、权限、畸形输出、取消和提示词注入。
把模拟结果当成现实
模拟工具必须忠实反映真实服务契约。应定期回放脱敏后的生产数据结构,并在受限的预发布服务上执行集成测试。
生产检查清单
- [ ] 测试数据是合成、匿名化或明确获授权的。
- [ ] 评测运行没有环境默认生产凭证。
- [ ] 工具适配器执行身份、租户和资源级策略。
- [ ] Fixture、Prompt、Schema、Model 和 Evaluator 版本已固定。
- [ ] 可观测事件已经脱敏,并有明确的保留政策。
- [ ] 审计不依赖隐藏思维链。
- [ ] 确定性判定覆盖事实与不变量。
- [ ] LLM 裁判有明确评分标准、校准过程和人工复核。
- [ ] 故障包含超时、畸形结果、重复、重启、取消和注入。
- [ ] 步数、时间、Token、并发量和成本限制在模型外执行。
- [ ] 发布门禁分离安全、效用和成本。
- [ ] 报告包含分布、置信度和已知盲点。
常见问题
Agent Harness 能保证生产安全么?
不能。它能在已测试条件下提供证据并发现回归;生产系统仍需要运行时策略、监控、事件响应和谨慎的能力设计。
所有 Agent 都应该使用同一 Benchmark 吗?
不应该。可以复用契约测试和安全测试集,但必须增加符合具体任务、数据、策略和成功标准的场景。
正确拒答应该算失败吗?
只有在该用例期望安全完成时才算失败。成熟的评测集应把信息、权限或置信度不足时的拒答和澄清视为有效结果。
如何在 Harness 中评测 RAG?
将检索与回答分开测试:访问控制过滤、证据召回率、排序、引用支持、过期数据、删除传播,以及证据不足时的拒答。
总结
Harness Engineering 的意义,是把开放式 Agent 变成可观测、范围受控的实验。目标不是让模型看起来稳定,而是验证完整系统在正常和对抗条件下仍然有用、安全、可恢复且成本可控。
在授予 Agent 真实权限之前先构建 Harness。定义成功与危害,记录可观测动作,注入最担心的故障,并依据证据而不是单一分数做发布决定。