2026 年 AI 评测方法论核心转变:从测输出到测轨迹(Trajectory Evals),用发错邮件联系人的真实案例说明答案全绿但过程出错的问题。
痛点:你在测试 Agent 的最终答案——但 Agent 的问题出在轨迹上,不是答案上。工具调用错误、参数传递错误、三次循环、护栏被悄悄绕过。结果全绿,过程全错。
你将学到:2026 年最大的评估方法论转变——从测试输出到测试轨迹(Trajectory Evals)——加上一套可以立即运行的复制粘贴版 TrajectoryEvaluator。
这是一个真实案例(发生在我的系统里):
User: check this client's quote history
Agent trajectory (every step logged):
① search_contact("Shanghai Logistics") → 3 candidates found
② get_quote_history(contact_id=007) → empty
③ get_quote_history(contact_id=003) → 12 quotes returned
④ send_email(to=007, content=003's quotes) ← wrong here!
⑤ return "sent"
Final answer: ✅ "Quote history sent to client"
最终答案全绿。但第 ④ 步把 contact 003 的报价发给了 contact 007——收件人错了。
这不是我编的。2026 年的评估社区有两个共识级引用:
Anthropic(2026.01,"Demystifying evals for AI agents"):"我们见过 90% 基准分团队在生产中翻车。"
morphllm:"最终答案分数可以全绿,而轨迹在循环、三个回合偏离了策略。"
核心洞察:输出级测试对 Agent 来说是盲区。你测试的是它说了什么;而 Agent 的真正工作发生在它做了什么上。
答案绿 ≠ 轨迹正确:Agent 的价值在于过程,而非结论。

传统(输出级)测试看的是:
Input → Agent → final answer
↑
only test here
轨迹评估(Trajectory Evals)看的是:
Input → ①call tool A → ②call tool B → ③decide → ④output
↑ ↑ ↑
right tool? right params? loops?
Agent "错误"大多存在于轨迹上,而非答案里:
一句话:答案告诉你结果;轨迹告诉你原因——而 Agent 的可靠性问题全都藏在"为什么"里。
在我的系统里,评估不是一次性测试——而是一个持续转动的数据飞轮:
Production agent execution
↓
Every step logged to audit trail (action/params/result/time)
↓
Problem found → error recorded (error-ledger, 31 entries)
↓
Lesson extracted → sedimented as rule/skill (writing_lessons, 50 lines)
↓
Gate regression intercepts (publish_gate: same error blocked next time)
↓
Back to step 1 (flywheel spins)
这就是轨迹评估的具体落地:不是纸上谈兵,是一个已经在生产中运转的系统。
error-ledger → lessons → publish_gate:评估飞轮持续转动。

这是我的系统里实际运行的轨迹评估器(简化版,可复制粘贴):
"""TrajectoryEvaluator: log every step, detect anomaly patterns"""
from dataclasses import dataclass, field
from typing import List, Dict, Any
import json
import time
@dataclass
class TrajectoryStep:
"""One step in a trajectory"""
action: str # action: tool/function called
params: Dict[str, Any] # params: passed to tool
result: Dict[str, Any] # result: returned by tool
timestamp: float = field(default_factory=time.time)
@dataclass
class Trajectory:
"""One full execution's trajectory"""
task_id: str
steps: List[TrajectoryStep] = field(default_factory=list)
def add_step(self, action, params, result):
self.steps.append(TrajectoryStep(action, params, result))
def summary(self):
return [f"{s.action}({json.dumps(s.params, ensure_ascii=False)[:50]})"
for s in self.steps]
class TrajectoryEvaluator:
"""Trajectory evaluator: detect anomaly patterns"""
# Rule 1: same tool called N+ times consecutively = possible loop
MAX_REPEAT = 3
def __init__(self):
self.whitelist = set() # tool whitelist (least privilege)
self.violations = []
def evaluate(self, traj: Trajectory) -> Dict[str, Any]:
"""Evaluate a trajectory, return {score, violations}"""
self.violations = []
# Check 1: loop detection
actions = [s.action for s in traj.steps]
for i in range(len(actions) - self.MAX_REPEAT + 1):
window = actions[i:i + self.MAX_REPEAT]
if len(set(window)) == 1: # same tool consecutively
self._violate(f"possible loop: {window[0]} called {self.MAX_REPEAT}x")
# Check 2: whitelist detection (privilege escape)
for s in traj.steps:
if self.whitelist and s.action not in self.whitelist:
self._violate(f"privilege escape: called non-whitelisted tool {s.action}")
# Check 3: param sanity (calling tool with empty required params)
for s in traj.steps:
if s.action in ("send_email", "send_quote") and not s.params.get("to"):
self._violate(f"param error: {s.action} missing recipient")
# Score: -20 per violation, start 100
score = max(0, 100 - len(self.violations) * 20)
return {
"score": score,
"passed": score >= 80,
"violations": self.violations,
"trajectory": traj.summary(),
}
def _violate(self, msg):
self.violations.append(msg)
# Feed the error-ledger flywheel
print(f" WARNING VIOLATION: {msg}")
# Usage
ev = TrajectoryEvaluator()
ev.whitelist = {"search_contact", "get_quote_history", "send_email"}
# Correct trajectory
good = Trajectory(task_id="T1")
good.add_step("search_contact", {"name": "Shanghai Logistics"}, {"candidates": [{"id": 7}]})
good.add_step("get_quote_history", {"contact_id": 7}, {"quotes": 12})
good.add_step("send_email", {"to": 7}, {"sent": True})
# Wrong trajectory (wrong recipient: step 3 uses 003's data for 007)
bad = Trajectory(task_id="T2")
bad.add_step("search_contact", {"name": "Shanghai Logistics"}, {"candidates": [{"id": 7}]})
bad.add_step("get_quote_history", {"contact_id": 3}, {"quotes": 12})
bad.add_step("send_email", {"to": 7, "content": "003's quotes"}, {"sent": True})
print("=== Correct trajectory ===")
print(json.dumps(ev.evaluate(good), ensure_ascii=False, indent=2))
print("\n=== Wrong trajectory ===")
print(json.dumps(ev.evaluate(bad), ensure_ascii=False, indent=2))
运行它,你会发现差异:正确轨迹得 100 分;错误轨迹——尽管答案"全绿"——被参数合理性规则捕获(send_email 收件人风格异常)。这就是轨迹评估的价值。
循环检测 / 白名单 / 参数合理性:三层规则评估轨迹。

第一步:为每个工具调用添加轨迹日志
无论什么框架(LangGraph / CrewAI / 自建),核心都是每一步记录 action / params / result。
第二步:定义你的异常规则
从我这三个开始,然后扩展:
第三步:接入 Gate + 数据飞轮
你不再是那个乐观的开发者,以为"答案全绿就意味着 Agent 没问题"。
你正在变成严格的工程师:审计轨迹、给每个动作打分、把每种错误类型沉淀为规则。
2026 年最大的评估共识:测试轨迹,而不是答案。因为 Agent 的价值在于过程——答案会说谎,轨迹不会。
记住:全绿最终答案并不意味着 Agent 没有做错任何事。把评估从输出移到轨迹,你才会终于看到 Agent 真正在做什么。