Published on

【译】你的 AI 产品需要评估

动机

五年前,我开始与语言模型合作,当时我领导的团队创建了 CodeSearchNet,这是 GitHub CoPilot 的前身。从那时起,我见证了许多成功和不成功的构建 LLM 产品的方法。我发现,不成功的产品几乎总是有一个共同的根本原因:未能创建强大的评估系统。

我目前是一名独立顾问,帮助公司构建特定领域的 AI 产品。我希望公司通过仔细阅读这篇文章能够节省数千美元的咨询费用。尽管我喜欢赚钱,但我讨厌看到人们一再犯同样的错误。

这篇文章概述了我对构建 LLM 驱动的 AI 产品评估系统的看法。

快速迭代 == 成功

与软件工程一样,AI 的成功取决于你能多快迭代。你必须拥有评估质量、调试问题和改变系统行为的流程和工具:

  1. 评估质量(例如:测试)。
  2. 调试问题(例如:记录和检查数据)。
  3. 改变行为或系统(提示工程、微调、编写代码)

许多人专注于上述第 #3 点,这使他们无法将 LLM 产品的改进超越演示。1 良好地完成这三项活动会创造一个良性循环,使优秀的 AI 产品与平庸的产品区分开来(请参见下面的图表以可视化此循环)。

如果你简化评估流程,所有其他活动都会变得容易。这与软件工程中的测试在长期内带来巨大的回报非常相似,尽管需要前期投资。

为了将这篇文章与现实世界的情况结合起来,我将通过一个案例研究来讲解我们如何构建一个快速改进的系统。我将主要关注评估,因为这是最关键的组成部分。

案例研究:Lucy,一个房地产 AI 助手

Rechat 是一个 SaaS 应用程序,允许房地产专业人士执行各种任务,例如管理合同、搜索房源、构建创意资产、管理约会等。Rechat 的论点是,你可以在一个地方完成所有事情,而不必在许多不同的工具之间切换上下文。

Rechat的AI助手,Lucy,是一个典型的AI产品:一个对话界面,消除了点击、输入和导航软件的需要。在Lucy的初始阶段,通过提示工程取得了快速进展。然而,随着Lucy的表面面积扩大,AI的性能达到了瓶颈。其症状包括:

  1. 解决一个失败模式导致其他模式的出现,类似于打地鼠游戏。
  2. 对AI系统在超出气氛检查的任务中的有效性可见性有限。
  3. 提示扩展为冗长且笨重的形式,试图涵盖众多边缘案例和示例。

问题:如何系统地改善AI?

为了突破这一瓶颈,我们创建了一种系统的方法来改善Lucy,以评估为中心. 我们的方法在下面的图表中进行了说明。

该图表是我改善AI系统的思维模型的诚意努力。实际上,这一过程是非线性的,可能呈现出许多不同的形式,可能与此图表相似,也可能不相似。

我在下面的评估背景中讨论了该系统的各个组成部分。

评估的类型

严格和系统的评估是整个系统中最重要的部分。这就是为什么“评估和策划”在图表中心以黄色突出显示的原因。你应该花费大部分时间来使你的评估更加稳健和高效。

需要考虑三个评估级别:

  • 级别 1:单元测试
  • 级别 2:模型与人工评估(包括调试)
  • 级别 3:A/B 测试

级别 3 的成本 > 级别 2 > 级别 1。这决定了你执行它们的节奏和方式。例如,我通常在每次代码更改时运行级别 1 评估,按设定节奏进行级别 2 评估,而级别 3 仅在重大产品更改后进行。在进入基于模型的测试之前,征服相当一部分级别 1 测试也是有帮助的,因为它们需要更多的工作和时间来执行。

没有严格的公式来决定何时引入每个测试级别。您需要平衡快速获取用户反馈、管理用户感知和您的 AI 产品目标。这与您在更一般的产品中必须进行的平衡行为并没有太大不同。

级别 1:单元测试

LLM 的单元测试是断言(就像您在 pytest 中编写的那样)。与典型的单元测试不同,您希望将这些断言组织起来,以便在单元测试之外的地方使用,例如数据清理和自动重试(使用断言错误进行纠正)在模型推理期间。重要的是,这些断言在您开发应用程序时应该快速且便宜地运行,以便您可以在每次代码更改时运行它们。如果您在思考断言时遇到困难,您应该批判性地检查您的跟踪和失败模式。此外,不要害怕使用 LLM 来帮助您头脑风暴断言!

步骤 1:编写范围测试

思考单元测试的最有效方法是将您的 LLM 的范围分解为功能和场景。例如,Lucy 的一个功能是查找房地产列表,我们可以将其分解为以下场景:

功能:列表查找器

要测试的这个功能是一个函数调用,它响应用户请求以查找房地产列表。例如,“请查找在加利福尼亚州圣荷西市,卧室超过 3 间且价格低于 200 万美元的房源”

LLM 将其转换为针对 CRM 运行的查询。然后,断言验证返回的结果数量是否符合预期。在我们的测试套件中,我们有三个用户输入触发以下每个场景,然后执行相应的断言(这是一个简化的示例,仅用于说明):

场景断言
只有一个列表匹配用户查询len(listing_array) == 1
多个列表匹配用户查询len(listing_array) > 1
没有列表匹配用户查询len(listing_array) == 0

还有一些通用测试,这些测试并不特定于任何一个功能。例如,下面是一个这样的通用测试的代码,它确保输出中没有提到 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')
}

CRM 返回给 LLM 的结果包含一些不应向用户展示的字段,例如与条目相关的 UUID。我们的 LLM 提示告知 LLM 不包括 UUID。我们使用简单的正则表达式来确保 LLM 的响应不包含 UUID。

Rechat 有数百个这样的单元测试。我们根据用户挑战 AI 或产品演变时观察到的新失败不断更新这些测试。 这些单元测试对于在迭代 AI 系统(提示工程、改进 RAG 等)时快速获取反馈至关重要。许多人最终会超越他们的单元测试,随着产品的成熟转向其他评估级别,但跳过这一步是至关重要的!

第 2 步:创建测试用例

要测试这些断言,您必须生成测试用例或输入,以触发您希望测试的所有场景。我经常利用 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 ([email protected])", 
  "What's the email address of John Smith?"]
]

利用上述提示,我们生成了以下测试用例:

[ 
    [
        'Create a contact for John Smith ([email protected]) 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 [email protected], 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 [email protected].', 
    'What\'s the partner name of Susan Brown?'
    ],
]

对于每个测试用例,我们执行第一个用户输入以创建联系人。然后,我们执行第二个查询以获取该联系人。如果 CRM 没有返回恰好 1 个结果,那么我们就知道在创建或获取联系人时出现了问题。我们还可以运行一些通用的断言,例如验证 UUID 不在响应中。随着你通过人工评估和调试观察数据,必须不断更新这些测试。关键是尽可能使这些测试具有挑战性,同时代表用户与系统的交互。

你不需要等待生产数据来测试你的系统。你可以对用户如何使用你的产品做出有根据的猜测,并生成合成数据。你还可以让一小部分用户使用你的产品,并让他们的使用情况来完善你的合成数据生成策略。一个你编写良好测试和断言的信号是模型在通过这些测试时遇到困难——这些失败模式成为你可以通过后续的微调等技术解决的问题。

在相关的注意事项中,与传统单元测试不同,你不一定需要 100% 的通过率。你的通过率是一个产品决策,取决于你愿意容忍的失败。

第 3 步:定期运行和跟踪你的测试

有很多方法可以协调 Level 1 测试。Rechat 一直在利用 CI 基础设施(例如,GitHub Actions、GitLab Pipelines 等)来执行这些测试。然而,这部分工作流程的工具仍处于初期阶段,并迅速发展。

我的建议是协调在你的技术栈中摩擦最小的测试。除了跟踪测试外,你还需要跟踪测试结果,以便你可以查看是否在取得进展。如果你使用 CI,你应该收集与测试/提示版本一起的指标,以便于分析和跟踪。

我建议从简单开始,利用你现有的分析系统来可视化你的测试结果。例如,Rechat 使用 Metabase 来跟踪他们的 LLM 测试结果。下面是 Rechat 使用 Metabase 构建的仪表板的截图:

此截图显示了在我们解决之前(左侧)与之后(右侧)Lucy中某个特定错误(以黄色显示)的普遍性。

Level 2: 人类与模型评估

在您建立了坚实的Level 1测试基础后,您可以继续进行其他形式的验证,这些验证不能仅通过断言进行测试。进行人类和基于模型的评估的前提是记录您的追踪。

记录追踪

追踪是一个在软件工程中已经存在了一段时间的概念,是一系列事件的日志,例如用户会话或通过分布式系统的请求流。换句话说,追踪是日志的逻辑分组。在LLM的上下文中,追踪通常指的是您与LLM之间的对话。例如,用户消息,接着是AI响应,然后是另一个用户消息,这就是一个追踪的例子。

目前有越来越多的解决方案用于记录LLM追踪。2 Rechat使用LangSmith,它记录追踪并允许您以人类可读的方式查看它们,并提供一个交互式的游乐场来迭代提示。有时,记录您的追踪需要您对代码进行仪器化。在这种情况下,Rechat使用LangChain,它会自动将追踪事件记录到LangSmith。以下是这看起来的截图:

我喜欢LangSmith - 它不要求您使用LangChain,并且直观易用。搜索、过滤和阅读追踪是您选择的任何解决方案的基本功能。我发现一些工具没有正确实现这些基本功能!

查看您的追踪

您必须消除查看数据过程中的所有摩擦。 这意味着以特定领域的方式呈现您的追踪。我经常发现自己构建数据查看和标记工具更好,这样我可以将所有需要的信息汇集到一个屏幕上。在Lucy的案例中,我们需要查看许多信息来源(追踪日志、CRM等)以理解AI的行为。这正是需要消除的摩擦类型。在Rechat的案例中,这意味着添加诸如:

  1. 评估了什么工具(功能)和场景。
  2. 跟踪是来自合成输入还是来自真实用户输入。
  3. 在不同工具和场景组合之间导航的过滤器。
  4. 当前记录的CRM和跟踪日志系统的链接。

我为我所处理的每个问题构建了不同版本的这个工具。有时,我甚至需要嵌入另一个应用程序,以查看用户交互的样子。下面是我们构建的用于评估Rechat跟踪的工具的截图:

针对Lucy的另一个设计选择是,我们注意到许多失败涉及LLM最终输出中的小错误(格式、内容等)。我们决定让最终输出可由人类编辑,以便我们可以策划和修复数据以进行微调。

这些工具可以使用轻量级前端框架如Gradio、Streamlit、Panel或Shiny在不到一天的时间内构建。上面显示的工具是用Python的Shiny构建的。此外,还有像Lilac这样的工具,它使用AI语义搜索和过滤数据,这在调试问题时非常方便,可以找到一组相似的数据点。

我通常从将示例标记为好或坏开始。我发现分配分数或更细粒度的评级比二元评级更难管理。您可以使用一些高级技术来提高人工评估的效率或准确性(例如,[主动学习](https://en.wikipedia.org/wiki/Active_learning_(machine_learning))、[共识投票](https://supervisely.com/blog/labeling-consensus/)等),但我建议从简单的开始。最后,像单元测试一样,您应该组织和分析您的人工评估结果,以评估您是否在不断进步。

如后文所述,这些标记的示例衡量您的系统质量,验证自动评估,并策划高质量的合成数据以进行微调。

您应该查看多少数据?

我经常被问到应该检查多少数据。在开始时,您应该尽可能多地检查数据。我通常至少阅读从所有测试用例生成的跟踪和用户生成的跟踪。__您永远无法停止查看数据——没有免费的午餐。__然而,您可以随着时间的推移对数据进行更多抽样,从而减轻负担。3

自动评估与 LLMs

许多供应商想要向您出售声称消除人类查看数据需求的工具。定期让人类评估至少一部分数据是个好主意。我经常发现“正确性”在某种程度上是主观的,您必须将模型与人类对齐。

您应该跟踪基于模型的评估与人类评估之间的相关性,以决定您可以多大程度上依赖自动评估。此外,通过收集标注者的批评,解释他们做出决策的原因,您可以通过提示工程或微调迭代评估模型,使其与人类对齐。然而,我倾向于使用提示工程来对齐评估模型。

我喜欢使用低技术解决方案,如 Excel,来迭代对齐基于模型的评估与人类。例如,我每隔几天就会向我的同事 Phillip 发送以下电子表格,以便对涉及 自然语言查询生成器 的不同用例进行评分。该电子表格将包含以下信息:

  1. 模型响应:这是 LLM 做出的预测。
  2. 模型批评:这是一个(通常更强大的)LLM 对您原始 LLM 预测的批评。
  3. 模型结果:这是批评模型对 模型响应 赋予的二元标签,表示为“好”或“坏”。

然后,Phillip 填写他版本的相同信息——即他的批评、结果和期望响应,通常一次处理 25-50 个示例(这些是下面以“phillip_”为前缀的列):

这些信息使我能够迭代批评模型的提示,使其随着时间的推移与 Phillip 充分对齐。这在电子表格中以低技术方式跟踪也很简单:

这是一个电子表格的截图,我们记录了对齐基于模型的评估与人类评估者的尝试。

关于基于模型的评估的一般提示:

  • 使用您能负担得起的最强大模型。通常需要高级推理能力才能很好地批评某些内容。相较于您在生产中使用的模型,您通常可以使用一个较慢但更强大的模型来批评输出。
  • 基于模型的评估是您更大问题中的一个元问题。您必须维护一个小型评估系统来跟踪其质量。我有时在这个阶段微调模型(但我尽量不这样做)。
  • 在将基于模型的评估者与人类对齐后,您必须继续进行定期练习,以监测模型与人类的一致性。

我最喜欢创建良好评估模型的一个方面是,它的批评可以用来策划高质量的合成数据,我稍后会提到这一点。

第三级:A/B 测试

最后,进行 A/B 测试总是好的,以确保您的 AI 产品能够驱动您期望的用户行为或结果。与其他类型的产品相比,LLM 的 A/B 测试并没有太大不同。如果您想了解更多关于 A/B 测试的信息,我推荐阅读 Eppo blog(这是我以前工作的同事创建的,他们在 A/B 测试方面非常出色)。

可以将这个阶段推迟到您充分准备并确信您的 AI 产品适合展示给真实用户时。这个级别的评估通常只适用于更成熟的产品。

评估 RAG

除了评估您的系统整体外,您还可以评估 AI 的子组件,例如 RAG。评估 RAG 超出了本文的范围,但您可以在 Jason Liu 的一篇文章 中了解更多关于这个主题的信息。

评估系统免费解锁超能力

除了快速迭代,评估系统还解锁了微调和调试的能力,这可以将您的 AI 产品提升到一个新的水平。

微调

Rechat 通过微调解决了许多仅通过提示工程无法解决的失败模式。微调最适合学习语法、风格和规则,而 RAG 等技术则为模型提供上下文或最新事实。

99% 的微调工作涉及到组装覆盖您 AI 产品表面区域的高质量数据。然而,如果您拥有像 Rechat 这样的稳健评估系统,您已经拥有一个强大的数据生成和策划引擎!我将在未来的帖子中进一步扩展微调的过程。4

数据合成与策划

为了说明一旦您拥有评估系统,数据策划和合成几乎是免费的原因,考虑一下您想为之前提到的列表查找器创建额外微调数据的情况。首先,您可以使用 LLMs 生成合成数据,提示如下:

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:

<ommitted for confidentiality>

Output should be a JSON code block array. Example:

[
"Homes under $500k in New York"
]

这几乎与生成测试用例的练习完全相同!然后,您可以使用您的 Level 1 和 Level 2 测试来过滤掉不合格的数据,这些数据会导致断言失败或被批评模型认为是错误的。您还可以使用现有的人类评估工具查看痕迹,以策划用于微调数据集的痕迹。

调试

当您收到投诉或看到与您的 AI 产品相关的错误时,您应该能够快速调试。如果您有一个强大的评估系统,您已经拥有:

  • 一个可以搜索和过滤的追踪数据库。
  • 一套机制(断言、测试等),可以帮助您标记错误和不良行为。
  • 日志搜索和导航工具,可以帮助您找到错误的根本原因。例如,错误可能是 RAG、代码中的 bug 或模型表现不佳。
  • 能够根据错误进行更改并快速测试其有效性。

简而言之,评估所需的基础设施与调试所需的基础设施之间有着极大的重叠。

结论

评估系统创建了一个飞轮,使您能够非常快速地迭代。这几乎总是人们在构建 AI 产品时遇到的瓶颈。我希望这篇文章能让您对如何构建评估系统有一些直觉。需要记住的一些关键要点:

  • 消除查看数据的所有摩擦。
  • 保持简单。不要购买花哨的 LLM 工具。首先使用您已有的工具。
  • 如果您没有查看大量数据,那么您就是在错误地进行。
  • 不要依赖通用评估框架来衡量您的 AI 质量。相反,创建一个特定于您问题的评估系统。
  • 编写大量测试并频繁更新它们。
  • LLM 可以用于解除创建评估系统的阻碍。示例包括使用 LLM 来:
    • 生成测试用例并编写断言
    • 生成合成数据
    • 批评和标记数据等。
  • 重新利用您的评估基础设施进行调试和微调。

Source: https://hamel.dev/blog/posts/evals