-
了解 Evaluations 框架
了解如何使用 Evaluations 框架来评估模型驱动的体验。在概率世界中,仅靠单元测试是不够的。探索如何定义指标、自动评估输出质量并汇总统计数据,以便确保由 AI 支持的功能在各个 Apple 平台上都能稳定可靠地运行。
章节
- 0:00 - Introduction
- 3:10 - Demo app Book Tacker: a manual evaluation
- 4:31 - Building your first evaluation
- 8:06 - Running the evaluation and reading the report
- 10:57 - Building robust datasets
- 14:20 - Refining metrics and evaluators
- 15:41 - Evaluation-driven development and hill-climbing
- 16:12 - Model judges: qualitative metrics
- 18:42 - Building a model judge
- 21:19 - Refining with score dimensions
- 23:45 - Reviewing dimension results
- 24:20 - Best practices
- 25:38 - Next steps
资源
-
搜索此视频…
你好 我是Yada 我是Rob 很高兴向大家介绍 Evaluations框架 这是一个全新框架 用于衡量智能功能的质量 让你能够自信地 发布App 去年我们推出了 Foundation Models框架 帮助你利用我们的端侧模型 为App添加智能功能 这些模型正是 Apple Intelligence的动力来源 使用生成式AI构建App功能 带来了新的测试挑战 因为相同的输入 可能产生不同的输出 这些模型打破了 软件测试的基本契约
以传统软件为例 特定输入 始终产生特定输出 你可以轻松用单元测试 来验证这种行为
可以保证相同输入 在任何设备上产生相同输出 包括你用户的设备
而对于智能软件 你无法依赖功能一致性 来验证行为 这意味着 单元测试是不够的 未经验证的行为 会削弱用户的信任 你的用户期望 App中的智能功能 像任何功能一样 安全 可信 可靠
发布一个 行为不可预测的功能 可能对App的声誉 造成不良影响
我们需要衡量 智能功能 了解它们如何 响应不同输入 既然功能测试无法 验证概率性行为 我们就需要一种 更健壮的新型测试
我们需要知道:我的App 产生意外结果的频率是多少 智能体走向意外路径来 生成答案的频率是多少 在什么情况下该功能 会产生不安全的结果 测试由生成式AI驱动的 智能功能所面临的挑战 正是我们构建 Evaluations框架 的根本原因
Evaluations框架 是一个灵活的系统 提供多种类型和协议 本视频将重点介绍 如何评估语言模型驱动的 智能功能 但你也可以评估 任何随机系统 例如分类器 和线性回归模型
Yada和我将带你了解 框架中的几种类型
我们将介绍数据加载 以及如何构建多样化数据集
用Evaluator和Metric 构建量化指标 并通过模型评判器 和评分维度精细化你的测量 以创建定性指标
在本视频中 你将开始使用Evaluations 构建好你的第一个Evaluation后 我们会向你展示 如何扩展该Evaluation 加入更多数据和测量指标 然后我们将教你如何用 简单的API构建强大的模型评判器 开始使用Evaluations吧
Yada和我正在开发一款 名为Book Tracker的App 我们都热爱读书 想要一款 管理藏书的App Yada刚添加了一个新功能 叫做BookTaggingService 它根据我们在Book Tracker中 写的书评自动为书籍打标签
我迫不及待想打开 Xcode试用一下
在BookTaggingService.swift中 添加一个#Playground宏
这是Yada在Book Tracker中 为"傲慢与偏见"添加的书评 说实话我自己也是书迷 来看看我们得到哪些标签
这是个不错的开始 但当我读到一些标签时 可以看到我们的服务 还需要做一些改进
9个标签比我预期的要多
我也不想把书名 作为标签
多词标签在UI中 会造成问题 所以也应该避免
来试试另一本书的书评 看看《德古拉》的效果
7个标签在我们预期范围内 仔细看一下
有些行为 是我希望看到更多的
它识别出了文学流派
以及一些能帮我 浏览更大书库的分类
好了 我们刚刚完成了 对该服务的第一次评估 我们创建了一份期望列表 并用人工判断 衡量服务的表现 每次评估都在衡量 智能功能的表现 与我们的期望相比如何
遗憾的是人工判断 无法规模化
但我们已经创建了 一种自动化和扩展评估的方式 你只需添加 import Evaluations 并实现Evaluation协议即可
来用代码构建一个Evaluation吧
我们从第一个期望开始 衡量服务是否生成 了正确数量的标签
构建和运行Evaluation 有五个步骤 定义你要测量的代码
然后定义你要 发送给代码的数据 接下来定义 你要进行的测量及方式
然后汇总你的测量结果 最后创建一个测试 来运行你的Evaluation
首先我们添加对 BookTaggingService的调用 并在subject(from:)方法 内部返回其输出
这些生成的标签 就是我们评估的主体
接下来定义我们将 输入给代码的样本数据
然后用ModelSample 包装相同的书评 就是我们之前在 #Playground中测试的那些 《傲慢与偏见》和《德古拉》 注意我们也定义了预期标签 这些是我们希望 服务返回的理想标签
现在是时候用Metric类型 来定义我们的测量了 我们添加一个名为"TagCount"的Metric 用于跟踪服务返回的 生成标签数量
我们需要一些东西 来测量生成的标签 Evaluator接收一个闭包 该闭包会传入服务 针对特定样本的输出 我们可以通过tags属性 的count来检查生成标签的数量
如果tags数组的长度 在3到8之间 我们从Evaluator返回 一个通过的Metric
否则返回失败的Metric
Evaluator每次 处理一个样本 但我们可以测量趋势 并寻找规律 在aggregateMetrics(using:)方法中 对所有样本进行汇总测量
我们来计算 服务生成正确数量 标签的平均次数 这样我们就能得出 服务行为正确的频率比例
好了 我们写完了 第一个Evaluation 接下来编写代码来运行它
Evaluations与Swift Testing集成 你可以在App的 测试目标中运行Evaluation 这里我们在Test Suite内部 实例化BookTaggingEvaluation 在测试套件内部
我们为Evaluation运行 添加一些备注 以便追踪 我们正在评估的配置 这在以后会很有用 当我们比较不同 Evaluation运行结果时
接下来用@Test宏 添加一个测试函数 以及一个新的@Test特性.evaluates 这个特性接收我们的Evaluation 和一个备注字典 就像我们之前 在@Suite中创建的那个
在我们的@Test内部 可以访问Evaluation结果包 其中包含所有Metric 以及来自此次 Evaluation运行的汇总指标 从结果中获取 所有tagCount指标 并对其平均值进行断言 我们将使用结果包上的 aggregateValue方法 然后在#expect宏中 对平均值进行断言 这里我期望服务在80% 的情况下生成正确数量的标签 为什么是80%? 如果服务性能 跌破80% 我想知道 而失败的测试是很好的信号 但如果我想更深入了解 Evaluation期间发生了什么呢
我们为Evaluation 提供了一份新的测试报告 这是深入了解 Evaluation详情并进一步分析的好方法
运行我们的测试 我来带你浏览报告 根据服务之前在#Playground 中返回的结果 特别是它为 "傲慢与偏见"生成的标签数量 我预计测试不会通过
好 测试没有通过 来查看报告 了解发生了什么 点击报告导航器 然后在测试报告中 选择Evaluations 这是测试套件的 Evaluation报告 双击该行 了解更多详情 我看到TagCount指标 仅通过了50%的情况 快速查看完整结果表 可以看到 "傲慢与偏见"样本 产生了失败结果 而"德古拉"样本 生成了正确数量的标签
可以在表格中选择每一行 查看更多详情 使用Xcode中的辅助编辑器 详情面板显示了提示词 以及ModelSample的每项测量结果 底部显示了 来自模型的完整响应
稍作总结 我们为BookTaggingService 构建了一个Evaluation 运行后发现未能达到 我们的优化目标
还记得我们的测试定义吗 这正是我们定义 优化目标的地方 我们表示功能 表现符合预期 当且仅当80%的情况下 生成了正确数量的标签
除了自动检查 优化目标之外 我们需要深入分析结果 并收集洞察 具体来说 思考可以做哪些改动 来提升功能的表现
我有个直觉 于是我回头查看 @Generable类型BookTags 也就是服务正在生成的类型 我们已经有一个@Guide宏 为模型提供额外指令 用于tags属性
我可以在那个@Guide中 指定一个count属性 它可以接受一个范围 这应该能指示模型 只生成3到8个标签
这是个有趣的理论 来进行这个修改吧
然后重新运行Evaluation 看看我是否正确 我们把这个过程叫做爬坡
好了 我做了修改并重新 运行了Evaluation 测试通过了 TagCount 在100%的情况下都通过了 但我注意到 一个可能奇怪的行为 修改之后服务 始终生成八个标签 嗯
现在我们已经设置好了Evaluations 来收集更多样本上的 更多测量结果 看看这个奇怪的行为 是否还会持续 我们的Evaluation 只从两个数据样本开始 如我们所见 这只给了我们 两条测量数据来提取趋势 好的Evaluation需要 数千个样本来提取趋势 同时也要以多种不同方式 测试你的功能 我们应该考虑 数据集的多样性 例如…… 我们希望服务能够 识别不同的流派 我们不能假设每个用户 都会写很详细的书评 所以书评应该 有不同的长度 你用不同的分类 浏览小说和非小说 样本应该 代表这种多样性 最后还应该 考虑不同的形式 长篇小说 短篇故事和散文
也让模型难一点 加入一些个人观点 这样我们就能衡量服务 在书评中忽略这些内容的能力
如果你想教功能 像你一样写标签 可以从在样本的 预期值中加入更多 你的个人风格开始
来看几个代码示例 这篇对"秘密花园"的书评 读起来与我们最初的 书评很不一样 因为我们写它时 像是一个热爱园艺的人 这里我们挑战模型 包含一位母亲读给 儿子听"金银岛"的个人书评 这篇书评中有很多个人观点
这位桌游爱好者 用了好几段话 来评价中国经典名著 "三国演义"
而这位休闲读者用一句话 描述了一位著名英国侦探的搭档 只用了一句话
当模型尝试解读这条书评时 游戏开始了
虽然想出这些例子很有趣 但人工创建数据同样无法规模化 考虑这些句子补全对 其中功能的输出 直接与预期答案 进行比较 这个Evaluation需要数千个 示例才能有效
幸好我们在Evaluations框架中 提供了SampleGenerator 你可以直接在 ModelSample数组上调用它 它会使用你选择的模型 合成生成更多样本
要了解更多关于如何 合成更大数据集的内容 以及ModelSample的 高级用法 请查看我们的视频 《为智能体App创建 健壮的Evaluation》
回到BookTagging 我将更新我的dataset属性 以包含我们藏书库中 所有书籍的书评 包括我们之前展示的四本
当我用扩展后的数据集 重新运行Evaluation时 测试通过了 TagCount平均值仍为100% 而且服务为所有书籍 都生成了八个标签 现在我们知道 服务中存在奇怪的行为
回顾我的期望 我已经构建了一个Evaluator 来跟踪标签数量是否在范围内 我觉得还需要 进一步细化 这是我当前的Metric 和Evaluator设置 首先我定义一个新的Metric 叫"TagTotal" 用于记录 生成标签的数量 然后构建一个简单的Evaluator 用于记录生成的 tags数组的长度 然后我们使用评分值 记录一次测量 而不是通过/失败值
使用"TagTotal"和"TagCount"指标 我们评估范围合规性 以及生成标签的分布情况 我们可以遵循类似的模式 来检查标签中的单词数量 这里我们检查每个标签 是否包含空格 如果有则返回失败指标 识别文学流派 同样简单 假设你在查找 一组已知流派 我们检查BookTaggingService 中的knownGenres 然后将每个生成的标签 与之进行匹配比较
我们的Evaluation正在不断完善 我们已经能够测量 最初五个期望中的三个 我们的Evaluation报告 提供了一幅丰富的图景 展示标签服务的表现情况 我们用五个汇总指标 来追踪三个期望 在这里我们可以看到 标签的分布情况 以及范围合规性 和包含流派标签的情况
使用我们的爬坡方法论 我们迭代改进了 服务的指令 这是我们最初的起点
经过对Evaluation的多次更新 以及循环中的多次运行
我们可以追踪 每次对指令的修改 通过我们为验证该修改 而添加到Evaluation中的期望
当你采用我们的 爬坡反馈循环 并以此为核心 构建你的开发流程 我们称之为评估驱动开发
但我们的服务还没有 完全达到规格要求 我们仍然期望标签 具有信息性 与书籍相关 并有助于浏览藏书库
有请Yada来讲述 关于模型评判器的内容 以及它们如何将 你的Evaluation提升到新水平 谢谢Rob 模型评判器是我们 大规模测量定性指标的方式 让我向你展示 如何构建和完善一个 来看一个具体的例子 这是Rob在Book Tracker中 为"爱丽丝梦游仙境"写的书评
以及我们的服务 生成的标签
六个标签 单词或连字符形式 并包含识别流派的标签 我们与Rob一起构建的 所有量化指标都通过了
但仔细看 "overrated"和"pretentious" 并不是在描述这本书—— 而是在描述读者对它的感受 而"whodunit" 甚至不是正确的流派 模型从"riddles he never answers" 中提取了这个词 它抓住了书评的语言 却没有理解这本书 我们的指标都通过了 但它们 没有给我们正确的反馈信号
但我觉得可以 请一个模型来帮助我们 如果一个人可以阅读这些标签 并告诉我们哪些有效 也许模型也可以
太好了! 模型确实识别出了 某些标签没有帮助
我想让模型评估 我的功能生成的所有标签 这正是模型评判器的作用! 这正是Model Judge的定义 Model Judge是一个用于 对功能输出进行评分的语言模型 它给出主观评分—— 那种人会做出的 判断性决定—— 但能一致地应用到 整个数据集上 那我们来谈谈它是如何工作的 这是驱动你智能功能的模型 我们的BookTaggingService 在端侧运行 因为它需要对每次 用户交互都快速且本地化 你可以使用第二个模型 作为评判器来评估你的功能 你的评判器至少应该与 你正在评估的模型一样强大 在我们的情况下 我们可以使用来自 Private Cloud Compute的更强大模型 模型评判器有几个关键组件 指令告诉模型 它将收到书评 以及应该如何评估它 功能输入是提供给 被评判功能的提示词 在我们的例子中就是书评
功能输出是我们 服务生成的标签 最后评分指南 告诉模型如何评估 并对功能进行评分 Evaluations框架为你处理 大部分这些工作 让你专注于评分指南
综合来看 这是一个简单的Model Judge 我们定义了一个"TagQuality"指标 采用1到4的评分制 每个等级都描述了 该分数的含义 偶数个选项可以防止 评判器默认选择 居中的中立分数 四个等级提供了 足够的区分度 而不会稀释每个 评分的含义
最后我们指定了 Private Cloud Compute作为评判模型 这给了我们一个 更强大的评估器 超越了我们正在评估的 端侧模型
在Evaluations框架中 Model Judge只是另一个Evaluator 它遵循与量化Evaluator 相同的协议 并产生相同的Metric类型 因此你可以在一个 Evaluation中自由混合使用 好了 来运行吧!
每个样本都获得了 3分或4分的质量评分 回到我们的 "爱丽丝梦游仙境"样本 Model Judge给这个样本 打了3分的质量评分
查看评判理由 我们可以发现模型 标记了"whodunit" 和"detective-fiction" 与这本书不相关 但我们本来也期望它 标记所有这些其他标签 这些标签要么反映的是 读者的观点 要么对浏览没有帮助 对于Model Judge来说 评判理由至关重要 它们让你了解评判器 为何给出这样的评分 关键在于 按照我们写的评分标准 评判器其实是对的 每个标签都与 用户写的内容有所关联 评判器忠实地遵循了 我们提供的评分指南 对于"relevant"和 "useful for browsing"我们有特定的含义 而评判器对这些词的理解 与我们不同
当我请模型代替我 对功能进行判断时 我期望它能给出 与我相似的评分 就像我对这些标签的评分一样 当Model Judge与我们的判断 出现不一致时 我们可以完善Model Judge 直到它能够代替 我们自己的判断
回头来看 我们第一个 Model Judge的问题 在于它太宽泛了 它同时在问两个不同的问题 当你发现自己对评分 有异议时 你应该尝试看看 是否可以拆分这些问题 在我们的案例中 相关性 和有用性实际上是两个不同的指标 来看看如何将"Relevance" 定义为ScoreDimension
当我们说标签是相关的 意思是每个标签描述了书本身的某种特质 主题或基调 而非小细节 或读者的个人反应
我们可以将这一点写成 ScoreDimension的description
要对这些标签进行评分 你需要逐一检查每个标签 识别哪些标签 差 哪些好 基于它们是否 有意义地描述了这本书 对每个标签重复此过程 在这种情况下 所有标签都是好的 在我们的1到4评分制中 可以获得4分 你会重复相同的过程来 定义评分指南中的每个等级 这就是我们的"Relevance"指标 包含指标名称 描述 以及Model Judge可使用的评分标准 我可以用相同的方式 来定义"Usefulness" 现在我可以将两个维度都添加 到ModelJudgeEvaluator中
但仅有维度还不够 它们告诉评判器要测量什么 但没有告诉它如何理解你的App 如果没有这些背景知识 一个评判Book Tracker标签的评判器 可能会把读者的评价 当作有效的书籍描述词 它没有办法知道 Book Tracker是个人书库 而不是评论平台 这就是ModelJudgePrompt 发挥作用的地方
这是ModelJudgePrompt 的一个示例 我们可以在指令中 告诉评判器它在评估标签 对于一个个人书库App 在evaluationTarget中 格式化响应 并将expectedTags作为参考 供模型进行比较
有关ModelJudgePrompt的更多详情 请查看我们的文档 现在我们的Model Judge 有了所需的背景知识 重新运行我们的Evaluation Quality现在被替换为 相关性和有用性评分 这是我们"爱丽丝梦游仙境" 书籍样本的Evaluation结果
注意两个评判理由 如何分别给出诊断 Relevance告诉我们 哪类标签有问题 Usefulness告诉我们 错误标签在浏览时如何失效
有了这些结果 我现在有了清晰的前进方向 我可以更新我的 BookTaggingService指令 再次运行Evaluation 看着评分变化 这就是Rob带我们了解的 反馈循环 现在由定性指标驱动 你什么时候上传到TestFlight? 好了Rob 我最近有点忙!
最后总结几条 评估App的最佳实践 从小处开始 一个包含20到30个样本的 专注数据集是很好的起点 通过思考你希望 模型如何行为来规划App 使用启发式方法 衡量可量化的特征 这些经验法则指标是 开始了解功能的好方法 经验法则是:如果可以用代码 来测量 那就是量化的 如果只能用文字描述 那就需要定性指标 使用ModelJudgeEvaluator 从简单的Model Judge开始 定义你的评分维度 运行并阅读评判理由 你从一次运行中学到的 比花数小时仔细规划更多 用评判理由来驱动下一次修改 如果评分都一样 说明你的问题太宽泛了 如果无法定位问题 则拆分维度 如果评判器不了解你的App 则添加背景知识 好吧 我们应该回去工作了 一定要查看我们的文档 以及示例代码 并观看其他介绍 Evaluations框架的讲座 《通过Evaluations爬坡 改进你的提示词》 以及《为智能体App 创建健壮的Evaluation》 再见! 拜拜!
-
-
4:54 - Define an Evaluation
// Evaluations import Evaluations struct BookTaggingEvaluation: Evaluation { } -
8:02 - Run with Swift Testing and an optimization target
// Optimization Target @Test("Book Tag Evaluations", .evaluates(evaluation, info: evaluationInfo)) func evaluateBookTagging() async throws { let result = EvaluationContext.current.result let rangeMetric = BookTagEvaluationTests.evaluation.tagCount #expect(result.aggregateValue(.mean(of: rangeMetric)) >= 0.8) } -
10:09 - Constrain output with a Generable @Guide
// BookTags.swift @Generable struct BookTags: Codable { @Guide(description: "Descriptive tags capturing themes, genres, moods, and topics from the summary", .count(3...8)) var tags: [String] } snippet. -
11:15 - Define the dataset with ModelSample
// BookTaggingEvaluation var dataset = ArrayLoader(samples: [ ModelSample(prompt: "okay I am OBSESSED and I need everyone to read this RIGHT NOW...", expected: BookTags(tags: ["classic", "romance", "wit", "regency"])), ModelSample(prompt: "Read this in one sitting between midnight and 4am and I cannot...", expected: BookTags(tags: ["classic", "gothic", "horror", "vampire", "suspense"])), ]) // Or load your whole library: var dataset = ArrayLoader(samples: Book.sampleBooks.map { book in ModelSample(prompt: book.review, expected: BookTags(tags: book.tags)) } ) -
12:53 - Synthesize more samples with a SampleGenerator
// Synthesizing more inputs let samples: [ModelSample<String>] = [ ModelSample(prompt: "The largest planet in our solar system...", expected: "Jupiter."), ModelSample(prompt: "The capital of Thailand...", expected: "Bangkok."), ModelSample(prompt: "Swift is...", expected: "a powerful programming language."), ModelSample(prompt: "All those moments will be lost in time...", expected: "Like tears in rain.") ] for try await sample in samples.makeSamples( """ Generate diverse sentence completions about the listed topics: - The Solar System - World Capitals """, targetCount: 1000) { samples.append(sample) } -
14:02 - More evaluators: word count and genre
let wordCount = Metric("WordCount") Evaluator { _, subject in for tag in subject.value.tags { if tag.contains(" ") { return wordCount.failing(rationale: "Tag \(tag) contains multiple words") } } return wordCount.passing() } let hasGenreTag = Metric("HasGenreTag") Evaluator { _, subject in let tags = subject.value.tags.map { $0.lowercased() } let knownGenres = await BookTaggingService.knownGenres for tag in tags { if knownGenres.contains(tag) { return hasGenreTag.passing(rationale: "Matched \(tag)") } } return hasGenreTag.failing() } -
14:03 - Define a Metric and Evaluator
let tagCount = Metric("TagCount") var evaluators: Evaluators { // Tag count is within the required 3–8 range Evaluator { _, subject in let count = subject.value.tags.count if (count >= 3 && count <= 8) { return tagCount.passing(rationale: "\(count) tags") } return tagCount.failing(rationale: "Got \(count) tags, expected 3–8") } } -
14:27 - Aggregate metrics across samples
let tagCount = Metric("TagCount") let tagTotal = Metric("TagTotal") func aggregateMetrics(using aggregator: inout MetricsAggregator) { aggregator.computeMean(of: tagCount) aggregator.group("Distribution of Tag Totals") { aggregator in aggregator.computeStandardDeviation(of: tagTotal) aggregator.computeMean(of: tagTotal) aggregator.computeVariance(of: tagTotal) } } -
15:33 - Iterate the feature's instructions (hill-climbing)
// BookTaggingService.swift let instructions = Instructions { """ You are a librarian and literary analyst. Given a reader's freeform summary of a book they read — describing their thoughts, feelings, and what stood out — generate a set of descriptive tags reflected in the summary. Rules: - Return between 3 and 8 tags. - Tags should be lowercase, concise (single word or hyphenated), and descriptive. - Tags should include the book's genre, chosen from the included list of known genres. Known Genres: - \(Self.knownGenres.joined(separator: ", ")) """ } -
18:53 - Build a model judge
ModelJudgeEvaluator( "TagQuality", scale: .numeric([ 4: "Tags are relevant and helpful for browsing", 3: "Mostly relevant, one tag too vague or generic", 2: "Several tags are wrong or generic", 1: "Unhelpful or irrelevant" ]), judge: PrivateCloudComputeLanguageModel() ) -
22:17 - Split into score dimensions
// BookTaggingEvaluation.swift ScoreDimension( "Relevance", description: """ Whether each tag describes a quality, theme, or tone of the book itself rather than incidental details or the reader's personal reactions. """, scale: .numeric([ 4: "Every tag describes the book itself", 3: "Most tags describe the book", 2: "Some tags describe personal reactions", 1: "Tags don't meaningfully describe the book" ]) ) // Define `usefulness` the same way as a second ScoreDimension. -
22:32 - Add dimensions to the judge
// BookTaggingEvaluation.swift var evaluators: Evaluators { Evaluator { } Evaluator { } Evaluator { } ModelJudgeEvaluator( judge: PrivateCloudComputeLanguageModel(), dimensions: [relevance, usefulness] ) } -
23:17 - Add app context with a ModelJudgePrompt
// BookTaggingEvaluation.swift ModelJudgeEvaluator( judge: PrivateCloudComputeLanguageModel(), dimensions: [relevance, usefulness], prompt: ModelJudgePrompt( instructions: """ You are evaluating tags generated for a personal book-tracking app where users organize their library by browsing and filtering tags. """, evaluationTarget: { value in "\(value.tags.count) Generated tags: " + value.tags.joined(separator: ", ") }, reference: { input, _ in let expectedTags = input.expected?.tags.joined(separator: ", ") return ["Expected Tags": expectedTags ?? "No expected tags defined"] } ) )
-
-
- 0:00 - Introduction
Rob Rhyne and Yada introduce the Evaluations framework. Generative-AI features break the "same input, same output" contract that unit tests rely on, so a new, more robust form of testing is needed to measure how often features produce unexpected or unsafe results.
- 3:10 - Demo app Book Tacker: a manual evaluation
Introduces the Book Tracker demo app and its BookTaggingService, which auto-tags books from reviews. Trying it in a #playground surfaces issues (too many tags, book title as a tag, multi-word tags) and produces a first human-judged list of expectations.
- 4:31 - Building your first evaluation
Implement the Evaluation protocol in five steps: define the subject (the code under test), the dataset of ModelSample inputs with expected values, a Metric and Evaluator (pass/fail on tag count), and an aggregateMetrics summary.
- 8:06 - Running the evaluation and reading the report
Run evaluations through Swift Testing with the evaluates trait and an optimization target (#expect average at least 80%). The new evaluation test report breaks down per-sample results, prompts, measurements, and the full model response.
- 10:57 - Building robust datasets
Two samples aren't enough; good datasets have thousands with variety (genres, review lengths, fiction/non-fiction, forms, personal opinions). Hand-authoring doesn't scale, so the framework's SampleGenerator synthesizes more samples from a seed set.
- 14:20 - Refining metrics and evaluators
Add metrics for deeper insight: TagTotal with a scoring (not pass/fail) evaluator, range-compliance and distribution, word-count, and genre checks against knownGenres, covering three of the five original expectations, tracked alongside instruction changes.
- 15:41 - Evaluation-driven development and hill-climbing
Recap the loop: a failing optimization target prompts analysis and a change (adding a count range to the @Guide on the BookTags Generable). Re-running to check the result is hill-climbing; centering development on it is evaluation-driven development.
- 16:12 - Model judges: qualitative metrics
Quantitative metrics can pass while tags are still wrong (reader opinions, mis-inferred genres). A model judge uses a second, at-least-as-capable model (here, Private Cloud Compute) to score output the way a person would, consistently across the dataset.
- 18:42 - Building a model judge
A ModelJudgeEvaluator is just another Evaluator producing the same Metric type. Define a TagQuality metric on a 1-to-4 scale (an even number of levels avoids a neutral default), specify the judge model, run it, and read the rationales.
- 21:19 - Refining with score dimensions
When you disagree with a score, the question is often too broad. Split it into ScoreDimensions (Relevance vs. Usefulness), each with its own description and scale, and add a ModelJudgePrompt to give the judge context about your app.
- 23:45 - Reviewing dimension results
Re-running yields separate relevance and usefulness scores whose rationales split the diagnosis: relevance shows what kind of tag is wrong, usefulness shows how it fails at browsing, giving a clear path back into the hill-climbing loop.
- 24:20 - Best practices
Start small (20 to 30 focused samples), use heuristics for quantitative traits (if you can measure it in code), use ModelJudgeEvaluator for qualitative ones, start simple with the judge, and let rationales drive the next change.
- 25:38 - Next steps
Pointers to the Evaluations framework documentation, the Book Tracker sample code, and the companion sessions on hill-climbing prompts and creating robust evaluations for agentic apps.