-
为智能体 App 打造稳健的评估
了解如何利用 Evaluations 框架的高级功能,为你的 App 构建稳健的评估。探索涉及工具调用和动态条件的评估流程,以及如何为你的用例定义正确的行为。了解如何生成合成数据、有效使用评审模型,并验证你的数据集以便获得可靠的结果。
章节
- 0:00 - Introduction
- 2:21 - The dataset problem in BookTracker
- 3:46 - Generating synthetic data with makeSamples
- 6:27 - Customizing generation with SampleGenerator
- 8:38 - Sampling strategies
- 10:11 - Validating synthetic samples
- 13:04 - Comparing evaluation results
- 15:09 - Tool calling and tool evaluations
- 18:54 - Trajectory expectations
- 21:26 - Building a tool call evaluation
- 22:02 - Synthetic data for tool evaluations
- 23:49 - Next steps
资源
-
搜索此视频…
你好 我是Ada 我是Kyle 我们是Evaluations团队的工程师 今天 我们非常高兴为你介绍 Evaluations框架的一些高级功能 Evaluations框架提供了一种方式 用于评估Swift应用中 由 Apple 智能驱动的功能 追踪功能随时间的改进 并确保生产质量 该框架在Xcode 27中全新推出 支持macOS iOS watchOS和visionOS 如果你还没看过 请观看讲座 《Meet the Evaluations framework》 了解Evaluations框架的构建模块 以及另一讲座 《Improve your prompts by hill climbing with Evaluations》 探索改进 Apple 智能功能的各种策略
本视频将讨论如何应对复杂性 以及评估的可扩展性 首先 我们将介绍 如何扩充评估数据集 通过生成和验证合成数据 然后介绍如何为智能体工作流 构建可靠的评估 这些工作流涉及一种 名为工具调用的特殊模型行为
在"Meet the Evaluations framework"视频中 我们介绍了爬山法过程 这说明了构建 测试和发布 Apple 智能功能的过程 本视频将主要关注 开发和评估步骤 在开发步骤中 我们通常从 少量样本开始进行评估 但功能的复杂度 往往超出数据集的覆盖范围 构建耗时 扩展困难 而且很难捕捉到所需的多样性 以真正了解功能 在现实世界中的表现 评估结果的质量 取决于背后数据的质量 编写优质评估数据非常困难 这正是合成数据发挥作用的地方 Evaluations框架提供了API 让你完全通过代码 定义样本生成方式 构建自己的生成管道 通过命令行运行 或直接集成到现有工作流中 支持基于文本的数据 并利用generable宏 生成结构化合成数据 我和同事一直在开发BookTracker 这是一款使用 Apple 智能功能的 个人图书馆应用 根据书评自动为书籍添加标签 让我们来看看书籍的定义方式 我们有一个名为Book的类 包含书名 作者 书评 标签和评分 我们还定义了其他变量 用于支持封面设计 我们还定义了sampleBooks 这是一个包含13个Book样本的数组 比如这个关于 《傲慢与偏见》的样本 这13个样本看起来是不错的起点 但这个小数据集 只能提供有限的视角 来了解功能的表现 评估结果可能看起来不错 却完全具有误导性 想想用于评估的 各种可能数据 来评估我们的标签生成功能
书籍数不胜数 数百种类型
以及用户评价刚读完的书籍 的无数种方式 我们还要考虑现实世界的情况 摘要可能模糊或不完整 十三个样本 无法涵盖所有这些情况 我们需要更广泛的覆盖 而且不能花费数天 手动编写示例 让我们来讨论如何扩展数据集 以捕获更多的多样性 从简单的开始 makeSamples API需要三个组件 一个提示词 一个数据集 以及一个目标数量 即样本的数量 也就是你希望合成生成的样本数量 包括你提供的数据集 在这里 我定义了一个提示词 要求模型建议 更多样化的书评样本 要编写定义良好的提示词 请考虑模型需要哪些信息 才能最好地理解任务 并处理用户可能提供的各种输入 对于我们的数据集 我传入了sampleBooks 其中包含我们最初的13个样本
在这里 我们使用新的ModelSamples API 将书评作为提示词 将书籍标签作为预期输出 对于目标数量 我设置为 一百个样本作为起点 请记住 targetCount是 最终数据集的总大小 包括我们最初的样本 因此模型实际上 会生成87个新样本 你可能想知道 多少数据才够用 答案是 视情况而定 对于BookTracker应用 一百的目标数量只是起点 合成数据生成通常是 一个迭代过程 包括定义初始数据集 生成合成数据 验证样本 然后分析数据 是否具有足够的代表性 并持续这个循环 直到你满意为止 因此 评估数据集 的正确目标数量 完全取决于你的功能 功能的用途 使用者 以及人们与其交互的方式 比数量更重要的是覆盖面
所以不要问 需要多少样本 而要问自己 我是否覆盖了功能实际被使用的 各种有意义的方式 现在 我已经定义了所需变量 可以使用makeSamples方法了 它返回一个包含 新生成样本的异步流 当我迭代这个流时 每个新样本都会被追加到 名为expandedDataset的变量中 该变量已用初始数据集初始化 默认情况下 框架使用 设备端模型进行生成 在大多数情况下 设备端模型是不错的选择 但你可能想使用自己的模型 或自定义模型的运行指令 框架提供了灵活性 让你为样本生成 定义自己的配置 让我们来看看如何实现
对于超出提示词 数据集 和目标数量的更复杂配置 框架提供了 SampleGenerator 让你完全掌控 生成过程 让我们来了解 这些配置
sessionProvider是一个闭包 返回一个LanguageModelSession 你可以在这里控制 哪个模型驱动生成 以及什么系统级指令 定义任务框架 对于我们的合成数据生成 我将使用 PrivateCloudComputeLanguageModel 因为其上下文大小更大 然后我将添加 自定义指令 将生成聚焦于 特定书籍 类型和情绪
我还指定了一组规则 对生成样本的期望 稍后我会详细介绍 先说明一下会话的使用方式 框架自动处理 批次大小 即生成过程中 每批处理的样本数量 生成器在运行开始时 调用一次sessionProvider 然后在各批次中 复用该会话 这有助于模型在生成过程中 保持上下文
但会话的增长有大小限制 一种例外情况是 当你发出大量请求时 提供大型提示词 或获得大量输出时 你可能会在运行途中耗尽 会话的上下文窗口并导致报错 在这种情况下 生成器 会再次调用sessionProvider 获取新的会话以继续生成 但新会话不包含 上一个会话的上下文 因此请确保sessionProvider中的指令 是自包含的 不要假设 它只会被调用一次 要了解更多缓解 上下文大小限制的方法 请观看视频"Build agentic app experiences with Foundation Models" 有了自定义会话提供器 你还可以使用SampleGenerator 来自定义samplingStrategy
它控制生成器 如何选择示例 从初始数据集中选取示例 作为模型的上下文示例 有两种采样策略可以指定 第一种是随机采样
该策略从初始样本中 随机选取子集 作为向模型展示的示例 确保没有重复 这保持了输出的多样性 无需仔细考虑 初始样本的排列顺序 第二种采样策略 是滑动窗口
该策略按顺序遍历 你的初始样本 跳过重复项 如果数据集具有有意义的顺序 可以考虑使用 滑动窗口策略
对于我们的生成器 我们将使用随机策略 因为我们的初始样本 没有有意义的排列顺序 由于这是默认策略 我们无需在此明确定义
现在 我们已经配置了生成器 使用了自定义sessionProvider 可以调用.run函数了 它返回一个 新合成样本的流 当我们遍历每个样本时 它会被添加到 之前定义的expandeDataset中
现在我们已经设置好配置了 让我们来探讨如何确保 合成数据符合预期 这就是验证器闭包 发挥作用的地方 验证器让你 定义自己的逻辑 来接受或拒绝 每个生成的样本 我们已经定义了一组规则 在之前会话提供器的指令中 但这并不能保证 输出会真正遵循这些规则 让我们来回顾一下 我们定义的第一条规则 是书评至少要有100个字符 每篇书评还应涵盖 广泛的类型 情绪和风格 书评长度也需要有所变化 模型还应该生成 3到8个书籍标签 标签必须为小写字母 为了了解需要 对哪些内容进行验证 我们需要考虑可以根据 这些规则进行系统性检查的内容 此外 验证器闭包 对每个样本独立进行验证 不包含对其他样本的上下文 回顾这些规则 我可以判断出书评的多样性 需要更多的主观判断 超出了简单验证检查的范围 书评长度的评估 需要综合所有样本进行考量
对于其他规则 我们可以系统地进行评估 使用验证器闭包 对于第一条规则 我们可以定义书评长度验证 以一本大家都熟悉的 经典书籍为例 比如Mary Shelley 的《弗兰肯斯坦》 我们可以检查生成的样本 是否定义了书评 且长度至少为100个字符 模型还会为每篇书评 生成标签 这意味着我们可以验证 标签数量是否在3到8个之间
最后 我们可以检查 标签是否全为小写字母
在这里 我已经在SampleGenerator中 定义了这3个验证指标 以检查样本 是否符合预期结构 那么结果最终存储在哪里呢 随着生成的进行 有效样本会被收集到 SyntheticGenerator的samples属性中 任何未通过验证的样本 会自动被归类 到invalidSamples中 两者在整个运行过程中 实时更新 你可以随时访问它们 可在迭代过程中查看进度 也可在循环结束后查看 然后你可以直接在应用中使用这些结果 或将数据集保存到本地 现在让我们查看 使用13个初始样本的评估结果 在Xcode 27中 我们推出了新的Evaluations报告 用于可视化你的评估结果 这是使用13个初始样本的 BookTaggingEvaluation 如你所见 标签质量 得分相当高 评估了相关性和实用性两个维度 我已经使用 包含100个样本的新数据集运行了评估 现在 我们可以使用Compare按钮 比较这两次评估 我们预期分数会下降
我们的预测是正确的 质量分数已经下降了 我们的标签生成功能之前 看起来表现良好 因为我们没有使用 全面的数据集对其进行测试 通过在更大的数据集上 运行评估 分数下降可能表明 多种不同的问题 思考一下这个信号 可能意味着什么 分数变化可能是由于 提示词或指令存在问题 你可以完善其中一个或两者 以更好地满足你的需求 你也可以考虑 Apple 智能功能中存在的不足 或者你可能需要 调整评估来了解 你实际上在评估什么 最后 你的数据集 可能仍不够具有代表性 需要捕获更多的变化 你可以继续扩充数据集 或包含更多边缘案例 使用合成数据API 这些是进一步提升结果的 核心方法
现在我们已经有了 构建可靠评估数据集的方法 使用合成数据 我想更进一步 到目前为止 我们一直在评估 书籍标签功能 但当我们的应用 变得更加复杂时 需要执行多个操作 才能完成搜索等任务时 这正是工具调用发挥作用的地方 我把这部分交给Kyle 来展示它是如何工作的 谢谢Ada 现在让我们继续 评估驱动开发 介绍工具评估 到目前为止 我们一直在评估 模型的生成内容 对于我们的功能 即书籍标签 但 Apple 智能功能通常需要 很多幕后步骤 才能生成输出 它们在应用中执行多个操作 每个操作都对结果有所贡献 工具为模型工作流添加了结构 当它们为应用用户 完成任务时
你使用它们来操作 人们每天使用的真实数据
它们可以使用 你定义的任何自定义业务逻辑运行
它们可以调用 用户可以直接调用的功能 或为 Apple 智能功能 提供全新逻辑 或两者的组合 关键是 模型可能会给你 一个听起来合理的答案 却从未调用正确的工具 最终输出可能看起来正确 但到达那里的路径并不正确 让我们来讨论这些挑战 以及工具评估 如何帮助你应对它们
首先是指令遵循 你需要告诉模型 如何使用每个工具 对细节的关注 至关重要
尝试自己逐字逐句地 遵循指令 看看你是否会遗漏某个步骤
然后是工具复杂性 工具可以接受简单指令 或需要微调参数范围
然后是边缘情况 工具对常见输入 可能运行良好 但在罕见输入上 可能表现出人意料
这就是我们需要工具评估的原因 它们让你验证的是方式 而不仅仅是结果
模型应该调用正确的工具 使用正确的参数 按预期的顺序 而且在此过程中 你还要检查中间 是否没有出现意外的工具调用
让我们来实践一下 构建第一个工具评估 在BookTracker应用中 我们添加了一个图书馆助手 用户可以搜索一本书 而不仅仅是根据书名 和其他字符串筛选书籍 模型使用应用的自定义工具 查找相关书籍
有一个searchBooks工具 用于查找可能有相似标签的书籍 还有一个getBookDetails工具 用于提取书籍元数据 如从搜索结果中提取出版日期
然后是findSimilarBooks工具 用于对相似书籍 进行语义搜索 我们将多个步骤链接在一起 每个步骤都是一次工具调用 这是SearchBooksTool
它符合Tool协议 有一个模型可以看到的名称 还有一个描述 告诉模型何时使用此工具
参数是一个Generable结构体 注意这些都是可选的 模型根据用户的请求 决定使用哪些筛选器
如果你提示模型 查找哥特风格书籍 我们期望它填充 tag参数 如果你提示模型 显示一些令人愉快的内容 我们期望生成情绪搜索 这些正是我们 想要评估的决策类型 好的 以上是对工具的简要回顾 现在让我们编写 第一个工具评估并查看表现 工具评估的主要组件 是轨迹期望 会话记录中包含 提示词 响应和工具调用
轨迹期望检查顺序 以及语言模型会话中 每次工具调用的类型 你可以将轨迹期望检查 想象成 回顾你在规划路线时 所做的决策列表 汽车 自行车和公共汽车都是工具 各有其适用的时间 和场合 但你可以评估它们 在特定旅途中每个路段的效用
期望会检查 所有工具调用 然后对每一个 根据你在评估中编写的期望 进行检验 这是代码形式的一个简单案例 我们的提示词是 "查找标记为gothic的书籍" 我们期望一次工具调用"searchBooks" 这是一个TrajectoryExpectation 它描述了我们期望 在模型记录中看到的工具调用 这里的unordered意味着 我们不在意工具调用发生的时间 只要它发生即可 我们可以通过 向期望添加参数来进一步细化 这里我添加了一个参数 期望标签为"gothic" 精确匹配并不总是你所需要的 如果提示词是 "查找令人愉快的内容" 模型可能会传递uplifting happy cheerful — 这些都可以
.naturalLanguage匹配器检查 值是否与意图匹配 而不是精确的字符串 还有一整套适用于 不同情况的匹配器 contains oneOf pattern range等等 查看开发者文档 获取更多信息 对于多步骤任务 顺序至关重要
这里模型必须 首先调用"searchBooks" 然后调用"getBookDetails" 如果智能体先尝试获取详情 此时还没有bookId 这就是个错误 轨迹期望能捕捉到这个问题 因为我们在检查过程 而不仅仅是目的地
有时 智能体不应该做什么 同样重要
如果提示词包含 "不要查找相似书籍"等要求 模型应该遵循指令 disallowed参数指定 不得出现在记录中的工具 如果智能体仍然调用了 "findSimilarBooks" — 这就是失败 这是所有轨迹期望 汇集的地方 在完整评估中 我们定义一个样本数据集 每个样本都有提示词 和轨迹期望 并使用ToolCallEvaluator对其评分 ToolCallEvaluator将 LanguageModelSession与工具结合 获取响应 并捕获 结构化的记录
工具调用评估结果 显示在Xcode助手中 与其他结果一起显示 你可以全面了解 Apple 智能功能的行为表现 等等 我们还可以使用Evaluations API 为工具评估 生成合成数据 好的 让我们这样做 轨迹期望也是可生成的 扩展工具评估的数据集 可能相当复杂 而Evaluations框架 让这一切变得更加简单 由于我们的工具调用评估 利用了ModelSample 和可生成的 TrajectoryExpectation 我们可以像之前一样 使用样本生成器合成更多样本 我已经定义了一个提示词 和sessionProvider的 自定义指令 在为工具评估创建 合成数据时请注意 模型不知道 你定义了哪些工具 也不知道工具 需要以什么顺序调用 所以在这里 我指定了可用工具 并解释了它们的用途 任何顺序期望 以及模型可能需要的其他上下文 然后我们可以定义sampleGenerator 并使用现有数据集 作为初始样本 以及100的targetCount 我们也可以在这里 指定验证指标 在这里 我确保 始终存在期望 我还确保合成样本 至少包含一个工具 最后 任何被调用的工具 都是我们已经定义的实际工具 这就是你如何生成 并验证工具评估的 合成样本 合成数据API 是一种强大的方式 将现有数据集 扩展到超出手动能力的范围 数据越具有代表性 分数就越能反映现实 好的 Kyle 交给你了 这就是一切汇聚的地方 之前我们构建了书籍标签评估 检查模型的生成内容 标签数量 类型覆盖率 质量分数 现在我们有了工具评估 它们检查模型如何达到目标 正确的工具 正确的参数 和正确的顺序 在同一评估套件中运行两者 你将对功能建立 端到端的信心 现在我们已经介绍了一些 让评估更加可靠的方法 你可以开始将这些想法 应用到应用和评估数据集中 要开始 请尝试 创建自己的合成数据 评估应用中的自定义工具 并查看开发者文档中的 示例应用和其他文章
哇Ada 我们今天覆盖了很多内容 是的 确实如此 但真正的惊喜 是你用它构建的东西 不过不剧透 希望你喜欢学习 Evaluations框架的内容
-
-
5:16 - Generate synthetic data with makeSamples
// Synthetic data let prompt = Prompt(""" Generate diverse range of book reviews and corresponding tags. Cover a wide range of genres, time periods, cultures, and reader personas. Do not repeat books already in the dataset. """) let dataset = Book.sampleBooks.map { book in ModelSample(prompt: book.review, expected: BookTags(tags: book.tags)) } let targetCount = 100 var expandedDataset = dataset for try await sample in dataset.makeSamples(prompt, targetCount: targetCount) { expandedDataset.append(sample) print("Generated \(expandedDataset.count) samples so far.") } 2. Configure a custom SampleGenerator — slides 30–43 // Define your own configuration let generator = SampleGenerator<ModelSample<BookTags>>( prompt, samples: dataset, targetCount: targetCount, sessionProvider: { LanguageModelSession( model: PrivateCloudComputeLanguageModel(), instructions: """ You are a synthetic data generator for a book-tracking app's evaluation suite. Your job is to produce realistic, diverse book entries that will stress-test a tagging system. Rules: - Review must be at least 100 characters long. - Review should cover a mix of genre, mood/tone, and themes. - Reviews should vary in length. - Create between 3 and 8 tags. - Tags must be lowercase. """ ) } ) -
5:53 - Configure a custom SampleGenerator
// Define your own configuration let generator = SampleGenerator<ModelSample<BookTags>>( prompt, samples: dataset, targetCount: targetCount, sessionProvider: { LanguageModelSession( model: PrivateCloudComputeLanguageModel(), instructions: """ You are a synthetic data generator for a book-tracking app's evaluation suite. Your job is to produce realistic, diverse book entries that will stress-test a tagging system. Rules: - Review must be at least 100 characters long. - Review should cover a mix of genre, mood/tone, and themes. - Reviews should vary in length. - Create between 3 and 8 tags. - Tags must be lowercase. """ ) } ) -
10:37 - Validate generated samples
// Define validation metrics validator: { sample in guard let book = sample.expected else { return false } // Review must be at least 100 characters guard sample.promptDescription.count >= 100 else { return false } // Must have between 3 and 8 tags guard (3...8).contains(book.tags.count) else { return false } // All tags must be lowercase guard book.tags.allSatisfy({ $0 == $0.lowercased() }) else { return false } return true } -
10:58 - Access valid and invalid results
// Accessing results for try await sample in generator.run() { // During iteration expandedDataset.append(sample) } // After iteration let allSamples = await generator.samples let invalidSamples = await generator.invalidSamples print("Generated \(allSamples.count) new samples. Total: \(expandedDataset.count)") -
15:30 - Define a tool's Generable argument
@Generable struct SearchBooksArguments { @Guide(description: "A freeform search term to match against titles, reviews, or tags") var query: String? @Guide(description: "Filter results to books with this specific tag") var tag: String? @Guide(description: "Filter results by mood") var mood: String? @Guide(description: "Filter results by genre") var genre: String? @Guide(description: "Maximum number of results to return. Defaults to 5.") var limit: Int? } -
16:37 - A basic trajectory expectation
// "Find books tagged gothic" TrajectoryExpectation( unordered: [ ToolExpectation( "searchBooks", arguments: [ .exact(argumentName: "tag", value: .string("gothic")) ] ) ] ) -
17:07 - Match arguments by intent (naturalLanguage)
// "Find something cheerful" TrajectoryExpectation( "searchBooks", arguments: [ .naturalLanguage( argumentName: "mood", criteria: "Should relate to uplifting, hopeful, or positive feelings" ) ] ) Other matchers available: .contains, .oneOf, .pattern, .range, and more. -
17:34 - Expect tool calls in order
// "Find gothic books and show details on the first" TrajectoryExpectation( ordered: [ ToolExpectation( "searchBooks", arguments: [ .exact(argumentName: "tag", value: .string("gothic")) ] ), ToolExpectation( "getBookDetails", arguments: [ .keyOnly(argumentName: "bookId") ] ) ] ) -
17:55 - Disallow specific tool calls
// "Show only sci-fi books. Don't look for similar ones." TrajectoryExpectation( unordered: [ ToolExpectation( "searchBooks", arguments: [ .naturalLanguage( argumentName: "genre", criteria: "Should refer to science fiction") ] ) ], disallowed: [ ToolExpectation("findSimilarBooks") ] ) -
18:14 - Build a tool call evaluation
// Tool call evaluations let samples = SampleArrayLoader(samples: [ ModelSample( prompt: "Find all the books tagged with 'gothic'.", instructions: "Help the user explore their book collection.", expectations: TrajectoryExpectation( ) ) ]) struct BookLibraryToolCallEval: Evaluation { var dataset = samples let pass = Metric("All Passed") let percent = Metric("Percentage Passed") var evaluators: Evaluators { ToolCallEvaluator(allPass: pass, percentagePass: percent) } } -
19:20 - Synthesize tool-evaluation samples
// Tool call evaluations let prompt = Prompt(""" Generate diverse user queries for a personal book library assistant. Each sample needs a prompt (what the user says), and a trajectory expectation describing which tools should be called and in what order. """) let instructions = """ AVAILABLE TOOLS: - searchBooks(query?, tag?, mood?, genre?, limit?): search the library - getBookDetails(bookId): full details for one book - findSimilarBooks(bookId, maxResults?): find books sharing tags ORDER REQUIREMENTS: - searchBooks must comes before getBookDetails or findSimilarBooks - Use TrajectoryExpectation(ordered:) when sequence matters, else (unordered:) USE THESE ARGUMENT MATCHERS: - .exact for precise values, .naturalLanguage for fuzzy matching - .keyOnly when any value is acceptable, .range for numeric constraints - .contains/.hasPrefix/.hasSuffix for partial string matching """ -
19:51 - Validate tool-evaluation samples
// Tool call evaluations validator: { sample in // Must have expectations defined guard sample.output.expectations != nil else { return false } let expectations = sample.output.expectations! // Must reference at least one tool let totalExpectations = expectations.ordered.count + expectations.unordered.count guard totalExpectations > 0 else { return false } // All tool names must be from the valid set let validTools: Set<String> = ["searchBooks", "getBookDetails", "findSimilarBooks"] let allExpectations = expectations.ordered + expectations.unordered + expectations.disallowed for expectation in allExpectations { guard validTools.contains(expectation.name) else { return false } } return true } ---
-
-
- 0:00 - Introduction
Ada Wong and Kyle Murray introduce advanced features of the Evaluations framework (new in Xcode 27). Outlines the agenda: growing your dataset with synthetic data, then building robust evaluations for agentic, tool-calling workflows, focused on the develop-and-evaluate step of hill-climbing.
- 2:21 - The dataset problem in BookTracker
The BookTracker app auto-tags books from reviews, but its 13 hand-written sampleBooks give only a narrow view. Real-world reviews span countless books, genres, lengths, and styles, too much variety to capture by hand.
- 3:46 - Generating synthetic data with makeSamples
The makeSamples API takes a prompt, a dataset (ModelSample with review to tags), and a target count (the full resulting size, including your seeds). It returns an async stream of new samples; coverage of real usage matters more than raw quantity.
- 6:27 - Customizing generation with SampleGenerator
For more control, SampleGenerator exposes a sessionProvider closure to pick the model (such as Private Cloud Compute) and instructions. The session is reused across batches but can exhaust its context window mid-run, so make instructions self-contained since the provider may be called again.
- 8:38 - Sampling strategies
The samplingStrategy controls which seed samples are shown to the model as in-context examples: random (a varied subset, the default) or slidingWindow (sequential, for datasets with meaningful order).
- 10:11 - Validating synthetic samples
A validator closure accepts or rejects each generated sample in isolation against systematic rules: review length at least 100 characters, 3 to 8 tags, lowercase tags. Valid samples collect in samples, rejects in invalidSamples, both updated in real time.
- 13:04 - Comparing evaluation results
Using the Xcode 27 Evaluations Report, compare the 13-sample run against the 100-sample run. The quality scores drop, the feature only looked good on the small dataset, and a drop can signal issues in the prompt, the feature, the evaluation, or the dataset.
- 15:09 - Tool calling and tool evaluations
Tool evaluations: features often take multiple behind-the-scenes tool calls, and a plausible answer can come from the wrong path. Tool evaluations verify the how: correct tools, correct arguments, correct order, no surprises, illustrated with searchBooks, getBookDetails, and findSimilarBooks.
- 18:54 - Trajectory expectations
A TrajectoryExpectation checks the kind and order of tool calls in a session transcript. Refine with argument matchers (exact, naturalLanguage, contains, oneOf, pattern, range), plus ordered expectations and a disallowed set for tools that must not be called.
- 21:26 - Building a tool call evaluation
Bring the trajectory expectations together: a dataset of samples (each a prompt plus expectation) scored by ToolCallEvaluator, which combines a LanguageModelSession with the tools, captures the structured transcript, and reports alongside your other results in Xcode.
- 22:02 - Synthetic data for tool evaluations
Because ModelSample and TrajectoryExpectation are Generable, you can synthesize tool-evaluation samples too, describing the available tools, order expectations, and context in the prompt, then validating that each sample has an expectation, at least one tool, and only real tools.
- 23:49 - Next steps
Run BookTaggingEvaluation (what the model produces) and tool evaluations (how it gets there) in one suite for end-to-end confidence. Next steps: create your own synthetic data, evaluate your app's custom tools, and explore the sample app and documentation.