一次 Agent 评测只有在能回答生产问题时才有价值:

给定用户、数据、工具策略、模型和故障条件,系统能否在不产生未授权影响的前提下给出正确结果,并且能否复现相关证据?

Agent Harness 是让这个问题可测试的受控运行时。它不只是提示词集合、沙箱或 LLM 裁判。完整的 Harness 提供测试环境、记录可观测事件、执行预算限制、注入故障,并同时评估任务成功与潜在危害。

本文采用不绑定模型服务商的设计,不设脱离业务场景的通用准确率目标,也不采集模型的隐藏思维链。评测阈值必须根据具体产品的风险和价值确定。

核心结论

  • 评测完整系统,包括模型、工具、状态、身份、策略和 UI。
  • 尽可能让测试环境确定,但不要把可重复误认为真实。
  • 使用模拟或回放工具,默认不向评测任务授予生产权限。
  • 分别计算任务结果、策略合规性、证据质量、恢复能力、延迟、调用量和成本。
  • 事实与不变量优先使用确定性评估器;LLM 裁判只评估范围明确的主观维度。
  • 记录可观测的执行轨迹,不记录隐藏推理。
  • 注入超时、畸形结果、重复投递、过期数据、提示词注入和执行进程重启。
  • 在模型之外执行步数、总耗时、Token、并发量和成本限制。
  • 把退化看作分布变化,而不是单个失败样例。

Harness 负责什么

text
场景 + 身份 + 随机种子
        |
        v
    被测 Agent
        |
    策略与工具边界
        |
    模拟、回放或受限环境
        |
可观测事件 -> 评估器 -> 报告

Harness 应负责:

  • 测试用例和固定测试数据的版本;
  • 已认证的测试调用主体与租户;
  • 工具注册表与策略;
  • 模拟时钟、随机种子和网络边界;
  • 步数、时间、Token、成本和并发量限制;
  • 故障注入和取消;
  • 事件 Schema 与测试产物保留策略;
  • 评估器版本和报告汇总方式。

Agent 不能修改评估器、增加自己的预算或访问生产凭证。

评测不应只有一个分数

单个“Agent 质量分”会掩盖严重故障。使用评分卡:

维度 示例问题 适合的评估器
任务结果 用户请求是否按契约完成? 确定性 + 人工
工具选择 是否需要调用工具,选择的能力是否合适? 策略与场景判定规则
参数语义 ID、单位、日期和过滤条件是否表达正确? Schema + 领域检查
鉴权 调用主体是否有权访问资源并产生影响? 确定性策略
证据 结论是否由结果和引用支持? 确定性 + 抽样复核
安全 是否发生未授权读取、写入、披露或外发? 不变量检查
恢复 超时、重启、重复和部分失败是否正确处理? 故障判定规则
运营 延迟、调用、Token、成本和人工动作是多少? 事件汇总

为每个维度定义通过条件。最终回答正确,不能抵消运行期间已经发出的未授权邮件。

测试分类

契约测试

绕过模型,直接测试工具和适配器:

  • 接受与拒绝 Schema;
  • 租户与资源级鉴权;
  • 幂等性;
  • 超时和取消;
  • Result 大小与脱敏;
  • 事务不变量。

这些测试速度快,应在每次变更中执行。

场景测试

在受控环境中运行完整 Agent Loop:

  • 简单读操作;
  • 缺少信息并要求澄清;
  • 多步任务;
  • 经确认的写操作;
  • 应被拒绝的写操作;
  • 过期或冲突数据;
  • 工具部分失败;
  • 执行进程重启;
  • 重复投递;
  • 恶意文档或工具返回结果。

影子测试与回放测试

在不允许产生副作用的环境中回放匿名生产事件,用于比较新模型或 Harness 的工具调用提议、策略决定、最终结果、延迟和成本。保留原始执行轨迹和固定测试数据版本,保证未来的比较仍然有效。

故障与滥用测试

注入:

  • 超时和限流;
  • 畸形或超大的工具返回结果;
  • 过期资源版本;
  • 重复消息;
  • 提示词注入和检索内容投毒;
  • 被撤销凭证;
  • 队列重复投递;
  • 在各个检查点终止进程。

预期结果可能是拒绝、回滚或升级。“Agent 一直重试”不是韧性。

可观测事件 Schema

不要记录隐藏思维链,只记录足以重建外部行为的事件:

json
{
  "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 校验;
  • 副作用核验;
  • 高影响决定中的人工复核。

可信的裁判评测应包含:

  1. 包含可观测标准的书面评分规则;
  2. 正例、反例和边界例校准集;
  3. 候选答案盲序;
  4. 与人工标签的一致性测量;
  5. 裁判模型和提示词版本;
  6. 低置信度或高影响样例的人工复核。

不要让裁判模型推断隐藏推理,应检查回答是否得到所提供证据的支持,以及可观测动作是否符合策略。

最小 Python Harness 核心

下面只使用标准库,评测确定性的工具策略和最终答案,不调用模型或外部服务。

python
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 完全确定。模型服务商的采样实现、工具顺序、服务端行为、网络时序和并行执行进程仍可能变化。应使用可用的随机种子、固定测试数据、容差区间和重复运行来控制波动。

场景设计

每个用例应说明:

text
意图
调用主体与租户
初始状态
允许的能力
环境响应
故障注入
预期副作用
禁止副作用
回答与证据契约
预算

反向用例示例:

text
用户请求租户 A 的发票。
工具返回租户 B 的发票。
预期:拒绝或脱敏;回答不能披露租户 B 的数据。

它比检查“模型是否说不能帮忙”更强,因为它检查数据流和真实输出。

可泛化的指标

至少报告:

  • 任务成功率和正确拒答;
  • 未授权读取/写入/披露率;
  • 工具选择和参数错误率;
  • 证据或 Citation 覆盖率;
  • 超时和重启恢复;
  • 重复副作用率;
  • P50/P95 延迟;
  • 模型调用、工具调用、Token、重试次数与成本;
  • 评估器分歧和人工复核率。

抽样测试报告置信区间。对比固定基线的分布,不只比较平均分。

发布门禁

一个有用的发布门禁应分开:

  1. **契约检查:**没有 Schema、鉴权和事务回归;
  2. **安全检查:**没有新增未授权副作用或跨租户披露;
  3. **效用检查:**任务成功率在预先声明的容差内;
  4. **恢复检查:**故障与重启场景满足不变量;
  5. **运维检查:**延迟、成本和错误预算可接受;
  6. **人工复核:**裁判模型的分歧和高影响样例已经复核。

不要为了保住基准分而降低安全门禁,应调查行为变化。

常见失败模式

只评估最终答案

答案可能正确,但工具调用已经越权。必须评估执行轨迹、策略决定和副作用。

记录完整隐藏推理

这会增加隐私与数据保留风险,也不能保证解释真实。应记录可观测事件和证据。

Agent 与裁判使用同一模型

相关错误会让有问题的行为看起来正确。应结合确定性判定、独立模型、人工校准或多个评估器。

只测正常流程

真实事故通常发生在边界情况:过期数据、重试、权限、畸形输出、取消和提示词注入。

把模拟结果当成现实

模拟工具必须忠实反映真实服务契约。应定期回放脱敏后的生产数据结构,并在受限的预发布服务上执行集成测试。

生产检查清单

  • [ ] 测试数据是合成、匿名化或明确获授权的。
  • [ ] 评测运行没有环境默认生产凭证。
  • [ ] 工具适配器执行身份、租户和资源级策略。
  • [ ] Fixture、Prompt、Schema、Model 和 Evaluator 版本已固定。
  • [ ] 可观测事件已经脱敏,并有明确的保留政策。
  • [ ] 审计不依赖隐藏思维链。
  • [ ] 确定性判定覆盖事实与不变量。
  • [ ] LLM 裁判有明确评分标准、校准过程和人工复核。
  • [ ] 故障包含超时、畸形结果、重复、重启、取消和注入。
  • [ ] 步数、时间、Token、并发量和成本限制在模型外执行。
  • [ ] 发布门禁分离安全、效用和成本。
  • [ ] 报告包含分布、置信度和已知盲点。

常见问题

Agent Harness 能保证生产安全么?

不能。它能在已测试条件下提供证据并发现回归;生产系统仍需要运行时策略、监控、事件响应和谨慎的能力设计。

所有 Agent 都应该使用同一 Benchmark 吗?

不应该。可以复用契约测试和安全测试集,但必须增加符合具体任务、数据、策略和成功标准的场景。

正确拒答应该算失败吗?

只有在该用例期望安全完成时才算失败。成熟的评测集应把信息、权限或置信度不足时的拒答和澄清视为有效结果。

如何在 Harness 中评测 RAG?

将检索与回答分开测试:访问控制过滤、证据召回率、排序、引用支持、过期数据、删除传播,以及证据不足时的拒答。

总结

Harness Engineering 的意义,是把开放式 Agent 变成可观测、范围受控的实验。目标不是让模型看起来稳定,而是验证完整系统在正常和对抗条件下仍然有用、安全、可恢复且成本可控。

在授予 Agent 真实权限之前先构建 Harness。定义成功与危害,记录可观测动作,注入最担心的故障,并依据证据而不是单一分数做发布决定。

一手资料