Harness Engineering 03|Eval & Trace Harness:验证和追溯的工程组织

Harness Engineering 03|Eval & Trace Harness:验证和追溯的工程组织

activity-dev-harness 在第三周遇到一个典型事故:Prompt 微调后,团队手试了 5 个 case 感觉"更好了",直接上线。三天后用户反馈质量下降,拉全量回归一看------通过率从 78% 跌到 61%。

追查过程更痛苦:回归能看到"跌了",但看不到"从哪一步开始跌的"。是上下文变了?工具调用变了?还是模型决策路径变了?没有执行轨迹,全靠猜。

最后花了两天才定位到根因:Prompt 微调改变了 Developer Agent 的工具选择倾向,原本优先用 grep 精确定位的行为,变成了先用 file_read 大面积扫描,导致 token 消耗膨胀、上下文质量下降。

这次事故暴露了两个缺失:没有自动化回归(Eval 缺失),没有行为级追溯(Trace 缺失)。 分别补上后,类似问题再没发生过。


手记系列讲了"什么是评测和观测",这篇讲"Harness 怎么组织它们"

手记 03 已经覆盖了评测和可观测性的基础概念:AI 测试金字塔、非确定性回归、5 层可观测模型。

这篇不重复那些内容。它要回答的是一个更具体的工程问题:当你有一套 Agent 系统,Eval 和 Trace 应该怎么组织成闭环,才能真正防住质量退化?

复制代码
手记 03 的视角                    本篇的视角
──────────────                   ──────────────────
什么是 Eval                       Eval 在 Harness 里怎么组织
什么是可观测性                     Trace 记什么、怎么用、服务谁
评测集怎么分类                     Eval + Trace 怎么串成闭环
非确定性回归的原理                 真实系统里的 Eval 流水线长什么样

Eval Harness:不是"跑几个 case",是验证流水线

大多数团队的"评测"停留在手动阶段:改完 Prompt → 手试几条 → 感觉不错 → 上线。

问题在于,Agent 系统的变化维度太多,手动试验完全覆盖不了:

复制代码
❌ 手动评测的覆盖盲区

你手试了 5 个 case,感觉"更好了"。但全量 35 个 case 里:

  case 类型          手试覆盖    实际表现
  ────────────      ────────    ────────
  简单 case(20个)   3/20 ✓     18/20 PASS(确实更好了)
  边界 case(10个)   2/10 ✓     5/10 PASS(退化了,但你没测到)
  历史故障(5个)     0/5        1/5 PASS(严重退化,完全没覆盖)

  你的结论:更好了 ✓
  真实情况:主流程更好了,边界和历史故障退化了 ✗

Harness 视角下的 Eval 不是"跑 case",是一条自动化验证流水线:

复制代码
┌──────────────────────────────────────────────────────────────────────┐
│                    Eval Harness 验证流水线                             │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  触发条件                                                            │
│  ├── Prompt 变更                                                     │
│  ├── 模型版本升级                                                     │
│  ├── 工具/检索策略变更                                                │
│  └── 定时回归(每日/每周)                                             │
│      │                                                               │
│      ▼                                                               │
│  ┌───────────────────────────────────────────────────┐               │
│  │  评测集(分层)                                     │               │
│  │  ├── gold case × 3      基准验证,必须全 PASS       │               │
│  │  ├── regression × 25    回归覆盖,统计通过率         │               │
│  │  └── failure × 7        历史故障回收,防重复犯错     │               │
│  └────────────────────────────┬──────────────────────┘               │
│                               ▼                                      │
│  ┌───────────────────────────────────────────────────┐               │
│  │  评估函数(三层)                                   │               │
│  │  ├── 结果层:SH-01~10 确定性检查(PASS/FAIL)      │               │
│  │  ├── 行为层:DC-01~07 深度一致性检查               │               │
│  │  └── 稳定层:同 case 跑 5 次,波动率 < 15%         │               │
│  └────────────────────────────┬──────────────────────┘               │
│                               ▼                                      │
│  ┌───────────────────────────────────────────────────┐               │
│  │  决策                                              │               │
│  │  ├── gold case 任一 FAIL → 自动阻断,不允许上线     │               │
│  │  ├── 回归通过率 < 基线 5pp → 黄灯,需人工审查       │               │
│  │  └── 回归通过率 ≥ 基线 → 绿灯,可上线              │               │
│  └────────────────────────────┬──────────────────────┘               │
│                               ▼                                      │
│  ┌───────────────────────────────────────────────────┐               │
│  │  回收                                              │               │
│  │  线上故障 case → 加入 failure 评测集 → 下次回归覆盖  │               │
│  └───────────────────────────────────────────────────┘               │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

评测三层:结果、行为、稳定性

只评"结果对不对"远远不够。Harness 视角下的评测至少三层:

复制代码
评测层        评什么                  怎么评                     为什么重要
──────        ────────────            ──────────────────         ──────────────────
结果层        最终输出是否正确         SH 脚本:文件存在、        最基础的质量底线
                                     空壳检测、参数一致

行为层        过程是否合理             DC 脚本:逻辑一致性、      防止"结果对了但过程
                                     边界检查、依赖完整         不可接受"

稳定层        多次运行是否一致         同 case 跑 N 次,          防止"这次对了下次
                                     计算波动率                 就不对了"

activity-dev-harness 的真实评测数据:

复制代码
评测集              case 数    评估函数           基线通过率    当前通过率
──────────────      ────────  ─────────────      ──────────   ──────────
gold case           3         SH×10 + DC×7       100%         100%
regression set      25        SH×10 + DC×7       72%          78% ↑
failure recovery    7         SH×10 + DC×7       43%          71% ↑↑
────────────────────────────────────────────────────────────────────────
加权通过率                                        68%          79%

稳定性评测(gold case,每 case 跑 5 次):
  case-001:  5/5 PASS  波动率 0%
  case-002:  4/5 PASS  波动率 20% → 需要调查
  case-003:  5/5 PASS  波动率 0%

Trace Harness:不是"多打日志",是行为级追溯

Eval 告诉你"跌了"。但"从哪一步开始跌的",只有 Trace 能回答。

复制代码
日志 vs Trace 的关键区别:

普通日志                                 Trace
────────                                ──────
[INFO] tool_call: grep                  ┌─ Task: case-017 暴击伤害修复
[INFO] tool_result: 3 files found       │
[INFO] tool_call: file_write            ├─ Step 1: 意图解析
[ERROR] test failed                     │   意图: 修复 calculateDamage 暴击倍率
                                        │   约束: 只改 combat/ 目录
记录事件                                │
无因果关系                               ├─ Step 2: 环境探索
无法回答"为什么选了这个工具"              │   grep "damage" → 6 results
                                        │   选择: combat/damage.lua (置信度高)
                                        │   跳过: utils/calc.lua (旧版,标记为 archive)
                                        │
                                        ├─ Step 3: 代码修改
                                        │   修改: combat/damage.lua:42-48
                                        │   diff: +3 -1 lines
                                        │   边界检查: 修改在 combat/ 内 ✓
                                        │
                                        ├─ Step 4: 验证
                                        │   pytest tests/test_damage.py → PASS
                                        │   SH-01~03 → ALL PASS
                                        │
                                        └─ Result: PASS
                                            耗时: 22s | tokens: 6.2K | 成本: $0.08
                                        
                                        记录因果链
                                        能回答"为什么选了 combat/damage.lua 而不是 utils/calc.lua"
                                        能回答"跌了的话是从第几步开始跌的"

Trace 五类必记信息

一条能用于排障、评测和治理的 Trace,至少记录五类信息:

复制代码
类别            记什么                              用于什么
──────          ────────────────────                ──────────────────
1. 任务起点     任务 ID、目标、约束、成功标准         归因:是不是意图就不对
2. 环境快照     读了哪些文件、检索了什么、             归因:是不是看到了错误信息
                加载了什么上下文
3. 决策节点     选了哪个工具、为什么选、               归因:是不是决策逻辑变了
                还有什么候选
4. 动作结果     调了什么、参数、返回值、               归因:是不是工具出了问题
                成功/失败/异常
5. 反馈终局     测试结果、评测分数、                   归因:是不是反馈链断了
                人工审核结论、接受/拒绝原因

真实案例:Trace 如何在 3 分钟内定位 Prompt 退化

回到开头的故事。Prompt 微调后通过率从 78% 跌到 61%。有了 Trace 之后的排查过程:

复制代码
排查步骤                         发现                                耗时
──────────────                  ──────────────────────              ──────
1. 拉退化 case 的 Trace          全部退化 case 都在 Step 2 出现异常    30s
2. 对比 Step 2 的决策节点         旧版:优先 grep 精确搜索              1min
                                 新版:优先 file_read 大面积扫描
3. 对比 token 消耗               旧版 Step 2: 平均 2.1K tokens         30s
                                 新版 Step 2: 平均 6.8K tokens(+224%)
4. 定位根因                       Prompt 微调增加了"先全面了解再动手"     1min
                                 的引导语,改变了工具选择倾向

总排查时间:3 分钟
没有 Trace 时的排查时间:2 天

Eval + Trace 闭环:从"跌了"到"知道为什么跌"

Eval 和 Trace 不是两个独立工具,而是闭环的两个环节:

复制代码
                  ┌───────────────┐
                  │  变更发生      │ Prompt / 模型 / 工具 / 检索策略
                  └───────┬───────┘
                          ▼
                  ┌───────────────┐
                  │  Eval 回归     │ 自动跑评测集,对比基线
                  └───────┬───────┘
                          │
              ┌───────────┼───────────┐
              ▼                       ▼
    ┌──────────────┐        ┌──────────────┐
    │ 通过率 ≥ 基线 │        │ 通过率 < 基线 │
    │ → 绿灯放行   │        │ → 触发排查   │
    └──────────────┘        └──────┬───────┘
                                   ▼
                          ┌───────────────┐
                          │  Trace 对比    │ 拉退化 case 的 Trace
                          │               │ vs 基线版本的 Trace
                          └───────┬───────┘
                                  ▼
                          ┌───────────────┐
                          │  定位退化层    │ 决策变了?工具变了?
                          │               │ 上下文变了?环境变了?
                          └───────┬───────┘
                                  ▼
                          ┌───────────────┐
                          │  修复 + 回收   │ 修复退化 + 加入
                          │               │ failure 评测集
                          └───────────────┘

没有 Eval,你不知道跌了。没有 Trace,你知道跌了但不知道为什么。两者串起来,才是完整的质量防线。


性能基准:Eval 管质量,Benchmark 管容量

除了质量评测,Harness 还需要性能基准。因为"答对了"不等于"跑得起来":

复制代码
activity-dev-harness 性能基准数据:

指标                         单 case          35-case 全量     高峰期(并发×3)
──────────────              ──────────       ──────────       ────────────────
端到端时延(中位数)          22s              ---              ---
端到端时延(P95)            48s              ---              ---
单 case 成本                 $0.08            $2.80            $3.50(+25%重试)
推理排队等待(中位数)        0.3s             1.2s             4.8s
推理排队等待(P95)          1.1s             5.6s             18.2s ← 不可接受
通过率                       82%              78%              71%(退化 7pp)

发现的问题:
  1. P95 时延是中位数的 2.2 倍 → 复杂 case 时延爆炸
  2. 并发×3 时推理排队从 1.2s 涨到 4.8s → 推理服务是瓶颈
  3. 高峰期通过率下降 7pp → 排队导致上下文超时,重试增加

性能基准的三层结构:

复制代码
层级            测什么                   发现什么
──────          ────────────────         ────────────────────────
组件基准        单次推理延迟              Sonnet TTFT: 0.8s, 全量: 4.2s
                单次工具调用              pytest: 3.1s, grep: 0.2s
                单次检索                  向量检索: 120ms, Rerank: 80ms

链路基准        一个完整 case 从头到尾     Developer 生成: 8s
                各阶段耗时占比            Developer 自测: 5s × 3 = 15s
                                         Reviewer 审查: 6s
                                         总计: 29s(理想路径)

工作负载基准    35 case 全跑              并发度: 3-5
                高峰期模拟                推理服务排队情况
                尾延迟分布                P99 是否可控

早期团队的最小 Eval + Trace 方案

复制代码
阶段        做什么                                成本
──────      ────────────────────────              ──────
Week 1      固定 3 个 gold case + 基线结果          半天
Week 2      每次重要变更后跑 gold case 回归         10 分钟/次
Week 3      补 Trace:记录任务起点 + 决策节点        1 天
            + 动作结果
Week 4      补 failure 评测集(回收线上问题)        持续
Month 2     自动化:变更触发 → Eval → 报告          2 天
Month 3     Trace 对比:退化 case 自动拉两版         3 天
            Trace 差异

不要先做什么:
  ❌ 评测平台(先用脚本跑)
  ❌ 可视化仪表盘(先用文本报告)
  ❌ 全量 Trace 存储(先记关键节点)
  ❌ 实时告警(先做每日回归)

Eval & Trace 在不同项目里的组织差异

复制代码
项目              Eval 重点                    Trace 重点
────────────      ──────────────────           ──────────────────────
activity-dev      多轮循环通过率 +              每轮 Developer/Reviewer
harness           稳定性(同 case 5 次)         的决策路径 + 工具选择变化

AIReview          规则命中精度 +                检索召回 → Rerank 排序 →
                  漏报率/误报率                 最终引用的完整链路

配置表风险        批量准确率 +                  每张表的风险判定依据 +
评估              高风险召回率                   RAG 命中的规则版本

Crashsight        根因分析准确率 +              历史相似 crash 匹配 →
                  与人工分析的一致性             根因推理路径

Eval & Trace Harness 的核心判断:Eval 防退化,Trace 解释退化。没有 Eval 的系统靠直觉迭代,没有 Trace 的系统靠运气排障。两者串成闭环,才是 Harness 视角下的质量验证体系------不是"有评测"就行,而是"变更 → 回归 → 归因 → 回收"这条完整链路必须自动化运转。

相关推荐
Vect__2 小时前
快速掌握Python之基础语法和数据结构
开发语言·python
jvvz afqh2 小时前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
aaajj2 小时前
【Android】防骚扰电话自动接听助手方案
android·人工智能
lsx2024062 小时前
SQL CREATE DATABASE
开发语言
直奔標竿2 小时前
Java开发者AI转型第九课!突破知识边界!企业级 RAG (检索增强生成) 核心架构与 ETL 管道初探
java·开发语言·人工智能·后端·spring
hhb_6182 小时前
R语言数据分析与可视化实战指南
开发语言·数据分析·r语言
skilllite作者2 小时前
SkillLite Rust 沙箱与 AI Agent 自进化实战指南
开发语言·人工智能·后端·架构·rust
我星期八休息2 小时前
Linux 进程核心原理全解:从冯诺依曼体系到进程控制全链路深度剖析
大数据·linux·服务器·开发语言·数据结构·c++·散列表
QCzblack2 小时前
php-ser-libs
android·开发语言·php