Copilot 前团队负责人深度剖析,强调评估系统(Evals)是 AI 产品成功的核心,而非模型本身。
五年前,我开始从事语言模型相关工作,当时我领导的团队创建了 CodeSearchNet,它是 GitHub CoPilot 的前身。从那以后,我目睹了许多成功和失败的 LLM 产品开发方式。我发现,失败的产品几乎总是有一个共同的根本原因:未能建立健壮的评估系统。
我目前是一名独立顾问,帮助公司开发领域特定的 AI 产品。我希望公司能通过仔细阅读这篇文章来节省数千美元的咨询费用。尽管我喜欢赚钱,但我讨厌看到人们一次次犯同样的错误。
本文概述了我关于为 LLM 驱动的 AI 产品构建评估系统的想法。
就像软件工程一样,AI 的成功取决于你迭代的速度有多快。你必须有以下流程和工具:
评估质量(例如:测试)。
调试问题(例如:日志记录和数据检查)。
改变行为或系统(提示词工程、微调、编写代码)
许多人只专注于上面的第 3 点,这使他们无法将 LLM 产品改进到演示之外。¹ 很好地完成这三项活动会形成一个良性循环,区分伟大和平庸的 AI 产品(见下面的图表以了解这个循环的可视化)。
如果你简化评估流程,所有其他活动都会变得容易。这与软件工程中的测试类似,尽管需要前期投资,但从长期来看会带来巨大的回报。
为了将这篇文章建立在实际情况基础上,我将通过一个案例研究来说明我们如何构建了一个快速改进的系统。我将主要关注评估,因为这是最关键的组件。
Rechat 是一个 SaaS 应用程序,允许房地产专业人士执行各种任务,例如管理合同、搜索清单、构建创意资产、管理预约等。Rechat 的核心论点是你可以在一个地方完成所有工作,而不必在许多不同的工具之间进行上下文切换。
Rechat 的 AI 助手 Lucy 是一个标准的 AI 产品:一个会话界面,消除了点击、输入和导航软件的需要。在 Lucy 的初期阶段,通过提示词工程取得了快速进展。然而,随着 Lucy 的功能范围扩展,AI 的性能出现了平台期。症状包括:
解决一个故障模式会导致其他故障模式的出现,类似于一个打地鼠游戏。
除了凭感觉检查外,对 AI 系统在各项任务中的有效性的可见性有限。
提示词扩展成了冗长而笨重的形式,试图涵盖众多边界情况和示例。
为了突破这个平台期,我们创建了以评估为中心的系统化方法来改进 Lucy。我们的方法如下图所示。
这个图表是尽力展示我对改进 AI 系统的心智模型。实际上,这个过程是非线性的,可以采取许多不同的形式,可能看起来不像这个图表。
我在下面关于评估的背景中讨论了这个系统的各个组件。
严格和系统的评估是整个系统中最重要的部分。这就是为什么"评估和管理"在图表中心用黄色突出显示。你应该花费大部分时间让你的评估更加健壮和精简。
需要考虑三个评估级别:
第 2 级:模型和人工评估(包括调试)
第 3 级的成本 > 第 2 级 > 第 1 级。这决定了你执行它们的节奏和方式。例如,我通常在每次代码更改时运行第 1 级评估,按设定的节奏运行第 2 级,只在重大产品变更后运行第 3 级。在进行基于模型的测试之前,先完成一大部分第 1 级测试是有帮助的,因为它们需要更多的工作和时间来执行。
没有严格的公式来确定何时引入每个级别的测试。你想要平衡快速获得用户反馈、管理用户认知和 AI 产品的目标。这与你必须为产品总体所做的平衡行为并没有太大不同。
LLM 的单元测试是断言(就像你在 pytest 中写的一样)。与典型的单元测试不同,你想要组织这些断言以在单元测试之外的地方使用,例如数据清理和模型推理期间的自动重试(使用断言错误来纠正路线)。重要的是这些断言在你开发应用程序时应该运行得快速且成本低廉,这样你可以在每次代码更改时运行它们。如果你在想到断言时遇到困难,你应该仔细检查你的跟踪和故障模式。另外,不要害怕使用 LLM 来帮助你头脑风暴断言!
思考单元测试的最有效方法是将 LLM 的范围分解为功能和场景。例如,Lucy 的一个功能是能够找到房地产清单,我们可以将其分解为以下场景:
功能:清单查找器
要测试的这个功能是一个函数调用,它响应用户查找房地产清单的请求。例如,"请在加州圣何塞找到 3 间以上卧室且价格低于 200 万美元的清单"
LLM 将其转换为对 CRM 运行的查询。断言然后验证返回预期数量的结果。在我们的测试套件中,我们有三个用户输入,它们触发以下每个场景,然后执行相应的断言(这是一个为说明目的而过度简化的示例):
还有一些通用测试不特定于任何一个功能。例如,这是一个这样的通用测试的代码,它确保 UUID 不会在输出中提及:
const noExposedUUID = message => {
// Remove all text within double curly braces
const sanitizedComment = message.comment.replace(/\{\{.*?\}\}/g, '')
// Search for exposed UUIDs
const regexp = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ig
const matches = Array.from(sanitizedComment.matchAll(regexp))
expect(matches.length, 'Exposed UUIDs').to.equal(0, 'Exposed UUIDs found')
}
返回给 LLM 的 CRM 结果包含不应该显示给用户的字段;例如与条目关联的 UUID。我们的 LLM 提示词告诉 LLM 不要包含 UUID。我们使用简单的正则表达式来断言 LLM 响应不包含 UUID。
Rechat 有数百个这样的单元测试。当用户挑战 AI 或产品演变时,我们根据在数据中观察到的新故障持续更新它们。这些单元测试对于在迭代 AI 系统(提示词工程、改进 RAG 等)时快速获得反馈至关重要。许多人最终会超越他们的单元测试,随着产品的成熟而进入其他级别的评估,但不跳过这一步至关重要!
为了测试这些断言,你必须生成将触发所有你想要测试的场景的测试用例或输入。我经常利用 LLM 来合成生成这些输入;例如,这是 Rechat 用于为创建和检索联系人的功能生成合成输入的提示词之一:
Write 50 different instructions that a real estate agent can give to his assistant to create contacts on his CRM. The contact details can include name, phone, email, partner name, birthday, tags, company, address and job.
For each of the instructions, you need to generate a second instruction which can be used to look up the created contact.
. The results should be a JSON code block with only one string as the instruction like the following:
[
["Create a contact for John (johndoe@apple.com)",
"What's the email address of John Smith?"]
]
使用上述提示词,我们生成如下所示的测试用例:
[
[
'Create a contact for John Smith (johndoe@apple.com) with phone number 123-456-7890 and address 123 Apple St.',
'What\'s the email address of John Smith?'
],
[
'Add Emily Johnson with phone 987-654-3210, email emilyj@email.com, and company ABC Inc.',
'What\'s the phone number for Emily Johnson?'
],
[
'Create a contact for Tom Williams with birthday 10/20/1985, company XYZ Ltd, and job title Manager.',
'What\'s Tom Williams\' job title?'
],
[
'Add a contact for Susan Brown with partner name James Brown, and email susanb@email.com.',
'What\'s the partner name of Susan Brown?'
],
…
]
对于这些测试用例中的每一个,我们执行第一个用户输入来创建联系人。然后执行第二个查询来获取该联系人。如果 CRM 没有返回恰好 1 个结果,那么我们就知道在创建或获取联系人时出现了问题。我们还可以运行通用断言,比如验证 UUID 不在响应中的断言。你必须不断更新这些测试,因为你通过人工评估和调试观察数据。关键是让这些测试尽可能具有挑战性,同时代表用户与系统的交互。
你不需要等待生产数据来测试你的系统。你可以根据对用户如何使用你的产品的合理推测来生成合成数据。你也可以让一小部分用户使用你的产品,让他们的使用方式来优化你的合成数据生成策略。一个表明你正在编写优质测试和断言的信号是,当模型难以通过它们时——这些失败模式会成为你稍后可以用微调等技术解决的问题。
相关的一点是,与传统单元测试不同,你不一定需要 100% 的通过率。你的通过率是一个产品决策,取决于你愿意容忍的失败。
有许多方式可以编排第 1 级测试。Rechat 一直在利用 CI 基础设施(例如 GitHub Actions、GitLab Pipelines 等)来执行这些测试。然而,这部分工作流的工具仍处于初期阶段并快速演进。
我的建议是编排那些在你的技术栈中摩擦最少的测试。除了追踪测试,你还需要随时间追踪测试结果,以便你可以看到是否在进步。如果你使用 CI,你应该在 CI 系统外收集指标以及测试/提示词版本,便于分析和追踪。
我建议从简单开始,利用你现有的分析系统来可视化你的测试结果。例如,Rechat 使用 Metabase 来追踪他们的 LLM 测试结果。下面是 Rechat 用 Metabase 构建的仪表板截图:
这个截图显示了一个特定错误(以黄色显示)在我们解决之前(左)与解决之后(右)在 Lucy 中的普遍程度。
在你建立了坚实的第 1 级测试基础后,你可以转向其他形式的验证,这些验证无法仅通过断言来测试。执行人工和基于模型的评估的前置条件是记录你的追踪。
追踪是一个在软件工程中已经存在许久的概念,是一系列事件的日志,比如用户会话或请求通过分布式系统的流程。换句话说,追踪是日志的逻辑分组。在 LLM 的背景下,追踪通常指的是你与 LLM 进行的对话。例如,一条用户消息,后跟一个 AI 响应,再后跟另一条用户消息,就是一个追踪的例子。
记录 LLM 追踪的解决方案越来越多。Rechat 使用 LangSmith,它记录追踪并允许你以人类可读的方式查看它们,还有一个交互式播放器来迭代提示词。有时,记录你的追踪需要你对代码进行仪表化。在这种情况下,Rechat 使用 LangChain,它自动将追踪事件记录到 LangSmith。以下是它看起来的样子的截图:
我喜欢 LangSmith——它不要求你使用 LangChain,而且直观易用。搜索、过滤和阅读追踪是任何你选择的解决方案的必要功能。我发现有些工具没有正确实现这些基本功能!
你必须消除查看数据过程中的所有摩擦。这意味着以领域特定的方式呈现你的追踪。我经常发现,构建自己的数据查看和标记工具更好,这样我可以将所有需要的信息汇聚到一个屏幕上。在 Lucy 的情况下,我们需要查看许多信息源(追踪日志、CRM 等)来理解 AI 做了什么。这正是需要消除的摩擦类型。在 Rechat 的情况下,这意味着添加以下信息:
我为我处理的每个问题构建了这个工具的不同变体。有时,我甚至需要嵌入另一个应用程序来查看用户交互的样子。下面是我们为评估 Rechat 追踪而构建的工具的截图:
Lucy 特定的另一个设计选择是,我们注意到许多失败涉及 LLM 最终输出中的小错误(格式、内容等)。我们决定让人工可以编辑最终输出,这样我们可以为微调策划和修复数据。
这些工具可以用轻量级的前端框架如 Gradio、Streamlit、Panel 或 Shiny 在不到一天内构建。上面显示的工具是用 Shiny for Python 构建的。此外,还有像 Lilac 这样的工具,它使用 AI 语义搜索和过滤数据,这对于在调试问题时找到一组相似的数据点来说非常方便。
我通常先将示例标记为好或坏。我发现分配分数或更细粒度的评分比二元评分更繁琐管理。有一些高级技术可以用来使人工评估更有效或准确(例如,主动学习、共识投票等),但我建议从简单的东西开始。最后,像单元测试一样,你应该组织和分析你的人工评估结果,以评估你是否随时间推进。
我经常被问到要检查多少数据。开始时,你应该检查尽可能多的数据。我通常至少读取从所有测试用例和用户生成的追踪生成的追踪。你永远不能停止查看数据——世上没有免费午餐。然而,你可以随着时间的推移对你的数据进行更多采样,减轻负担。
许多供应商想向你兜售声称可以消除人类查看数据需要的工具。让人工定期评估至少一部分追踪是个好主意。我经常发现"正确性"在某种程度上是主观的,你必须将模型与人类对齐。
你应该追踪基于模型和人工评估之间的相关性,以决定你可以依赖多少自动化评估。此外,通过收集标记者的批评,解释他们为什么做出决定,你可以通过提示词工程或微调迭代评估者模型以与人类对齐。然而,我倾向于对评估者模型对齐使用提示词工程。
我喜欢使用 Excel 这样的低科技解决方案来迭代对齐基于模型的评估与人类。例如,我每隔几天向我的同事 Phillip 发送以下电子表格来为涉及自然语言查询生成器的不同用例进行评分。这个电子表格包含以下信息:
Phillip 然后填写他的相同信息版本——意味着他的批评、结果和对 25-50 个示例的期望响应(这些是下面带有"phillip_"前缀的列):
这个信息允许我随时间迭代批评模型的提示词,使其与 Phillip 充分对齐。这也很容易以低科技方式在电子表格中追踪:
这是一个电子表格的截图,我们在其中记录了我们尝试将基于模型的评估与人工评估者对齐的尝试。
使用你能负担得起的最强大的模型。要好好批评某些东西通常需要先进的推理能力。相对于你在生产中使用的东西,你通常可以使用更慢、更强大的模型来批评输出。
基于模型的评估是你更大问题内的一个元问题。你必须维护一个迷你评估系统来追踪它的质量。我有时曾在这个阶段微调一个模型(但我尽量不这样做)。
在使基于模型的评估者与人类对齐之后,你必须继续进行定期的练习来监控模型和人类协议。
在这个例子中,我们使用模型与人工评估器之间的一致性,因为我们的数据集大致是平衡的(大约 50% 的实例是失败的)。然而,使用原始一致性通常不建议,当类别不平衡时可能会产生误导。相反,你应该通常单独测量精度和召回率,以更准确地了解你的评估器的一致性。
我最喜欢关于创建好的评估器模型的方面是,它的批评可以用来整理高质量的合成数据,我稍后会提到这一点。
最后,进行 A/B 测试总是很好的,以确保你的 AI 产品正在驱动你想要的用户行为或结果。与其他类型的产品相比,LLM 的 A/B 测试没有太大的不同。如果你想了解更多关于 A/B 测试的内容,我建议阅读 Eppo 博客(由我以前合作过的同事创建的,他们在 A/B 测试方面是明星)。
可以推迟这个阶段,直到你充分准备好并确信你的 AI 产品适合向真实用户展示。这种级别的评估通常只适合更成熟的产品。
除了评估你的系统整体外,你还可以评估你的 AI 的子组件,如 RAG。评估 RAG 超出了本文的范围,但你可以在 Jason Liu 的文章中了解更多关于这个主题的信息。
除了快速迭代外,评估系统还能够解锁微调和调试的能力,这可以将你的 AI 产品提升到下一个层次。
Rechat 通过微调解决了许多仅通过提示工程无法解决的失败模式。微调最适合学习语法、风格和规则,而像 RAG 这样的技术为模型提供上下文或最新的事实。
微调涉及的 99% 的工作是组装覆盖你的 AI 产品表面积的高质量数据。然而,如果你有像 Rechat 的那样坚实的评估系统,你已经有了一个强大的数据生成和整理引擎!我将在未来的文章中更详细地展开微调的过程。
为了说明为什么一旦你有了评估系统,数据整理和合成就几乎不费力,考虑这样的情况:你想为之前提到的列表查找器创建额外的微调数据。首先,你可以使用 LLM 生成合成数据,使用这样的提示:
Imagine if Zillow was able to parse natural language. Come up with 50 different ways users would be able to search listings there. Use real names for cities and neighborhoods.
You can use the following parameters:
<omitted for confidentiality>
Output should be a JSON code block array. Example:
[
"Homes under $500k in New York"
]
这几乎与生成测试用例的练习相同!然后,你可以使用你的第 1 级和第 2 级测试来过滤掉失败断言或批评模型认为是错误的不理想数据。你也可以使用你现有的人工评估工具来查看跟踪记录,以为微调数据集整理跟踪记录。
当你收到有关你的 AI 产品的投诉或看到错误时,你应该能够快速调试。如果你有一个强大的评估系统,你已经有了:
一个可以搜索和过滤的跟踪记录数据库。
一组机制(断言、测试等),可以帮助你标记错误和不良行为。
日志搜索和导航工具,可以帮助你找到错误的根本原因。例如,错误可能是 RAG、代码中的错误或模型表现不佳。
快速测试响应错误的改变的效果的能力。
简而言之,评估所需的基础设施与调试所需的基础设施之间存在着巨大的重叠。
评估系统创建了一个飞轮,使你能够非常快速地迭代。这几乎总是人们在构建 AI 产品时卡住的地方。我希望这篇文章能给你关于如何构建你的评估系统的直觉。要记住的一些关键要点:
消除查看数据的所有摩擦。
保持简单。不要购买花哨的 LLM 工具。先用你已经有的。
如果你没有查看大量数据,说明你做错了。
不要依赖通用评估框架来衡量你的 AI 的质量。相反,为你的具体问题创建一个特定的评估系统。
编写大量测试并频繁更新它们。
LLM 可以用来解锁评估系统的创建。例子包括使用 LLM 来:
重用你的评估基础设施进行调试和微调。
如果你觉得这篇文章有帮助或有任何问题,我很乐意听到你的意见。我的电子邮件是 hamel@parlance-labs.com。
这篇文章改编自我与 Emil Sedgh 和 Hugo Browne-Anderson 在 Vanishing Gradients 播客上的对话。感谢 Jeremy Howard、Eugene Yan、Shreya Shankar、Jeremy Lewi 和 Joseph Gleasure 审阅这篇文章。