本文探讨了使用大型语言模型(LLM)构建产品的关键经验教训,并分享了作者团队在过去一年中从实际应用中总结出的宝贵经验。文章分为三个部分:战术、运营和战略,本篇是第一部分,重点介绍了LLM的战术技巧,包括提示、检索增强生成、流程工程以及评估和监控。

➡️ 提示

  • 作者建议从提示开始构建新的应用,因为它在正确使用时可以取得显著的效果,但同时也不要过高估计它的重要性,因为即使是基于提示的应用也需要大量的工程工作才能使其有效。
  • 作者推荐了一些经过验证的提示技巧,包括:
    • N-shot 提示 + 上下文学习:提供几个示例来演示任务,并使输出与预期相符。
    • 思维链提示:鼓励LLM在返回最终答案之前解释其思考过程。
    • 提供相关资源:通过检索增强生成(RAG)提供模型可以直接利用的文本片段,帮助模型更好地理解上下文。
  • 作者强调了结构化输入和输出的重要性,并举例说明了如何使用结构化输入来帮助模型更好地理解输入,以及如何使用结构化输出来简化与下游系统的集成。
  • 作者建议将大型提示分解成多个小型提示,每个提示只做一件事,并专注于做好这件事。
  • 作者还建议仔细思考模型需要的上下文信息,并优化上下文结构,以突出上下文各个部分之间的关系。

➡️ 信息检索/RAG

  • 作者指出,RAG的有效性取决于检索到的文档的相关性、密度和细节。
  • 作者建议不要忽视关键词搜索,将其作为基线并将其用于混合搜索。
  • 作者认为,在大多数情况下,混合搜索效果最好,即使用关键词匹配来查找明显的匹配项,并使用嵌入来查找同义词、上位词和拼写错误,以及多模态(例如图像和文本)。
  • 作者建议在新的知识方面优先考虑RAG而不是微调。
  • 作者认为,即使出现了长上下文模型,RAG也不会过时。

➡️ 调整和优化工作流程

  • 作者建议使用分步、多轮“流程”来提高性能。
  • 作者建议优先考虑确定性工作流程,因为它们更容易部署和调试。
  • 作者建议使用缓存来降低成本和消除生成延迟。
  • 作者建议在必要时进行微调,但要权衡其成本和收益。

➡️ 评估和监控

  • 作者建议创建一些基于断言的单元测试,这些测试基于真实的输入/输出样本,并根据至少三个标准来预期输出。
  • 作者建议使用LLM作为评判者来评估其他LLM的输出,但要意识到它不是万能的。
  • 作者建议使用“实习生测试”来评估生成,即如果将相同的输入和上下文提供给相关专业的普通大学生,他们是否能够成功完成任务?
  • 作者警告说,过度强调某些评估指标可能会损害整体性能。
  • 作者建议将标注简化为二元任务或成对比较。
  • 作者认为,无参考评估和护栏可以互换使用。
  • 作者指出,LLM即使不应该生成输出也会生成输出,因此需要使用护栏来检测和过滤/重新生成不希望的输出。
  • 作者认为,幻觉是一个顽固的问题,需要结合提示工程和事实不一致护栏来解决。

原文

我们从一年与大语言模型 (LLMs) 的构建中学到了什么 (第一部分)

现在是一个用大语言模型 (Large Language Models, LLMs) 构建产品的激动人心的时刻。在过去的一年里,LLMs 已经达到了“足够好”可以应用于实际场景的水平。LLMs 的进步速度加上社交媒体上的众多演示,预计将推动到 2025 年对人工智能的 2000 亿美元投资。LLMs 也广泛开放,使得不仅仅是机器学习工程师和科学家,所有人都可以将智能融入到他们的产品中。虽然构建 AI 产品的门槛已经降低,但要创建那些在演示之外也能有效的产品仍然是一个复杂的任务。

我们已经发现了一些开发基于 LLMs 产品的关键但常常被忽视的教训和方法。了解这些概念可以让你在无需机器学习专业知识的情况下,比大多数同行更具竞争力!在过去的一年里,我们六个人一直在构建基于 LLMs 的实际应用。我们意识到有必要将这些经验汇集在一起,造福社区。

我们来自不同的背景,担任不同的角色,但我们都亲身经历了使用这项新技术的挑战。我们中的两人是独立顾问,帮助众多客户将 LLM 项目从初始概念转化为成功的产品,看到了成功与失败的模式。我们中有一人是研究人员,研究机器学习/人工智能团队的工作方式及其工作流程的改进。我们中的两人在应用 AI 团队中担任领导职务:一人在科技巨头公司,另一人在初创公司。最后,我们中的一人教授过数千人学习深度学习课程,现在致力于使 AI 工具和基础设施更易于使用。尽管我们的经历不同,但我们惊讶地发现我们学到的经验有着一致的主题,这些见解没有被广泛讨论。

我们的目标是制作一个实用指南,帮助大家围绕 LLMs 构建成功的产品,从我们的经验中汲取教训,并指出行业中的一些例子。我们在过去的一年里亲自动手,获取了宝贵的经验,往往是通过艰难的方式。虽然我们不敢说代表整个行业,但我们在这里分享了一些对任何构建 LLM 产品的人都有用的建议和经验。

这项工作分为三个部分:战术、运营和战略。这是三篇中的第一篇,深入探讨了使用 LLMs 的战术细节。我们分享了关于提示、设置检索增强生成 (Retrieval-Augmented Generation, RAG)、应用流程工程以及评估和监控的最佳实践和常见陷阱。无论你是构建 LLMs 的实践者还是周末项目的爱好者,这一

部分都是为你而写的。请关注即将推出的运营和战略部分。

准备好深入研究了吗?让我们开始吧。

战术

在本节中,我们分享了新兴 LLM 堆栈核心组件的最佳实践:提示技巧以提高质量和可靠性,评估策略以评估输出,检索增强生成的创意以改进基础,以及更多内容。我们还探讨了如何设计人机交互工作流程。尽管技术仍在迅速发展,但我们希望这些经验教训——我们共同进行无数实验的副产品——能够经得起时间的考验,帮助你构建和发布强大的 LLM 应用。

提示

我们建议在开发新应用程序时从提示开始。很容易低估和高估提示的重要性。低估是因为正确的提示技术如果使用得当,可以走得很远。高估是因为即使是基于提示的应用程序也需要大量工程工作才能正常运行。

专注于充分利用基本的提示技术

一些提示技术在各种模型和任务中一致地帮助提高性能:n-shot 提示加上上下文学习、链式思维 (Chain-of-Thought, CoT) 以及提供相关资源。

n-shot 提示的上下文学习是指向 LLM 提供一些示例,以展示任务并使输出与我们的期望一致。以下是一些提示:

  • 如果 n 太低,模型可能会过度依赖这些特定示例,损害其泛化能力。作为经验法则,n 应大于或等于 5,不要害怕达到几十个。
  • 示例应代表预期输入分布。如果你在构建电影摘要生成器,请包括不同类型的样本,大致按你期望在实际中看到的比例。
  • 你不一定需要提供完整的输入-输出对。在许多情况下,所需输出的示例就足够了。
  • 如果你使用支持工具使用的 LLM,你的 n-shot 示例也应使用你希望智能体使用的工具。

在链式思维 (CoT) 提示中,我们鼓励 LLM 在返回最终答案之前解释其思维过程。可以将其视为给 LLM 提供了一个草稿板,这样它就不必全靠记忆来完成。最初的方法是简单地在指令中添加短语“让我们一步一步思考”。然而,我们发现将 CoT 具体化更有帮助,添加一两句话的具体性通常可以显著减少幻觉。例如,在要求 LLM 总结会议记录时,我们可以明确步骤,例如:

  • 首先,在草稿板上列出关键决策、跟进项目和相关负责人。
  • 然后,检查草稿板中的细节是否与会议记录一致。
  • 最后,将关键点综合成简洁的摘要。

最近,有一些质疑认为这一技术是否真的如想象的那么强大。此外,对于链式思维在推理过程中到底发生了什么,存在重大争议。尽管如此,当可能时,这一技术仍值得尝试。

提供相关资源是一种扩展模型知识库、减少幻觉并增加用户信任的强大机制。通常通过检索增强生成 (RAG) 来实现,向模型提供它可以直接利用的文本片段是一种基本技术。当提供相关资源时,不仅仅是包含它们;别忘了告诉模型优先使用这些资源,直接参考它们,有时还要提到当没有足够资源时。这些都有助于将智能体的响应“基础”到一组资源上。

结构化你的输入和输出

结构化输入和输出有助于模型更好地理解输入并返回可以可靠地与下游系统集成的输出。向输入添加序列化格式可以帮助提供更多线索,说明上下文中 Token 之间的关系,向特定 Token 添加额外的元数据 (例如类型),或将请求与模型训练数据中的类似示例关联。

例如,互联网上许多关于编写 SQL 的问题都以指定 SQL 架构开始。因此,你可能会期望,文本到 SQL 的有效提示应包括结构化的架构定义;确实如此。

结构化输出具有类似的作用,但它还简化了与系统下游组件的集成。Instructor 和 Outlines 适用于结构化输出。(如果你正在导入 LLM API SDK,请使用 Instructor;如果你正在导入 Huggingface 以自托管模型,请使用 Outlines。) 结构化输入清晰地表达任务,并类似于训练数据的格式,从而提高了获得更好输出的概率。

使用结构化输入时,要注意每个 LLM 系列都有其偏好。Claude 偏好 XML,而 GPT 偏好 Markdown 和 JSON。使用 XML 时,你甚至可以通过提供 response 标签预填 Claude 的响应,如下所示:

messages=[
    {
        "role": "user",
        "content": """Extract the <name>, <size>, <price>, and <color> 
        from this product description into your <response>.   
        <description>The SmartHome Mini 
        is a compact smart home assistant 
        available in black or white for only $49.99. 
        At just 5 inches wide, it lets you control 
        lights, thermostats, and other connected 
        devices via voice or app—no matter where you
        place it in your home. This affordable little hub
        brings convenient hands-free control to your
        smart devices.             
        </description>"""
   },
   {
        "role": "assistant",
        "content": "<response><name>"
   }
]

小提示要做一件事,并且只做一件事

在软件中,一个常见的反模式/代码气味是“God Object”,即一个类或函数做所有事情。同样的道理也适用于提示。

一个提示通常从简单开始:几句话的指令,几个示例,我们就可以开始了。但当我们试图提高性能并处理更多边缘情况时,复杂性逐渐增加。更多的指令。多步骤推理。几十个示例。转眼间,我们最初简单的提示现在变成了 2000 个 Token 的怪物。而且更糟糕的是,它在更常见和更简单的输入上的性能变差了!GoDaddy 分享了这一挑战,作为他们在构建 LLMs 时的第一个教训。

就像我们努力 (读:挣扎) 保持系统和代码简单一样,我们也应该对提示保持同样的态度。与其为会议记录摘要生成器编写一个全能的提示,我们可以将其分解为多个步骤:

  • 提取关键决策、行动项目和负责人的结构化格式
  • 检查提取的细节与原始记录的一致性
  • 从结构化细节生成简洁的摘要

结果,我们将单个提示分解为多个简单、专注且易于理解的提示。通过分解它们,我们现在可以分别迭代和评估每个提示。

打造你的上下文 Token

重新思考并挑战你关于需要发送给智能体的上下文量的假设。像米开朗基罗一样,不要建立你的上下文雕塑——雕刻掉多余的材料直到雕塑显现出来。RAG 是收集所有可能相关块的流行方法,但你做了什么来提取必要的内容?

我们发现,将发送给模型的最终提示——包含所有上下文构建、元提示和 RAG 结果——放在一个空白页面上阅读,确实有助于重新思考你的上下文。通过这种方法,我们发现了冗余、自相矛盾的语言和糟糕的格式。

另一个关键优化是你的上下文结构。你的文档袋表示对人类没有帮助,不要认为它对智能体有用。仔细考虑如何构建你的上下文以突出部分之间的关系,并使提取尽可能简单。

信息检索/RAG

除了提示,另一种引导 LLM 的有效方法是作为提示的一部分提供知识。这使 LLM 在提供的上下文基础上进行上下文学习。这被称为检索增强生成 (RAG)。实践者发现 RAG 在提供知识和改善输出方面有效,而所需的努力和成本远低于微调。RAG 的效果取决于检索到的文档的相关性、密度和细节

RAG 输出的质量取决于检索到的文档的质量,可以从几个方面来考虑。

第一个也是最明显的指标是相关性。通常通过排名指标量化,例如平均倒数排名 (Mean Reciprocal Rank, MRR) 或归一化折现累计增益 (Normalized Discounted Cumulative Gain, NDCG)。MRR 评估系统在排名列表中将第一个相关结果放置得多好,而 NDCG 考虑所有结果的相关性及其位置。它们衡量系统将相关文档排名靠前和无关文档排名靠后的能力。例如,如果我们在检索用户摘要以生成电影评论摘要,我们希望将特定电影的评论排名靠前,而排除其他电影的评论。

像传统推荐系统一样,检索到的项目的排名将对 LLM 在下游任务中的表现产生重大影响。为了衡量影响,运行基

于 RAG 的任务,但将检索到的项目打乱——RAG 输出的表现如何?

其次,我们还要考虑信息密度。如果两个文档同样相关,我们应该偏好更简洁、无多余细节的文档。回到我们的电影示例,我们可能会认为电影剧本和所有用户评论在广义上都是相关的。然而,评分最高的评论和编辑评论可能信息密度更高。

最后,考虑文档提供的细节水平。假设我们正在构建一个 RAG 系统以从自然语言生成 SQL 查询。我们可以仅提供表架构和列名作为上下文。但如果我们包括列描述和一些代表值呢?额外的细节可以帮助 LLM 更好地理解表的语义,从而生成更正确的 SQL。

不要忘记关键字搜索;将其作为基线并在混合搜索中使用。

鉴于基于嵌入的 RAG 演示的普遍性,很容易忘记或忽视信息检索领域数十年的研究和解决方案。

尽管嵌入无疑是一种强大的工具,但它们并不是万能的。首先,虽然它们擅长捕捉高层语义相似性,但它们可能难以处理更具体的基于关键字的查询,如用户搜索名字 (例如 Ilya)、首字母缩略词 (例如 RAG) 或 ID (例如 claude-3-sonnet)。基于关键字的搜索,如 BM25,专为此设计。经过多年的基于关键字的搜索,用户可能已将其视为理所当然,如果未检索到他们预期的文档,可能会感到沮丧。

向量嵌入并不能神奇地解决搜索问题。实际上,繁重的工作在于你使用语义相似性搜索重新排序之前的步骤。真正超越 BM25 或全文搜索是很难的。 — Aravind Srinivas, Perplexity.ai CEO

我们已经向客户和合作伙伴传达了这一点几个月了。使用简单嵌入的最近邻搜索产生的结果非常嘈杂,你可能更适合从基于关键字的方法开始。 — Beyang Liu, Sourcegraph CTO

其次,更容易理解为什么使用关键字搜索检索到某个文档——我们可以查看与查询匹配的关键字。相比之下,基于嵌入的检索则不那么可解释。最后,得益于 Lucene 和 OpenSearch 等经过数十年优化和实战测试的系统,关键字搜索通常更具计算效率。

在大多数情况下,混合方法效果最佳:关键字匹配用于明显的匹配,嵌入用于同义词、上位词和拼写错误以及多模态 (例如图像和文本)。Shortwave 分享了他们如何构建 RAG 管道,包括查询重写、关键字 + 嵌入检索和排名。

新知识优先选择 RAG 而不是微调

RAG 和微调都可以用来将新信息纳入 LLM 并提高特定任务的性能。那么,应该先尝试哪一个呢?

最近的研究表明 RAG 可能更有优势。一项研究将 RAG 与无监督微调 (即持续预训练) 进行了比较,在 MMLU 子集和当前事件上进行了评估。他们发现 RAG 在训练期间遇到的知识以及全新知识方面,表现都优于微调。在另一篇论文中,他们将 RAG 与有监督微调在农业数据集上进行了比较。同样,RAG 的性能提升比微调更大,尤其是对于 GPT-4 (参见论文的表 20)。

除了提高性能之外,RAG 还具有几个实际优势。首先,与持续预训练或微调相比,保持检索索引最新更容易——而且更便宜!其次,如果我们的检索索引包含有问题的文档 (例如含有有害或偏见内容),我们可以轻松删除或修改这些有问题的文档。

此外,RAG 中的“检索”部分提供了对如何检索文档的更细粒度控制。例如,如果我们为多个组织托管 RAG 系统,通过划分检索索引,我们可以确保每个组织只能从自己的索引中检索文档。这确保我们不会无意中将一个组织的信息泄露给另一个组织。

长上下文模型不会使 RAG 过时

随着 Gemini 1.5 提供多达 1000 万个 Token 大小的上下文窗口,一些人开始质疑 RAG 的未来。

我倾向于认为 Sora 对 Gemini 1.5 的宣传过度了。1000 万个 Token 的上下文窗口有效地使现有的大多数 RAG 框架变得不必要——你只需将你的数据放入上下文中,像往常一样与模型对话。想象一下,这对所有以 RAG 为中心的初创公司/智能体/LangChain 项目意味着什么,其中大部分工程工作都在 RAG 上😅。或者一句话:1000 万上下文杀死了 RAG。好工作,Gemini。 — Yao Fu

虽然确实长上下文对分析多个文档或与 PDF 进行对话等用例将是一个游戏规则改变者,但 RAG 的消亡传闻被大大夸大了。

首先,即使有一个 1000 万 Token 的上下文窗口,我们仍然需要选择要提供给模型的信息。其次,除了狭窄的针-in-a-haystack 评估外,我们还没有看到令人信服的数据表明模型可以有效地推理如此大的上下文。因此,如果没有好的检索 (和排名),我们有可能用分散注意力的信息淹没模型,甚至可能用完全不相关的信息填充上下文窗口。

最后是成本。Transformer 的推理成本随着上下文长度呈二次 (或线性空间和时间) 增长。仅仅因为存在可以在回答每个问题之前读取你组织的整个 Google Drive 内容的模型并不意味着这是个好主意。考虑一个类比我们如何使用 RAM:我们仍然从磁盘读取和写入,尽管存在具有数十TB内存的计算实例。

所以不要急着扔掉你的 RAG。这种模式即使在上下文窗口变大时也会继续有用。

调优和优化工作流程

提示 LLM 只是开始。要充分利用它们,我们需要超越单个提示并拥抱工作流程。例如,我们如何将一个大提示分解为多个小提示,可以获得更好的结果。例如,AlphaCodium:通过从单一提示切换到多步骤工作流程,他们将 GPT-4 在 CodeContests 上的准确率 (pass@5) 从 19% 提高到 44%。该工作流程包括:

  • 反思问题
  • 推理公共测试
  • 生成可能的解决方案
  • 对可能的解决方案进行排序
  • 生成合成测试
  • 在公共和合成测试上迭代解决方案。

小任务具有明确目标,使智能体或流程提示最有效。并非每个智能体提示都要求结构化输出,但结构化输出有助于与负责协调智能体与环境交互的系统接口。

一些可以尝试的事情:

  • 明确的规划步骤,尽可能具体。考虑预定义的计划供选择 (例如 https://youtu.be/hGXhFa3gzBs?si=gNEGYzux6TuB1del)。
  • 将原始用户提示重写为智能体提示。小心,这个过程是有损的!
  • 智能体行为作为线性链、DAG 和状态机;不同的依赖和逻辑关系对不同的规模可能更适合。你能从不同的任务架构中挤出性能优化吗?
  • 规划验证;你的规划可以包括如何评估其他智能体响应的指示,以确保最终的组合正常工作。
  • 固定上游状态的提示工程——确保你的智能体提示针对可能在之前发生的变体集合进行评估。

目前优先考虑确定性工作流程

虽然 AI 智能体可以动态响应用户请求和环境,但其非确定性本质使其部署成为挑战。智能体采取的每一步都有失败的可能,恢复错误的几率很低。因此,智能体成功完成多步骤任务的可能性随着步骤数量的增加呈指数下降。结果是,构建智能体的团队发现很难部署可靠的智能体。

一种有前途的方法是生成确定性计划的智能体系统,然后以结构化、可重复的方式执行这些计划。在第一步中,给定一个高层次的目标或提示,智能体生成一个计划。然后,计划按确定性执行。这使得每一步更可预测和可靠。好处包括:

  • 生成的计划可以作为少样本示例来提示或微调智能体。
  • 确定性执行使系统更可靠,因此更容易测试和调试。此外,失败可以追溯到计划中的特定步骤。
  • 生成的计划可以表示为有向无环图 (DAG),相对于静态提示,更容易理解和适应新情况。

最成功的智能体构

建者可能是那些在管理初级工程师方面有丰富经验的人,因为生成计划的过程类似于我们指导和管理初级工程师的方式。我们给初级工程师明确的目标和具体的计划,而不是模糊的开放式指示,我们也应该对智能体这样做。

最终,构建可靠、工作的智能体的关键可能在于采用更结构化、确定性的方法,并收集数据以完善提示和微调模型。如果没有这些,我们将构建的智能体可能在某些时候表现非常出色,但平均而言会让用户失望,导致用户留存率低。

获得更多多样化输出,不仅仅是调整温度参数

假设你的任务需要 LLM 的输出具有多样性。也许你正在编写一个 LLM 管道,基于用户以前购买的产品列表建议购买的产品。当多次运行提示时,你可能会注意到结果推荐过于相似——所以你可能会增加 LLM 请求中的温度参数。

简而言之,增加温度参数使 LLM 响应更加多样化。在采样时,下一个 Token 的概率分布变得更平坦,意味着通常不太可能的 Token 更经常被选中。然而,即使增加温度,你可能会注意到一些与输出多样性相关的失败模式。例如,一些可能合适的产品可能从未被 LLM 输出。少数几个产品在输出中可能会被过度代表,因为根据 LLM 在训练时学到的知识,它们很可能跟随提示。如果温度过高,你可能会得到引用不存在产品 (或胡言乱语) 的输出。

换句话说,增加温度并不能保证 LLM 将从你期望的概率分布中采样输出 (例如,均匀随机)。尽管如此,我们还有其他方法可以增加输出多样性。最简单的方法是调整提示中的元素。例如,如果提示模板包含一个项目列表,如历史购买,每次将这些项目插入提示时打乱顺序可以产生显著差异。

此外,保持一个最近输出的短列表可以帮助防止重复。在我们推荐产品的示例中,通过指示 LLM 避免建议这个最近列表中的项目,或通过拒绝和重新采样类似于最近建议的输出,我们可以进一步多样化响应。另一种有效策略是改变提示中的措辞。例如,在提示中加入“选择用户会经常使用的项目”或“选择用户可能会推荐给朋友的产品”这样的短语可以改变焦点,从而影响推荐产品的多样性。

缓存被低估了

缓存通过消除对相同输入重新计算响应的需要来节省成本并消除生成延迟。此外,如果响应之前已经过防护栏,可以提供这些经过验证的响应,减少提供有害或不适当内容的风险。

一种简单的缓存方法是使用被处理项目的唯一 ID,例如,如果我们在总结新闻文章或产品评论。当请求到来时,我们可以检查缓存中是否已有摘要。如果有,我们可以立即返回;如果没有,我们生成、防护栏并提供它,然后将其存储在缓存中以备将来请求。

对于更开放式的查询,我们可以借用搜索领域的技术,该领域也利用缓存来处理开放式输入。自动完成和拼写纠正等功能也有助于标准化用户输入,从而提高缓存命中率。

何时进行微调

我们可能会遇到一些任务,即使是最巧妙设计的提示也无法胜任。例如,即使进行了大量的提示工程,我们的系统可能仍然无法返回可靠、高质量的输出。如果是这样,那么可能需要为你的特定任务微调模型。

成功的例子包括:

  • Honeycomb 的自然语言查询助手:最初,“编程手册”与 n-shot 示例一起提供用于上下文学习。虽然这工作得相当不错,但微调模型使其在领域特定语言的语法和规则方面表现更好。
  • ReChat 的 Lucy:LLM 需要以一种非常特定的格式生成响应,该格式结合了结构化和非结构化数据,以便前端正确呈现。微调对于使其始终如一地工作至关重要。

尽管如此,虽然微调可能有效,但它带来了显著的成本。我们必须标注微调数据、微调和评估模型,最终自托管它们。因此,考虑是否值得支付较高的前期成本。如果提示让你达到了 90% 的目标,那么微调可能不值得投资。然而,如果我们决定进行微调,为了降低收集人工标注数据的成本,我们可以生成并在合成数据上微调,或在开源数据上引导。

评估与监控

评估 LLM 可以是一片雷区。LLM 的输入和输出是任意文本,我们为它们设置的任务是多种多样的。尽管如此,严格和深思熟虑的评估至关重要——OpenAI 的技术领导者致力于评估并对个人评估提供反馈并非巧合。

评估 LLM 应用程序需要多种定义和减少:它只是单元测试,还是更像可观察性,或者只是数据科学。我们发现所有这些观点都很有用。在接下来的部分中,我们分享了一些关于构建评估和监控管道的重要经验。

从实际输入/输出样本中创建一些基于断言的单元测试

创建单元测试 (即断言),由生产中的输入和输出样本组成,并基于至少三个标准对输出的期望进行评估。虽然三个标准似乎是任意的,但它是一个实际的起点;更少的标准可能表明你的任务定义不够明确或过于开放,如通用聊天机器人。这些单元测试或断言应由管道的任何更改触发,无论是编辑提示、通过 RAG 添加新上下文,还是其他修改。此写作中有一个实际用例的断言测试示例。

考虑从指定短语或想法在所有响应中包括或排除的断言开始。还可以考虑确保单词、项目或句子计数在范围内的检查。对于其他类型的生成,断言可以看起来不同。执行评估是评估代码生成的一种强大方法,其中你运行生成的代码并确定运行时状态是否足以满足用户请求。

例如,如果用户请求一个名为 foo 的新函数;那么在执行智能体生成的代码后,foo 应该是可调用的!执行评估中的一个挑战是智能体代码经常将运行时留在与目标代码略有不同的形式。放宽断言到任何可行答案都会满足的绝对最弱假设可能是有效的。

最后,按预期用作客户使用你的产品 (即“内部使用”) 可以提供关于实际数据中故障模式的见解。这种方法不仅有助于识别潜在的弱点,还提供了一个有用的生产样本来源,可以转换为评估。

LLM 作为评判者可以工作 (在某种程度上),但它不是万能的

LLM 作为评判者,即使用强大的 LLM 来评估其他 LLM 的输出,受到了一些人的怀疑 (我们中的一些人最初是巨大的怀疑者)。尽管如此,当实现得当时,LLM 作为评判者可以实现与人类判断的良好相关性,至少可以帮助建立对新提示或技术表现的先验假设。具体来说,在进行成对比较 (例如对照与处理) 时,LLM 作为评判者通常可以正确判断方向,尽管胜/负的幅度可能会有噪音。

以下是一些从 LLM 作为评判者中获得最佳效果的建议:

  • 使用成对比较:不要让 LLM 在李克特量表上为单个输出评分,而是呈现两个选项并要求选择更好的一个。这往往会导致更稳定的结果。
  • 控制位置偏差:呈现选项的顺序会影响 LLM 的决策。为了减轻这种影响,每次成对比较两次,交换每次的顺序。只要确保交换后将胜利归因于正确的选项!
  • 允许平局:在某些情况下,两个选项可能同样好。因此,允许 LLM 宣布平局,这样它就不必随意选择赢家。
  • 使用链式思维:让 LLM 在给出最终偏好之前解释其决策可以提高评估的可靠性。作为奖励,这样你可以使用较弱但更快的 LLM 仍然达到类似的结果。由于这一部分的管道通常是批处理模式的,因此链式思维带来的额外延迟不是问题。
  • 控制响应长度:LLMs 倾向于偏向较长的响应。为了减轻这一点,请确保响应对的长度相似。

LLM 作为评判者的一个特别强大的应用是检查新提示策略是否出现回归。如果你跟踪了一组生产结果,有时你可以使用新提示策略重新运行这些生产示例,并使用 LLM 作为评判者快速评估新策略可能会受挫的地方。

这是一个简单但有效的方法来迭代 LLM 作为评判者,我们只是记录 LLM

响应、评判者的评论 (即链式思维) 和最终结果。然后与利益相关者一起审查以确定改进领域。经过三次迭代,与人类和 LLM 的一致性从 68% 提高到 94%!

LLM 作为评判者并不是万能的。语言的一些微妙方面,即使是最强大的模型也无法可靠评估。此外,我们发现传统分类器和奖励模型可以比 LLM 作为评判者实现更高的准确性,并且成本和延迟更低。对于代码生成,LLM 作为评判者可能比更直接的评估策略 (如执行评估) 弱。

评估生成的“实习生测试”

我们喜欢在评估生成时使用以下“实习生测试”:如果你将语言模型的确切输入,包括上下文,作为任务交给相关专业的普通大学生,他们能成功吗?需要多长时间?

如果答案是否定的,因为 LLM 缺乏所需的知识,请考虑丰富上下文的方法。

如果答案是否定的,并且我们根本无法改进上下文以解决问题,那么我们可能遇到了对当代 LLM 来说过于困难的任务。

如果答案是肯定的,但需要一些时间,我们可以尝试减少任务的复杂性。它是可分解的吗?任务的某些方面可以变得更加模板化吗?

如果答案是肯定的,他们会很快掌握,那么就该深入研究数据了。模型做错了什么?我们能找到失败的模式吗?尝试在模型响应之前或之后让模型解释自己,帮助你建立思维理论。

过分强调某些评估可能会损害整体性能

“当一个度量成为目标时,它就不再是一个好的度量。” — 古德哈特定律

一个例子是大海捞针 (NIAH) 评估。最初的评估帮助量化了随着上下文大小增加模型的召回率,以及针位置对召回率的影响。然而,它被过度强调,以至于成为 Gemini 1.5 报告的图 1。评估涉及将特定短语 (如“特定魔法 {city} 数字是:{number}”) 插入重复 Paul Graham 文章的长文档中,然后提示模型回忆魔法数字。

虽然一些模型实现了近乎完美的召回率,但 NIAH 是否真正反映了实际应用中所需的推理和召回能力是值得怀疑的。考虑一个更实际的场景:给定一个小时的会议记录,LLM 能否总结出关键决策和下一步行动,并正确地将每个项目归属于相关人员?这个任务更现实,不仅超越了机械记忆,还考虑了解析复杂讨论、识别相关信息和综合摘要的能力。

这是一个实用 NIAH 评估的例子。使用医生-患者视频通话的记录,LLM 被询问患者的药物。它还包括一个更具挑战性的 NIAH,将随机披萨配料的短语插入,例如“完美披萨所需的秘密配料是:浸泡在浓缩咖啡中的枣子、柠檬和山羊奶酪。”在药物任务上的召回率约为 80%,在披萨任务上的召回率约为 30%。

顺便提一下,过分强调 NIAH 评估可能导致在提取和总结任务上的性能下降。因为这些 LLM 被如此微调以关注每一句话,它们可能会开始将不相关的细节和干扰视为重要,从而在最终输出中包括它们 (当它们不应该时!)

这也可能适用于其他评估和用例。例如,总结。强调事实一致性可能会导致摘要不那么具体 (因此不太可能在事实上一致) 并可能不那么相关。相反,强调写作风格和文雅可能会导致更花哨、更多营销类型的语言,可能会引入事实不一致。

简化注释为二元任务或成对比较

为模型输出提供开放式反馈或评分 (例如在李克特量表上) 是认知负担很重的任务。结果是,收集的数据更噪音——由于人类评分者之间的变异性——因此不那么有用。一种更有效的方法是简化任务并减轻注释者的认知负担。两种有效的任务是二元分类和成对比较。

在二元分类中,注释者被要求对模型的输出做出简单的是或否的判断。他们可能被问到生成的摘要是否与源文档在事实上一致,或建议的响应是否相关,或是否包含有害内容。相比李克特量表,二元决策更精确,评分者之间的一致性更高,并且具有更高的吞吐量。这就是 Doordash 设置他们的标注队列以通过一系列是-否问题标记菜单项的方式。

在成对比较中,注释者会看到一对模型响应并被要求选择更好的一个。因为人类更容易说“A 比 B 好”而不是为 A 或 B 单独评分,这会导致更快和更可靠的注释 (相对于李克特量表)。在 Llama2 会议上,Llama2 论文的作者之一 Thomas Scialom 证实,相对于收集监督微调数据 (例如书面回应),成对比较速度更快且成本更低。前者的成本为每单位 3.5 美元,而后者的成本为每单位 25 美元。

如果你开始编写标注指南,这里有一些来自 Google 和 Bing 搜索的参考指南。

(无参考) 评估和防护栏可以互换使用

防护栏有助于捕捉不适当或有害内容,而评估有助于衡量模型输出的质量和准确性。在无参考评估的情况下,它们可以被视为同一硬币的两面。无参考评估是指不依赖“黄金”参考 (如人工撰写的答案) 来评估输出质量,并且可以仅根据输入提示和模型的响应进行评估。

一些例子是总结评估,我们只需考虑输入文档即可评估摘要的事实一致性和相关性。如果摘要在这些指标上的得分很低,我们可以选择不向用户显示它,实际上将评估用作防护栏。同样,无参考的翻译评估可以在不需要人工翻译参考的情况下评估翻译质量,同样允许我们将其用作防护栏。

LLMs 将在不应输出时返回输出

使用 LLMs 时的一个关键挑战是它们经常会生成不应生成的输出。这可能导致无害但无意义的响应,或更严重的缺陷,如有害或危险内容。例如,当被要求从文档中提取特定属性或元数据时,LLM 可能会自信地返回即使这些值实际上不存在的值。或者,模型可能会以非英语语言响应,因为我们提供了非英语文档作为上下文。

虽然我们可以尝试提示 LLM 返回“不可用”或“未知”响应,但这并不可靠。即使日志概率可用,它们也是输出质量的糟糕指标。虽然日志概率指示了 Token 出现在输出中的可能性,但它们不一定反映生成文本的正确性。相反,对于训练生成连贯响应的指令调优模型,日志概率可能未很好校准。因此,尽管高日志概率可能表明输出流畅且连贯,但这并不意味着它是准确或相关的。

虽然仔细的提示工程在某种程度上可以有所帮助,但我们应辅以强大的防护栏,以检测和过滤/再生不需要的输出。例如,OpenAI 提供了一个内容审核 API,可以识别不安全的响应,如仇恨言论、自我伤害或性内容。同样,有许多包用于检测个人身份信息 (PII)。一个好处是防护栏在很大程度上与用例无关,因此可以广泛应用于给定语言的所有输出。此外,通过精确检索,我们的系统可以在没有相关文档时确定性地响应“我不知道”。

这里的一个推论是,当预期输出时,LLMs 可能会无法生成输出。这可能由于各种原因发生,从 API 提供商的长尾延迟等简单问题到更复杂的问题,如输出被内容审核过滤器阻止。因此,重要的是始终记录输入和 (潜在缺乏的) 输出以进行调试和监控。

幻觉是个顽固的问题

与内容安全或 PII 缺陷相比,事实不一致顽固存在且更难检测。它们更常见,发生率为 5-10%,而且从我们从 LLM 提供商那里了解到的情况来看,即使在简单任务 (如总结) 上,也很难将其降到 2% 以下。

为了解决这个问题,我们可以结合提示工程 (生成上游) 和事实不一致防护栏 (生成下游)。对于提示工程,链式思

维等技术有助于通过让 LLM 在最终返回输出之前解释其推理过程来减少幻觉。然后,我们可以应用事实不一致防护栏来评估摘要的事实性,并过滤或再生幻觉。在某些情况下,幻觉可以确定性地检测。当使用来自 RAG 检索的资源时,如果输出是结构化的并识别资源是什么,你应该能够手动验证它们是否来自输入上下文。

关于作者

Eugene Yan 设计、构建和运营机器学习系统,为全球数百万客户服务。他目前是亚马逊的高级应用科学家,在 RecSys 2022 主题演讲中,他构建了 RecSys,为全球数百万客户提供服务,并在 2023 年 AI 工程峰会主题演讲中应用 LLMs 为客户提供更好的服务。此前,他领导了 Lazada (被阿里巴巴收购) 的机器学习团队,并领导了一家健康科技 A 轮公司。他在 eugeneyan.com 和 ApplyingML.com 撰写和演讲关于机器学习、推荐系统、LLMs 和工程的内容。

Bryan Bischof 是 Hex 的 AI 负责人,领导构建 Magic 的工程师团队——数据科学和分析助手。Bryan 在整个数据堆栈中工作,领导分析、机器学习工程、数据平台工程和 AI 工程团队。他创建了 Blue Bottle Coffee 的数据团队,领导了 Stitch Fix 的几个项目,并建立了 Weights and Biases 的数据团队。Bryan 曾共同撰写了 O’Reilly 的《Building Production Recommendation Systems》一书,并在 Rutgers 研究生院教授数据科学和分析。他的博士学位是纯数学。

Charles Frye 教人们构建 AI 应用。在发表了精神药理学和神经生物学方面的研究之后,他在加州大学伯克利分校获得了博士学位,研究论文题为神经网络优化。通过在 Weights and Biases、Full Stack Deep Learning 和 Modal 的教育和咨询工作,他已经教授了数千人从线性代数基础到 GPU 奥秘和构建可辩护业务的整个 AI 应用开发堆栈。

Hamel Husain 是一位拥有超过 25 年经验的机器学习工程师。他曾与 Airbnb 和 GitHub 等创新公司合作,其中包括 OpenAI 使用的早期 LLM 研究用于代码理解。他还领导并参与了众多流行的开源机器学习工具。Hamel 目前是一名独立顾问,帮助公司实现大语言模型 (LLMs) 的运营化,加速其 AI 产品之旅。

Jason Liu 是一位知名的机器学习顾问,以领导团队成功发布 AI 产品而闻名。Jason 在个性化算法、搜索优化、合成数据生成和 MLOps 系统方面有技术专长。他的经验包括 Stitch Fix,在那里他创建了一个推荐框架和处理每天 3.5 亿次请求的可观察性工具。其他角色包括 Meta、NYU 和初创公司 Limitless AI 和 Trunk Tools。

Shreya Shankar 是一名机器学习工程师和加州大学伯克利分校计算机科学博士生。她是两家初创公司的第一位机器学习工程师,从头开始构建 AI 驱动的产品,每天为数千用户服务。作为一名研究人员,她的工作重点是通过以人为中心的方法解决生产机器学习系统中的数据挑战。她的工作出现在顶级数据管理和人机交互会议上,如 VLDB、SIGMOD、CIDR 和 CSCW。

联系我们

我们很想听听你对这篇文章的想法。你可以通过 [email protected] 联系我们。我们中的许多人愿意接受各种形式的咨询和建议。如果适当,我们将在联系时将你转到相关专家。

致谢

这一系列文章始于群聊中的一次对话,Bryan 打趣说他受到了“写一年的 AI 工程”启发。然后,群聊中发生了✨魔法✨,我们都被激励着分享我们迄今为止学到的东西。

作者们要感谢 Eugene 领导了大部分文档集成和总体结构的主要工作,还要感谢他对主要编辑和文档方向的负责。作者们要感谢 Bryan 提供了这个写作的灵感,将写作重组为战术、运营和战略部分及其介绍,并推动我们考虑如何更大程度地帮助社区。作者们要感谢 Charles 深入研究成本和 LLMOps,并将文档编织得更连贯、更紧密——你可以感谢他让这篇文章变成 30 页而不是 40 页!作者们感谢 Hamel 和 Jason 他们从客户咨询中获得的见解和第一线的经验,分享了广泛可推广的客户教训以及对工具的深入了解。最后,感谢 Shreya 提醒我们评估和严格生产实践的重要性,并将她的研究和原始结果带入本文。

最后,作者们要感谢所有团队在你们自己的写作中如此慷慨地分享了你们的挑战和教训,我们在本文中多次引用了这些文章,以及 AI 社区的热情参与和与本小组的互动。