智能体评估与测试体系

面向已经能用 LangChain/LangGraph 跑通 Agent、但开发测试"效果时好时坏、上线没底气"的开发者。

核心论点:Agent 的可靠性不是调出来的,是测出来的。 当你的 Agent 还停留在"我试了几个例子都对了"这种评估方式时,它就不该进生产。

文中代码基于 LangChain 1.0 / LangGraph 1.0langsmith>=0.4.29(支持组合评估器与多轮评估)pydantic>=2


目录

  1. [为什么评估是 Agent 落地的最大短板](#为什么评估是 Agent 落地的最大短板)
  2. 传统测试为何失效
  3. 评估体系分层
  4. 评估维度模型
  5. 方法论一:可执行断言(确定性评估)
  6. 方法论二:LLM-as-Judge(轨迹级评估)
  7. 方法论三:参考轨迹对比与回归
  8. 测试类型
  9. [工具链实战:用 LangSmith 构建评估流水线](#工具链实战:用 LangSmith 构建评估流水线)
  10. 在线评估与持续监控闭环
  11. 总结

一、为什么评估是 Agent 落地的最大短板

Gartner 预测到 2027 年底超 40% 的 Agent 项目被取消,MIT 研究显示 95% 的生成式 AI 试点未带来可衡量回报。在无数失败案例背后,工程能力的缺位 里最隐蔽、也最致命的一项,就是缺乏系统化的评估与测试体系

一个尴尬的现实:

  • 让 Agent 在 10 个手搓的例子上正确回答;
  • 不知道它在第 11 个没见过的输入上会不会陷入死循环;
  • 无法回答"这个版本比上个版本好多少、好在哪"的问题。

没有评估,Agent 的迭代就是盲飞。而评估的难,又远超传统软件------这正是本文要解决的问题。


二、传统测试为何失效

#mermaid-svg-HUqU8IqbX4kBSI3R{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HUqU8IqbX4kBSI3R .error-icon{fill:#552222;}#mermaid-svg-HUqU8IqbX4kBSI3R .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HUqU8IqbX4kBSI3R .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HUqU8IqbX4kBSI3R .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HUqU8IqbX4kBSI3R .marker.cross{stroke:#333333;}#mermaid-svg-HUqU8IqbX4kBSI3R svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HUqU8IqbX4kBSI3R p{margin:0;}#mermaid-svg-HUqU8IqbX4kBSI3R .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HUqU8IqbX4kBSI3R .cluster-label text{fill:#333;}#mermaid-svg-HUqU8IqbX4kBSI3R .cluster-label span{color:#333;}#mermaid-svg-HUqU8IqbX4kBSI3R .cluster-label span p{background-color:transparent;}#mermaid-svg-HUqU8IqbX4kBSI3R .label text,#mermaid-svg-HUqU8IqbX4kBSI3R span{fill:#333;color:#333;}#mermaid-svg-HUqU8IqbX4kBSI3R .node rect,#mermaid-svg-HUqU8IqbX4kBSI3R .node circle,#mermaid-svg-HUqU8IqbX4kBSI3R .node ellipse,#mermaid-svg-HUqU8IqbX4kBSI3R .node polygon,#mermaid-svg-HUqU8IqbX4kBSI3R .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HUqU8IqbX4kBSI3R .rough-node .label text,#mermaid-svg-HUqU8IqbX4kBSI3R .node .label text,#mermaid-svg-HUqU8IqbX4kBSI3R .image-shape .label,#mermaid-svg-HUqU8IqbX4kBSI3R .icon-shape .label{text-anchor:middle;}#mermaid-svg-HUqU8IqbX4kBSI3R .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HUqU8IqbX4kBSI3R .rough-node .label,#mermaid-svg-HUqU8IqbX4kBSI3R .node .label,#mermaid-svg-HUqU8IqbX4kBSI3R .image-shape .label,#mermaid-svg-HUqU8IqbX4kBSI3R .icon-shape .label{text-align:center;}#mermaid-svg-HUqU8IqbX4kBSI3R .node.clickable{cursor:pointer;}#mermaid-svg-HUqU8IqbX4kBSI3R .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HUqU8IqbX4kBSI3R .arrowheadPath{fill:#333333;}#mermaid-svg-HUqU8IqbX4kBSI3R .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HUqU8IqbX4kBSI3R .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HUqU8IqbX4kBSI3R .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HUqU8IqbX4kBSI3R .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HUqU8IqbX4kBSI3R .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HUqU8IqbX4kBSI3R .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HUqU8IqbX4kBSI3R .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HUqU8IqbX4kBSI3R .cluster text{fill:#333;}#mermaid-svg-HUqU8IqbX4kBSI3R .cluster span{color:#333;}#mermaid-svg-HUqU8IqbX4kBSI3R div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HUqU8IqbX4kBSI3R .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HUqU8IqbX4kBSI3R rect.text{fill:none;stroke-width:0;}#mermaid-svg-HUqU8IqbX4kBSI3R .icon-shape,#mermaid-svg-HUqU8IqbX4kBSI3R .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HUqU8IqbX4kBSI3R .icon-shape p,#mermaid-svg-HUqU8IqbX4kBSI3R .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HUqU8IqbX4kBSI3R .icon-shape .label rect,#mermaid-svg-HUqU8IqbX4kBSI3R .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HUqU8IqbX4kBSI3R .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HUqU8IqbX4kBSI3R .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HUqU8IqbX4kBSI3R :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 传统软件测试
输入确定 → 输出确定
可穷举路径
断言相等
Agent 测试
输入相同 → 输出可能不同
路径组合爆炸
需评估'质量'而非'对错'
故障是软性的: 自信地错

  1. 非确定性:同一问题两次跑,推理路径、措辞、甚至工具选择都可能不同。"相等断言"失效,必须评估"分布质量"。
  2. 路径组合爆炸 :ReAct 循环里每一步都有分支,无法穷举所有轨迹。覆盖率思维要让位于抽样 + 代表性数据集
  3. 软故障:Agent 不会"报错崩溃",而是"自信地给出一个错误的、看似合理的答案"。传统异常捕获抓不到。
  4. 多目标冲突 :一个回答"更准"可能"更慢更贵";"更简洁"可能"漏了关键步骤"。评估必须是多维度的权衡,而非单指标。

专家判断:Agent 评估的本质,是把"概率系统的质量"翻译成"可被工程团队比较、回归、门禁的数字"。


三、评估体系分层

一个完整的评估体系应覆盖三条时间线,缺一不可:
#mermaid-svg-uYqefMec51hbJvdC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uYqefMec51hbJvdC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uYqefMec51hbJvdC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uYqefMec51hbJvdC .error-icon{fill:#552222;}#mermaid-svg-uYqefMec51hbJvdC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uYqefMec51hbJvdC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uYqefMec51hbJvdC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uYqefMec51hbJvdC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uYqefMec51hbJvdC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uYqefMec51hbJvdC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uYqefMec51hbJvdC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uYqefMec51hbJvdC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uYqefMec51hbJvdC .marker.cross{stroke:#333333;}#mermaid-svg-uYqefMec51hbJvdC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uYqefMec51hbJvdC p{margin:0;}#mermaid-svg-uYqefMec51hbJvdC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uYqefMec51hbJvdC .cluster-label text{fill:#333;}#mermaid-svg-uYqefMec51hbJvdC .cluster-label span{color:#333;}#mermaid-svg-uYqefMec51hbJvdC .cluster-label span p{background-color:transparent;}#mermaid-svg-uYqefMec51hbJvdC .label text,#mermaid-svg-uYqefMec51hbJvdC span{fill:#333;color:#333;}#mermaid-svg-uYqefMec51hbJvdC .node rect,#mermaid-svg-uYqefMec51hbJvdC .node circle,#mermaid-svg-uYqefMec51hbJvdC .node ellipse,#mermaid-svg-uYqefMec51hbJvdC .node polygon,#mermaid-svg-uYqefMec51hbJvdC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uYqefMec51hbJvdC .rough-node .label text,#mermaid-svg-uYqefMec51hbJvdC .node .label text,#mermaid-svg-uYqefMec51hbJvdC .image-shape .label,#mermaid-svg-uYqefMec51hbJvdC .icon-shape .label{text-anchor:middle;}#mermaid-svg-uYqefMec51hbJvdC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uYqefMec51hbJvdC .rough-node .label,#mermaid-svg-uYqefMec51hbJvdC .node .label,#mermaid-svg-uYqefMec51hbJvdC .image-shape .label,#mermaid-svg-uYqefMec51hbJvdC .icon-shape .label{text-align:center;}#mermaid-svg-uYqefMec51hbJvdC .node.clickable{cursor:pointer;}#mermaid-svg-uYqefMec51hbJvdC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uYqefMec51hbJvdC .arrowheadPath{fill:#333333;}#mermaid-svg-uYqefMec51hbJvdC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uYqefMec51hbJvdC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uYqefMec51hbJvdC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uYqefMec51hbJvdC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uYqefMec51hbJvdC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uYqefMec51hbJvdC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uYqefMec51hbJvdC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uYqefMec51hbJvdC .cluster text{fill:#333;}#mermaid-svg-uYqefMec51hbJvdC .cluster span{color:#333;}#mermaid-svg-uYqefMec51hbJvdC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uYqefMec51hbJvdC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uYqefMec51hbJvdC rect.text{fill:none;stroke-width:0;}#mermaid-svg-uYqefMec51hbJvdC .icon-shape,#mermaid-svg-uYqefMec51hbJvdC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uYqefMec51hbJvdC .icon-shape p,#mermaid-svg-uYqefMec51hbJvdC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uYqefMec51hbJvdC .icon-shape .label rect,#mermaid-svg-uYqefMec51hbJvdC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uYqefMec51hbJvdC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uYqefMec51hbJvdC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uYqefMec51hbJvdC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 韧性评估 Resilience --- 定期
在线评估 Online --- 发布后
离线评估 Offline --- 发布前
单元测试 节点级
集成测试 图级
端到端 黄金集
回归测试
生产 Trace 采样
LLM-as-Judge 实时监控
用户反馈闭环
混沌测试 故障注入
对抗测试 Prompt 注入
质量门禁 CI
持续优化
鲁棒性加固

  • 离线评估解决"这次改动有没有变好/变坏"。
  • 在线评估解决"真实流量下到底行不行"。
  • 韧性评估解决"极端/恶意情况下会不会崩"。

四、评估维度模型

不要只测"最终答案对不对"。Agent 的质量是分层的:
#mermaid-svg-cZ162mcbvEzuZGgn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cZ162mcbvEzuZGgn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cZ162mcbvEzuZGgn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cZ162mcbvEzuZGgn .error-icon{fill:#552222;}#mermaid-svg-cZ162mcbvEzuZGgn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cZ162mcbvEzuZGgn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cZ162mcbvEzuZGgn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cZ162mcbvEzuZGgn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cZ162mcbvEzuZGgn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cZ162mcbvEzuZGgn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cZ162mcbvEzuZGgn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cZ162mcbvEzuZGgn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cZ162mcbvEzuZGgn .marker.cross{stroke:#333333;}#mermaid-svg-cZ162mcbvEzuZGgn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cZ162mcbvEzuZGgn p{margin:0;}#mermaid-svg-cZ162mcbvEzuZGgn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cZ162mcbvEzuZGgn .cluster-label text{fill:#333;}#mermaid-svg-cZ162mcbvEzuZGgn .cluster-label span{color:#333;}#mermaid-svg-cZ162mcbvEzuZGgn .cluster-label span p{background-color:transparent;}#mermaid-svg-cZ162mcbvEzuZGgn .label text,#mermaid-svg-cZ162mcbvEzuZGgn span{fill:#333;color:#333;}#mermaid-svg-cZ162mcbvEzuZGgn .node rect,#mermaid-svg-cZ162mcbvEzuZGgn .node circle,#mermaid-svg-cZ162mcbvEzuZGgn .node ellipse,#mermaid-svg-cZ162mcbvEzuZGgn .node polygon,#mermaid-svg-cZ162mcbvEzuZGgn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cZ162mcbvEzuZGgn .rough-node .label text,#mermaid-svg-cZ162mcbvEzuZGgn .node .label text,#mermaid-svg-cZ162mcbvEzuZGgn .image-shape .label,#mermaid-svg-cZ162mcbvEzuZGgn .icon-shape .label{text-anchor:middle;}#mermaid-svg-cZ162mcbvEzuZGgn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cZ162mcbvEzuZGgn .rough-node .label,#mermaid-svg-cZ162mcbvEzuZGgn .node .label,#mermaid-svg-cZ162mcbvEzuZGgn .image-shape .label,#mermaid-svg-cZ162mcbvEzuZGgn .icon-shape .label{text-align:center;}#mermaid-svg-cZ162mcbvEzuZGgn .node.clickable{cursor:pointer;}#mermaid-svg-cZ162mcbvEzuZGgn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cZ162mcbvEzuZGgn .arrowheadPath{fill:#333333;}#mermaid-svg-cZ162mcbvEzuZGgn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cZ162mcbvEzuZGgn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cZ162mcbvEzuZGgn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cZ162mcbvEzuZGgn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cZ162mcbvEzuZGgn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cZ162mcbvEzuZGgn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cZ162mcbvEzuZGgn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cZ162mcbvEzuZGgn .cluster text{fill:#333;}#mermaid-svg-cZ162mcbvEzuZGgn .cluster span{color:#333;}#mermaid-svg-cZ162mcbvEzuZGgn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cZ162mcbvEzuZGgn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cZ162mcbvEzuZGgn rect.text{fill:none;stroke-width:0;}#mermaid-svg-cZ162mcbvEzuZGgn .icon-shape,#mermaid-svg-cZ162mcbvEzuZGgn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cZ162mcbvEzuZGgn .icon-shape p,#mermaid-svg-cZ162mcbvEzuZGgn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cZ162mcbvEzuZGgn .icon-shape .label rect,#mermaid-svg-cZ162mcbvEzuZGgn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cZ162mcbvEzuZGgn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cZ162mcbvEzuZGgn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cZ162mcbvEzuZGgn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 评估维度
任务层

成功率 / 目标达成 / 用户满意度
轨迹层

工具选择正确 / 步骤效率 / 无死循环
质量层

幻觉率 / 忠实度 / 合规
成本层

Token·任务 / 步数·任务 / 首字延迟
安全层

注入抵抗 / 越权拦截 / 数据泄露

关键认知:任务层(答案对不对)只是冰山一角。一个答案"看起来对"的 Agent,可能走了 23 步、调了 9 次工具、烧了 8 万 Token。


五、方法论一:可执行断言(确定性评估)

零成本、可复现、应作为第一道防线。 适用于有明确契约的场景:预期调用了哪些工具、返回了特定结构、状态满足约束。

5.0 被测 Agent 的 v1 构建方式

评估的前提是有一个"被测对象"。在 LangChain 1.0 / LangGraph 1.0 中,官方推荐用 langchain.agents.create_agent(取代已弃用的 create_react_agent)构建,模型用 init_chat_model 统一初始化:

python 复制代码
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langgraph.checkpoint.memory import InMemorySaver  # v1 由 MemorySaver 重命名

# v1 统一模型入口:一个函数初始化任意厂商模型,格式为 "provider:model"
llm = init_chat_model("openai:gpt-5", temperature=0)

app = create_agent(
    model=llm,
    tools=[search, calculator, summarize],
    system_prompt="你是订单分析助手,按需调用工具完成任务。",
    checkpointer=InMemorySaver(),   # 持久化 + 可中断(human-in-the-loop 的基础)
    # middleware=[...]              # v1 中间件:历史摘要 / 人工介入 / PII 脱敏可插拔
)
# 注:create_react_agent 已在 v1 弃用;AgentState 现为 langchain.agents.AgentState(不再使用 pydantic 状态)

5.1 执行并重建轨迹

python 复制代码
from langchain_core.messages import AIMessage, ToolMessage, HumanMessage
from langchain_core.utils.uuid import uuid7

def run_and_capture(query: str, thread_id: str | None = None) -> dict:
    """执行 Agent 并重建轨迹,供断言与 Judge 使用。"""
    thread_id = thread_id or str(uuid7())
    result = app.invoke(
        {"messages": [HumanMessage(content=query)]},
        # v1 中 thread_id 仍走 configurable;recursion_limit 控制图级步数上限,
        # 与 create_agent 内部的 remaining_steps(ManagedValue)共同防死循环
        config={"configurable": {"thread_id": thread_id}, "recursion_limit": 25},
    )
    traj = []
    for m in result["messages"]:
        if isinstance(m, AIMessage) and m.tool_calls:
            traj.append({"role": "ai", "tools": [t["name"] for t in m.tool_calls]})
        elif isinstance(m, ToolMessage):
            traj.append({"role": "tool", "id": m.tool_call_id})
        elif isinstance(m, AIMessage):
            traj.append({"role": "ai", "content": m.content})
    return {"output": result["messages"][-1].content, "trajectory": traj}

def tool_coverage_assertion(run, example) -> dict:
    """断言:预期工具集合被完整调用。"""
    expected = set(example.outputs["expected_tools"])   # 如 {"search", "calc"}
    actual = {t for step in run["trajectory"]
              if step["role"] == "ai" for t in step.get("tools", [])}
    ok = expected.issubset(actual)
    return {"key": "tool_coverage", "score": 1.0 if ok else 0.0,
            "comment": f"expected {expected}, got {actual}"}

def no_deadloop_assertion(run, example) -> dict:
    """断言:步数不超过上限(防死循环)。"""
    steps = len([s for s in run["trajectory"] if s["role"] in ("ai", "tool")])
    ok = steps <= example.metadata.get("max_steps", 12)
    return {"key": "bounded_steps", "score": 1.0 if ok else 0.0,
            "comment": f"steps={steps}"}

要点 :断言类评估不花 LLM 钱、可进 CI、毫秒级,应该覆盖所有"能写成规则的"质量点。把 Judge 留给那些必须靠语义判断的部分。


六、方法论二:LLM-as-Judge(轨迹级评估)

当"对不对"需要语义理解时,用另一个(更强的)LLM 当评委。关键是结构化输出 + 明确 rubric,避免"请给个分数"这种模糊指令。
评分体系 Judge LLM 评估器 评分体系 Judge LLM 评估器 #mermaid-svg-y3h35ZdWGVeBpdyt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y3h35ZdWGVeBpdyt .error-icon{fill:#552222;}#mermaid-svg-y3h35ZdWGVeBpdyt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y3h35ZdWGVeBpdyt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y3h35ZdWGVeBpdyt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y3h35ZdWGVeBpdyt .marker.cross{stroke:#333333;}#mermaid-svg-y3h35ZdWGVeBpdyt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y3h35ZdWGVeBpdyt p{margin:0;}#mermaid-svg-y3h35ZdWGVeBpdyt .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-y3h35ZdWGVeBpdyt text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-y3h35ZdWGVeBpdyt .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-y3h35ZdWGVeBpdyt .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-y3h35ZdWGVeBpdyt .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-y3h35ZdWGVeBpdyt .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-y3h35ZdWGVeBpdyt #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-y3h35ZdWGVeBpdyt .sequenceNumber{fill:white;}#mermaid-svg-y3h35ZdWGVeBpdyt #sequencenumber{fill:#333;}#mermaid-svg-y3h35ZdWGVeBpdyt #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-y3h35ZdWGVeBpdyt .messageText{fill:#333;stroke:none;}#mermaid-svg-y3h35ZdWGVeBpdyt .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-y3h35ZdWGVeBpdyt .labelText,#mermaid-svg-y3h35ZdWGVeBpdyt .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-y3h35ZdWGVeBpdyt .loopText,#mermaid-svg-y3h35ZdWGVeBpdyt .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-y3h35ZdWGVeBpdyt .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-y3h35ZdWGVeBpdyt .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-y3h35ZdWGVeBpdyt .noteText,#mermaid-svg-y3h35ZdWGVeBpdyt .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-y3h35ZdWGVeBpdyt .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-y3h35ZdWGVeBpdyt .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-y3h35ZdWGVeBpdyt .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-y3h35ZdWGVeBpdyt .actorPopupMenu{position:absolute;}#mermaid-svg-y3h35ZdWGVeBpdyt .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-y3h35ZdWGVeBpdyt .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-y3h35ZdWGVeBpdyt .actor-man circle,#mermaid-svg-y3h35ZdWGVeBpdyt line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-y3h35ZdWGVeBpdyt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 问题 + 实际轨迹 + 参考答案 + 评分标准(rubric) with_structured_output 结构化打分 TrajGrade{score, reasoning, deviation} 归一化分数 + 偏离说明

python 复制代码
from pydantic import BaseModel, Field
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate

class TrajGrade(BaseModel):
    score: int = Field(ge=1, le=5, description="轨迹质量 1-5")
    reasoning: str = Field(description="扣分/加分理由")
    deviation: str = Field(description="与最优路径的偏离点")
    hallucination: bool = Field(description="是否出现事实性错误")

# v1:Judge 同样用 init_chat_model 统一初始化,选更强的模型
llm_strong = init_chat_model("anthropic:claude-opus-4-7", temperature=0)
judge = llm_strong.with_structured_output(TrajGrade)

JUDGE_PROMPT = ChatPromptTemplate.from_messages([
    ("system", "你是严格的 Agent 轨迹评审专家。只依据给定信息客观打分,禁止臆测。"),
    ("human", """
用户问题: {question}
参考最优轨迹: {reference}
实际轨迹: {trajectory}
最终答案: {answer}
请按标准评分:
- 5: 路径最优、无冗余、答案正确忠实
- 3: 路径有冗余或绕路,但答案正确
- 1: 路径严重错误/死循环/答案错误
"""),
])

def trajectory_judge(run, example) -> dict:
    grade: TrajGrade = (JUDGE_PROMPT | judge).invoke({
        "question": example.inputs["question"],
        "reference": example.outputs["reference_trajectory"],
        "trajectory": run["trajectory"],
        "answer": run["output"],
    }).model_dump()
    return {
        "key": "trajectory_quality",
        "score": grade.score / 5.0,
        "comment": f"{grade.reasoning} | 偏离: {grade.deviation} | 幻觉: {grade.hallucination}",
    }

Judge 防偏置三原则

  1. rubric 写死:分数档位必须有明确行为锚点,不能让模型"自由发挥"。
  2. 参考轨迹比对:给 Judge 看"理想路径",它才判得出"有没有绕路",否则只会无脑给高分。
  3. 关键项独立打分:把"幻觉/忠实度/合规"拆成独立布尔/分数字段,避免被"答案通顺"带偏整体分。

专家提醒 :LLM-as-Judge 自身也会错。生产环境务必保留人工抽检 + 黄金校准集(一批你人工标过分的样本),定期量 Judge 与你人工打分的一致性(如 Cohen's κ),κ 低于 0.6 就该换 prompt 或换模型。


七、方法论三:参考轨迹对比与回归

Agent 迭代最怕"改好 A 坏 B 却不知道"。回归测试就是答案:把每个历史 bug / 边界 case 固化成数据集里的一条,每次发版自动重跑。

python 复制代码
# 把一次真实故障固化为回归用例(LangSmith dataset 中一条 example)
example = {
    "inputs":  {"question": "帮我查一下订单 O123 的物流并总结"},
    "outputs": {
        "expected_tools": ["query_order", "query_logistics", "summarize"],
        "reference_trajectory": ["ai→query_order", "tool", "ai→query_logistics",
                                  "tool", "ai→summarize", "tool", "ai→final"],
    },
    "metadata": {"case_type": "regression", "bug_id": "AG-217", "max_steps": 12},
}

回归集的维护纪律:

  • 每次线上故障 → 必加一条回归用例
  • 每条用例带 case_type:功能 / 边界 / 回归 / 安全,便于按维度看趋势。
  • 版本间对比 :用 experiment_prefix 区分 agent-v2.1 / agent-v2.2,在 LangSmith 里直接 diff 分数分布。

八、测试类型

#mermaid-svg-czaGWLND8ShaE56c{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-czaGWLND8ShaE56c .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-czaGWLND8ShaE56c .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-czaGWLND8ShaE56c .error-icon{fill:#552222;}#mermaid-svg-czaGWLND8ShaE56c .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-czaGWLND8ShaE56c .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-czaGWLND8ShaE56c .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-czaGWLND8ShaE56c .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-czaGWLND8ShaE56c .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-czaGWLND8ShaE56c .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-czaGWLND8ShaE56c .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-czaGWLND8ShaE56c .marker{fill:#333333;stroke:#333333;}#mermaid-svg-czaGWLND8ShaE56c .marker.cross{stroke:#333333;}#mermaid-svg-czaGWLND8ShaE56c svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-czaGWLND8ShaE56c p{margin:0;}#mermaid-svg-czaGWLND8ShaE56c .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-czaGWLND8ShaE56c .cluster-label text{fill:#333;}#mermaid-svg-czaGWLND8ShaE56c .cluster-label span{color:#333;}#mermaid-svg-czaGWLND8ShaE56c .cluster-label span p{background-color:transparent;}#mermaid-svg-czaGWLND8ShaE56c .label text,#mermaid-svg-czaGWLND8ShaE56c span{fill:#333;color:#333;}#mermaid-svg-czaGWLND8ShaE56c .node rect,#mermaid-svg-czaGWLND8ShaE56c .node circle,#mermaid-svg-czaGWLND8ShaE56c .node ellipse,#mermaid-svg-czaGWLND8ShaE56c .node polygon,#mermaid-svg-czaGWLND8ShaE56c .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-czaGWLND8ShaE56c .rough-node .label text,#mermaid-svg-czaGWLND8ShaE56c .node .label text,#mermaid-svg-czaGWLND8ShaE56c .image-shape .label,#mermaid-svg-czaGWLND8ShaE56c .icon-shape .label{text-anchor:middle;}#mermaid-svg-czaGWLND8ShaE56c .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-czaGWLND8ShaE56c .rough-node .label,#mermaid-svg-czaGWLND8ShaE56c .node .label,#mermaid-svg-czaGWLND8ShaE56c .image-shape .label,#mermaid-svg-czaGWLND8ShaE56c .icon-shape .label{text-align:center;}#mermaid-svg-czaGWLND8ShaE56c .node.clickable{cursor:pointer;}#mermaid-svg-czaGWLND8ShaE56c .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-czaGWLND8ShaE56c .arrowheadPath{fill:#333333;}#mermaid-svg-czaGWLND8ShaE56c .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-czaGWLND8ShaE56c .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-czaGWLND8ShaE56c .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-czaGWLND8ShaE56c .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-czaGWLND8ShaE56c .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-czaGWLND8ShaE56c .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-czaGWLND8ShaE56c .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-czaGWLND8ShaE56c .cluster text{fill:#333;}#mermaid-svg-czaGWLND8ShaE56c .cluster span{color:#333;}#mermaid-svg-czaGWLND8ShaE56c div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-czaGWLND8ShaE56c .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-czaGWLND8ShaE56c rect.text{fill:none;stroke-width:0;}#mermaid-svg-czaGWLND8ShaE56c .icon-shape,#mermaid-svg-czaGWLND8ShaE56c .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-czaGWLND8ShaE56c .icon-shape p,#mermaid-svg-czaGWLND8ShaE56c .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-czaGWLND8ShaE56c .icon-shape .label rect,#mermaid-svg-czaGWLND8ShaE56c .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-czaGWLND8ShaE56c .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-czaGWLND8ShaE56c .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-czaGWLND8ShaE56c :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 单元测试 节点级

大量·快·确定性
集成测试 图级

中量
端到端 黄金集

中量·慢
混沌 / 对抗

少量·高价值

① 单元测试(节点级) :把每个 node 当纯函数测。create_agent 的扩展机制是中间件(middleware) ------before_model / after_model / wrap_tool_call 等 Hook 把"消息裁剪、历史摘要、工具错误兜底"抽成了可独立单测的单元,这正是 v1 提升可测性的关键设计。

python 复制代码
def test_router_node():
    state = {"messages": [HumanMessage("查下天气")], "remaining_steps": 10}
    out = route_node(state)
    assert out["next"] in ("research", "action", "casual")

要点:node 里不要有外部副作用(DB 写、网络),用 mock 注入,保证单测快且可重复。

② 集成测试(图级):编译后的图跑通,验证边/条件边/降级分支。

python 复制代码
def test_deadloop_fallback():
    # 构造一个会触发循环上限的输入
    res = app.invoke(loop_trigger_input, config={"recursion_limit": 25})
    assert "人工介入" in res["messages"][-1].content  # 验证降级分支

③ 端到端(黄金集) :见第九节 evaluate 全流程。

④ 混沌 / 对抗测试:主动制造失败,验证韧性。

python 复制代码
import random
class FlakyTool:
    """故障注入工具:按比例随机抛错,验证 Agent 重试/回退。"""
    def __init__(self, fail_rate: float = 0.4):
        self.fail_rate = fail_rate
    def __call__(self, query: str) -> str:
        if random.random() < self.fail_rate:
            raise RuntimeError("injected fault: downstream unavailable")
        return real_search(query)

# 把 FlakyTool 注入图,跑黄金集,统计 task_success_rate 跌幅
# 跌幅 > 阈值 → 容错设计不合格

专家判断 :绝大多数团队只做 ①②,跳过 ④。但生产环境的工具一定会超时、限流、返回脏数据------混沌测试是唯一能在上线前暴露"容错是纸糊的"的手段。


九、工具链实战:用 LangSmith 构建评估流水线

LangSmith 是 LangChain 生态里把"数据集 + 执行 + 多评估器 + 实验对比"串起来的标准工具。下面是一套可直接落地的流水线。

python 复制代码
from langsmith import Client, evaluate

client = Client()

def predict(inputs: dict) -> dict:
    """evaluate 要求的 target:输入 dataset 的 inputs,返回 outputs。"""
    return run_and_capture(inputs["question"], thread_id=inputs.get("thread_id", "eval"))

# 评估器清单:确定性断言 + 语义 Judge 并行
evaluators = [tool_coverage_assertion, no_deadloop_assertion, trajectory_judge]

experiment = evaluate(
    predict,
    data="agent_golden_v1",          # LangSmith 中的数据集名
    evaluators=evaluators,
    experiment_prefix="agent-v2.2",  # 用于版本间对比
    metadata={"model": "gpt-5", "framework": "langchain-1.0", "recursion_limit": 25},
    max_concurrency=4,
)

运行后你会得到:每条用例的多维分数、整体均值/分布、与历史 experiment 的 diff。这就是回答"这版比上版好在哪"的硬证据。

接 CI 质量门禁

python 复制代码
# 在门禁里读取评估结果,低于阈值则阻断合入
stats = experiment._summary_stats  # 或解析 experiment.results
if stats["trajectory_quality"]["mean"] < 0.8:
    raise SystemExit("质量门禁未通过:trajectory_quality 均值 < 0.8")
if stats["tool_coverage"]["mean"] < 0.95:
    raise SystemExit("工具覆盖不足")

9.1 LangSmith 的三大评估增强

LangSmith 在 v1 周期内(2025 下半年)新增了多项与 Agent 强相关的评估能力,建议直接复用:

  1. 多轮评估(Multi-turn Evals) :针对"端到端多轮交互"而非单轮输入输出评估。对对话型 / 多步 Agent 尤其关键------它能评估整段会话是否最终达成目标,而非只看最后一句。
  2. 组合评估器(Composite Evaluators,需 langsmith>=0.4.29 :把多个评估器的分数按权重合成单一分数(加权平均 / 加权和)。在 UI 中可对数据集或 Tracing Project 配置;也可在 SDK 中用自定义聚合函数把 tool_coverage / bounded_steps / trajectory_quality 合成一个"综合质量分"用于门禁。
  3. 预置评估器 & LangSmith Engine:内置 Trajectory / Security / Quality / Safety 等模板,工作区级复用;LangSmith Engine 还会自动监控生产 trace、检测问题并给出修复建议。

补充工具选型

场景 工具 说明
Agent/LLM 全流程 LangSmith 数据集 + 多评估器 + 实验对比 + 多轮评估 + 组合评估器,LangChain 生态零成本
RAG 子模块质量 Ragas Context Precision/Recall、Faithfulness 等指标
独立 Agent 评测 DeepEval / Giskard 内置大量 agentic 评估指标与对抗测试
本地无平台 自定义 evaluate 脚本 init_chat_model(...).with_structured_output(...) 当 Judge,JSON 存结果,门槛低

十、在线评估与持续监控闭环

离线评估是"发布前快照",在线评估才是"真实体温"。二者必须形成闭环:
#mermaid-svg-XO71Pc186CMU98qT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XO71Pc186CMU98qT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XO71Pc186CMU98qT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XO71Pc186CMU98qT .error-icon{fill:#552222;}#mermaid-svg-XO71Pc186CMU98qT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XO71Pc186CMU98qT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XO71Pc186CMU98qT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XO71Pc186CMU98qT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XO71Pc186CMU98qT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XO71Pc186CMU98qT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XO71Pc186CMU98qT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XO71Pc186CMU98qT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XO71Pc186CMU98qT .marker.cross{stroke:#333333;}#mermaid-svg-XO71Pc186CMU98qT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XO71Pc186CMU98qT p{margin:0;}#mermaid-svg-XO71Pc186CMU98qT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XO71Pc186CMU98qT .cluster-label text{fill:#333;}#mermaid-svg-XO71Pc186CMU98qT .cluster-label span{color:#333;}#mermaid-svg-XO71Pc186CMU98qT .cluster-label span p{background-color:transparent;}#mermaid-svg-XO71Pc186CMU98qT .label text,#mermaid-svg-XO71Pc186CMU98qT span{fill:#333;color:#333;}#mermaid-svg-XO71Pc186CMU98qT .node rect,#mermaid-svg-XO71Pc186CMU98qT .node circle,#mermaid-svg-XO71Pc186CMU98qT .node ellipse,#mermaid-svg-XO71Pc186CMU98qT .node polygon,#mermaid-svg-XO71Pc186CMU98qT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XO71Pc186CMU98qT .rough-node .label text,#mermaid-svg-XO71Pc186CMU98qT .node .label text,#mermaid-svg-XO71Pc186CMU98qT .image-shape .label,#mermaid-svg-XO71Pc186CMU98qT .icon-shape .label{text-anchor:middle;}#mermaid-svg-XO71Pc186CMU98qT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XO71Pc186CMU98qT .rough-node .label,#mermaid-svg-XO71Pc186CMU98qT .node .label,#mermaid-svg-XO71Pc186CMU98qT .image-shape .label,#mermaid-svg-XO71Pc186CMU98qT .icon-shape .label{text-align:center;}#mermaid-svg-XO71Pc186CMU98qT .node.clickable{cursor:pointer;}#mermaid-svg-XO71Pc186CMU98qT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XO71Pc186CMU98qT .arrowheadPath{fill:#333333;}#mermaid-svg-XO71Pc186CMU98qT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XO71Pc186CMU98qT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XO71Pc186CMU98qT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XO71Pc186CMU98qT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XO71Pc186CMU98qT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XO71Pc186CMU98qT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XO71Pc186CMU98qT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XO71Pc186CMU98qT .cluster text{fill:#333;}#mermaid-svg-XO71Pc186CMU98qT .cluster span{color:#333;}#mermaid-svg-XO71Pc186CMU98qT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XO71Pc186CMU98qT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XO71Pc186CMU98qT rect.text{fill:none;stroke-width:0;}#mermaid-svg-XO71Pc186CMU98qT .icon-shape,#mermaid-svg-XO71Pc186CMU98qT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XO71Pc186CMU98qT .icon-shape p,#mermaid-svg-XO71Pc186CMU98qT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XO71Pc186CMU98qT .icon-shape .label rect,#mermaid-svg-XO71Pc186CMU98qT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XO71Pc186CMU98qT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XO71Pc186CMU98qT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XO71Pc186CMU98qT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生产环境
Trace 采样
LLM Judge 批量评估
异常归因
回归测试集更新
下一轮离线评估
告警 / 人工抽检

落地要点:

  • 采样评估:全量 Judge 太贵,按 1%~5% 采样 + 高风险 case(长轨迹/高成本/低置信)全量评估。
  • 用户信号回流:点赞/点踩、人工修正、中途退出,都是强标签,直接进回归集。
  • 告警联动可观测性:当某节点成功率或轨迹质量周环比下跌超阈值,自动告警并定位到具体节点(与可观测性体系打通)。
  • 流式监控节点名变更 :若用 app.stream(..., stream_mode="updates") 实时观测轨迹,注意 v1 中模型节点名已由旧版的 agent 改为 model------监控订阅与日志解析需同步更新。

总结

Agent 的可靠性是测出来的,不是调出来的。本文围绕"把概率系统的质量翻译成可比较、可回归、可门禁的数字"这一核心,给出一套分层、多维、可落地的评估与测试体系:

  • 评估为什么难:非确定性、路径组合爆炸、软故障(自信地错)、多目标冲突,使传统"输入确定→断言相等"的测试彻底失效。
  • 三层时间线:离线(单元/集成/端到端/回归)、在线(Trace 采样 + Judge 监控 + 用户反馈)、韧性(混沌/对抗注入),缺一不可。
  • 五个维度:任务层、轨迹层、质量层、成本层、安全层------答案对不对只是冰山一角,一个"看起来对"的 Agent 可能走了 23 步、烧了 8 万 Token。
  • 三类方法论:① 可执行断言(零成本确定性评估,作第一道防线);② LLM-as-Judge(结构化输出 + 写死 rubric + 参考轨迹比对,需黄金校准集防偏);③ 参考轨迹对比与回归(固化历史故障,防"改好 A 坏 B")。
  • 工程闭环:以 LangSmith 串起数据集 + 多评估器 + 实验对比 + CI 质量门禁,线上线下形成"采样评估 → 用户信号回流 → 异常自动进回归集"的持续优化闭环。

落到实践:上线前用一份覆盖功能/边界/回归/安全的黄金数据集,配确定性断言兜底 + 校准过的 Judge + CI 门禁,缺一项就是风险敞口。


本文为 Agent 工程化系列之一,聚焦评估与测试,全部示例基于 LangChain 1.0 / LangGraph 1.0。数据集构建、Judge 校准、混沌工程的具体踩坑可继续深入交流。

相关推荐
lzjava20243 小时前
LangChain Prompt提示词工程
前端·langchain·prompt
拾年2754 小时前
RAG 灵魂三问:你的文档是怎么"喂"给大模型的?
langchain·node.js·llm
陳陈陳4 小时前
LLM的"消化系统":一文讲透RAG文档加载与智能切割
数据库·langchain·llm
CS semi6 小时前
【LangChain篇章】@tool使用(创建+绑定+调用)
langchain
先吃饱再说13 小时前
一篇文章被切成 100 块还怎么保持语义:RAG 分块的底层逻辑
langchain·llm
新知图书20 小时前
多模态响应解析与后处理
人工智能·agent·多模态·ai agent·智能体
想你依然心痛21 小时前
HarmonyOS 6(API 23)实战:基于HMAF的「智链中枢」——PC端AI智能体多Agent协同编排与任务调度平台
人工智能·华为·harmonyos·智能体
BraveWang1 天前
【LangChain 1.x】06、Tool Calling|工具的四种定义方式与手动调用全流程
langchain
中微极客1 天前
LangChain vs LlamaIndex:RAG框架选型深度对比
人工智能·langchain