微信扫码
与创始人交个朋友
我要投稿
当下正是使用大型语言模型(LLM)构建应用的好时机。过去一年,LLM 已经发展到了足够用于实际应用的水平。LLM 的进化速度与社交媒体层出不穷的演示应用,将在 2025 年吸引对 AI 领域的约 2000 亿美元投资。LLM 的门槛也很低,让每个人(而不仅仅是 ML 工程师和科学家)都可以将智能融入他们的产品中。不过虽然构建 AI 产品比以前要容易得多,但创建出超越演示范畴,真正可用的产品仍是一项较为困难的工作。
在过去的一年里,我们六个人一直在基于 LLM 构建现实世界的应用程序。我们意识到有必要将这些经验提炼出来造福大众。
我们有着不同的背景,担任不同的角色,但大家都亲身经历了使用这项新技术所带来的挑战。我们中的两位是独立顾问,他们帮助众多客户将 LLM 项目从最初的概念转变为成功的产品,从而总结出了决定项目成败的模式。有一位是研究人员,研究 ML/AI 团队的工作方式以及如何改进他们的工作流程。还有两位是 AI 应用团队的领导者:一位在科技巨头公司,一位在初创公司。最后一位已经向数千人教授了深度学习知识,现在致力于让 AI 工具和基础设施更加易用。在过去的一年里,我们艰难前行,收获了很多宝贵的经验教训来向大家分享。
这项工作分为三个部分:战术、运营和战略。这篇文章是第一部分,深入探讨了使用 LLM 的战术细节。文章分享了有关提示、设置检索增强生成、应用流程工程以及评估和监控等领域的一众最佳实践和常见陷阱。
我们建议大家在开发新应用程序时从提示开始。人们很容易低估或高估它的重要性。所谓低估,是因为如果我们有效使用正确的提示技术可以走得很远。所谓高估,是因为即使是基于提示的应用程序也需要围绕提示进行大量工程设计才能正常工作。
以下提示技术总能用来提高各种模型和任务的性能:n-shot 提示 + 情境学习、思维链,以及为模型提供相关资源。
通过 n-shot 提示进行情境学习的方法,具体来说是为 LLM 提供一些示例来演示任务,并使输出符合我们的期望。一些技巧如下:
如果 n 太低,模型可能会过度锚定这些特定示例,从而损害其泛化能力。根据经验法则,n 至少要 ≥ 5,几十个也不嫌多。
示例应该代表预期的输入分布。如果你正在构建一个电影摘要应用,请用各种类型的样本构建示例,其比例应大致与你期望在实践中看到的比例一致。
你不一定需要提供完整的输入 - 输出对。在许多情况下,提供输出的期望示例就足够了。
如果你使用的 LLM 支持工具,那么你的 n-shot 示例也应该使用那些你希望代理使用的工具。
在思维链(CoT)提示方法中,我们鼓励 LLM 在返回最终答案之前解释其思维过程。可以把它看作是为 LLM 提供一个草图板,这样它就不需要全凭记忆做事了。一开始的方法是简单地在提示中加入“让我们一步一步思考”的短语,后来我们发现 CoT 更具体会更有用,通过一两句额外提示来增加特异性通常会显著降低幻觉率。例如,要求 LLM 总结会议记录时,我们可以明确说明各个步骤,例如:
首先,在草图板中列出关键决策、后续项目和相关负责人;
然后,检查草图板中的细节是否与会议文本在事实上一致;
最后,将要点综合成一个简明的总结。
最近,有人质疑这种技术是否真的那么强大。此外,思维链的推理过程也是颇受争议的。无论如何,只要可行的话,这种技术是值得尝试的。
提供相关资源是一种强大的机制,可以扩展模型的知识库、减少幻觉,并增加用户的信任度。它通常通过检索增强生成(RAG)技术来实现,为模型提供可以在其响应中直接使用的文本片段是一种很重要的技术。在提供相关资源时,仅仅喂给模型是不够的,还要告诉模型应该优先使用它们、直接引用它们,并在资源不足时提及它们。这些方法能够让代理输出的响应尽量围绕这些资源展开。
结构化的输入和输出能够帮助模型更好地理解输入,以及返回能够可靠地与下游系统集成的输出。向输入添加序列化格式可以为模型提供更多线索,帮助它了解上下文中各个 token 之间的关系、特定 token 的附加元数据(如类型),或将请求与模型训练数据中的类似示例关联起来。
例如,互联网上许多关于 SQL 代码编写的问题都是从指定 SQL 模式开始的。因此,你可能会想到有效的 Text-to-SQL 提示应该包括结构化的模式定义。
结构化输出的用途类似,但它也简化了与系统下游组件的集成。Instructor 和 Outlines 非常适合结构化输出。(如果你要导入 LLM API SDK,请使用 Instructor;如果你要为自托管模型导入 Huggingface,请使用 Outlines。)结构化输入可以清楚地表达任务,且与训练数据的格式类似,这样获得更好输出的概率就会增加。
使用结构化输入时,请注意每个 LLM 家族都有自己的偏好。Claude 更喜欢 xml,而 GPT 则喜欢 Markdown 和 JSON。使用 XML 时,你甚至可以提供如下的 response 标签来预填充 Claude 的响应。
</> pythonmessages=[ { "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>" } ]
软件中常见的一种反模式是“上帝对象”,说的是用一个类或函数做所有事情。提示也得避免这种模式。
提示一开始往往很简单,几句说明、几个例子就可以起步了。但当我们尝试提高性能并处理更多极端情况时,复杂性就会逐渐显现。更多的说明、多步骤推理、几十个示例……不知不觉中,我们的提示现在变成了一个 2000 token 的怪物。更糟糕的是,它在更常见和更直接的输入上的表现反而更差!
就像我们努力让系统和代码保持简洁一样,提示也是一回事。以会议记录摘要应用为例:
将关键决策、行动项目和负责人提取为结构化格式
根据原始文本检查提取出来的内容细节以确保一致性
从结构化的细节内容中生成简明摘要
也就是说我们要将单个提示拆分为多个简单、有针对性且易于理解的提示,每个提示都能单独迭代和评估。
你实际需要向代理发送多少上下文?不管你之前的假设是怎样的,现在都要重新思考并挑战这个假设。你得像米开朗基罗一样,不是堆砌起来那座上下文雕塑,而是凿掉多余的材料,直到雕塑显露出来。RAG 是一种整理所有可能相关的内容的流行方法,但你该如何提取出真正重要的部分呢?
我们发现,将发送给模型的最终提示(包括所有上下文构造、元提示和 RAG 结果)放在一起再读几遍,能够帮助你重新思考上下文。这种方法能让你找出提示中的冗余、自相矛盾的语言和糟糕的格式。
另一个关键优化是上下文的结构。如果你的文档包在人类眼中就是一团糟,那就不要假设它对代理有任何好处。仔细考虑如何构建上下文以强调其各部分之间的关系,让提示尽可能简洁清晰。
除了提示之外,引导 LLM 的另一种有效方法是在提示中加入知识,这被称为检索增强生成(RAG)。从业者发现 RAG 能够有效地为模型提供知识并提高产出,同时与微调相比,它所需的工作量和成本要少得多。RAG 的好坏取决于检索到的文档的相关性、密度和细节
第一个也是最明显的指标是相关性,一般来说通过几种排名指标来量化,例如平均倒数排名(MRR)或归一化折扣累积增益(NDCG)。MRR 评估的是系统有多大可能将第一个相关结果放在排名列表中,而 NDCG 则考虑所有结果及其位置的相关性。它们衡量系统有没有很好地将相关文档排在较高位置和将不相关文档排在较低位置。例如,如果我们要检索用户摘要以生成电影评论摘要,我们会希望将特定电影的评论排在较高位置,同时排除其他电影的评论。
与传统推荐系统一样,检索到的项目的排名将对 LLM 在下游任务中的表现产生重大影响。为了衡量这种影响,我们可以运行一个基于 RAG 的任务,但对检索到的项目乱序排列——那么 RAG 输出的表现如何?
其次,我们还想考虑信息密度。如果两个文档的相关性一样高,我们应该选择更简洁、无关细节更少的文档。回到我们的电影示例,我们可能会认为电影脚本和所有用户评论在广义上都是相关的。尽管如此,评分最高的评论和专业编辑评论的信息密度可能会更高。
最后,考虑文档中提供的详细程度。假设我们正在构建一个 RAG 系统来从自然语言生成 SQL 查询。我们可以简单地用带有列名的表模式作为上下文。但是,如果我们加入列描述和一些代表性的值呢?额外的细节可以帮助 LLM 更好地理解表的语义,从而生成更正确的 SQL。
由于基于嵌入的 RAG 演示非常流行,我们很容易忘记或忽略信息检索领域数十年来的研究成果和解决方案积累。
无论如何,虽然嵌入无疑是一种强大的工具,但它们并不是万能的。首先,虽然它们擅长捕捉高级语义的相似性,但它们可能难以处理更具体的,基于关键字的查询,比如说当用户搜索名称(如 Ilya)、首字母缩略词(例如 RAG)或 ID(例如 claude-3-sonnet)时就是这样。基于关键字的搜索(例如 BM25)是专门为此设计的。有了多年的基于关键字的搜索经验后,用户可能已经将其视为理所当然,如果搜索没有返回他们期望检索的文档,他们可能会感到很沮丧。
向量嵌入并不能神奇地解决搜索问题。事实上,在使用语义相似性搜索方法重新做排名之前的步骤才是重头戏。对 BM25 或全文搜索做出真正的改进是很难的事情。
——Aravind Srinivas,Perplexity.ainormal 首席执行官
几个月来,我们一直在向客户和合作伙伴传达这一点。使用简单嵌入的最近邻搜索会产生非常嘈杂的结果,你最好从基于关键字的方法开始。
——Beyang Liu,Sourcegraphnormal 首席技术官
其次,使用关键字搜索可以更直接地理解系统为什么会检索到某份文档——我们查看与查询匹配的关键字即可。相比之下,基于嵌入的检索就不太容易解释了。最后,得益于 Lucene 和 OpenSearch 等经过数十年优化和实战考验的系统,关键字搜索通常在计算上更高效。
在大多数情况下,混合方法效果最好:关键字匹配方法用于明显的匹配,嵌入用于同义词、上位词和拼写错误以及多模态(例如图像和文本)情况。Shortwave 分享了他们如何构建 RAG 管道,包括查询重写、关键字 + 嵌入检索和排名。(https://www.shortwave.com/blog/deep-dive-into-worlds-smartest-email-ai/)
RAG 和微调都可用来将新信息纳入 LLM 并提高特定任务的性能。那么,我们应该先尝试哪一个呢?
最近的研究表明 RAG 可能有优势。一项研究将 RAG 与无监督微调(又称持续预训练)方法作了对比,对 MMLU 的一个子集和一些当前事件做了评估。他们发现,无论是针对在训练期间遇到的知识还是全新的知识,RAG 方法总是优于微调。在另一篇论文中,他们用一个农业数据集对 RAG 与监督微调做了对比。同样,RAG 的性能提升大于微调,尤其是对于 GPT-4 而言。
除了提高性能之外,RAG 还有几个实际优势。首先,与持续预训练或微调相比,它更容易保持检索索引在最新状态,也更便宜!其次,如果我们的检索索引中存在包含有害或有偏见内容的问题文档,我们可以轻松删除或修改有问题的文档。
此外,RAG 中的 R 可以更精细地控制我们检索文档的方式。例如,如果我们为多个组织托管 RAG 系统,那么通过对检索索引进行分区,我们可以确保每个组织只能从自己的索引中检索文档。这确保了我们不会无意中将一个组织的信息泄露给另一个组织。
由于 Gemini 1.5 提供了高达 10M 个 token 大小的上下文窗口,一些人开始质疑 RAG 的未来。
我倾向于认为 Sora 的炒作让 Gemini 1.5 的光芒被大大掩盖了。10M 个 token 的上下文窗口实际上让大多数现有的 RAG 框架变得没有必要了——你只需将数据放入上下文中,然后像往常一样与模型对话即可。想象一下它对所有初创公司 / 代理 /LangChain 项目的影响,他们的大部分工程工作都投入到了 RAG 上? 或者用一句话来概括:10m 上下文杀死了 RAG。干得好,Gemini。
——Yao Fu
虽然长上下文确实会改变诸如分析多个文档或在聊天中用到很多 PDF 等用例的游戏规则,但有关 RAG 消亡的谣言被大大夸大了。
首先,即使上下文窗口包含 10M 个 token,我们仍然需要一种方法来选择要输入到模型中的信息。其次,除了狭隘的大海捞针式评估之外,我们还没有看到令人信服的数据表明模型可以在如此大的上下文中依旧能有效地推理。因此,如果没有良好的检索(和排名),我们可能会让模型被干扰项的重担压倒,甚至可能用完全不相关的信息填充上下文窗口。
最后,还有成本。Transformer 的推理成本与上下文长度成二次方(或在空间和时间上呈线性)关系。仅仅因为存在一个可以在回答每个问题之前读取你组织的整个 Google Drive 内容的模型,并不意味着这是一个好主意。考虑一下我们使用 RAM 的方式:即使存在拥有数十 TB 内存的计算实例,我们仍然会从磁盘读取和写入。
所以不要把你的 RAG 扔进垃圾桶。即使上下文窗口的大小增加,这种模式仍然有用。
给 LLM 写提示只是一个开始。为了最大限度地利用它们,我们需要改变单一提示方法,并用上各种工作流程。例如,我们如何将单个复杂任务拆分为多个更简单的任务?微调或缓存何时有助于提高性能并减少延迟 / 成本?在本节中,我们将分享一些经过验证的策略和真实示例,以帮助你优化和构建可靠的 LLM 工作流程。
我们已经知道,通过将单个大提示分解为多个较小的提示,我们可以获得更好的结果。AlphaCodium 就是一个例子:通过从单个提示切换到多步骤工作流程,他们将 CodeContests 上的 GPT-4 准确率(pass@5)从 19% 提高到了 44%。他们的工作流程包括:
反思问题
用公共测试来推理
生成可能的解决方案
对可能的解决方案进行排名
生成综合测试
在公共和综合测试中迭代解决方案。
一系列目标明确的小任务可以成为最好用的代理或提示流。每个代理提示都不需要请求结构化输出,但结构化输出可以帮我们和那些协调代理同环境交互的系统做交互。
一些值得尝试的事情:
明确的规划步骤,尽可能严格定义。可以从预定义的计划中选择步骤(参见 https://youtu.be/hGXhFa3gzBs?si=gNEGYzux6TuB1del)。
将原始的用户提示重写为代理提示。小心,这个过程是有损的!
将代理行为作为线性链、DAG 和状态机;不同的依赖关系和逻辑关系可能更适合或更不适合不同的规模。你能从不同的任务架构中挤出性能优化吗?
规划验证;你的规划可以加入如何评估其他代理的响应,以确保最终的架构能够良好协同的说明。
具有固定上游状态的提示工程——确保你的代理提示是根据可能发生的一系列变体来评估的。
虽然 AI 代理可以动态地响应用户请求和环境,但它们的非确定性使其部署起来颇具挑战性。代理采取的每个步骤都有失败的可能,并且从错误中恢复的机会很小。因此,随着步骤数量的增加,代理成功完成多步骤任务的可能性呈指数下降。因此,构建代理的团队发现他们很难部署很多可靠的代理。
一种有前途的方法是让代理系统生成确定性计划,然后以结构化、可重复的方式执行。在第一步中,给定一个高级目标或提示,代理会生成一个计划。然后,该计划以确定性的方式执行。这使每个步骤都更加可预测和可靠。这样做的好处包括:
生成的计划可以作为提示或微调一个代理的 few-shot 样本。
确定性执行使系统更可靠,从而更容易测试和调试。此外,故障可以追溯到计划中的具体步骤上。
生成的计划可以表示为有向无环图(DAG),相对于静态提示,它更容易理解和适应新情况。
那些在管理初级工程师方面拥有丰富经验的员工可能会成为最成功的代理构建员,因为生成计划的过程与我们指导和管理初级工程师的方式很像。我们为初级工程师提供明确的目标和具体的计划,而不是模糊的开放式方向,我们也应该为我们的代理做同样的事情。
最后,做出可靠、有效的代理的关键可能是采用更结构化、确定性的方法,以及收集数据来改进提示和微调模型。如果没有这一点,我们构建的代理可能会在某些时候表现得非常好,但平均而言会让用户失望,从而导致留存率变低。
假设你的任务需要 LLM 输出的多样性。也许你正在编写一个 LLM 管道,根据用户之前购买的产品列表,从你的目录中推荐要购买的产品。多次运行提示时,你可能会注意到生成的建议太过相似,因此你可能会增加 LLM 请求中的温度参数。
简而言之,增加温度参数会使 LLM 响应更加多样化。在采样时,下一个 token 的概率分布变得更平坦,这意味着通常不太可能被选中的 token 会被更频繁地选中。不过,在增加温度时,你可能会注意到一些与输出多样性相关的故障模式。例如,目录中某些可能很合适的产品可能永远不会被 LLM 输出。如果根据 LLM 在训练时学到的内容,它们很可能遵循提示,那么输出中可能会经常重复一少部分产品。如果温度过高,你可能会得到引用不存在产品(或乱码!)的输出。
换句话说,增加温度并不能保证 LLM 会按你期望的概率分布(例如均匀随机)来采样输出。不过我们还有其他技巧可以增加输出多样性。最简单的方法是调整提示中的元素。例如,如果提示模板包含项目列表(例如历史购买记录),则每次将这些项目插入提示时打乱其顺序可能会产生很大的不同。
此外,保留一份简短的近期输出列表可以防止冗余。在我们的推荐产品示例中,通过指示 LLM 避免从这个近期列表中推荐项目,或者通过拒绝与近期建议相似的输出并重新采样,我们可以进一步使响应多样化。另一种有效的策略是改变提示中使用的措辞。例如,加入“选择用户经常喜欢使用的物品”或“选择用户可能会推荐给朋友的产品”等短语可以转移焦点,从而影响推荐产品的多样性。
缓存可以节省成本,消除生成延迟,因为系统无需重新计算相同输入的响应。此外,如果响应之前已受到保护,我们可以提供这些经过审查的响应,并降低提供有害或不适当内容的风险。
缓存的一种简单方法是使用唯一 ID 来处理正在处理的项目,例如,如果我们要总结新文章或产品评论。当请求进入时,我们可以检查缓存中是否已经存在摘要。如果是,我们可以立即返回;如果不是,我们会生成、守护和提供摘要,然后将其存储在缓存中以供将来的请求使用。
对于更开放的查询,我们可以借用搜索领域的技术,搜索领域也利用缓存来处理开放式输入。自动完成和拼写更正等功能也有助于规范用户输入,从而提高缓存命中率。
我们可能会遇到一些任务,其中即使是最巧妙设计的提示也会失败。例如,即使做了大量提示工程,我们的系统可能仍无法返回可靠、高质量的输出。如果是这样,就可能需要针对你的特定任务微调模型。
成功的例子包括:
Honeycomb 的自然语言查询助手:最初,他们在提示中提供了“编程手册”,以及用于上下文学习的 n-shot 示例。虽然这种方法效果不错,但微调模型可以更好地输出特定领域语言的语法和规则。
ReChat 的 Lucy:LLM 需要以非常特定的格式生成响应,该格式结合了结构化和非结构化数据,以便前端正确呈现。微调对于它的持续正常工作来说非常重要。
不过虽然微调可能有效,但它的成本很高。我们必须注释微调数据,微调和评估模型,最后还要自己托管它们。因此,请考虑更高的前期成本是否物有所值。如果提示能帮你完成 90% 的工作,那么微调可能就不值得投资了。但是,如果我们决定进行微调,以降低收集人工注释数据的成本,我们可以生成合成数据并对其进行微调,或者用开源数据来入手。
LLM 的评估可能是一个雷区。LLM 的输入和输出是任意文本,我们为它们设置的任务也各不相同。尽管如此,严格而周到的评估是非常重要的——OpenAI 的技术领导者很重视评估,并对单个评估提供反馈并不是一种巧合。
LLM 应用程序的评估可以引申出多种说法:它只是单元测试,或者更像是可观察性,或者可能只是数据科学。我们发现所有这些观点都很有用。在下一节中,我们将提供一些关于构建评估和监控管道时重要因素的经验教训。
创建由生产中的输入和输出样本组成的单元测试(即断言),并根据至少三个标准对输出给出预期。虽然三个标准可能看起来很随意,但这是一个实用的入门数字;更少的标准可能表明你的任务定义不够明确或过于开放,就像通用聊天机器人一样。这些单元测试或断言应该由管道的任何更改触发,无论是编辑提示、通过 RAG 添加新上下文还是其他修改都一样。这里有一个基于断言的测试示例(https://hamel.dev/blog/posts/evals/#step-1-write-scoped-tests)。
考虑从断言开始,指定要包含或排除在所有响应中的短语或想法。还请考虑做检查来确保单词、项目或句子计数在一定范围内。对于其他类型的生成,断言可能看起来不太一样。执行评估是一种评估代码生成的强大方法,你可以在其中运行生成的代码并确定运行时状态是否足以满足用户请求。
例如,如果用户请求一个名为 foo 的新函数;那么在执行代理生成的代码后,foo 应该是可调用的!执行评估中的一个挑战是代理代码经常以与目标代码略有不同的形式离开运行时。将断言“放宽”到任何可行答案都能满足的绝对最弱的假设可能也挺好用。
最后,按照客户预期的方式使用你的产品(即“内部测试”),可以深入了解真实数据的故障模式。这种方法不仅有助于识别潜在的弱点,而且还提供了可以转换为评估的,好用的生产样本来源。
LLM-as-Judge,指的是我们使用强大的 LLM 来评估其他 LLM 的输出,但这种方法遭到了一些人的质疑。(我们中的一些人最初是持怀疑态度的。)尽管如此,如果实施得当,LLM-as-Judge 可以与人类判断实现良好的相关性,并且至少可以帮助建立有关新提示或技术如何执行的先验知识。具体来说,在进行成对比较(例如,对照组与治疗组)时,LLM-as-Judge 通常能得到正确的方向,尽管胜负的幅度可能很混乱。
以下是一些充分利用 LLM-as-Judge 的建议:
使用成对比较:不要要求 LLM 在李克特量表上对单个输出打分,而是向其提供两个选项并要求其选择更好的一个。这往往会产生更稳定的结果。
控制位置偏见:呈现选项的顺序可能会影响 LLM 的决策。为了缓解这种情况,请进行两次成对比较,每次交换成对的顺序。但要确保在交换位置后将胜利归因于正确的选项!
允许平局:在某些情况下,两个选项可能同样好。因此,允许 LLM 宣布平局,这样它就不必随机挑选获胜者了。
使用思维链:在给出最终偏好之前要求 LLM 解释其决定可以提高评估可靠性。作为奖励,这允许你使用较弱但更快的 LLM 并仍然获得类似的结果。由于管道的这一部分通常处于批处理模式,因此来自 CoT 的额外延迟不是问题。
控制响应长度:LLM 倾向于给较长的响应打高分。为了缓解这种情况,请确保响应对的长度相似。
LLM-as-Judge 的一个特别强大的应用是检查新的提示策略是否与回归相关。如果你跟踪了一组生产结果,有时你可以使用新的提示策略重新运行这些生产示例,并使用 LLM-as-Judge 快速评估新策略可能受到影响的地方。
这里是一个简单但有效的 LLM-as-Judge 迭代方法的示例(https://hamel.dev/blog/posts/evals/#automated-evaluation-w-llms),我们只需记录 LLM 的回应、judge 的批评(即 CoT)和最终结果,然后与利益相关者一起审查它们以确定需要改进的地方。经过三次迭代,人类和 LLM 的一致性从 68% 提高到 94%!
不过,LLM-as-Judge 并非灵丹妙药。语言中存在一些微妙的方面,即使是最强大的模型也无法可靠地评估它们。此外,我们发现传统的分类器和奖励模型可以实现比 LLM-as-Judge 更高的准确度,并且成本和延迟更低。对于代码生成,LLM-as-Judge 可能比执行评估等更直接的评估策略更弱。
我们喜欢在评估生成结果时使用“实习生测试”:如果你将语言模型的精确输入(包括上下文)作为一项任务交给相关专业的普通大学生,他们能成功吗?需要多长时间?
如果答案是否定的,原因是 LLM 缺乏所需的知识,请考虑丰富上下文的方法。
如果答案是否定的,并且我们根本无法通过改进上下文来修复它,那么我们可能遇到了一项对于当代 LLM 来说太难的任务。
如果答案是肯定的,但需要一段时间,我们可以尝试降低任务的复杂性。它可分解吗?任务的某些方面是否可以变得更加模板化?
如果答案是肯定的,他们很快就能做出来,那么是时候深入研究数据了。模型做错了什么?我们能找到失败的模式吗?试着在模型响应之前或之后要求它解释自己,帮你看清里面的思想。
“当一个指标成为目标时,它就不再是一个好的指标。”
——古德哈特定律
一个例子是大海捞针(NIAH)评估。一开始这种评估有助于量化随着上下文大小的增加而出现的模型回忆,并判断回忆如何受到针头位置的影响。然而,它被过分强调了,以至于它被作为 Gemini 1.5 报告中的图 1 来展示。这里的评估将特定短语(“特殊魔法 {city} 数字是:{number}”)插入到一份不断重复 Paul Graham 文章的长文档中,然后提示模型回忆这个魔法数字。
虽然有些模型实现了近乎完美的回忆,但 NIAH 是否真正反映了现实世界应用中所需的推理和回忆能力是值得怀疑的。考虑一个更实际的场景:给定一个小时会议的记录,LLM 能否总结关键决策和后续步骤,并正确地将每项内容归因于相关人员?这项任务更加现实,超越了死记硬背,还考虑了解析复杂讨论、识别相关信息和综合总结的能力。
这里是一个实际的 NIAH 评估示例(https://observablehq.com/@shreyashankar/needle-in-the-real-world-experiments)。给定医生与患者视频通话的记录,LLM 被询问患者的药物情况。它还包括一项更具挑战性的 NIAH,插入一个表示披萨配料的随机成分的短语,例如“制作完美披萨所需的秘密成分是:浸泡在浓缩咖啡中的枣、柠檬和山羊奶酪。”药物任务的回忆率约为 80%,披萨任务的回忆率约为 30%。
顺便说一句,过分强调 NIAH 评估可能会导致提取和总结任务的性能下降。由于这些 LLM 经过精心调整,会关注每个句子,它们可能会开始将不相关的细节和干扰项当成重要内容,从而将它们包含在最终输出中(而它们不应该这样做!)
这也适用于其他评估和用例,总结就是一个例子。强调事实一致性可能会导致总结不太具体(因此不太可能出现事实不一致)并且可能不太相关。相反,强调写作风格和口才可能会导致更多华丽的营销语言,从而导致事实不一致。
在李克特量表上提供开放式反馈或模型输出评级的做法需要很高的认知水平。因此,由于人类评分者之间的差异,收集到的数据会更加嘈杂,因此用处更少。更有效的方法是简化任务并减轻注释者的认知负担。两个效果良好的任务是二元分类和成对比较。
在二元分类中,注释者被要求对模型的输出做出简单的是或否判断。他们可能会被问及生成的摘要是否与源文档在事实上一致,或者所提出的响应是否相关,或者是否包含毒性。与李克特量表相比,二元决策更精确,评分者之间的一致性更高,并且吞吐量更高。这就是 Doordash 通过是非问题树设置标签队列以标记菜单项的方式。
在成对比较中,注释者会看到一对模型响应并被问及哪个更好。因为人类更容易说“A 比 B 好”,而不是单独为 A 或 B 分配单个分数,所以这可以更快、更可靠地进行注释(优于李克特量表)。在一次 Llama2 聚会上,Llama2 论文作者 Thomas Scialom 证实,成对比较比收集监督微调数据(例如书面回复)更快、更便宜。前者的成本为每单位 3.5 美元,而后者的成本为每单位 25 美元。
如果你开始编写标签指南,以下是来自谷歌和必应搜索的一些参考指南。
护栏有助于捕捉不适当或有害的内容,而评估有助于衡量模型输出的质量和准确性。在无参考评估的情况下,它们可以被视为同一枚硬币的两面。无参考评估是不依赖于“黄金”参考(例如人工书写的答案)的评估,并且可以仅根据输入提示和模型的响应来评估输出的质量。
摘要评估就是一个例子,我们只需考虑输入文档即可评估摘要的事实一致性和相关性。如果摘要在这些指标上的得分很低,我们可以选择不向用户显示它,这样就能把评估当作一种护栏。同样,无参考翻译评估可以在不需要人工翻译参考的情况下评估翻译的质量,这也能当作护栏来用。
使用 LLM 时的一个关键挑战是,它们通常会在不应返回输出时生成输出。这可能会导致无害但无意义的响应,或更严重的缺陷,如毒性或危险内容。例如,当被要求从文档中提取特定属性或元数据时,LLM 可能会自信地返回值,即使这些值实际上并不存在。或者,由于我们在上下文中提供了非英语文档,因此模型可能会以英语以外的语言来响应。
虽然我们可以尝试提示 LLM 返回“不适用”或“未知”的响应,但这并不是万无一失的。即使对数概率可用,它们也不是输出质量的糟糕指标。虽然对数概率表示标记出现在输出中的可能性,但它们不一定反映生成文本的正确性。相反,对于经过训练以响应查询并生成连贯响应的,用指令调整过的模型来说,对数概率可能没有得到很好的校准。因此,虽然高对数概率可能表明输出是流畅且连贯的,但并不意味着输出是准确或相关的。
虽然谨慎的提示设计可以在一定程度上起作用,但我们应该用强大的护栏来补足它,以检测和过滤 / 重新生成不需要的输出。例如,OpenAI 提供了一个内容审核 API,可以识别不安全的响应,例如仇恨言论、自残或性输出。同样,有许多用于检测个人身份信息(PII)的软件包。一个好处是护栏在很大程度上与用例无关,因此可以广泛应用于给定语言的所有输出。此外,如果没有精确检索到相关文档,我们的系统可以确定地回答“我不知道”。
这里的一个推论是,LLM 可能无法在预期时产生输出。这种情况可能出于各种原因,从 API 提供商的长尾延迟等简单因素到内容审核过滤器阻止输出等更复杂的因素都有可能。因此,持续记录输入和(可能缺少)的输出,用于调试和监控目的是很有必要的。
内容安全或 PII 缺陷受到很多关注,因此很少发生,相比之下与事实不一致的问题非常顽固,总是出现,更难检测。它们更常见,基线发生率为 5-10%,从我们从 LLM 提供商那里了解到的情况来看,即使在诸如摘要之类的简单任务中,也很难将其降至 2% 以下。
为了解决这个问题,我们可以结合提示工程(生成的上游)和事实不一致护栏(生成的下游)来应对。对于提示工程,CoT 等技术有助于减少幻觉,方法是让 LLM 在最终返回输出之前解释其推理过程。然后,我们可以应用事实不一致护栏来评估摘要的真实性并过滤或再生幻觉。在某些情况下,我们可以确定地检测到幻觉。当使用来自 RAG 检索的资源时,如果输出是结构化的并且能够识别资源内容,则你应该能够手动验证它们是否来自输入上下文。
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-05-28
2024-04-26
2024-08-21
2024-04-11
2024-07-09
2024-08-13
2024-07-18
2024-10-25
2024-07-01
2024-06-17