agent-v2 真实 traces 导出与采样
agent-v2 真实 traces 导出与采样
日期: 2026-06-22 阶段: Phase 1 - 产品定义×评测×可观测底座 标签: #traces #sampling #error-analysis #observability
核心问题
Day 3 立下规矩:evals 不是先拍指标再测量,而是从真实失败归纳指标。但「真实失败」存在哪里、怎么取出来看,才是错误分析能不能落地的前提。本仓库已有 src/agent/trace/useTraceStore.ts——它在浏览器内记录了 orchestrator→sub-agent 的多步 run,但这些 trace 是易失的 zustand 内存态,刷新即丢,更没有导出与采样层。
今天回答三件事:(1) 把 trace 当作分析的最小单元时,粒度该取在 span 树还是整任务级;(2) 该看多少条、怎么抽——为什么「随机抽 100 条」在长尾失败上是个统计学陷阱,分层/重要性采样如何纠偏,并给出采样量与置信区间的量化关系;(3) 为什么错误分析必须从真实 traces 起步,而不是凭空想象指标(Hamel/Shreya「看 100 条真实数据」原则,2025-09)。
关键内容
A. trace 作为分析单元:span 树 vs 任务级,粒度怎么选
一条生产 LLM trace 不是 3 个 span,而是以用户请求为根的 30–300 个 span 的树(futureagi 2026 观测指南口径):根 span 是用户可见操作,每个 LLM 调用 / 检索步 / 工具调用是其子 span。本仓库 useTraceStore 的数据模型恰好已经是这棵树:
RunTrace (root, = 一次用户提问 / 一个任务)
├─ StepTrace (agent: orchestrator | research | knowledge | portfolio)
│ ├─ ToolCallTrace (name, args, result?, error?) ← 叶子
│ └─ ToolCallTrace ...
└─ StepTrace ...
(字段: inputTokens/outputTokens/startedAt/endedAt/totalCost/aborted/abortReason)
粒度选择是一个分析目标的函数,不是越细越好:
| 分析目标 | 取哪一级单元 | 理由 |
|---|---|---|
| 错误分析 / 开放编码(Day 9) | 任务级 RunTrace(带下钻) | 人脑一次只能对「这次提问对不对」下判断;先看根,需要时再下钻到 span 定位根因 |
| 工具可靠性回归 | ToolCallTrace(span 叶子) | error 字段直接是 tool_failure 信号,按 name 聚合算失败率 |
| 成本/延迟归因 | StepTrace(span 中间层) | inputTokens/outputTokens/duration 在 step 级才有意义 |
| LLM-as-judge 打分 | RunTrace(整任务输出) | judge 评的是「最终答案好不好」,不是单 span |
反直觉洞察(粒度陷阱):错误分析的单元必须是任务级,但失败的归因往往在 span 级。Hamel/Shreya 的硬规则是「每条 trace 只记第一个失败」——因为一旦根 span 失败,下游 span 的错误大多是连锁反应而非独立缺陷,若按 span 计数会把一个根因放大成五个「失败」,污染频次统计。所以正确做法是:以 RunTrace 为计数单位,下钻到 span 找根因,但只给那条 trace 记一个 first-failure 标签。
B. 分层/重要性采样的统计学:为什么随机采样漏长尾
错误分析要「看 100 条」,但100 条怎么抽决定了你能不能看见长尾失败。设某失败模式(如 typology_misjudge)在真实分布中占比 p=2%。随机抽 n 条,至少抽到 1 条该失败的概率是:
P(命中≥1) = 1 - (1 - p)^n
p=0.02, n=100 → 1 - 0.98^100 ≈ 0.867 # 还有 13.3% 概率一条都没抽到
p=0.005,n=100 → 1 - 0.995^100 ≈ 0.394 # 6 成概率完全漏掉
反过来用**「三的法则」(rule of three)** 看采样的「看不见≠不存在」:若在 n 条随机样本里零次观察到某失败,对其真实发生率 p 只能给出 95% 置信上界 p ≤ 3/n(来自 p≈−ln(0.05)/n≈2.996/n,Wikipedia Rule of three / Cochrane Handbook 16.9.4):
| 随机样本量 n | 零观测时 95% 置信上界 p ≤ 3/n | 含义 |
|---|---|---|
| 30 | 10% | 抽 30 条没看到,发生率仍可能高达 1/10 |
| 100 | 3% | Hamel 的「看 100 条」起步——只能排除 >3% 的高频失败 |
| 300 | 1% | 才敢说「未见的失败 <1%」 |
| 1000 | 0.3% | 监管级长尾(如漏报 SAR)才需要 |
随机采样在罕见缺陷上方差极高、预算效率极低(arXiv Stratified Importance Sampling 2601.22326 / 2026-01 的判断)。分层采样把样本空间切成不相交的层,按层抽样以压低层内方差——对 AML 这种「类别天然不均衡」的场景尤其关键:
分层采样(按任务结果 × 类型学分层)算法:
1. 用廉价信号给每条 RunTrace 打"层标签":
L1 = aborted/有 tool error 的 run (高失败率层, 重点采)
L2 = 用户给了负反馈的 run (高信号层)
L3 = topTypology 命中但低分(贴阈值)的 run (边界层, FP/FN 高发)
L4 = 普通成功 run (背景层, 少采)
2. 按层分配预算(Neyman 配额近似):
n_h ∝ N_h · σ_h # 层越大、层内失败率越分散,分得越多
实操简化:L1/L2/L3 各采尽量多,L4 随机补足到 100
3. 估计总体失败率时按层权重 N_h/N 加权回插,纠正过采样偏差
反直觉洞察(重要性采样的偏差纠正):分层/重要性采样故意过采样高失败层(L1–L3),这会让你「看见」的失败率高于真实失败率——这是特性不是 bug。但若不做按层权重回插就直接报「失败率 30%」,就把过采样的偏差当成了真实指标。正确口径:用过采样发现并归类失败模式(定性,Day 9–10 用),用加权回插或独立随机样本估真实发生率(定量,进 PRD 指标用)。两者混用是 eval 里最隐蔽的口径错误。
Hamel/Shreya 原文给的工程化抽法正是分层的近似:clustering(聚类去重,避免 100 条里 80 条是同一种平凡成功)、sort by user feedback(按负反馈排序)、sort by high-probability failure patterns(按失败概率排序)(hamel.dev error-analysis FAQ,2025-09 抓取)。在观测管线上对应 tail-based sampling:trace 跑完后再决定是否全量留存——比 head-based(请求开始即决定)更能抓住错误案例(futureagi 2026)。
C. 错误分析为何必须从真实 traces 起步
Hamel/Shreya 的核心主张:错误分析是「evals 里最重要的活动」,目的是让你的评测指标由真实应用行为支撑,而不是反生产力的通用指标(why-is-error-analysis FAQ,2025-09 抓取)。两条操作纪律:
- 看至少 100 条真实 trace,逐条开放编码,直到理论饱和(theoretical saturation)——「连续 ~20 条不再涌现新失败类别即可停」。
- 用的是representative traces of user interactions(真实用户交互的代表性 trace),不是工程师拍脑袋的测试用例。
为什么合成数据不够?合成数据只覆盖你已经想到的失败。本仓库 src/aml/generator.ts 是个绝佳反例与正例并存的样本:它的 genNormalCase 里 index===0 的「现金密集型商户」3 笔贴线现金存款,是刻意埋的真实感 FP——这说明合成器能制造你已知的边界。但真实调查员可能因为「memo 字段写了售车款但金额整千」这类生成器没编码的特征组合而误判,这种失败只存在于真实分布里。
真实 traces 起步 vs 凭空设指标,对比:
凭空设指标: PM 想象"会幻觉" → 写 hallucination eval → 只测到想象中的失败
(盲区: 没想到的 context_pollution 永远测不到)
真实起步: 导出 agent-v2 真实 run → 开放编码 → 涌现出未预期的失败类别
→ 这些类别才进 failure taxonomy (Day 11)
反直觉洞察(合成数据的视野上界):合成数据集的失败覆盖率严格不超过其生成规则的表达力。
generator.ts永远生成不出「memo 与金额语义矛盾」类失败,因为它的 memo 是从固定池r.pick(...)选的、与金额独立。所以合成金标可以校准已知类型学的 recall/precision(Day 11 用),但 failure taxonomy 的新类别必须从真实 traces 涌现——这就是为什么 Day 8 的导出层必须接 agent-v2 的真实 run,而不能只跑getGoldenDataset()。
设计要点/决策表
| 决策 | 选择 | 理由 |
|---|---|---|
| 分析单元 | RunTrace(任务级)为主,span 下钻 | 人工判断在任务级,根因在 span 级 |
| 失败计数口径 | 每 trace 只记 first failure | 防连锁反应放大频次(A 节陷阱) |
| 采样策略 | 分层(L1 失败/L2 负反馈/L3 边界/L4 背景) | 随机采样漏长尾(B 节 rule of three) |
| 真实率估计 | 按层权重回插 或 独立随机样本 | 过采样不能直接当真实率(B 节偏差) |
| 数据来源 | agent-v2 真实 run 优先,合成仅校准已知 | 新失败类别只在真实分布出现(C 节) |
| 留存机制 | tail-based 全量留错误 trace,背景层抽样 | 错误案例信息密度最高 |
| 隐私 | 导出默认不含 prompt/completion 文本,opt-in | 对齐 OTel GenAI semconv(2026-03 仍 experimental) |
对本项目的落地
- 新建
src/agent/trace/exportTraces.ts:从useTraceStore的runs序列化为 JSONL(一行一 RunTrace),落localStorage+ 「下载 .jsonl」按钮,把易失内存态变成可离线分析的语料。字段直接复用RunTrace,新增计算字段firstFailureSpanId(A 节口径)。 - 新建
src/agent/trace/sampleTraces.ts:实现 B 节分层采样。层标签函数从现有字段廉价推断——L1 用run.aborted || step.toolCalls.some(t => t.error),L3「边界层」复用src/aml/typology.ts的assessCase输出scores贴近ASSESS_THRESHOLD=0.5的 run。提供estimateTrueRate(samples, stratumWeights)做加权回插,并暴露ruleOfThreeUpperBound(n)=3/n供「零观测」时报置信上界。 - 采样配额默认值:目标 100 条进开放编码(Day 9),L1+L2+L3 尽采、L4 随机补足——与 Hamel「看 100 条 + 饱和即停」对齐;导出工具同时打印各层
N_h与回插后的真实失败率估计。 - 诚实标注:当前 agent-v2 真实流量样本量小,W2 先用
generator.ts的 66 案金标 + 少量真人手动 run 凑样本;笔记与代码注释明确「真实率估计在样本量 <300 时只给 rule-of-three 上界,不报点估计」——避免把小样本当成生产指标(B 节偏差陷阱的工程化护栏)。 - 接 Day 9:
sampleTraces.ts的输出(100 条 RunTrace + 层标签)正是开放编码的输入;exportTraces.ts的 JSONL 格式预留annotations: []字段,供 Day 9 逐条自由标注写回。
参考资料
- Hamel Husain — Why is "error analysis" so important in LLM evals, and how is it performed?(看 ≥100 条真实 trace、理论饱和「20 条无新类即停」、clustering/sort-by-feedback/sort-by-failure-prob 采样、每 trace 只记 first failure)hamel.dev evals-faq (2025-09)
- Hamel Husain & Shreya Shankar — LLM Evals: Everything You Need to Know / AI Evals 课程(错误分析方法论,已训练 Anthropic/OpenAI/Google/Meta 团队)hamel.dev / Maven (2025-09,FAQ 版 2026-01)
- Rule of three (statistics) — 零观测时 95% 置信上界 p≤3/n(Wikipedia;Cochrane Handbook 16.9.4 Confidence intervals when no events are observed)经典统计结论,配近期实践 statology.org 指南 (2025)
- Label-Efficient Monitoring via Stratified Importance Sampling — 随机采样在罕见缺陷上方差高、预算效率低;分层切层压低层内方差,arXiv 2601.22326 (2026-01)
- FutureAGI — What Is LLM Observability? 2026 Guide(trace = 30–300 span 树为单元;head-based vs tail-based 采样;evaluator 分数挂回同一 span_id)(2026)
- OpenTelemetry GenAI semantic conventions —
gen_ai.usage.input_tokens等属性、默认不采集 prompt/completion 内容(opt-in),spec 口径 experimental (2026-03)
SOTA 检查 (2026-06-11)
- 错误分析「看真实 traces」方法论仍是 2026-06 事实标准:Hamel/Shreya 的 FAQ 版本 2026-01 仍在更新,采样建议(clustering / sort-by-feedback / sort-by-failure-prob)未被取代;本日 WebSearch 未发现替代框架。
- 分层/重要性采样是活跃前沿:arXiv 2601.22326(2026-01)把「标注预算下的罕见类监控」形式化为分层重要性采样,方向与本笔记 B 节一致;说明「随机抽 100 条」在工业界正被「按层/按失败概率抽」替代。
- 观测管线 trace-as-tree + tail-based 采样已成共识:FutureAGI/MLflow 2026 指南口径一致;但 OTel GenAI semconv 仍 experimental(2026-03)——导出层属性映射必须留独立适配层(与 Day 3 W4 决策一致),W4 开工前复查是否转 stable。
- 过时警示:把「跑一次合成金标 recall」当成产品质量门,是 2024 式做法——本笔记 C 节明确合成只能校准已知失败,新类别必须从真实分布涌现。
- 待跟踪:agent-v2 真实流量样本量增长后,把 rule-of-three 上界升级为带置信区间的点估计的样本量门槛(B 节 n≥300);OTel GenAI semconv 是否在 2026 下半年转 stable。