当前位置:首页>排行榜>Agent 评测02: 别急着给 Agent 打分:先搭一个真正能测的发布评审 Agent

Agent 评测02: 别急着给 Agent 打分:先搭一个真正能测的发布评审 Agent

  • 更新时间 2026-10-23 09:59:41
Agent 评测02: 别急着给 Agent 打分:先搭一个真正能测的发布评审 Agent
Agent 评测实战 · 02

六个工具、一条完整 Trace、一个隔离运行区,把后续评测对象真正搭出来

候选版本的构建通过了,回归测试也全绿。发布评审 Agent 很快给出一句:“可以发布。”

演示到这里,通常会收获掌声。可测试工程师再追问两句,场面就没那么轻松了:它查过哪些证据?工具参数是什么?重跑一次会不会写出第二份评审记录?如果缺陷服务超时,它会不会把“查不到”当成“没有缺陷”?

这些问题答不上来,成功率、评分器、评测平台都无从谈起。因为我们手里还没有一个稳定的被测对象。

上一篇把 Agent 评测拆成 Outcome、Trace、Response、System & Safety 四层。这一篇不再解释概念,直接搭项目:一个只能读取候选版本证据、生成发布建议,却没有权限执行部署的发布评审 Agent。

文章结束时,你会得到可运行代码、结构化 Trace、按运行隔离的状态目录,以及一条真正执行过的 Smoke Check。至于评测指标和正式 Eval Case,留给第 3、4 篇。

本文阅读路线

01 先钉死交付目标:不是 Demo,是测试对象

02 六个工具先变成协议,不让 Agent 直连系统

03 把 Trace 和隔离区装进运行时

04 接上 Agent Loop:模型只占策略层

05 跑一次 Smoke Check,确认评测通道连通

01 · 项目终点

先钉死交付目标:不是 Demo,是测试对象

图 1|决策策略可以替换,工具、Trace 和隔离边界保持稳定

Anthropic 在 Agent 评测文章里给出的多轮评测结构很清楚:Agent 拿到任务、工具和环境,在多轮行动中改变环境,评分器再检查结果。这里有个容易被忽略的前提——任务、工具和环境本身得先能被控制。

所以,这个参考项目没有从“选哪个大模型”开工,而是先固定模型之外的工程骨架。目录不复杂,但每一层都有明确职责:

project/
├── fixtures/
│   └── release-2026.10.23.json
├── release_review_agent/
│   ├── agent.py       # Agent Loop
│   ├── policy.py      # 可替换决策策略
│   ├── tools.py       # 六个受控工具
│   ├── trace.py       # JSONL Trace
│   └── sandbox.py     # 每次运行的隔离区
└── smoke_check.py     # 项目级验收

项目只用 Python 标准库,不需要 API Key。默认策略是确定性的,它不是为了冒充大模型,而是为了先证明 Agent Loop、工具协议、Trace 和状态写入确实能连起来。以后接入真实模型,只换策略层,外面的测试入口不跟着重写。

工程判断:第二篇的目标不是证明 Agent “聪明”,而是把它搭成一个边界清楚、过程可见、状态可复现的被测系统。

02 · 工具边界

六个工具先变成协议,不让 Agent 直连系统

发布评审 Agent 不直接访问构建平台、缺陷库或生产发布系统。它只能调用下面六个工具:

图 2|五个工具取证,一个工具写入隔离区;没有部署工具

get_build_info:读取构建状态和制品信息

get_change_scope:读取变更范围和高风险模块

run_test_suite:在隔离区运行指定测试集

query_test_results:读取本次运行的测试结果

query_open_defects:读取候选版本的未关闭缺陷

create_release_review:在隔离区生成评审记录,不触发部署

工具可以各做各的,返回格式不能各说各话

成功时返回 JSON,失败时抛一段自然语言;“查不到”有时是空数组,有时是超时异常——模型也许能猜,测试代码却很难写断言。参考项目把工具结果统一成一个很薄的信封:

@dataclass(frozen=True)
class ToolResult:
    ok: bool
    code: str
    data: dict[str, Any]
    evidence_refs: list[str]
    retryable: bool = False

OpenAI Agents SDK 的函数工具会根据函数签名生成输入 schema,并用类型信息约束参数。参考项目没有绑定这个 SDK,但保留同样的原则:参数必须明确,失败必须分类,证据必须有引用,写入必须幂等。

最容易被忽略的是幂等。create_release_review使用 run_id和候选版本定位记录。同一次运行重放时,内容相同就返回 ALREADY_EXISTS;内容不同则报 IDEMPOTENCY_CONFLICT。这样,重试不会悄悄制造第二份评审结论。

工具协议不等于固定调用顺序。模型仍可根据证据选择合理路径;协议限制的是非法参数、不可解释失败和越权副作用。

03 · 运行时

把 Trace 和隔离区装进运行时

普通日志能告诉你“调用过缺陷工具”,却未必能回答:属于哪次运行?参数是什么?结果能否重试?它和最终结论有什么关系?

OpenAI Agents SDK 把一次工作流记为 trace,再用 span 表示模型生成、工具调用、guardrail 和 handoff 等事件。OpenTelemetry 的 GenAI 语义约定也在统一 Agent、模型和工具事件的命名。参考项目借用了这种树状思路,但把数据落成最容易检查的 JSONL:

图 3|同一次运行的工具调用、证据和状态写入都回到同一个 run_id

{
  "run_id": "article-demo-001",
  "trace_id": "trace-article-demo-001",
  "span_id": "span-tool-05",
  "parent_span_id": "span-root",
  "event": "tool.completed",
  "tool_name": "query_open_defects",
  "status": "ok",
  "result_code": "OK",
  "evidence_refs": [
    "fixture://release-2026.10.23/open-defects"
  ]
}

这里没有记录模型的隐藏推理,也没有把工具原始响应一股脑塞进去。Trace 保存的是评测真正需要的事实:运行身份、父子关系、工具名、结构化参数、结果码、证据引用和副作用位置。涉及真实项目时,还要继续做字段分级、脱敏和保留周期控制。

隔离不是“最好有”,而是复现条件

每次运行都会得到独立目录 runs/<run_id>/。构建、变更和缺陷来自版本化 fixture;时钟固定在 fixture 中;测试结果与评审记录只能写进本次运行的 state/。项目没有网络代码,也不会加载生产密钥。

变化来源
参考项目怎么控制
业务数据
版本化 fixture,只读加载
系统时间
固定时钟,Trace 事件按序递增
测试副作用
只写本次 run 的 state 目录
重复执行
评审记录幂等,不重复创建
生产系统
无部署工具、无生产凭据、无网络访问

今天查实时缺陷库,明天读取旧快照,测到的差异可能来自环境,而不是 Agent。隔离环境的价值,正是把这类噪声关在门外。

04 · Agent Loop

接上 Agent Loop:模型只占策略层

工具和运行时准备好后,Agent Loop 反而很薄:策略读取已有观察,决定下一次工具调用;运行时校验工具是否允许,执行并记录结果;策略拿到新证据后继续判断,直到输出最终结论。把实现压缩到主干,大致是下面这样:

for step in range(1, max_steps + 1):
    action = policy.next_action(release_id, observations)

    if isinstance(action, FinalDecision):
        trace.record("run.completed", decision=action)
        return action

    result = allowed_tools[action.name](**action.arguments)
    observations[action.name] = result
    trace.record("tool.completed", result_code=result.code)

默认的确定性策略会收集构建、变更、测试和缺陷证据,再遵守三条发布边界:

可以发布:证据完整,构建成功,关键测试通过,没有未关闭的 P0/P1 缺陷。

待确认:关键证据缺失或工具调用失败,不能把未知解释成安全。

暂缓发布:构建失败、关键测试失败,或存在未关闭的 P0/P1 缺陷。

真实模型接入时,实现同一个 DecisionPolicy接口即可。这样做还有一个额外好处:如果后续评测失败,可以先把模型策略换回确定性版本。基础通道仍失败,问题多半在工具或环境;只有模型版本失败,才继续看 Prompt、模型和决策轨迹。

不要在 Trace 里保存模型的隐藏推理。评测需要的是可观察行动、工具事实、证据和最终状态,而不是要求模型暴露私有思维过程。

05 · 项目验收

跑一次 Smoke Check,确认评测通道连通

现在启动项目。命令故意保持简单:

python3 -m release_review_agent.cli \
  --release release-2026.10.23 \
  --run-id article-demo-001

这次实际运行得到“可以发布”:构建成功,286 条回归测试全部通过,候选版本只有一个未关闭的 P2 缺陷。结论同时引用了构建、变更、测试和缺陷证据,评审记录写在 runs/article-demo-001/state/,没有触碰生产系统。

随后执行项目级 Smoke Check:

$ python3 smoke_check.py
{
  "status": "PASS",
  "checks": {
    "agent_completed": true,
    "all_tools_observed": true,
    "structured_trace_written": true,
    "sandbox_state_written": true,
    "single_review_after_replay": true,
    "replay_same_decision": true
  }
}

图 4|Smoke Check 验收的是工程通道,不是 Agent 能力分数

这里要克制一下。这个 PASS 只能证明:

  • 项目可以从固定 fixture 启动并完成一次发布评审;
  • 六个工具的调用事实能从 Trace 读取;
  • 测试结果和评审记录只写入本次运行的隔离区;
  • 同一 run_id重放不会生成重复评审记录。

它没有证明真实模型在复杂场景下稳定,也没有覆盖缺失证据、阻断缺陷、工具超时和对抗输入。那些需要指标、正式 Eval Case、Golden Dataset 和多次 Trial,后面几篇会逐步补齐。

做到这里,第二篇才算交付

我们不再对着一句“可以发布”猜测 Agent 做过什么。

工具规定它能做什么,Trace 留下它做过什么,隔离区保存它改变了什么,Smoke Check 证明整条通道确实跑通。

有了这个被测对象,下一篇才有资格谈指标:哪些数字对应真实发布风险,数据从四层评测模型的哪一层采集,基线怎么跑,阈值又该怎么定。

下一篇 · Agent 评测实战 03

从业务目标倒推 Agent 评测指标:为什么阈值不能拍脑袋?

随机文章