Eval-Driven Development:AI时代的"测试驱动开发"
Eval-Driven Development:AI时代的"测试驱动开发"
1. 引言:AI开发的"布朗运动"困境
你改了一版Prompt,跑了一个Case,输出看起来不错,于是上线了。三天后用户投诉,你又改了一版,跑了同一个Case,"嗯这次好多了",又上线了。一周后另一批用户投诉。你在做工程吗?不,你在扔骰子。
这是当前大量LLM应用开发团队的真实写照。没有评测集就优化Prompt,跟没有地基就盖楼一样——迟早会塌。
问题的根源在于:传统软件开发的质量保障体系,在AI时代失效了。
传统测试的核心假设是确定性——同样的输入,必须产生同样的输出。一个单元测试可以是assert(add(1, 2) === 3),结果非对即错。
LLM的输出是概率性的——同样的Prompt,十次可能给你八种不同的回答。它没有"标准答案",只有"好"与"坏"的连续谱。
这种根本差异意味着:"跑通一次"在传统软件里能说明问题,在LLM应用里毫无意义。
于是,我们需要一套新的方法论——评估驱动开发(Eval-Driven Development,简称EDD)。
2. 什么是评估驱动开发(EDD)?
一句话定义:先建评测集,再迭代Prompt/模型/检索,每次改动都拿评测集回归验证。
EDD是LLM系统的"测试驱动开发"(TDD)。两者的核心思想完全一致——先定义"什么是对的",再去做。区别只在于度量方式从"通过/失败"变成了"评分/阈值"。
| 环节 | TDD(传统软件) | EDD(AI应用) |
|---|---|---|
| 第一步 | 先写测试用例 | 先建评测数据集 |
| 第二步 | 写代码让测试通过 | 写Prompt/调模型让评测分数上升 |
| 第三步 | 重构,确保测试仍通过 | 优化,确保评测不回归 |
| 核心纪律 | 不写测试不写代码 | 不建评测集不优化Prompt |
| 度量方式 | 通过率100% | 评测分数≥阈值 |
EDD解决的核心问题是:AI应用没有确定性输出,无法用传统断言验证,必须用一套可重复、可量化的评估体系来驱动开发。
3. EDD的核心要素
3.1 黄金数据集(Golden Dataset)
EDD依赖的测试数据不是越多越好,而是越精越好。
建议从100条左右的高质量样本开始,最多扩展到500条。原因是EDD的每次运行可能涉及LLM API调用,有成本和速度的约束。
更重要的是,研究表明1000条精心策划的示例足以让650亿参数模型的表现对齐到GPT-4的水平。质量远比数量重要。
黄金数据集应该包含:
- 理想案例:模型应该完美通过的典型场景
- 边缘案例:带噪音、格式混乱、边界条件的真实输入
3.2 评估指标(Metrics)
EDD的指标分为两类:
确定性指标:可以用代码硬编码检查。例如:
- 输出是否为合法JSON
- 是否包含某些关键词
- 字符数是否在限制范围内
主观质量指标:需要模型或人工评判。例如:
- 语气是否合适
- 内容是否有用
- 是否出现幻觉
关键在于:找到"什么对你用户来说算好" ,这件事没有人能替你回答,因为"这正是护城河的来源——那些别人不容易复制的、对你产品独有的洞察"。
3.3 评判者(Judge)
评估主观指标需要"评判者":
- 代码检查:快、免费、适合确定性规则
- 人工反馈:精度高、不可扩展,适合早期探索
- LLM-as-Judge:用另一个模型来评分,可扩展但有偏差风险
实践中通常组合使用:用代码检查做快速过滤,用LLM-as-Judge评估主观维度。
3.4 评估类型
EDD中的评估像传统测试一样分层:
- 单元评估:测试特定组件(如检索精度、工具调用准确率)
- 回归评估:端到端测试,每次改动后运行,确保整体性能不退化
4. 一个最小的可工作例子
假设我们在构建一个客服支持Agent,它需要根据公司政策回答用户问题。初始版本没有产品知识,只会根据LLM的通用知识"胡诌"。
步骤1:构建评测数据集
eval_data = [
{
"question": "How do I reset my password?",
"expected_response": "Go to the login page, click 'Forgot Password', enter your email, and follow the link in the email. The link expires after 24 hours."
},
{
"question": "What's your refund policy?",
"expected_response": "Full refunds are available within 30 days of purchase. After 30 days, we offer store credit. Refunds are processed in 5-7 business days."
},
{
"question": "Can I change my shipping address?",
"expected_response": "Go to Order History, select the order, and click 'Edit Shipping Address'. Changes can only be made before the order ships."
}
]
只有3条样本也可以启动,5条足以开始,但建议逐步扩展到覆盖更多边缘案例。
步骤2:定义评估器
使用MLflow的评分器,从三个维度评估:
from mlflow.genai.scorers import Correctness, RelevanceToQuery, Guidelines
# 正确性:回答是否匹配预期事实
correctness = Correctness()
# 相关性:回答是否针对问题
relevance = RelevanceToQuery()
# 政策遵守:是否给出具体步骤、时间线,不编造政策
support_policies = Guidelines(
name="support_policies",
guidelines=[
"Always include specific steps or actions the user can take",
"Include relevant timeframes, deadlines, or SLAs when applicable",
"Never make up policies -- only state facts from the company knowledge base"
]
)
步骤3:编写Agent(初版)
import openai
import mlflow
SYSTEM_PROMPT_V1 = "You are a customer support agent."
@mlflow.trace
def support_agent(question: str) -> str:
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": SYSTEM_PROMPT_V1},
{"role": "user", "content": question}
]
)
return response.choices[0].message.content
步骤4:运行评估
baseline_results = mlflow.genai.evaluate(
data=eval_data,
predict_fn=support_agent,
scorers=[correctness, relevance, support_policies]
)
print(baseline_results.metrics)
# 输出示例:
# {'correctness/mean': 0.2, 'relevance_to_query/mean': 1.0, 'support_policies/mean': 0.4}
初版Agent相关性不错,但正确性仅0.2——因为它不知道公司实际政策,胡编乱造。
步骤5:迭代改进
现在我们知道了问题所在,可以针对性地优化:给System Prompt加入公司政策知识库。
SYSTEM_PROMPT_V2 = """
You are a customer support agent for our company.
Follow these policies exactly:
- Password reset: Users must go to login page, click 'Forgot Password', enter email, link expires in 24 hours
- Refunds: Full refund within 30 days, store credit after 30 days, processed in 5-7 business days
- Shipping changes: Can edit in Order History before shipment
Never invent policies. Always give concrete steps and timeframes.
"""
重新运行同样的评估:
improved_results = mlflow.genai.evaluate(
data=eval_data, # 同一个数据集!
predict_fn=support_agent_improved,
scorers=[correctness, relevance, support_policies]
)
print(improved_results.metrics)
# 输出示例:
# {'correctness/mean': 0.9, 'relevance_to_query/mean': 1.0, 'support_policies/mean': 0.95}
正确性从0.2提升到0.9。同一个评测集告诉我们:这个改动确实有效,而且没有引起其他维度的退化。
步骤6:持续集成
将这个评估流程纳入CI/CD,每次PR自动运行:
# CI配置
mlflow.genai.evaluate(data=eval_data, predict_fn=support_agent)
if aggregate_score < 0.85:
exit(1) # 阻止合并
"绿色的评测套件就是发布的门禁" 。
5. EDD vs TDD vs SDD:对比与辨析
5.1 EDD vs TDD
TDD(测试驱动开发)的流程是"红灯→绿灯→重构":先写一个失败的测试,再写代码让它通过,最后重构保持通过。
EDD的流程是"低分→高分→优化":先运行评估看到低分,再改进Prompt/架构让分数提升,最后优化确保不退化。
| 维度 | TDD | EDD |
|---|---|---|
| 输出性质 | 确定性 | 概率性 |
| 验证方式 | 断言(对/错) | 评分(好/坏程度) |
| 测试数量 | 尽可能多 | 约100-500条精样本 |
| 执行成本 | 几乎免费 | 可能昂贵(LLM API调用) |
| 回归发现 | 自动发现崩溃 | 不跑评测不知道退化 |
| 改动影响 | 局部可控 | 改一个词可能全局雪崩 |
TDD是"确定性工程的纪律",EDD是"概率性工程的纪律" 。
5.2 EDD vs SDD
SDD在不同语境下有不同含义:
在传统软件工程中,SDD有时指规格说明驱动开发(Specification-Driven Development)——先写详细的需求规格说明书,再编码实现。这是一种"文档先行"的模式,强调"写清楚再动手"。
而EDD是"评估先行"——不是写文档,而是建立可自动运行的评估系统来定义"什么算做完了"。
两者的核心区别:
| 维度 | SDD(规格说明驱动) | EDD(评估驱动) |
|---|---|---|
| 先写什么 | 需求文档 | 评估数据集 |
| "完成"的定义 | 文档通过评审 | 评估分数达标 |
| 反馈周期 | 文档→实现→验证(长) | 评估→改进→评估(短) |
| 适用场景 | 需求相对稳定的系统 | 需求随AI能力演进的系统 |
另有说法称SDD为支持驱动开发(Support-Driven Development) ,强调开发与客户支持协作,但这不是主流定义。
5.3 与BDD/ATDD的关系
BDD(行为驱动开发)和ATDD(验收测试驱动开发)强调用业务语言写测试,促进业务方、开发、测试三方协作。
EDD可以视为BDD思想在AI领域的延续——只不过"行为示例"变成了"输入输出样本","验收测试"变成了"评估器"。EDD同样建议与领域专家一起定义"什么是好的输出",特别是识别那些只有专家才能发现的细微故障模式。
5.4 总结对比
| 方法论 | 核心驱动力 | 验证方式 | 适用场景 |
|---|---|---|---|
| TDD | 单元测试 | 断言通过/失败 | 确定性代码 |
| BDD/ATDD | 验收场景 | 业务用例验证 | 需求明确的业务系统 |
| SDD | 规格文档 | 文档评审 | 需求稳定的系统 |
| EDD | 评估数据集 | 评分≥阈值 | LLM/概率性系统 |
6. EDD的落地要点
6.1 什么时候使用EDD?
EDD并非万能,以下情况应该使用:
- 构建的AI应用已有足够的问题轮廓,能定义"做完了"长什么样
- 需要频繁迭代Prompt、切换模型或调整架构
- 有明确的失败模式和期望行为
以下情况可以跳过或延后:
- 还在探索阶段,团队都不知道"好的输出"是什么样,快速手动迭代比写评测更有价值
6.2 常见陷阱
1. 过拟合评测集
如果在构建功能时同时编写评测用例,可能不自觉写出"匹配当前Agent行为"而非"匹配理想行为"的用例。应该基于期望行为而非观察到的行为来写评测。
2. 评测标准过于严格或模糊
精确匹配会拒绝合理的同义表达。应使用基于结果的评判(检查状态、测试套件)或语义等价评判,而非字符串匹配。
3. 评测集太小
5条样本足以开始,但不足以可靠检测回归。应随着发现边缘案例不断扩充数据集。
6.3 成熟度阶梯
将EDD实践分为几个阶段,可以对照评估团队当前状态:
| 阶段 | 特征 | 评测覆盖率 |
|---|---|---|
| 凭感觉调Prompt | 没有评测在CI中运行 | 0% |
| 冒烟测试 | 对几个固定用例做简单断言 | <20% |
| 系统化EDD | 黄金数据集+多维度评估+CI门禁 | 核心路径覆盖 |
| 生产反馈闭环 | 生产样本自动采样、人工标注后加入数据集 | 持续增长 |
7. 结语:从"感觉工程"到"度量工程"
AI开发很容易陷入一种幻觉:模型能说会道,所以它在做"正确的事情"。但"看起来合理"和"实际正确"之间,横亘着一条叫做"可度量性"的鸿沟。
TDD让软件开发从"写完再测"变成了"测完再写",EDD让AI应用开发从"凭感觉调Prompt"变成了"用数据驱动决策" 。
EDD的核心不是"评测"本身,而是用评测驱动决策——每次改动Prompt、换模型、改架构,评测数据集都能告诉你:这件事是让产品变好了,还是变坏了?
没有评测集就优化AI应用,不是在做工程,是在扔骰子。
而扔骰子的人,没法赢得长期信任。