返回学习路线/阶段 06 · 从能跑到可靠交付
LESSON 16 / 18

用评估代替感觉

建立离线测试集,同时检查结果、轨迹、成本和延迟。

35 分钟 · 含动手练习实战Evals回归测试

学完这一课,你将能够

  • 设计覆盖正常与异常场景的评估集
  • 把通过率与拒答率分开统计
  • 根据失败轨迹定位问题

学习目标

  • 设计有明确预期的评估样本。
  • 分别检查任务结果、工具行为和未知问题处理。
  • 根据失败轨迹定位问题,而不是凭感觉改提示词。

前置要求:完成至少一个工具或知识助手基础项目。 本节评估一个离线基线,不需要付费模型调用。

“看起来不错”为什么不够

一个精彩例子只能说明那一次输入表现不错。 改了提示词后,简单样本可能变好,少数重要的边界却退步。 保留固定评估集,才能比较版本变化,而不是比较记忆中的印象。

先定义任务成功是什么。 库存助手要检查金额和库存;知识助手要检查答案依据;研究助手要检查来源与覆盖范围。 不要把文字长度或礼貌程度当成任务完成率。

一个样本至少包含什么

{
  "id": "expense-deadline",
  "input": "报销应在多少天内提交?",
  "expected_status": "answered",
  "expected_fact": "30 天",
  "expected_source": "expense-v2"
}

固定规则适合检查结构、金额、工具名和引用 ID。 自然语言的事实支持和完整性可能需要人工标注或有明确评分标准的模型审核。 模型审核也会出错,应抽样核查,并避免让同一份提示词同时生成与无条件自评。

运行一次诚实的基线评估

下面故意放入一个词项检索基线的缺陷:只要出现“报销”就回答提交期限。 评估应该发现它无法处理“报销能买电脑吗”这类证据不足的问题。

保存为 evaluate.py 并运行:

import json

def baseline(question):
    if "报销" in question:
        return {"status": "answered", "text": "报销须在 30 天内提交。", "sources": ["expense-v2"]}
    return {"status": "abstained", "text": "现有资料无法回答。", "sources": []}

cases = [
    {"id": "deadline", "input": "报销应在多少天内提交?", "status": "answered", "fact": "30 天", "source": "expense-v2"},
    {"id": "unsupported", "input": "报销能买电脑吗?", "status": "abstained", "fact": None, "source": None},
    {"id": "unrelated", "input": "今天会下雨吗?", "status": "abstained", "fact": None, "source": None},
]

results = []
for case in cases:
    actual = baseline(case["input"])
    checks = {"status": actual["status"] == case["status"]}
    if case["fact"] is not None:
        checks["fact"] = case["fact"] in actual["text"]
    if case["source"] is not None:
        checks["source"] = case["source"] in actual["sources"]
    results.append({
        "id": case["id"], "passed": all(checks.values()),
        "checks": checks, "actual": actual,
        "expected_abstention": case["status"] == "abstained",
    })

passed = sum(item["passed"] for item in results)
unknown = [item for item in results if item["expected_abstention"]]
correct_abstentions = sum(item["actual"]["status"] == "abstained" for item in unknown)
print(json.dumps(results, ensure_ascii=False, indent=2))
print(f"样本通过:{passed}/{len(results)}")
print(f"未知问题正确弃答:{correct_abstentions}/{len(unknown)}")

预期总体通过 2/3,未知问题正确弃答 1/2。 这是脚本中三个教学样本的结果,不能推广为真实产品准确率。 失败样本比一条漂亮回答更有价值:它告诉你需要检查证据是否充分。

结果之外,还要评估轨迹

一个正确答案可能来自错误工具或碰巧猜中。 对工具助手记录调用名称、参数是否有效、失败次数和是否越过预算。 对 RAG 记录检索候选、实际使用证据和最终引用。 这样才能分清是检索问题、决策问题还是回答组织问题。

保留训练用样本之外的验证集

如果每次都针对同几条样本修改提示词,你可能只是记住了测试题。 留出一组不参与日常调试的验证样本,在准备交付时检查泛化表现。 样本需要覆盖正常、模糊、无答案、工具失败和尝试改变规则的输入。

真实模型具有变化性,对重要用例重复运行并记录配置。 统计成本和延迟时,至少报告样本数和运行环境,避免比较不可比的数字。

练习与验收

把样本增加到至少 12 条,并标注类别。 为每个失败写一句原因,再决定改检索、提示词、工具还是业务逻辑。

  • 能复现每条样本的实际结果。
  • 未知问题处理独立统计。
  • 每次改动保留评估版本与配置。
  • 报告中至少包含一个未解决的失败及其影响。

延伸阅读

下一课将这些结果与运行日志、预算和部署流程连接起来。

让这一课,真正成为你的收获

完成练习后标记完成,也可以随时回来复习。

笔记与进度保存在当前浏览器,无需登录

AgentStudy · Learn by building.以理解为起点,以作品为答案