第 9 章:评估、观测与调试
章节定位
前面几章已经把 LangChain、RAG、Tool Calling 和 LangGraph 的基本能力搭起来了。到了这一章,真正的问题不再是“能不能跑”,而是“为什么这样跑、哪里出错、效果到底好不好”。
这一章的目标,是让 AI 学习助手 从一个能工作的原型,变成一个可持续迭代、可定位问题、可做质量判断的工程系统。
配套示例
- 目录:
examples/chapter-09 - 入口:
examples/chapter-09/main.py - 依赖:
examples/chapter-09/requirements.txt - 运行:
cd examples/chapter-09 && python3 main.py
示例层级与边界
- 层级:
更真实的工程版 - 本章重点:建立评估、trace 和调试的工程习惯,重点是流程可重复、结果可比较。
- 不要误判:虽然它更接近真实工程实践,但仍是本地离线样例,不等于完整的线上观测与评测平台。
本章目标
学完这一章,你应该能做到:
- 分清“功能正确”和“效果足够好”是两件事
- 为
AI 学习助手建立最小可用的评估集 - 用日志和 trace 找到问题发生在链路的哪一步
- 理解为什么 LLM 应用不能只靠手工点几次就下结论
- 为后续公开部署和版本迭代建立基础
前置知识
你需要已经掌握:
- LangChain 的基本链式调用
- 基础 RAG 流程
- Tool Calling 和 Agent 的基本工作方式
- LangGraph 的节点和状态概念
如果前面章节还没完全吃透,这一章也可以先读。因为调试和评估本身就是帮助你反向理解系统的最好方式。
核心概念
1. LLM 应用为什么难调
传统程序的输出通常是确定的,输入一样,输出也一样。LLM 应用不是这样。即使输入不变,输出也可能因为模型随机性、检索内容变化、上下文长度、工具结果不同而变化。
这意味着:
- 你不能只看一次结果
- 你不能只看最终回答
- 你必须知道中间发生了什么
2. 评估、观测、调试的区别
这三个词经常混在一起,但实际职责不同:
- 评估是判断效果好不好
- 观测是记录系统运行时发生了什么
- 调试是根据记录去定位并修复问题
如果没有观测,调试只能靠猜。
如果没有评估,你不知道改动到底有没有变好。
如果没有调试能力,系统一复杂就无法维护。
3. 最小评估集
对于 AI 学习助手,最小评估集不需要上来就很大。你可以先准备 10 到 20 个固定问题,覆盖这些场景:
- 基础概念解释
- 课程内容总结
- 检索式问答
- 工具调用问题
- 边界问题和无答案问题
目的不是一次测完所有情况,而是建立一个稳定的回归基线。
4. Trace
Trace 指的是把一次请求从输入到输出的关键中间步骤完整记录下来,例如:
- 用户问题是什么
- 检索到了哪些文档
- 模型看到了哪些上下文
- 哪个工具被调用了
- 哪一步抛出了异常
对 LLM 应用来说,Trace 往往比单个日志更重要,因为问题常常出在“中间链路”而不是最终输出。
分模块讲解
9.1 为什么 AI 学习助手 不能只靠人工试用
一个教学型 AI 应用很容易出现一种错觉:只要你自己测几次觉得“差不多”,就以为系统可以上线了。
实际上,人工试用只能发现最明显的问题,发现不了下面这些情况:
- 某一类问题总是检索不到内容
- 某个 Prompt 改动让结构化输出不稳定
- 工具调用在长上下文下会退化
- 某些问题会反复触发幻觉答案
所以第一步不是优化模型,而是建立可重复的检查方式。
9.2 为课程项目建立评估维度
对 AI 学习助手,建议至少从四个维度看:
- 正确性:答案是否符合事实或课程内容
- 完整性:是否覆盖了问题要求的关键点
- 可解释性:是否能说明依据来自哪里
- 稳定性:多次运行结果是否波动过大
如果是 RAG 场景,还要额外看:
- 检索命中率
- 引用片段是否相关
- 回答是否真的基于检索内容
如果是 Agent 场景,还要额外看:
- 是否选择了合适工具
- 是否反复循环
- 是否在错误后能恢复
9.3 最小评估集怎么设计
一个实用的做法是把评估集拆成三层:
- 基础层:定义性问题,比如“什么是 Runnable”
- 实战层:带步骤的问题,比如“如何搭建 RAG”
- 边界层:故意测试系统失败能力,比如“我没给资料也要你回答课程细节”
每条评估样本最好包含:
idquestionexpected_pointsscenariopriority
这里不要求一开始就写复杂评分器,先把问题集整理清楚更重要。
9.4 观测什么
对于每一次请求,至少要记录这些信息:
- 输入问题
- 当前章节或模式
- 检索 query
- 检索结果摘要
- 最终回答
- 是否调用工具
- 是否报错
- 耗时
如果你后面接入 LangSmith 或类似工具,这些信息会被组织成 trace 页面,方便逐层展开查看。
9.5 调试的基本顺序
遇到问题时,不要直接改 Prompt。推荐顺序是:
- 先看输入是不是错的
- 再看检索内容对不对
- 再看上下文是否被截断
- 再看模型输出是否符合预期格式
- 最后才改 Prompt 或链路结构
这个顺序的好处是避免“盲目调参”。
最小示例说明
下面这个最小示例展示的是“固定评估集 + 逐条检查输出”的思路。它不追求复杂,但足够帮你建立第一个回归测试流程。
python
from dataclasses import dataclass
@dataclass
class EvalCase:
id: str
question: str
expected_points: list[str]
EVAL_CASES = [
EvalCase(
id="basic-01",
question="什么是 LangChain 的 Runnable?",
expected_points=["可组合", "可链式执行", "统一接口"],
),
EvalCase(
id="rag-01",
question="为什么 RAG 能降低幻觉?",
expected_points=["检索外部知识", "让答案基于资料", "减少纯生成猜测"],
),
]
def score_answer(answer: str, expected_points: list[str]) -> int:
score = 0
for point in expected_points:
if point in answer:
score += 1
return score
def run_eval(answer_fn):
results = []
for case in EVAL_CASES:
answer = answer_fn(case.question)
score = score_answer(answer, case.expected_points)
results.append((case.id, score, answer))
return results这个示例的关键点不是评分算法有多高级,而是你开始把“感觉不错”变成“可记录、可比较、可复查”。
本章实践
建议你为 AI 学习助手 完成下面三个任务:
任务 1:建立评估集
先选 10 个问题,覆盖:
- 前 4 章的基础概念
- 1 个 RAG 问题
- 1 个工具调用问题
- 1 个边界问题
任务 2:记录每次运行的 trace
至少记录:
- 问题
- 检索结果
- 最终回答
- 耗时
任务 3:找出 3 个真实问题
不要追求全对,先找出以下类型的问题:
- 哪些题回答不完整
- 哪些题回答偏题
- 哪些题在没有资料时会乱答
把它们写进 docs/faq/ 或单独的调试说明里。
常见坑
1. 只看最终回答,不看中间步骤
很多问题不是模型本身的问题,而是检索内容、上下文拼接或工具结果的问题。
2. 评估集太大
一开始就做几百题,通常只会增加维护成本。先做小而稳定的基线集。
3. 把一次成功当成稳定效果
LLM 应用需要多次运行观察波动,尤其是开放式问答。
4. 只评估“能不能答”,不评估“答得是否可靠”
对于课程学习助手,可靠性和可解释性和答案本身一样重要。
5. 没有版本意识
当 Prompt、检索策略或模型版本变化时,不记录版本就没法比较改动效果。
练习题
- 为
AI 学习助手写出 10 个评估问题,分别属于基础层、实战层和边界层。 - 设计一个你自己的回答评分规则,至少包含“正确性”和“完整性”两个维度。
- 选一个你前面章节的示例,列出它的输入、检索、输出和可能失败点。
- 想一想,如果用户问了课程范围外的问题,你应该如何记录和处理。
本章总结
这一章的核心不是某个具体框架,而是建立一种工程习惯:任何 AI 应用都要能被观察、能被比较、能被复盘。
对于 AI 学习助手 来说,这意味着你不再只是“做出一个回答器”,而是在做一个可以长期迭代的学习产品。只要这一点成立,后面的部署、扩展、公开传播才有基础。
学完本章,你现在应该会
- 为
AI 学习助手设计一组覆盖基础层、实战层和边界层的小型评估集 - 说明 trace、日志、评分规则分别解决什么问题
- 判断一个回答问题该从评估数据、中间链路还是版本变更去排查
- 把“感觉不错”转换成可记录、可比较的指标
最小验收 checklist
- [ ] 我已经列出一组最小评估问题,而不是只靠临时提问
- [ ] 我知道每次运行至少要记录哪些关键信息
- [ ] 我能举出 1 到 3 个真实失败案例,而不是只展示成功结果
- [ ] 我会在 Prompt、检索或模型变化时记录版本背景
建议你动手改一版
- 先写 10 个评估问题并分组,后面每改一次链路都跑同一组问题
- 给日志增加一个最小字段集,例如问题、检索结果、最终答案、耗时
- 选一个历史输出,补写一段失败分析,训练自己从链路而不是从感觉定位问题
卡住时先回看这里
- 如果你还在凭直觉判断效果,回看 chapter-09-evaluation-observability-debugging.md 里的
## 核心概念 - 如果你不知道最小评估该从哪里起步,回看 chapter-09-evaluation-observability-debugging.md 里的
## 本章实践 - 如果你只盯着最终回答不看中间过程,回看 chapter-09-evaluation-observability-debugging.md 里的
## 常见坑
下一步
如果你要继续完善这个项目,下一步就是把前九章的成果整合成一个完整可访问的公开版本。第 10 章会把文档站、示例代码和部署流程连起来,形成最终的交付闭环。