LLM 应用架构最佳实践全景
系统总结开发 LLM 应用的架构模式,含 API 设计、缓存、容错等。程序员入门 LLM 应用的实用参考。
系统总结开发 LLM 应用的架构模式,含 API 设计、缓存、容错等。程序员入门 LLM 应用的实用参考。
这是学习和应用大型语言模型的一个迷人时代。每天都有新的进展被宣布!
在本指南中,我分享对当前数据驱动语言模型应用的架构最佳实践的分析。即使以大型语言模型的标准来看,这个特定的学科领域也经历着巨大的研究热潮 - 在本指南中,我引用了8篇研究论文和4个软件项目,中位数初始发表日期为2022年11月22日。
在大型语言模型(LLM)的几乎所有实际应用中,都存在一些场景,你希望语言模型根据特定数据生成答案,而不是基于模型的训练集提供通用答案。例如,公司聊天机器人应该能够引用公司网站上的特定文章,而律师分析工具应该能够引用同一案件的之前备案。引入这些外部数据的方式是一个关键的设计问题。
在高层次上,引用特定数据的两种主要方法是:
将数据作为上下文插入模型提示中,并指导模型利用这些信息进行响应
通过提供数百或数千个提示<>完成对,对模型进行微调
这两种方法单独使用都有显著的不足。
对于基于上下文的方法:
模型的上下文大小有限,最新的 davinci-003 模型在单个请求中只能处理最多4000个token。许多文档无法放入这个上下文中。
处理更多token意味着更长的处理时间。在面向客户的场景中,这会损害用户体验。
处理更多token意味着更高的API成本,如果上下文中的信息不够针对性,可能不会导致更准确的响应。
对于微调方法:
生成提示<>完成对既耗时又可能昂贵。
你想要引用信息的许多存储库都很大。例如,如果你的应用是为参加美国MLE的医学生提供学习辅助,一个全面的模型必须在众多学科中提供训练示例。
一些外部数据源变化很快。例如,基于每天或每周轮换的客户支持案例队列来重新训练客户支持模型并不最优。
围绕微调的最佳实践仍在发展中。LLM本身可以用来协助生成训练数据,但这可能需要一定的复杂性才能有效。
上述设计有多种名称,最常见的是"检索增强生成"或"RETRO"。相关链接和概念:
RAG: Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
RETRO: Improving language models by retrieving from trillions of tokens
REALM: Retrieval-Augmented Language Model Pre-Training
检索增强生成a) 从语言模型外部检索相关数据(非参数化),b) 通过在LLM的提示中添加上下文来增强数据。该架构清晰地绕过了微调和仅基于上下文方法的大部分限制。
相关信息的检索值得进一步解释。如你所见,根据用例,数据可能来自多个来源。为了使数据有用,它必须足够小,以便多个部分可以放入上下文中,并且必须有某种方式来识别相关性。所以典型的先决条件是将文本分割成部分(例如,通过LangChain包中的实用程序),然后在这些块上计算嵌入。
语言模型嵌入是文本中概念的数值表示,似乎有无尽的用途。它们的工作方式如下:嵌入模型将文本转换为一个大的、评分的向量,它可以被有效地与其他评分向量进行比较,以帮助完成推荐、分类和搜索(以及更多)任务。我们将此计算的结果存储到我一般称为搜索索引和实体存储的内容中 - 关于这一点的更高级讨论见下文。
回到流程 - 当用户提交问题时,LLM以多种方式处理消息,但关键步骤是计算另一个嵌入 - 这次,是用户文本的嵌入。现在,我们可以通过将新的嵌入向量与全套预计算向量进行比较来对搜索索引和实体存储进行语义搜索。这种语义搜索基于语言模型的"学习"概念,不限于仅搜索关键词。从搜索结果中,我们可以定量地识别一个或多个可能有助于回答用户问题的相关文本块。
使用相关文本块构建提示很直接。提示从一些基本的提示工程开始,指导模型避免"幻觉",即编造听起来合理但虚假的答案。如果适用,我们指导模型以某种格式回答问题,例如"高"、"中"或"低"用于序数排名。最后,我们提供语言模型可以用来使用特定数据回答的相关信息。在最简单的形式中,我们只是追加("Document 1: "+ 文本块 1 + "\nDocument 2: " + 文本块 2 + …)直到上下文被填满。
最后,组合的提示被发送到大型语言模型。从完成中解析答案并传递给用户。
就是这样!虽然这是设计的一个简单版本,但它廉价、准确,对许多轻量级用例完美。我在一个行业原型中使用了这个设置并取得了巨大成功。openai-cookbook存储库中可以找到这个方法的即插即用版本,是一个方便的起点。
我想花点时间讨论几个可能进入检索增强生成架构的研究发展。我的看法是,应用型LLM产品将在6到9个月内实现这些特性的大部分。
这类方法涉及在检索相关数据之前用LLM处理用户输入。
基本上,用户的问题缺乏一些信息性答案将展示的相关性模式。例如,"Python中列表推导式的语法是什么?"与代码存储库中的示例差异很大,比如片段"newlist = [x for x in tables if "customer" in x]"。一个提议的方法使用"假设文档嵌入"来生成一个假设的上下文文档,可能包含虚假细节,但模仿真实答案。对该文档进行嵌入并搜索数据存储中的相关(真实)示例会检索到更相关的结果;这些相关结果用于生成用户看到的实际答案。
一个类似的名为生成然后读取(GenRead)的方法通过在多个上下文文档生成上实现聚类算法来进行构建。实际上,它生成多个示例上下文,并确保它们在有意义的方式上有所不同。这种方法偏向语言模型返回更多不同的假设上下文文档建议,这(在嵌入后)从数据存储中返回更多不同的结果,并增加了完成中包含准确答案的可能性。
GPT Index项目很优秀,值得一读。它利用由语言模型创建并为其优化的数据结构集合。GPT Index支持下面更详细描述的多种索引类型。基本响应合成是"选择k个最相关的文档并将其追加到上下文中",但有多种策略可以这样做。
List Index - 每个节点代表一个文本块,否则未做修改。在默认设置中,所有节点都被组合到上下文中(响应合成步骤)。
Vector Store Index - 这等价于我在前一部分解释的简单设计。每个文本块与嵌入一起存储;比较查询嵌入与文档嵌入返回k个最相似的文档以输入到上下文中。
Keyword Index - 这支持对特定字符串进行快速高效的词法搜索。
Tree Index - 当你的数据组织成层次结构时,这非常有用。考虑临床文档应用:你可能希望文本包括高级说明("这是改善心脏健康的一般方法")和低级文本(参考特定血压药物治疗方案的副作用和说明)。有几种不同的方式来遍历树以生成响应,下面显示了其中两种。
GPT Index提供索引的可组合性,意味着你可以在其他索引之上构建索引。例如,在代码助手场景中,你可以在内部GitHub存储库上构建一个树索引,在维基百科上构建另一个树索引。然后,你在树索引上分层放置一个关键字索引。
本文中概述的一些方法听起来"不太正式",因为它们涉及当前模型中相对较小的上下文大小的变通办法。有重大的研究努力旨在扩展这一限制。
预计GPT-4将在接下来的1-3个月内推出。据传言它具有更大的上下文大小。
来自Google AI的人员的这篇论文探索了工程权衡的多个方面。其中一个配置允许最多43,000个token的上下文长度。
一个新的状态空间模型架构与上下文大小缩放约线性关系,而不是像变压器模型中看到的那样二次方关系。虽然该模型在其他领域的性能滞后,但它表明重大的研究努力针对改进诸如上下文大小之类的模型考虑因素。
在我看来,上下文大小的进展将与对更多数据检索的需求并行扩展;换句话说,可以安全地假设文本分割和细化将继续是必需的,即使某些配置随着时间的推移而发展。
当LLM以对话形式呈现给用户时,一个主要挑战是在上下文中维持该会话历史。
相关策略的概述超出了本文的范围;有关涉及渐进式总结和知识检索的最近代码演示示例,请参阅本LangChain示例。
Haystack library for semantic search and other NLP applications
Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
How to implement Q&A against your documentation with GPT3, embeddings and Datasette
FAISS for vector similarity calculations
Generate rather than Retrieve: Large Language Models are Strong Context Generators