本页目录
Lesson 08 — 轨迹评估原理:评过程,不只评答案
本课目标:手写 TrajectoryEvaluator——输入轨迹 jsonl → 输出指标卡(成功率/步数/工具正确率/循环检测/失败归因),并在 service.py 落盘每次运行的轨迹。这是评估主线的闭环点:L01–L07 每个机制的收益都要能在这些指标上显形。
学完你能回答:「怎么评估一个 Agent?你的评估和 ragas 有什么区别?」——ragas 评一次回答,我评一整条决策过程。
0. 为什么 ragas 不够用了
rag-lessons 学过 ragas:评 RAG 的「检索质量 + 生成质量」。但它评的是一次问答——query 进、answer 出。
Agent 不是一次问答,是一串决策:拆题→搜索→汇总→写报告→审稿→(可能重写/补研)。评 Agent 要评这整条链,而不只是最后那个报告。
ragas 评的(单步) 轨迹评估评的(全过程)
───────────── ──────────────────
query → answer split → researcher → summarize → writer → reviewer
评:答案对不对 评:过程好不好
- 步数合理吗?(有没有绕路)
- 工具用对了吗?(该用没用/不该用乱用)
- 原地打转了吗?(循环)
- 错在哪一环?(归因)
🎯 核心认知:Agent 的价值在决策过程,不只是最终答案。同样写出好报告,10 步的和 50 步的不是一回事;同样失败,错在检索的和错在规划的修正方向完全不同。评过程才能找到真正的改进点。
1. 指标卡设计
五类指标
┌─────────────────── 轨迹指标卡 ───────────────────┐
│ │
│ ① 任务成功率(终态 judge) │
│ 最后的输出完成了任务吗? │
│ │
│ ② 步数效率 │
│ 同样成功,谁步数少?(1/steps) │
│ │
│ ③ 工具调用正确率 │
│ 该用没用/不该用乱用?失败率多少? │
│ │
│ ④ 循环检测 │
│ 同一节点连续 3+ 次且输出相似 = 原地打转 │
│ │
│ ⑤ 失败归因 │
│ 错在规划/检索/生成哪一环? │
│ │
│ + 机制触发检测(L01-L07 各机制有没有用上) │
│ 记忆召回/反思/冲突修正/代码执行 │
│ │
└───────────────────────────────────────────────────┘
机制触发检测(评估主线的关键)
指标卡里有四个布尔值,记录 L01-L07 的机制有没有在轨迹里被触发:
- has_memory_recall:记忆召回(L01)
- has_reflection:反思(L04)
- has_conflict_correction:冲突修正(L05)
- has_code_execution:代码执行(L07)
💡 为什么重要:这四个布尔值 + 任务成功率,就能量化每个机制的收益。比如对比"开了记忆 vs 关了记忆"的成功率差——前提是轨迹里能看到记忆确实被召回了。机制触发检测让"加了 X 有没有用"可验证。
2. 流派对比
问题:怎么评估 Agent 的轨迹?
| 流派 | 做法 | 取舍 |
|---|---|---|
| ① 终态 judge | 只看最终输出对不对 | ✅ 便宜;🚫 看不到过程病(绕路/循环/工具滥用) |
| ② 全轨迹 LLM judge | LLM 逐步评估每个决策 | ✅ 可归因、细致;🚫 贵(每步一次 LLM) |
| ③ 规则+judge 混合(本课选它) | 步数/循环/工具用规则,成功率/归因用 judge | ✅ 成本可控、覆盖关键指标;🚫 不如全 judge 细致 |
选 ③ 的理由:与 ops-L05 攻击判定同思路——能用规则的不用 judge(步数、循环、工具统计都是确定性的),只有需要语义理解的才用 judge(成功/失败判断)。成本可控(一条轨迹最多 1-2 次 judge 调用),覆盖了最重要的指标。
3. 循环检测
策略
def _detect_loops(trace):
# 按节点分组连续段
segments = group_consecutive_same_node(trace)
for node, steps in segments:
if len(steps) >= 3: # 连续 3+ 次同节点
outputs = [s.output[:50] for s in steps]
if len(set(outputs)) <= 2: # 输出高度重复
# 这是循环(原地打转)
为什么 3+ 次且输出相似
- 2 次同节点可能是正常的重写/补研(reviewer 合法地触发了一次重写)
- 3+ 次同节点 + 输出几乎一样 = 没有实质进展(真正的循环)
⚠️ 诚实标注:这个检测是字符级相似度,粗糙。两个输出措辞不同但语义相同会漏检。生产可换 embedding 相似度或 LLM judge("这两步有没有实质进展")。本课的字符级检测能抓到明显的打转(如完全重复的输出)。
4. 失败归因
三环归因模型
研究 Agent 的三大环节:
规划(split) → 检索(researcher) → 生成(writer)
失败时归因:
split 失败 → 规划问题(子问题拆得不好)
researcher 失败 → 检索问题(搜索/知识获取出错)
writer 失败 → 生成问题(报告撰写出错)
归因的价值:知道错在哪一环,才知道改哪里。检索错→换搜索策略/工具;规划错→改拆题 prompt;生成错→改 writer prompt。
规则降级
无 LLM 时,看哪个节点的输出含失败信号("失败/超时/错误")→ 归因到对应环节。粗糙但能跑。
5. 轨迹落盘(service.py)
每次运行产出 traces/run_<ts>.jsonl,格式对齐 L00 的 baseline_trace:
{"step": 1, "node": "research_team", "input": "...", "output": "...", "ts": "..."}
{"step": 2, "node": "writer", "input": "...", "output": "...", "ts": "..."}
这个轨迹文件就是 TrajectoryEvaluator 的输入——从运行到评估的数据管道。
评估主线闭环
L00 裸基线轨迹(失忆)
↓ L01 加记忆
L01 轨迹(有 recall)
↓ L04 加反思
L04 轨迹(有反思)
↓ ... 每个机制
↓
L08 TrajectoryEvaluator 统一评估
→ 指标卡对比:每个机制的收益量化
→ L09 harness 自动化这个对比
6. 落地清单
改了哪些文件
| 文件 | 改动 | 说明 |
|---|---|---|
src/research_assistant/trajectory_eval.py |
新增 | TrajectoryEvaluator(指标卡 + 循环检测 + 归因) |
src/research_assistant/service.py |
加轨迹落盘 | 每次运行产出 traces/run_<ts>.jsonl |
tests/test_trajectory_eval.py |
新增 15 个测试 | 指标/循环/机制检测/归因/文件评估 |
如何验证
cd portfolio-projects/research-assistant
# 1. 全量测试(77 原有 + 15 新增 = 92 全绿)
.venv/Scripts/python.exe -m pytest tests/ -q
# 预期:92 passed
# 2. 演示轨迹评估(用 L00 基线轨迹)
cd ../../frontier-lessons/08_trajectory_eval
PYTHONIOENCODING=utf-8 ../../.venv/Scripts/python.exe code.py
# 预期:L00 基线轨迹 → 指标卡(失忆/无反思/无冲突修正)
# 构造的"原地打转"轨迹 → 循环检测抓到
# 3. 评估真实轨迹(跑过研究后)
# Traces 在 traces/ 目录,用 TrajectoryEvaluator 评估
7. 本课在两条主线上的位置
- 评估主线:本课是评估主线的闭环点。从 L00 立基线开始,到 L08 建立评估体系,中间 L01-L07 每个机制的收益现在都能量化了。L09 的 harness 会把这个评估自动化(机制开关矩阵 × 轨迹评估 = 收益表)。没有本课,"加了记忆更好"永远只是感觉。
- 上下文工程主线:轨迹评估间接服务上下文工程——通过归因发现"检索失败多",就知道要改 researcher 的上下文(换搜索词策略/加记忆注入)。评估是上下文工程改进的指南针。
🎯 面试话术
「评 Agent 我不只看答案,看轨迹。我写了 TrajectoryEvaluator:成功率(终态 judge)、步数效率、工具调用正确率、循环检测、失败归因。混合策略——能用规则的用规则(步数/循环/工具统计),需要语义的才用 judge(成功/失败判断)。还有机制触发检测:记忆有没有召回、反思有没有发生、冲突有没有修正、代码有没有执行——这四个布尔值让每个机制的收益可量化。ragas 评一次回答,我的 harness 评一整条决策过程。」