第01章 为什么评估难:LLM输出的不确定性与评估困境
第01章 为什么评估难:LLM输出的不确定性与评估困境
“你无法改进你无法测量的东西。” —— 彼得·德鲁克
绝大多数 Agent 开发者都遇到过同样的困境:改了 Prompt,感觉好了一点,但说不清楚到底好在哪里,下次改了会不会又退回去。这种感觉不是个人能力的问题,而是 LLM 评估本质上就是困难的。
1.1 传统软件 vs LLM 输出:为什么评估完全不同
# ====================================================
# 评估困境:LLM 输出的独特挑战
# ====================================================
"""
传统软件评估的世界:
input("1 + 1") → 期望: 2
如果输出 2 → ✅ PASS
如果输出 3 → ❌ FAIL
规则明确,答案唯一,评估自动化,100% 可重复。
LLM 输出的世界:
input("分析苹果公司的竞争优势") → 期望: ???
可能的好答案有无数个:
- "苹果有强大的生态系统、品牌溢价和供应链管理能力..."
- "苹果的护城河在于硬件软件服务的垂直整合..."
- "从波特五力模型看,苹果在买方议价能力上有显著优势..."
所有这些都可能是好答案,也都可能不够好,取决于:
- 评估者的背景和偏好
- 任务的具体使用场景
- 当前的质量标准
"""
import asyncio
import json
import statistics
from dataclasses import dataclass, field
from typing import List, Dict, Optional, Any, Callable
from openai import AsyncOpenAI
client = AsyncOpenAI()
# 演示 LLM 输出的随机性有多真实
async def demonstrate_nondeterminism():
"""
运行同一个 Prompt 5次,观察输出的差异
"""
print("=== LLM 输出的非确定性 ===\n")
print("问题:简短描述AI的主要优势(30字内)\n")
outputs = []
for i in range(5):
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "简短描述AI的主要优势(30字以内)"}],
max_tokens=80,
temperature=0.9, # 高温度,增加随机性
)
output = response.choices[0].message.content
outputs.append(output)
print(f"第{i+1}次: {output}")
# 分析差异
lengths = [len(o) for o in outputs]
print(f"\n字符数范围: {min(lengths)} - {max(lengths)}")
print(f"字符数标准差: {statistics.stdev(lengths):.1f}")
print("\n观察:即使是同一个问题,5次运行也产生了5个不同的答案。")
print("这意味着:你不能用'输出是否等于期望值'来判断好坏。")
asyncio.run(demonstrate_nondeterminism())
1.2 评估的三大陷阱
# ====================================================
# 常见评估错误:开发者最容易犯的三个错误
# ====================================================
"""
陷阱 #1:用你自己当裁判
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
错误做法:
改了 Prompt 之后,自己读一读,感觉好多了 → 发布
问题:
你是这个系统的创建者,对它有情感偏见
你知道你想要什么,所以你"看到"了你想看到的
你的偏好不一定代表用户的偏好
你无法评估每个可能的输入场景
更好的做法:
制定评估标准(写下来,不是记在脑子里)
让没有参与开发的人评估
或者用 LLM-as-Judge(第4章详细讲)
陷阱 #2:在太少的样本上测试
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
错误做法:
改了 Prompt,测试了5个例子,都通过了 → 发布
问题:
5个例子无法代表用户的真实分布
你很可能选了你知道能成功的例子(确认偏误)
边缘情况(长输入、模糊问题、特殊格式)通常不在这5个里
更好的做法:
建立最少50个案例的测试集(覆盖常见+边缘)
测试集要从真实用户数据中采样,不是手工构造
陷阱 #3:只测最新版本,不做对比
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
错误做法:
新 Prompt 跑完,80%的案例通过 → 发布(感觉不错)
问题:
如果旧 Prompt 也能通过85%,那新版本是退步
如果某一类案例从100%通过降到60%,你不知道
更好的做法:
始终与 baseline 比较(旧版本)
按类别分析,不只看总体分数
A/B 测试确认提升(第6章)
"""
@dataclass
class EvaluationPitfall:
"""记录一个评估陷阱"""
name: str
description: str
symptom: str # 怎么发现自己犯了这个错误
fix: str # 修复方法
COMMON_PITFALLS = [
EvaluationPitfall(
name="主观裁判偏误",
description="用自己的主观感受代替系统性评估",
symptom="当被问到'你的评估标准是什么'时,无法给出书面的、可量化的答案",
fix="在动手评估之前,先把评估维度和打分标准写成文档",
),
EvaluationPitfall(
name="小样本幸存者偏误",
description="在少量手工构造的案例上测试,忽略了真实分布",
symptom="测试案例都是自己写的,而不是从真实用户数据中采样的",
fix="从生产日志中随机采样100个真实案例,并覆盖边缘情况",
),
EvaluationPitfall(
name="无基线比较",
description="只知道当前版本的绝对分数,不知道相对上一版本的变化",
symptom="说不出来新版本比旧版本好多少,或在哪些维度退步了",
fix="每次评估都和 baseline 版本同时运行,做差异分析",
),
EvaluationPitfall(
name="指标与业务脱钩",
description="测量的指标和用户真正关心的价值无关",
symptom="技术指标很好,但用户投诉没有减少",
fix="从业务结果倒推,问'什么指标的提升意味着用户更满意'",
),
]
def print_pitfall_analysis():
print("\n=== Agent 评估常见陷阱 ===\n")
for i, pitfall in enumerate(COMMON_PITFALLS, 1):
print(f"陷阱 #{i}: {pitfall.name}")
print(f" 问题: {pitfall.description}")
print(f" 发现信号: {pitfall.symptom}")
print(f" 解决方案: {pitfall.fix}")
print()
print_pitfall_analysis()
1.3 评估的四个层次
# ====================================================
# 评估层次:从"能运行"到"持续改进"
# ====================================================
"""
层次1:冒烟测试(Smoke Test)
验证 Agent 基本上不会崩溃
问题:输入进去了,有输出吗?格式对吗?
典型指标:成功率、格式合规率
层次2:质量评估(Quality Evaluation)
验证 Agent 的输出质量达到标准
问题:输出准确吗?有用吗?符合要求吗?
典型指标:准确率、相关性得分、满足度评分
层次3:对比评估(Comparative Evaluation)
比较不同版本或配置
问题:A 比 B 好吗?好多少?在哪些场景下好?
典型指标:胜率、改善幅度、按类别分析
层次4:持续监控(Continuous Monitoring)
在生产中实时追踪质量变化
问题:今天的质量和上周一样吗?有没有退化?
典型指标:质量趋势、退化告警、用户满意度
"""
@dataclass
class EvaluationLevel:
level: int
name: str
question: str
metrics: List[str]
when_to_use: str
minimum_sample: int
EVALUATION_LEVELS = [
EvaluationLevel(
level=1,
name="冒烟测试",
question="Agent 基本能运行吗?",
metrics=["成功率", "格式合规率", "超时率"],
when_to_use="每次代码变更后,作为 CI 门禁",
minimum_sample=20,
),
EvaluationLevel(
level=2,
name="质量评估",
question="输出质量达到标准了吗?",
metrics=["准确率", "相关性分(1-5)", "完整性分(1-5)", "有害内容率"],
when_to_use="每次发布前,和用户看到新版本之前",
minimum_sample=100,
),
EvaluationLevel(
level=3,
name="对比评估",
question="新版本比旧版本好吗?",
metrics=["对比胜率", "改善幅度", "回退率", "分类别表现"],
when_to_use="评估 Prompt 改进、模型升级、参数调整的效果",
minimum_sample=200,
),
EvaluationLevel(
level=4,
name="持续监控",
question="生产中的质量是否稳定?",
metrics=["实时质量分", "退化告警", "用户满意度", "问题分类"],
when_to_use="系统上线后,持续运行",
minimum_sample=500,
),
]
def print_evaluation_roadmap():
print("\n=== Agent 评估层次路线图 ===\n")
for level in EVALUATION_LEVELS:
print(f"层次 {level.level}:{level.name}")
print(f" 核心问题: {level.question}")
print(f" 关键指标: {', '.join(level.metrics)}")
print(f" 使用时机: {level.when_to_use}")
print(f" 最小样本量: {level.minimum_sample}")
print()
print("建议起步策略:")
print(" 第1周: 建立层次1(冒烟测试)→ 加入 CI,每次提交自动运行")
print(" 第2周: 建立层次2(质量评估)→ 定义评分标准,建立基准")
print(" 第4周: 建立层次3(对比评估)→ 每次改进都和上一版本比较")
print(" 上线后: 建立层次4(持续监控)→ 生产质量的实时追踪")
print_evaluation_roadmap()
本章小结
-
LLM 输出的非确定性是评估困难的根本原因:同一个输入,不同时间运行会得到不同答案,而且多个不同答案都可能同样"正确"。这就是为什么不能像传统软件那样用"是否等于期望值"来评估。
-
主观感受是评估中最危险的成分:开发者对自己的系统有情感偏见,容易看到自己想看到的。在动手评估之前,先把评估标准写成文档——如果写不出来,说明标准还不清晰。
-
没有基线的评估没有意义:知道当前版本得了 80 分没有意义,除非你知道上一个版本得了多少分。每次评估都必须同时运行基线版本作为对比。
-
评估要从业务目标出发,不能只看技术指标:BLEU 分高了,用户投诉可能还是一样多。正确的做法是先问"什么业务结果变好了意味着 Agent 在变好",然后倒推出技术指标。
-
评估是一个系统,不是一次行为:从冒烟测试、质量评估、对比评估到持续监控,不同层次的评估在不同时机发挥作用。建立完整的评估体系,而不是在出了问题后才想起来评估一下。
# 核心行动:今天就给你的 Agent 建立最基础的评估框架
def create_minimal_eval_framework(agent_func, test_cases: List[dict]) -> dict:
"""
最小可行的评估框架
test_cases: [{"input": "...", "expected_contains": ["关键词1", "关键词2"]}]
"""
results = []
for case in test_cases:
# 简单的关键词检查(入门级评估)
output = agent_func(case["input"])
passed = all(kw in output for kw in case.get("expected_contains", []))
results.append({"input": case["input"], "passed": passed})
pass_rate = sum(1 for r in results if r["passed"]) / len(results)
return {"pass_rate": pass_rate, "details": results}
本章提示词模板
【模板1:评估维度设计提示词】
我有一个 AI Agent,负责:{agent_description}
它的主要输出是:{output_type}
请帮我设计评估维度:
1. 对用户最重要的3个质量维度是什么(按重要性排序)?
2. 每个维度如何用1-5分量化?请给出每分值的具体描述
3. 有哪些是"一票否决"的硬性要求(满足才能发布)?
4. 我应该用哪些代表性案例来测试这些维度?
输出:一份可以直接发给标注员的评分手册
【模板2:评估计划制定提示词】
我准备对 Agent 进行正式评估,以下是背景:
当前状态:{current_state}(如:首次评估/版本对比/回归测试)
可用资源:{resources}(如:3天时间,无预算,只有1人)
最关心的问题:{key_question}(如:新Prompt是否真的更好)
请给出:
1. 在我的资源限制下,最务实的评估方案
2. 最小测试集规模(多少个案例才够)
3. 如何快速建立一个基线版本
4. 评估结束后,我应该如何根据结果做决策