本教程将简要介绍 LangChain 的核心概念。
LangChain 介绍
LangChain 是一个用于开发由语言模型驱动的应用程序的框架。
LangChain 框架的设计目标
支持应用程序让其不仅会通过 API 调用语言模型,而且还会数据感知(将语言模型连接到其他数据源),Be agentic(允许语言模型与其环境交互),最终让应用程序更强大和更具差异化。
LangChain 框架的价值支柱
- 组件:LangChain 为使用语言模型所需的组件提供模块化抽象。LangChain 也有所有这些抽象的实现集合。这些组件旨在易于使用,无论您是否使用 LangChain 框架的其余部分。
- Use-Case Specific Chains:链可以被认为是以特定方式组装这些组件,以便最好地完成特定用例。这些旨在成为一个更高级别的界面,人们可以通过它轻松地开始使用特定的用例。这些链条还设计为可定制。
LangChain 组件
Schema
整个代码库中使用的基本数据类型和模式。
Text(文字)
使用语言模型时,与它们交互的主要界面是文本。作为过度简化,许多模型都是“文本输入,文本输出”。因此,LangChain 中的很多界面都是以文本为中心的。
ChatMessages(聊天信息)
最终用户与之交互的主要界面是聊天界面。出于这个原因,一些模型提供者甚至开始以期望聊天消息的方式提供对底层 API 的访问。这些消息有一个内容字段(通常是文本)并与用户相关联。目前支持的用户是系统、人类和人工智能。
- SystemChatMessage:代表信息的聊天消息应该是对 AI 系统的指令。
- HumanChatMessage:代表来自与 AI 系统交互的人的信息的聊天消息。
- AIChatMessage:代表来自 AI 系统的信息的聊天消息。
Examples(示例)
示例是输入/输出对,表示函数的输入以及预期的输出。它们可用于模型的训练和评估。
这些可以是模型或链的输入/输出。两种类型的示例都有不同的用途。模型示例可用于微调模型。链的示例可用于评估端到端链,或者甚至可以训练模型来替换整个链。
Document(文档)
一段非结构化数据。由 page_content(数据的内容)和 metadata(描述数据属性的辅助信息)组成。
Models
LangChain 中使用的不同类型的模型。
Language Model
语言模型将文本作为输入并返回文本作为输出。
Chat Model
聊天模型将 ChatMessages 列表作为输入并返回 ChatMessage。
Text Embedding Model
文本嵌入模型将一段文本作为输入,并以浮点列表的形式对该文本进行数字表示。
Prompts
编程模型的新方法是通过提示。“提示”指的是模型的输入。此输入很少被硬编码,而是通常由多个组件构成。LangChain 提供了几个类和函数来简化提示的构建和使用。
Prompt Value(提示值)
表示模型输入的类。
“提示”是指传递给基础模型的内容。主要抽象在 LangChain 中有提示,因此所有处理文本数据。对于其他数据类型(图像、音频),还正在努力添加抽象,目前还没有。
不同的模型可能需要不同的数据格式。在可能的情况下,我们希望允许在不同的模型类型中使用相同的提示。出于这个原因,我们有一个 PromptValue 的概念。这是一个类,它公开要转换为每个模型类型期望的确切输入类型的方法(目前是文本或 ChatMessages)
Prompt Template(提示模板)
负责构造 PromptValue 的类。
PromptValue 是最终传递给模型的内容。大多数时候,这个值不是硬编码的,而是根据用户输入、其他非静态信息(通常来自多个来源)和固定模板字符串的组合动态创建的。我们将负责创建 PromptValue 的对象称为 PromptTemplate。该对象公开了一种用于获取输入变量并返回 PromptValue 的方法。
Example Selectors(示例选择器)
通常,在提示中包含提示示例很有用。这些示例可以硬编码,但如果它们是动态选择的,通常会更强大。ExampleSelectors 是接受用户输入然后返回要使用的示例列表的对象。
Output Parser(输出解析器)
语言模型(和聊天模型)输出文本。但很多时候,您可能希望获得更多的结构化信息,而不仅仅是文本回复。这就是输出解析器发挥作用的地方。输出解析器负责 (1) 指示模型应如何格式化输出,(2) 将输出解析为所需的格式(包括在必要时重试)。
输出解析器是帮助构建语言模型响应的类。输出解析器必须实现两个主要方法:
get_format_instructions() -> str
:一种返回字符串的方法,该字符串包含有关如何格式化语言模型输出的说明。parse(str) -> Any
:一种接收字符串(假设是语言模型的响应)并将其解析为某种结构的方法。
然后是一个可选的:
parse_with_prompt(str) -> Any
:一种方法,它接受一个字符串(假设是来自语言模型的响应)和一个提示(假设生成这样一个响应的提示)并将其解析为某种结构。提示主要是在 OutputParser 想要以某种方式重试或修复输出的情况下提供的,并且需要来自提示的信息才能这样做。
Indexs
索引指的是构建文档的方式,以便 LLM 可以最好地与它们交互。该模块包含用于处理文档、不同类型索引的实用函数,以及在链中使用这些索引的示例。
在链中使用索引的最常见方式是在“检索”步骤中。此步骤指的是接受用户的查询并返回最相关的文档。我们之所以做出这种区分,是因为 (1) 索引可以用于检索以外的其他用途,以及 (2) 检索可以使用索引以外的其他逻辑来查找相关文档。因此,我们有一个“检索器”接口的概念——这是大多数链使用的接口。
大多数时候,当我们谈论索引和检索时,我们谈论的是索引和检索非结构化数据(如文本文档)。要与结构化数据(SQL 表等)或 API 进行交互,请参阅相应的用例部分以获取相关功能的链接。LangChain 支持的主要索引和检索类型目前以矢量数据库为中心,因此我们深入研究了这些主题的许多功能。
Document Loaders(文档加载器)
负责从各种来源加载文档的类。
文档加载器负责加载文档对象列表。
Text Splitters(文本拆分器)
负责将文本拆分为更小块的类。
通常,您希望将大型文本文档拆分为更小的块,以便更好地使用语言模型。TextSplitter 负责将文档拆分成更小的文档。
Vectorstore
最常见的索引类型。一种依赖于嵌入。
最常见的索引类型是为每个文档创建数字嵌入(使用嵌入模型)的索引。vectorstore 存储文档和关联的嵌入,并提供通过嵌入查找相关文档的快速方法。
Retriever
用于获取相关文档以与语言模型结合的接口。
一种存储数据的方式,这样它就可以被语言模型查询。该对象必须公开的唯一接口是一个get_relevant_texts
方法,该方法接受一个字符串并返回一个文档列表。
Memory
Memory 是在对话过程中存储和检索数据的概念。主要有两种方法:
- 根据输入,获取任何相关数据。
- 根据输入和输出,相应地更新状态。
有两种主要类型的 Memory:短期和长期。
短期记忆一般是指如何在单个对话的上下文中传递数据(一般是以前的 ChatMessages 或它们的摘要)。
长期记忆处理如何在对话之间获取和更新信息。
Chat Message History
目前与语言模型的主要界面是通过聊天界面。ChatMessageHistory 类负责记住所有以前的聊天交互。然后可以将这些直接传递回模型,以某种方式或某种组合进行总结。
ChatMessageHistory 公开了两种方法和一个属性。它公开的两个方法是add_user_message
和add_ai_message
,用于存储来自用户的消息和来自 AI 的相应响应。它公开的属性是一个messages
属性,用于访问所有以前的消息。
Chain
链是一个令人难以置信的通用概念,它返回以特定方式组合以完成常见用例的一系列模块化组件(或其他链)。
最常用的链类型是 LLMChain,它结合了 PromptTemplate、Model 和 Guardrails 以获取用户输入,相应地格式化,将其传递给模型并获得响应,然后验证和修复(如有必要)模型输出。
Chain(链)
链只是多个单独组件的端到端包装器。
LLMChain(LLM 链)
LLMChain 是最常见的链类型。它由一个 PromptTemplate、一个模型(LLM 或 ChatModel)和一个可选的输出解析器组成。此链采用多个输入变量,使用 PromptTemplate 将它们格式化为提示。然后它将其传递给模型。最后,它使用 OutputParser(如果提供)将 LLM 的输出解析为最终格式。
Index-related chains(索引相关链)
此类链用于与索引交互。这些链的目的是将您自己的数据(存储在索引中)与 LLM 相结合。最好的例子是对您自己的文档进行问答。
其中很大一部分是了解如何将多个文档传递给语言模型。有几种不同的方法或链可以这样做。LangChain 支持四种更常见的 - 我们正在积极寻求包括更多,所以如果您有任何想法,请联系我们!请注意,没有一种最好的方法——使用哪一种方法的决定通常是非常具体的。从最简单到最复杂的顺序:
Stuffing
Stuffing 是最简单的方法,您只需将所有相关数据填充到提示中作为上下文传递给语言模型。这在 LangChain 中作为StuffDocumentsChain
。
优点:只对 LLM 进行一次调用。生成文本时,LLM 可以一次访问所有数据。
缺点:大多数 LLM 都有上下文长度,对于大型文档(或许多文档),这将不起作用,因为它会导致提示大于上下文长度。
这种方法的主要缺点是它只适用于较小的数据块。一旦处理大量数据,这种方法就不再可行。接下来的两种方法旨在帮助解决这个问题。
Map Reduce
此方法涉及对每个数据块运行初始提示(对于摘要任务,这可能是该块的摘要;对于问答任务,它可能是仅基于该块的答案)。然后运行不同的提示以组合所有初始输出。这在 LangChain 中作为MapReduceDocumentsChain
。
优点:可以扩展到比StuffDocumentsChain
。对单个文档的 LLM 调用是独立的,因此可以并行化。
缺点:需要比 。LLM 更多的呼叫StuffDocumentsChain
。在最后的联合通话中丢失了一些信息。
Refine
此方法涉及对第一个数据块运行初始提示,生成一些输出。对于其余文档,该输出与下一个文档一起传入,要求 LLM 根据新文档改进输出。
优点:可以引入更相关的上下文,并且可能比MapReduceDocumentsChain
。
缺点:需要比 。LLM 更多的呼叫StuffDocumentsChain
。这些调用也不是独立的,这意味着它们不能像MapReduceDocumentsChain
。文档的排序也有一些潜在的依赖性。
Map-Rerank
这种方法涉及对每个数据块运行一个初始提示,它不仅会尝试完成一项任务,还会给出答案的确定性分数。然后根据这个分数对响应进行排序,并返回最高分。
优点:与MapReduceDocumentsChain
。与 。相比,需要更少的调用MapReduceDocumentsChain
。
缺点:无法合并文档之间的信息。这意味着当您希望在单个文档中有一个简单的答案时,它最有用。
Prompt Seletor (提示选择器)
LangChain 中链的目标之一是使人们能够尽快开始使用特定的用例。其中很大一部分是有好的提示。
问题是适用于一种型号的提示可能不适用于另一种型号。我们希望链能够很好地适用于所有类型的模型。因此,我们没有硬编码默认提示以在链中使用,而是有了 PromptSelector 的概念。此 PromptSelector 负责根据传入的模型选择默认提示。
PromptSelectors 最常见的用例是为 LLM 和聊天模型设置不同的默认提示。然而,这也可以用于为不同的模型提供者设置不同的默认提示,应该选择。
Agent
某些应用程序不仅需要预先确定的对 LLM/其他工具的调用链,还可能需要依赖于用户输入的未知链。在这些类型的链中,有一个“代理”可以访问一套工具。根据用户输入,代理可以决定调用这些工具中的哪一个(如果有的话)。
Tool(工具)
语言模型如何与其他资源交互。
围绕函数的特定抽象,使语言模型可以轻松地与之交互。具体来说,工具的界面具有单一的文本输入和单一的文本输出。
ToolKit(工具包)
组合使用时可以完成特定任务的工具集。
解决特定问题可以使用/必需的工具组。
Agent(代理)
驱动决策制定的语言模型。
代理是模型的包装器,它接收用户输入并返回对应于要采取的“动作”和相应的“动作输入”的响应。
Agent Executor(代理执行器)
使用工具运行代理的逻辑。
代理执行器是一个代理和一组工具。代理执行器负责调用代理,取回动作和动作输入,用相应的输入调用动作引用的工具,获取工具的输出,然后将所有信息传递回代理以获取它应该采取的下一步行动。
用例
私人助理
私人助理是一个完美的应用程序,因为它们结合了 LangChain 的两个核心价值支柱(行动采取和个性化数据)。为了构建个人助理,您应该了解以下概念:
- PromptTemplate:这将指导您的私人助理如何行动。他们很时髦吗?有帮助吗?这些可以用来给你的私人助理一些个性。
- Memory:您的私人助理应该可以记住一些事情。他们绝对应该能够进行对话(短期记忆),他们也应该有一些长期记忆的概念。
- Tool:您的私人助理将因您提供的工具而与众不同。它应该知道怎么做?
- Agent:你的私人助理必须了解它应该采取什么行动。尽可能构建最好的代理很重要。
- Agent Executor:在您获得工具和代理后,为了将其付诸实践,您需要为代理设置一个环境以使用这些工具。这是代理执行器发挥作用的地方。
文档问答
尽管 LLMs 很强大,但他们不知道他们没有接受过培训的信息。如果您想使用 LLM 来回答有关未受过培训的文档的问题,则必须向其提供有关这些文档的信息。最常见的方法是通过“检索增强生成”。
检索增强生成的想法是,当给定一个问题时,您首先执行检索步骤以获取任何相关文档。然后,您将这些文档连同原始问题一起传递给语言模型并让它生成响应。然而,为了做到这一点,您首先必须将您的文档设置为可以以这种方式查询的格式。本页介绍了这两个步骤之间的高级概念:(1) 将文档提取为可查询格式,然后 (2) 检索增强生成链。
提取
为了使用语言模型与您的数据进行交互,您首先必须采用合适的格式。该格式将是一个Index
。通过将数据放入索引,您可以轻松地让任何下游步骤与其交互。
有几种类型的索引,但到目前为止最常见的一种是 Vectorstore。可以通过以下步骤将文档提取到向量存储中:
- 装入文档(使用文档加载器);
- 拆分文档(使用文本拆分器);
- 为文档创建嵌入(使用文本嵌入模型);
- 将文档和嵌入存储在 vectorstore 中。
生成
现在我们有了一个索引,我们如何使用它来生成呢?这可以分为以下步骤:
- 接收用户提问;
- 在与问题相关的索引中查找文档;
- 根据问题和任何相关文档构造 PromptValue(使用 PromptTemplate);
- 将 PromptValue 传递给模型;
- 取回结果返回给用户。
聊天机器人
ChatGPT 以全新的界面——聊天——展示了强大的语言模型,席卷了整个世界。构建聊天机器人需要几个组件。
- Models——您可以从普通语言模型或聊天模型构建聊天机器人。要记住的重要一点是,即使您使用的是聊天模型,API 本身也是无状态的,这意味着它不会记住以前的交互 - 您必须将它们传递进来。
- PromptTemplate——这将指导您的聊天机器人如何行动。他们很时髦吗?有帮助吗?这些可以用来给你的聊天机器人一些个性。
- Memory——如上所述,模型本身是无状态的。内存为表带来了一些状态概念,允许它记住以前的交互
聊天机器人通常非常强大,并且在与其他数据源结合时更具差异化。支撑“文档问答”的相同技术也可以在这里使用,让您的聊天机器人可以访问该数据。
查询表格数据
许多数据和信息存储在表格数据中,无论是 csvs、excel 表还是 SQL 表。此页面涵盖了 LangChain 中用于处理此格式数据的所有可用资源。
文件
如果您有以表格格式存储的文本数据,您可能希望将数据加载到文档中,然后像处理其他文本/非结构化数据一样对其进行索引。为此,您应该使用像 CSVLoader 这样的文档加载器,然后您应该在该数据上创建一个索引,并以这种方式查询它。
Querying(查询)
如果你的数值型表格数据比较多,或者数据量很大不想索引,也可以直接使用语言模型与之交互。
Chains(链)
如果你刚刚起步,并且你有相对较小/简单的表格数据,你应该开始使用链。链是一系列预先确定的步骤,因此可以很好地开始,因为它们可以让您更好地控制并让您更好地了解正在发生的事情。
Agents(代理)
代理更复杂,涉及对 LLM 的多次查询以了解要做什么。代理人的缺点是您的控制权较少。好处是它们更强大,这使您可以在更大的数据库和更复杂的模式上使用它们。
与 API 交互
API 非常强大,因为它们既允许您通过它们执行操作,也允许您通过它们查询数据。此页面涵盖了 LangChain 中用于使用 API 的所有可用资源。
Chains(链)
如果您刚刚起步,并且您的 API 相对较小/简单,那么您应该从链开始。链是一系列预先确定的步骤,因此可以很好地开始,因为它们可以让您更好地控制并让您更好地了解正在发生的事情。
Agents(代理)
代理更复杂,涉及对 LLM 的多次查询以了解要做什么。代理人的缺点是您的控制权较少。好处是它们更强大,这使您可以在更大或更复杂的 API 上使用它们。
萃取
语言模型实际上非常擅长从非结构化文本中提取结构化信息。这很有用,因为很多信息都存储为文本,但为了使其在下游最有用,将其转换为结构化格式通常很方便。
这里要理解的最有用的概念是 OutputParsers 的概念。OutputParsers 负责指定语言模型应响应的模式,然后将其原始文本输出解析为该结构化格式。
使用这些进行提取的方法是在 OutputParser 中定义要提取的信息的架构。然后,您将创建一个 PromptTemplate,它接收原始文本 blob,并附有以指定格式提取信息的说明。
评估
这部分文档涵盖了我们如何处理和思考 LangChain 中的评估。既有对内部链/代理的评估,也有我们建议人们如何在 LangChain 方法评估之上构建。
问题
评估 LangChain Chain 和 Agent 可能真的很难。这有两个主要原因:
1:缺乏数据
在开始项目之前,您通常没有大量数据来评估您的链/代理。这通常是因为大型语言模型(大多数链/代理的核心)是极好的小样本和零样本学习者,这意味着您几乎总是能够开始执行特定任务(文本到 SQL、问答等) ) 没有大量的示例数据集。这与传统机器学习形成鲜明对比,在传统机器学习中,您甚至必须先收集一堆数据点,然后才能开始使用模型。
2:缺乏指标
大多数 Chains / Agents 正在执行的任务没有很好的指标来评估绩效。例如,最常见的用例之一是生成某种形式的文本。评估生成的文本比评估分类预测或数字预测要复杂得多。
解决方案
LangChain 试图解决这两个问题。到目前为止,我们所拥有的只是解决方案的初步通过——我们认为我们没有完美的解决方案。因此,我们非常欢迎对此的反馈、贡献、整合和想法。
到目前为止,这是我们对每个问题的看法:
1:缺乏数据
我们已经在 Hugging Face 上启动了 LangChainDatasets 社区空间。我们打算将其作为一个开源数据集的集合,用于评估公共链和代理。我们已经贡献了我们自己的五个数据集作为开始,但我们非常希望这是社区的努力。为了贡献数据集,您只需加入社区,然后您就可以上传数据集。
我们还致力于让人们尽可能轻松地创建自己的数据集。作为第一步,我们添加了一个 QAGenerationChain,它给一个文档提供了问答对,可以用来评估该文档的问答任务。
2:缺乏指标
对于缺少指标,我们有两种解决方案。
第一个解决方案是不使用任何指标,而只是依靠肉眼观察结果来了解链/代理的执行情况。为此,我们开发了(并将继续开发)Tracing,这是一种基于 UI 的链和代理运行可视化工具。
我们推荐的第二种解决方案是使用语言模型本身来评估输出。为此,我们有一些不同的链条和提示来解决这个问题。
总结
一个常见的用例是想要总结长文档。这自然会遇到上下文窗口限制。与问答不同,你不能只做一些语义搜索技巧来只选择与问题最相关的文本块(因为在这种情况下,没有特定的问题——你想总结所有内容)。那你怎么办?
解决这个问题的最常见方法是将文档分成块,然后以递归方式进行汇总。我们的意思是,您首先自己总结每个块,然后将摘要分组并总结每个块的摘要,并继续这样做,直到只剩下一个。