了解 Evaluations 框架
Meet the Evaluations framework
2026年6月10日
一句话判断
✅ 单元测试在 AI 时代正式破产,Evaluations 框架把“调 Prompt”从玄学变成了可量化的 CI/CD 工程。
这场 Session 讲了什么
写传统代码,输入 A 必然输出 B,XCTest 跑个断言就完事了。但接入 Foundation Models 后,模型是概率性的,同样的书评可能生成 3 个标签也可能生成 9 个。如果你还在用 XCTAssertEqual 测 AI 输出,你的 CI 会天天飘红。这场 Session 最值得关注的一件事,就是 Apple 终于给出了“概率性代码”的测试标准答案。
Apple 推出了 Evaluations 框架,直接和 Swift Testing 深度绑定。它不要求 100% 匹配,而是让你定义 Metric(指标)和 Evaluator(评估器),跑几千个 ModelSample,然后看聚合统计(比如“80% 的情况下标签数量在 3-8 个之间”)。
这对所有在 App 里塞了 AI 功能的开发者影响巨大。它意味着“评估驱动开发”(Evaluation-driven development)成为标配:改一句 Prompt 或 @Guide,跑一遍 Evaluation 看报告,像爬山一样优化模型表现,而不是靠肉眼看几个 Demo 就敢发版。
值得深挖的点
用 AI 测试 AI (Model Judge) 解开定性评估的死结
定量指标(比如标签字数、数量、是否包含特定分类)很好写,用代码算就行。但 AI 最恶心的是“定性”问题:标签数量对了,但标签内容是胡言乱语怎么办?以前只能靠人肉 Review,根本没法自动化。
Evaluations 框架给出了 ModelJudgeEvaluator。说白了,就是用一个“至少一样聪明”的模型(比如服务端的 Private Cloud Compute)来当裁判,给本地小模型的输出打分。这里有个极其精妙的设计细节:Apple 建议评分量表使用偶数个等级(比如 1 到 4 分),而不是 1 到 5 分。因为奇数量表会让模型倾向于选中间的“3分”和稀泥。偶数量表强迫裁判模型做出“偏上”或“偏下”的明确站队。配合 ScoreDimensions(把“质量”拆分为“相关性”和“实用性”),你能精确知道 AI 到底是没看懂文本,还是生成的词不适合做 UI 标签。
把“调 Prompt”从玄学变成 CI/CD 工程
以前调 Prompt 叫“炼丹”,改个词,肉眼看几个 case,感觉不错就上了。Session 里展示的 Hill-climbing(爬坡)循环彻底改变了这个工作流。
当你发现评估报告里“标签数量”不达标时,你不是去改 Prompt 里的那句“请生成 3 到 8 个标签”,而是直接在 Swift 代码里给 @Generable 结构体的属性加上 @Guide(.count(3...8))。把约束从自然语言下沉到强类型的代码层。改完代码,跑一遍 @Test(.evaluates),Xcode 会生成专属的 Evaluation Report,告诉你通过率从 50% 涨到了 100%。这种“修改约束 -> 跑批量样本 -> 看统计报告”的闭环,让 AI 功能的迭代终于能像普通业务逻辑一样接入 CI/CD,有了明确的质量护栏。
代码片段
1. 用 Swift Testing 跑 AI 评估
场景:把 AI 评估塞进日常的测试流,设定 80% 的通过率为及格线。
// 使用 Swift Testing 的 @Test 宏和 .evaluates trait
@Test("书评标签评估", .evaluates(evaluation, info: evaluationInfo))
func evaluateBookTagging() async throws {
let result = EvaluationContext.current.result
// 获取我们定义的 TagCount 指标
let rangeMetric = BookTagEvaluationTests.evaluation.tagCount
// 核心断言:期望标签数量合格的平均值大于等于 80%
#expect(result.aggregateValue(.mean(of: rangeMetric)) >= 0.8)
}
坑:别把优化目标设为 1.0 (100%),概率模型总有极端 case,设为 80%-95% 之间能让 CI 保持稳定。
2. 用 @Guide 进行硬约束 (Hill-climbing 的关键)
场景:发现模型生成的标签数量忽多忽少,用代码级约束代替 Prompt 里的自然语言哀求。
@Generable
struct BookTags: Codable {
// 别在 description 里写 "请生成 3 到 8 个",模型不一定听。
// 直接用 .count 修饰符,框架会在底层转化为强约束。
@Guide(description: "提取主题、流派和情绪的描述性标签", .count(3...8))
var tags: [String]
}
坑:自然语言 Prompt 约束是软性的,.count(3...8) 这种结构化约束是硬性的,优先使用后者来修复评估失败的 case。
3. 定义 Model Judge 进行定性打分
场景:标签数量对了,但内容全是废话。用另一个大模型来当裁判打分。
// 定义一个 1-4 分的定性指标
let tagQuality = Metric("TagQuality")
// 使用 ModelJudgeEvaluator,让裁判模型按维度打分
let judge = ModelJudgeEvaluator(
metric: tagQuality,
// 量表一定要用偶数(如 1...4),避免模型遇到模棱两可的情况时直接给中间分和稀泥。
scale: 1...4,
dimensions: [
ScoreDimension(name: "Relevance", description: "标签是否准确反映了书评内容"),
ScoreDimension(name: "Usefulness", description: "标签是否适合用于 App 的分类浏览")
],
judgeModel: .privateCloudCompute // 用云端更强的模型来评判端侧小模型
)
坑:单一分数(如“整体质量”)太宽泛,模型不知道你为什么给低分。一定要用 ScoreDimension 拆分成具体维度,方便看 rationale(裁判理由)来指导下一步修改。
最佳实践
- 已有项目的迁移策略:如果你现在是用 Playground 或者写个死循环
for跑几十次肉眼看 AI 输出,立刻停下来。挑出 20-30 个最具代表性的真实用户输入(包含极端 case),用ModelSample包装起来,写你的第一个Evaluation。 - 新项目的采用建议:采用“评估驱动开发”。在写
@Generable和 Prompt 之前,先定义好 Metric 和 Evaluator。把 Xcode 的 Evaluation Report 当作你的 Dashboard,不要凭直觉改代码,让报告里的 failing rationale 驱动你的修改。 - 实战中容易踩的坑:能用代码算的定量指标(字数、正则匹配、枚举值校验),绝对不要用 Model Judge。Model Judge 调用成本高且慢,好钢用在刀刃上,只用来评估“语气是否合适”、“总结是否遗漏关键情感”这类纯定性问题。
还有什么值得关注
- SampleGenerator 合成数据:手写几千个测试样本不现实,框架提供了
SampleGenerator,你可以喂给它 10 个种子样本,让它自动合成包含不同长度、流派、甚至夹杂个人情绪的多样化数据集。 - Xcode 专属 Evaluation Report:这不是普通的测试日志。在 Xcode 的 Report Navigator 里,你可以直接看到每个失败样本的 Prompt、模型完整响应以及裁判模型的 Rationale(打分理由),找 Bug 的效率比看 Console 高几个数量级。
- 聚合统计的丰富度:
MetricsAggregator不仅能算平均值(mean),还能算标准差(standard deviation)和方差(variance),帮你发现模型输出是不是“偶尔极好,偶尔极差”的不稳定状态。