跳到主要内容LangChain 核心概念与架构详解 | 极客日志PythonAI算法
LangChain 核心概念与架构详解
综述由AI生成LangChain 是一个用于开发语言模型驱动应用程序的框架,支持数据感知和智能体交互。其核心组件包括 Schema、Models、Prompts、Indexes、Memory、Chain 和 Agent。通过链式调用和代理机制,LangChain 实现了文档问答、聊天机器人、表格查询等多种用例。详细解析了各组件的功能、设计目标及实际应用方案,涵盖从基础数据结构到高级检索增强生成(RAG)的完整技术路径,并补充了部署时的最佳实践与注意事项。
CodeArtist25 浏览 LangChain 介绍
LangChain 是一个用于开发由语言模型驱动的应用程序的框架。它旨在简化构建基于大语言模型(LLM)应用的流程,提供模块化组件以支持数据感知、智能体交互等高级功能。
LangChain 框架的设计目标
LangChain 的设计目标是让应用程序不仅通过 API 调用语言模型,还能实现以下能力:
- 数据感知:将语言模型连接到其他数据源,使其能够访问外部信息。
- 具备代理能力(Agentic):允许语言模型与其环境交互,自主决定工具的使用。
- 增强应用差异化:最终让应用程序更强大,具备独特的业务逻辑。
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:一种方法,它接受一个字符串(假设是来自语言模型的响应)和一个提示(假设生成这样一个响应的提示)并将其解析为某种结构。
Indexes(索引)
索引指的是构建文档的方式,以便 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)
LLM 链(LLMChain)
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 调用是独立的,因此可以并行化。
- 缺点:需要比
StuffDocumentsChain 更多的 LLM 呼叫。在最后的联合通话中丢失了一些信息。
Refine
此方法涉及对第一个数据块运行初始提示,生成一些输出。对于其余文档,该输出与下一个文档一起传入,要求 LLM 根据新文档改进输出。
- 优点:可以引入更相关的上下文,并且可能比
MapReduceDocumentsChain 更好。
- 缺点:需要比
StuffDocumentsChain 更多的 LLM 呼叫。这些调用也不是独立的,这意味着它们不能像 MapReduceDocumentsChain 那样并行化。文档的排序也有一些潜在的依赖性。
Map-Rerank
这种方法涉及对每个数据块运行一个初始提示,它不仅会尝试完成一项任务,还会给出答案的确定性分数。然后根据这个分数对响应进行排序,并返回最高分。
- 优点:与
MapReduceDocumentsChain 相比,需要更少的调用。
- 缺点:无法合并文档之间的信息。这意味着当您希望在单个文档中有一个简单的答案时,它最有用。
提示选择器(Prompt Selector)
LangChain 中链的目标之一是使人们能够尽快开始使用特定的用例。其中很大一部分是有好的提示。
问题是适用于一种型号的提示可能不适用于另一种型号。我们希望链能够很好地适用于所有类型的模型。因此,我们没有硬编码默认提示以在链中使用,而是有了 PromptSelector 的概念。此 PromptSelector 负责根据传入的模型选择默认提示。
PromptSelectors 最常见的用例是为 LLM 和聊天模型设置不同的默认提示。然而,这也可以用于为不同的模型提供者设置不同的默认提示。
Agent(代理)
某些应用程序不仅需要预先确定的对 LLM/其他工具的调用链,还可能需要依赖于用户输入的未知链。在这些类型的链中,有一个'代理'可以访问一套工具。根据用户输入,代理可以决定调用这些工具中的哪一个(如果有的话)。
工具(Tool)
语言模型如何与其他资源交互。围绕函数的特定抽象,使语言模型可以轻松地与之交互。具体来说,工具的界面具有单一的文本输入和单一的文本输出。
工具包(ToolKit)
组合使用时可以完成特定任务的工具集。解决特定问题可以使用/必需的工具组。
代理(Agent)
驱动决策制定的语言模型。代理是模型的包装器,它接收用户输入并返回对应于要采取的'动作'和相应的'动作输入'的响应。
代理执行器(Agent Executor)
使用工具运行代理的逻辑。代理执行器是一个代理和一组工具。代理执行器负责调用代理,取回动作和动作输入,用相应的输入调用动作引用的工具,获取工具的输出,然后将所有信息传递回代理以获取它应该采取的下一步行动。
典型用例
私人助理
私人助理是一个完美的应用程序,因为它们结合了 LangChain 的两个核心价值支柱(采取行动和个性化数据)。为了构建个人助理,您应该了解以下概念:
- PromptTemplate:这将指导您的私人助理如何行动。他们很时髦吗?有帮助吗?这些可以用来给你的私人助理一些个性。
- Memory:您的私人助理应该可以记住一些事情。他们绝对应该能够进行对话(短期记忆),他们也应该有一些长期记忆的概念。
- Tool:您的私人助理将因您提供的工具而与众不同。它应该知道怎么做?
- Agent:你的私人助理必须了解它应该采取什么行动。尽可能构建最好的代理很重要。
- Agent Executor:在您获得工具和代理后,为了将其付诸实践,您需要为代理设置一个环境以使用这些工具。这是代理执行器发挥作用的地方。
文档问答
尽管 LLMs 很强大,但他们不知道他们没有接受过培训的信息。如果您想使用 LLM 来回答有关未受过培训的文档的问题,则必须向其提供有关这些文档的信息。最常见的方法是通过'检索增强生成'(RAG)。
检索增强生成的想法是,当给定一个问题时,您首先执行检索步骤以获取任何相关文档。然后,您将这些文档连同原始问题一起传递给语言模型并让它生成响应。然而,为了做到这一点,您首先必须将您的文档设置为可以以这种方式查询的格式。
提取
为了使用语言模型与您的数据进行交互,您首先必须采用合适的格式。该格式将是一个 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 可能真的很难。这有两个主要原因:
- 缺乏数据:在开始项目之前,您通常没有大量数据来评估您的链/代理。这通常是因为大型语言模型(大多数链/代理的核心)是极好的小样本和零样本学习者,这意味着您几乎总是能够开始执行特定任务(文本到 SQL、问答等)没有大量的示例数据集。这与传统机器学习形成鲜明对比,在传统机器学习中,您甚至必须先收集一堆数据点,然后才能开始使用模型。
- 缺乏指标:大多数 Chains / Agents 正在执行的任务没有很好的指标来评估绩效。例如,最常见的用例之一是生成某种形式的文本。评估生成的文本比评估分类预测或数字预测要复杂得多。
解决方案
LangChain 试图解决这两个问题。到目前为止,我们所拥有的只是解决方案的初步通过——我们认为我们没有完美的解决方案。因此,我们非常欢迎对此的反馈、贡献、整合和想法。
缺乏数据
我们已经在 Hugging Face 上启动了 LangChainDatasets 社区空间。我们打算将其作为一个开源数据集的集合,用于评估公共链和代理。我们已经贡献了我们自己的五个数据集作为开始,但我们非常希望这是社区的努力。为了贡献数据集,您只需加入社区,然后您就可以上传数据集。
我们还致力于让人们尽可能轻松地创建自己的数据集。作为第一步,我们添加了一个 QAGenerationChain,它给一个文档提供了问答对,可以用来评估该文档的问答任务。
缺乏指标
对于缺少指标,我们有两种解决方案。
第一个解决方案是不使用任何指标,而只是依靠肉眼观察结果来了解链/代理的执行情况。为此,我们开发了(并将继续开发)Tracing,这是一种基于 UI 的链和代理运行可视化工具。
我们推荐的第二种解决方案是使用语言模型本身来评估输出。为此,我们有一些不同的链条和提示来解决这个问题。
总结
一个常见的用例是想要总结长文档。这自然会遇到上下文窗口限制。与问答不同,你不能只做一些语义搜索技巧来只选择与问题最相关的文本块(因为在这种情况下,没有特定的问题——你想总结所有内容)。那你怎么办?
解决这个问题的最常见方法是将文档分成块,然后以递归方式进行汇总。我们的意思是,您首先自己总结每个块,然后将摘要分组并总结每个块的摘要,并继续这样做,直到只剩下一个。
最佳实践与注意事项
在实际部署 LangChain 应用时,除了掌握上述组件外,还需注意以下几点以确保系统的稳定性和效率:
- 上下文管理:始终关注 Token 消耗。在处理长文档或复杂对话时,合理设计 Memory 策略,避免超出模型的上下文窗口限制。对于超长文档,优先使用 Map-Reduce 或 Refine 策略而非简单的 Stuffing。
- 延迟优化:链式调用和代理执行涉及多次 API 请求。在生产环境中,应考虑异步处理和缓存机制,以减少响应时间。对于频繁查询的文档,可使用本地向量数据库加速检索。
- 安全性:确保输入数据经过清洗,防止提示注入攻击。在使用 Agent 调用外部工具时,务必对工具权限进行最小化限制,避免敏感数据泄露。
- 监控与追踪:利用 Tracing 工具记录每一步的输入输出,便于调试和优化。建立日志系统,监控 Token 成本和错误率。
- 版本兼容性:LangChain 迭代迅速,建议在项目中锁定依赖版本,避免因框架更新导致的行为不一致。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online