Langchain(v0.2)

官网:https://python.langchain.com/v0.2/docs/introduction/

1、介绍

LangChain 是一个用于开发由大型语言模型提供支持的应用程序的框架 (LLMs)。

LangChain 简化了LLM应用程序生命周期的每个阶段:

  • 开发:使用 LangChain 的开源构建块、组件和第三方集成构建应用程序。使用 LangGraph 构建具有一流流式处理和人机回圈支持的有状态代理。
  • 产品化:使用 LangSmith 检查、监控和评估您的链条,以便您可以放心地持续优化和部署。
  • 部署:使用 LangGraph Cloud 将您的 LangGraph 应用程序转变为生产就绪的 API 和助手。

在这里插入图片描述
该框架由以下开源库组成:

  • langchain-core :基本抽象和 LangChain 表达式语言。
  • langchain-community :第三方集成。
  • langchain :构成应用程序认知架构的链、代理和检索策略。
  • LangGraph:LLMs通过将步骤建模为图形中的边和节点,构建健壮且有状态的多角色应用程序。与 LangChain 无缝集成,但可以在没有 LangChain 的情况下使用。
  • LangServe:将 LangChain 链部署为 REST API。
  • LangSmith:一个开发人员平台,可让您调试、测试、评估和监控LLM应用程序。

1.1、LangChain 表达式语言 (LCEL)

LangChain Expression Language 或 ,是 LCEL 链接 LangChain 组件的声明性方式。LCEL 从第一天开始就被设计为支持将原型投入生产,无需更改代码,从最简单的 “prompt + LLM” 链到最复杂的链(我们已经看到人们在生产中成功运行了 LCEL 链,其中包含 100 多个生产步骤)。特点如下:

  • 一流的流式处理支持:当您使用 LCEL 构建链时,您可以获得最佳的第一个令牌时间(直到第一个输出块出来所经过的时间)。对于某些链,这意味着例如。我们将令牌直接从 流式传输LLM到流式输出解析器,然后您以与LLM提供商输出原始令牌相同的速率返回解析的增量输出块。
  • 异步支持:使用 LCEL 构建的任何链既可以使用同步 API(例如,在原型设计时在 Jupyter 笔记本中)调用,也可以使用异步 API(例如,在 LangServe 服务器中)调用。这使得对原型和生产使用相同的代码,具有极佳的性能,并能够在同一服务器中处理许多并发请求。
  • 优化的并行执行:每当您的 LCEL 链具有可以并行执行的步骤时(例如,如果您从多个检索器中获取文档),我们都会在同步和异步接口中自动执行,以实现尽可能小的延迟。
  • Retries and fallbacks(重试和回退):为 LCEL 链的任何部分配置重试和回退。这是使您的链条在规模上更可靠的好方法。我们目前正在努力为重试/回退添加流式处理支持,以便您可以获得更高的可靠性,而不会产生任何延迟成本。
  • 访问中间结果: 对于更复杂的链,甚至在生成最终输出之前访问中间步骤的结果通常非常有用。这可以用来让最终用户知道正在发生的事情,甚至只是调试你的链。您可以流式传输中间结果,并且它在每个 LangServe 服务器上都可用。
  • 输入和输出架构 输入和输出架构为每个 LCEL 链提供从链结构中推断出的 Pydantic 和 JSONSchema 架构。这可用于验证输入和输出,并且是 LangServe 不可或缺的一部分。
  • 无缝 LangSmith 追踪 随着链条变得越来越复杂,了解每一步到底发生了什么变得越来越重要。使用 LCEL,所有步骤都会自动记录到 LangSmith 中,以实现最大的可观察性和可调试性。
1.1.1、Runnable 接口

为了尽可能轻松地创建自定义链,我们实现了 “Runnable” 协议。许多 LangChain 组件都实现了该 Runnable 协议,包括聊天模型、LLMs输出解析器、检索器、提示模板等。还有一些有用的 primitives 用于处理 runnables。

这是一个标准接口,可以轻松定义自定义链并以标准方式调用它们。标准接口包括:

  • stream :流回响应的块
  • invoke :在输入上调用链
  • batch :在输入列表上调用链

这些还具有相应的 异步方法,这些方法应该与 asyncio await 语法一起使用以实现并发:

  • astream :流回响应的块 async
  • ainvoke :在输入 async 上调用链
  • abatch :在输入列表上异步调用链
  • astream_log :除了最终响应外,还在发生中间步骤时向后流
  • astream_events :链中发生的 beta 流事件(在 0.1.14 中 langchain-core 引入)

输入类型和输出类型因组件而异:

Component Input Type Output Type
Prompt Dictionary PromptValue
ChatModel Single string, list of chat messages or a PromptValue ChatMessage
LLM Single string, list of chat messages or a PromptValue String
OutputParser The output of an LLM or ChatModel Depends on the parser
Retriever Single string List of Documents
Tool Single string or dictionary, depending on the tool Depends on the tool

所有 runnables 都公开了输入和输出架构以检查输入和输出:

  • input_schema :从 Runnable 的结构中自动生成的输入 Pydantic 模型
  • output_schema :从 Runnable 的结构自动生成的输出 Pydantic 模型

1.2、Components组件

LangChain 为各种组件提供标准的可扩展接口和外部集成,这些组件可用于使用 LLMs.一些组件是 LangChain 实现的,一些组件我们依赖第三方集成,还有一些是混合的。

1.2.1、Chat models 聊天模型

使用一系列消息作为输入并返回聊天消息作为输出(而不是使用纯文本)的语言模型。这些是传统的新型号(旧型号通常是 LLMs ,见下文)。聊天模型支持为对话消息分配不同的角色,有助于区分来自 AI、用户和系统消息等指令的消息。

尽管底层模型是 messages in, message out,但 LangChain 包装器还允许这些模型将字符串作为输入。这意味着您可以轻松地使用聊天模型代替 LLMs.

当字符串作为 input 传入时,它会被转换为 a HumanMessage ,然后传递给底层模型。

LangChain 不托管任何聊天模型,而是依赖第三方集成。

在构建 ChatModel 时,我们有一些标准化的参数:

  • model :模型的名称
  • temperature :模型创新度
  • timeout :请求超时
  • max_tokens :要生成的最大令牌数
  • stop :默认停止序列
  • max_retries :重试请求的最大次数
  • api_key :模型提供程序的 API 密钥
  • base_url :将请求发送到的端点
1.2.2、Messages 消息

某些语言模型将消息列表作为输入并返回消息。有几种不同类型的消息。所有消息都 role 具有 、 contentresponse_metadata 属性。

role描述了 谁 正在说这个信息。标准角色包括 “user”、“assistant”、“system” 和 “tool”。LangChain 针对不同的角色有不同的消息类。

content 属性描述消息的内容。这可以是字符串(大多数模型处理此类内容)或者字典列表(用于多模式输入,其中字典包含有关该输入类型和该输入位置的信息)。

(可选)消息可以具有一个 name 属性,该属性允许区分具有相同角色的多个说话人。

HumanMessage

这表示角色为 “user” 的消息。

AIMessage

这表示角色为 “assistant” 的消息。除了 content 属性之外,这些消息还具有:

response_metadata

response_metadata 属性包含有关响应的其他元数据。此处的数据通常特定于每个模型提供商。这是可以存储 log-probs 和 token 使用情况等信息的地方。

tool_calls

这些表示语言模型对调用工具的决策。它们作为 AIMessage 输出的一部分包含在内。他们可以从那里通过 .tool_calls 参数进入。

此属性返回 ToolCall s 的列表。A ToolCall 是具有以下参数的字典:

  • name :应调用的工具的名称。
  • args :该工具的参数。
  • id :该工具调用的 ID。
SystemMessage

这表示一条带有角色 “system” 的消息,它告诉模型如何表现。并非每个模型提供程序都支持此功能。

ToolMessage

这表示一条角色为 “tool” 的消息,其中包含调用工具的结果。除了 rolecontent 之外,此消息还包含:

  • 一个 tool_call_id 字段,用于传达对为生成此结果而调用的工具的调用 ID。
  • 一个 artifact 字段,可用于传递工具执行的任意工件,这些工件可用于跟踪,但不应将其发送到模型。
1.2.3、Prompt templates提示模板

提示模板有助于将用户输入和参数转换为语言模型的说明。这可用于指导模型的响应,帮助它理解上下文并生成相关且连贯的基于语言的输出。

Prompt Templates 将字典作为输入,其中每个键代表 Prompt 模板中要填写的变量。

提示模板输出 PromptValue。此 PromptValue 可以传递给 LLM 或 ChatModel,也可以强制转换为字符串或消息列表。此 PromptValue 存在的原因是为了便于在字符串和消息之间切换。

几种不同类型的提示模板:

字符串 PromptTemplates

这些提示模板用于格式化单个字符串,通常用于更简单的输入。例如,构造和使用 PromptTemplate 的常用方法如下所示:

from langchain_core.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template("Tell me a joke about {topic}")

prompt_template.invoke({"topic": "cats"})
ChatPrompt模板

这些提示模板用于设置消息列表的格式。这些 “模板” 由模板本身的列表组成。例如,构造和使用 ChatPromptTemplate 的常用方法如下所示:

from langchain_core.prompts import ChatPromptTemplate

prompt_template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    ("user", "Tell me a joke about {topic}")
])

prompt_template.invoke({"topic": "cats"})
MessagesPlaceholder

此提示模板负责在特定位置添加消息列表。在上面的 ChatPromptTemplate 中,我们看到了如何格式化两条消息,每条消息都是一个字符串。但是,如果我们希望用户传入一个消息列表,我们将将其放入特定位置,该怎么办?这就是使用 MessagesPlaceholder 的方式。

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage

prompt_template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    MessagesPlaceholder("msgs")
])

prompt_template.invoke({"msgs": [HumanMessage(content="hi!")]})

这将生成一个包含两条消息的列表,第一条是系统消息,第二条是我们传入的 HumanMessage。如果我们传入了 5 条消息,那么它总共会产生 6 条消息(系统消息加上传入的 5 条消息)。这对于将邮件列表放入特定位置非常有用。

1.2.4、Output parsers输出解析器

Output parser 负责获取模型的输出并将其转换为更适合下游任务的格式。当您使用 生成结构化数据或规范化聊天模型和 LLMs的输出时LLMs,这很有用。

支持生成json、xml、csv、yaml等格式

1.2.5、Chat history聊天记录

ChatHistory 是指 LangChain 中的一个类,该类可用于包装任意链。这将 ChatHistory 跟踪底层链的输入和输出,并将它们作为消息附加到消息数据库。然后,未来的交互将加载这些消息,并将它们作为 input 的一部分传递到链中。

1.2.6、Documents文件

LangChain 中的 Document 对象包含有关某些数据的信息。它有两个属性:

  • page_content: str :本文档的内容。当前只是一个字符串。
  • metadata: dict :与此文档关联的任意元数据。可以跟踪文档 ID、文件名等。

文档加载器加载 Document 对象。LangChain 与各种数据源进行了数百次集成,以加载数据:Slack、Notion、Google Drive 等。

每个 DocumentLoader 都有其自己的特定参数,但都可以使用 .load 该方法以相同的方式调用它们。示例用例如下:

from langchain_community.document_loaders.csv_loader import CSVLoader

loader = CSVLoader(
    ...  # <-- Integration specific parameters here
)
data = loader.load()
1.2.7、Text splitters文本拆分器

加载文档后,您通常需要对其进行转换以更好地适应您的应用程序。最简单的例子是你可能想把一个长文档拆分成更小的块,以适应你的模型的上下文窗口。LangChain 具有许多内置的文档转换器,可以轻松拆分、组合、筛选和以其他方式操作文档。

当您想要处理长文本时,有必要将该文本拆分为块。这听起来很简单,但这里有很多潜在的复杂性。理想情况下,您希望将语义相关的文本片段放在一起。“语义相关”的含义可能取决于文本的类型。此笔记本展示了几种实现此目的的方法。

文本拆分器的工作原理如下:

  1. 将文本拆分为具有语义意义的小块(通常是句子)。
  2. 开始将这些小块组合成一个更大的块,直到达到一定大小(由某个函数测量)。
  3. 一旦你达到这个大小,就把那个数据块设为它自己的文本,然后开始创建一个新的文本数据块,并有一些重叠(以保持数据块之间的上下文)。

可以沿文本的拆分方式和如何测量块大小两个不同的轴自定义文本分割器

1.2.8、Embedding models 嵌入模型

嵌入模型创建一段文本的矢量表示。您可以将向量视为捕获文本语义含义的数字数组。通过以这种方式表示文本,您可以执行数学运算,从而允许您执行一些操作,例如搜索含义最相似的其他文本片段。这些自然语言搜索功能支持多种类型的上下文检索,其中我们为 提供LLM有效响应查询所需的相关数据。
在这里插入图片描述
Embeddings 类是专为与文本嵌入模型交互而设计的类。有许多不同的嵌入模型提供程序(OpenAI、Cohere、Hugging Face 等)和本地模型,此类旨在为所有这些模型提供标准接口。

LangChain 中的基类 Embeddings 提供了两种方法:一种用于嵌入文档,另一种用于嵌入查询。前者采用多个文本作为输入,而后者采用单个文本。

1.2.9、Vector stores 矢量存储

存储和搜索非结构化数据的最常见方法之一是嵌入数据并存储生成的嵌入向量,然后在查询时嵌入非结构化查询并检索与嵌入查询“最相似”的嵌入向量。矢量存储负责存储嵌入的数据并为您执行矢量搜索。

大多数矢量存储还可以存储有关嵌入矢量的元数据,并支持在相似性搜索之前对该元数据进行筛选,从而允许您更好地控制返回的文档。

可以通过执行以下操作将 vector store 转换为 retriever 接口:(后续案例中会讲到)

vectorstore = MyVectorStore()
retriever = vectorstore.as_retriever()
1.2.10、Retrievers检索器

检索器是一个接口,它返回给定非结构化查询的文档。它比 vector store 更通用。检索器不需要能够存储文档,只需返回(或检索)它们。检索器可以从矢量存储创建。

检索器接受字符串查询作为输入,并返回 Document 的列表作为输出。

1.2.11、Key-value stores 键值存储

对于某些技术,例如每个文档具有多个向量的索引和检索或缓存嵌入,拥有某种形式的键值 (KV) 存储会很有帮助。

LangChain 包括一个 BaseStore 接口,允许存储任意数据。但是,需要 KV 存储的 LangChain 组件接受存储二进制数据的更具体的 BaseStore[str, bytes] 实例(称为 a ByteStore ),并在内部负责根据其特定需求对数据进行编码和解码。

1.2.12、Tools 工具

工具是设计为由模型调用的工具:它们的输入设计为由模型生成,其输出设计为传递回模型。每当您希望模型控制代码的某些部分或调用外部 API 时,都需要工具。

工具包括:

  1. 工具名称
  2. 工具功能描述
  3. 工具JSON schema输入
  4. 函数

将工具绑定到模型时,名称、描述和 JSON 方案将作为模型的上下文提供。给定一个工具列表和一组指令,模型可以请求使用特定输入调用一个或多个工具。典型用法可能如下所示:

tools = [...] # Define a list of tools
llm_with_tools = llm.bind_tools(tools)
ai_msg = llm_with_tools.invoke("do xyz...")
# -> AIMessage(tool_calls=[ToolCall(...), ...], ...)
1.2.13、Toolkits 工具包

工具包是旨在一起用于特定任务的工具集合。他们有方便的加载方法。

所有 Toolkit 都公开了一个 get_tools 返回工具列表的方法。因此,您可以执行以下操作:

# Initialize a toolkit
toolkit = ExampleTookit(...)

# Get list of tools
tools = toolkit.get_tools()
1.2.14、Agents 代理

语言模型本身无法执行操作 - 它们只是输出文本。LangChain 的一大用例是创建代理。代理是用作LLM推理引擎的系统,用于确定要采取哪些操作以及这些操作的输入应该是什么。然后,这些操作的结果可以反馈给代理,并确定是否需要更多操作,或者是否可以完成。

LangGraph 是 LangChain 的扩展,专门用于创建高度可控和可定制的代理。

一种流行的项目代理架构是 ReAct。

一般流程如下所示:

  1. 该模型将“考虑”响应输入和任何先前的观察结果而采取的步骤。
  2. 然后,模型将从可用工具中选择一个操作(或选择响应用户)。
  3. 该模型将为该工具生成参数。
  4. 代理运行时(执行程序)将解析出所选工具,并使用生成的参数调用它。
  5. 执行程序会将工具调用的结果作为观察结果返回给模型。
  6. 此过程将重复,直到代理选择响应。
1.2.15、Callbacks 回调

LangChain 提供了一个回调系统,允许您挂接LLM到应用程序的各个阶段。这对于日志记录、监控、流式处理和其他任务非常有用。

您可以使用整个 API 中可用的 callbacks 参数来订阅这些事件。

回调处理程序可以是 syncasync 。在运行时,LangChain 会配置一个合适的回调管理器(例如 CallbackManager 或 AsyncCallbackManager,当事件触发时,它将负责在每个“已注册”的回调处理程序上调用适当的方法。

1.3、Techniques技术

1.3.1、Streaming 流

LangChain 中的大多数模块都包含 .stream() method(以及异步环境的等效 .astream() 方法)作为符合人体工程学的流式接口。 .stream() 返回一个迭代器,您可以通过简单的 for 循环来使用该迭代器。下面是一个聊天模型的示例:

from langchain_anthropic import ChatAnthropic

model = ChatAnthropic(model="claude-3-sonnet-20240229")

for chunk in model.stream("what color is the sky?"):
    print(chunk.content, end="|", flush=True)
1.3.2、Function/tool calling 函数/工具调用

工具调用允许聊天模型通过生成与用户定义的架构匹配的输出来响应给定的提示。

LangChain 为工具调用提供了一个标准化的接口,该接口在不同模型之间是一致的。

标准接口包括:

  • ChatModel.bind_tools() :一种用于指定模型可调用的工具的方法。此方法接受 LangChain 工具以及 Pydantic 对象。
  • AIMessage.tool_calls :从模型返回的 属性 AIMessage ,用于访问模型请求的工具调用。
Tool usage 工具使用

在模型调用 tools 后,您可以通过调用该工具,然后将参数传递回模型来使用该工具。LangChain 提供了 Tool 抽象来帮助您处理此问题。

使用流程:

  1. 使用聊天模型生成工具调用以响应查询。
  2. 使用生成的 tool call 作为参数来调用相应的工具。
  3. 将工具调用的结果格式化为 ToolMessages
  4. 将整个消息列表传回给模型,以便它可以生成最终答案(或调用更多工具)。
    在这里插入图片描述
1.3.3、Structured output 结构化输出

LLMs能够生成任意文本。这使模型能够适当地响应各种输入,但对于某些用例,将 LLM的输出限制为特定格式或结构可能很有用。这称为结构化输出。

例如,如果输出要存储在关系数据库中,则如果模型生成的输出符合定义的架构或格式,则要容易得多。从非结构化文本中提取特定信息是另一种特别有用的情况。最常见的是,输出格式是 JSON,尽管其他格式(如 YAML)也很有用。下面,我们将讨论从 LangChain 中的模型获取结构化输出的几种方法。

为方便起见,一些 LangChain 聊天模型支持一种方法 .with_structured_output() 。此方法只需要一个 schema 作为输入,并返回一个 dict 或 Pydantic 对象。通常,此方法仅存在于支持下面描述的更高级方法之一的模型上,并且将在后台使用其中一种方法。它负责导入合适的输出解析器,并以适合模型的格式格式化架构。

示例:

from typing import Optional

from langchain_core.pydantic_v1 import BaseModel, Field


class Joke(BaseModel):
    """Joke to tell user."""

    setup: str = Field(description="The setup of the joke")
    punchline: str = Field(description="The punchline to the joke")
    rating: Optional[int] = Field(description="How funny the joke is, from 1 to 10")

structured_llm = llm.with_structured_output(Joke)

structured_llm.invoke("Tell me a joke about cats")

输出结果

Joke(setup='Why was the cat sitting on the computer?', punchline='To keep an eye on the mouse!', rating=None)
1.3.4、Retrieval augmented generation ( RAG )检索增强生成

LLMs在大型但固定的数据集上进行训练,从而限制了他们对私人或最新信息进行推理的能力。LLM用具体事实进行微调是缓解这种情况的一种方法,但通常不适合事实回忆,而且成本很高。 Retrieval 是向 提供相关信息LLM以改进其对给定输入的响应的过程。 Retrieval augmented generationRAG ) 论文是基于大模型检索信息的过程。

RAG 的好坏取决于检索到的文档的相关性和质量。幸运的是,可以采用一组新兴的技术来设计和改进 RAG 系统。我们专注于对其中许多技术进行分类和总结(见下图),并将在以下部分中分享一些高级战略指导。您可以而且应该尝试将不同的部分一起使用。
在这里插入图片描述

Query Translation 查询翻译

首先,考虑用户对 RAG 系统的输入。理想情况下,RAG 系统可以处理各种输入,从措辞不佳的问题到复杂的多部分查询。使用 LLM to review 并选择性地修改输入是查询翻译背后的中心思想。这用作通用缓冲区,优化检索系统的原始用户输入。例如,这可以像提取关键词一样简单,也可以像为复杂查询生成多个子问题一样复杂。

名字 适用情形 描述
Multi-query 多查询 当您需要涵盖问题的多个角度时。 从多个角度重写用户问题,检索每个重写问题的文档,返回所有查询的唯一文档。
Decomposition 分解 当一个问题可以分解为更小的子问题时。 将问题分解为一组子问题/问题,可以按顺序解决(使用第一个答案 + 检索回答第二个)或并行解决(将每个答案合并为最终答案)。
Step-back 后退 当需要更高层次的概念理解时。 首先提示 询问LLM有关更高级别概念或原则的通用后退问题,并检索有关它们的相关事实。使用此接地来帮助回答用户问题。
HyDE 如果您在使用原始用户输入检索相关文档时遇到挑战。 使用 一个 LLM 将问题转换为回答问题的假设文档。使用嵌入的假设文档来检索真实文档,前提是 doc-doc 相似性搜索可以产生更相关的匹配项。
Routing 路由

其次,考虑您的 RAG 系统可用的数据源。您希望跨多个数据库或跨结构化和非结构化数据源进行查询。使用 查看LLM输入并将其路由到相应的数据源是跨源查询的一种简单而有效的方法。

名字 适用情形 描述
Logical routing 逻辑路由 何时可以提示 LLM with 规则来决定将输入路由到何处。 逻辑路由可以使用 an LLM 对查询进行推理,并选择最合适的数据存储。
Semantic routing 语义路由 当语义相似性是确定将输入路由到何处的有效方法时。 语义路由嵌入了 query 和 set of prompt(通常是一组提示)。然后,它会根据相似性选择合适的提示。
Query Construction 查询构造

第三,考虑您的任何数据源是否需要特定的查询格式。许多结构化数据库使用 SQL。矢量存储通常具有用于将关键字过滤器应用于文档元数据的特定语法。使用一个 LLM 将自然语言查询转换为查询语法是一种常用且功能强大的方法。特别是,元数据过滤器的文本到 SQL、文本到 Cypher 和查询分析是分别与结构化数据库、图形数据库和矢量数据库交互的有用方法。

名字 适用情形 描述
Text to SQL 文本转 SQL 如果用户提出的问题需要存储在关系数据库中的信息,可通过 SQL 访问。 这使用 将LLM用户输入转换为 SQL 查询。
Text-to-Cypher 文本到密码 如果用户提出的问题需要存储在图形数据库中的信息,可通过 Cypher 访问。 这使用 an LLM 将用户输入转换为 Cypher 查询。
Self Query 自查询 如果用户提出的问题,通过根据元数据而不是与文本的相似性来获取文档可以更好地回答。 这使用 an LLM 将用户输入转换为两件事:(1) 一个用于语义查找的字符串,(2) 一个与之配套的元数据过滤器。这很有用,因为问题通常是关于文档的 METADATA (而不是内容本身)。
Indexing 索引

第四,考虑文档索引的设计。一个简单而强大的想法是将您为检索编制索引的文档与您传递给 LLM for generation 的文档分离。索引经常使用带有向量存储的嵌入模型,这些模型将文档中的语义信息压缩为固定大小的向量。

许多 RAG 方法侧重于将文档拆分为块,并根据与输入问题的相似性检索一些数字。LLM但是 chunk size 和 chunk number 可能很难设置,并且如果它们没有提供完整的上下文来LLM回答问题,则会影响结果。

有两种方法解决(1) Multi Vector retriever 使用 一个 LLM 将文档翻译成任何适合索引的形式(例如,通常转换为摘要),但将完整的文档返回给 LLM for generation。(2) ParentDocument 检索器嵌入文档块,但也返回完整文档。这个想法是两全其美:使用简洁的表示形式(摘要或块)进行检索,但使用完整的文档来生成答案。

名字 索引类型 LLM的使用 适用情形 描述
Vector store 矢量存储 矢量存储 - 如果您刚刚开始并寻找快速简便的东西。 这是最简单的方法,也是最容易上手的方法。它涉及为每段文本创建嵌入。
ParentDocument 父文档 矢量存储 + 文档存储 - 如果你的页面有很多较小的不同信息,最好单独索引,但最好一起检索。 这涉及为每个文档的多个 chunk 编制索引。然后,您找到在嵌入空间中最相似的块,但您检索整个父文档并返回该文档(而不是单个块)。
Multi Vector 多向量 矢量存储 + 文档存储 有时在索引编制期间 如果您能够从文档中提取您认为比文本本身更相关的信息。 这涉及为每个文档创建多个向量。每个向量都可以以多种方式创建 - 示例包括文本摘要和假设问题。
Time-Weighted Vector store时间加权向量存储 矢量存储 - 如果您有与文档关联的时间戳,并且想要检索最新的时间戳 这将根据语义相似性(如普通向量检索)和新近度(查看索引文档的时间戳)的组合来获取文档

第五,考虑提高相似性搜索本身质量的方法。嵌入模型将文本压缩为固定长度(矢量)表示形式,以捕获文档的语义内容。这种压缩对于搜索/检索很有用,但会给单个向量表示带来沉重的负担,以捕获文档的语义细微差别/细节。在某些情况下,不相关或冗余的内容可能会稀释嵌入的语义有用性。

ColBERT 是一种通过更高粒度嵌入来解决此问题的有趣方法:(1) 为文档和查询中的每个标记生成受上下文影响的嵌入,(2) 对每个查询标记和所有文档标记之间的相似性进行评分,(3) 取最大值,(4) 对所有查询标记执行此操作,以及 (5) 取所有查询标记的最高分数之和(在步骤 3 中),以获得查询-文档相似性分数;这种代币评分可以产生强大的结果。
在这里插入图片描述

名字 适用情形 描述
ColBERT 当需要更高粒度的嵌入时。 ColBERT 对文档和查询中的每个标记使用受上下文影响的嵌入,以获得精细的查询-文档相似性分数。
Hybrid search 混合搜索 将基于关键字的相似性和语义相似性组合在一起时。 混合搜索结合了关键字和语义相似性,结合了两种方法的优势。
最大边际相关性 (MMR) 当需要使搜索结果多样化时。 MMR 尝试使搜索结果多样化,以避免返回相似和冗余的文档。
Post-processing 后处理

第六,考虑筛选或排序检索到的文档的方法。如果要合并从多个来源返回的文档,这将非常有用,因为它可以降低不太相关的文档的排名和/或压缩类似的文档。

名字 索引类型 LLM的使用 适用情形 描述
Contextual Compression 上下文压缩 Any Sometimes 如果您发现检索到的文档包含太多不相关的信息,并且分散了 LLM. 这会将后处理步骤放在另一个检索器之上,并仅从检索到的文档中提取最相关的信息。这可以通过嵌入或 LLM.
Ensemble 整体 Any No 如果您有多种检索方法并希望尝试组合它们。 这将从多个检索器中获取文档,然后合并它们。
Re-ranking 重新排名 Any Yes 如果要根据相关性对检索到的文档进行排名,特别是如果要合并来自多个检索方法的结果。 给定一个查询和一个文档列表, Rerank 将与查询的语义最相关到最不相关的文档进行索引。
Generation 生成

最后,考虑将自我纠正内置到 RAG 系统中的方法。RAG 系统可能会出现低质量检索(例如,如果用户问题超出索引的域)和/或生成中的幻觉。天真的检索-生成管道无法检测或自行纠正这些类型的错误。“流工程”的概念已在代码生成的上下文中引入:使用单元测试迭代构建代码问题的答案,以检查和自我纠正错误。一些作品应用了这种 RAG,例如 Self-RAG 和 Corrective-RAG。在这两种情况下,都会在 RAG 答案生成流程中执行文档相关性、幻觉和/或答案质量的检查。

逻辑流如下(红色 - 路由,蓝色 - 回退,绿色 - 自我纠正):

  • 路由:自适应 RAG。将问题路由到不同的检索方法
  • 回退:校正RAG。如果文档与查询无关,则回退到 Web 搜索
  • 自我纠正:自我纠正RAG(论文)。修正带有幻觉的答案或不解决问题
    在这里插入图片描述
    | 名字 | 适用情形 | 描述 |
    | ----------------------- | ---------------------------------------- | ------------------------------------------------------------ |
    | Self-RAG 自我 RAG | 当需要修复带有幻觉或不相关内容的答案时。 | Self-RAG 在 RAG 答案生成流程中执行文档相关性、幻觉和答案质量检查,迭代构建答案并自我纠正错误。 |
    | Corrective-RAG 矫正 RAG | 当需要低相关性文档的回退机制时。 | 如果检索到的文档与查询无关,则 Corrective-RAG 包括回退(例如,到 Web 搜索),从而确保更高的质量和更相关的检索。 |
Text splitting 文本拆分

LangChain 提供了许多不同类型的 text splitters .这些都位于包中 langchain-text-splitters

名字 拆分器如何拆分文本 是否添加元数据 描述
Recursive 递归 RecursiveCharacterTextSplitter, RecursiveJsonSplitter 用户定义的字符列表 - 递归拆分文本。这种拆分是试图将相关的文本片段彼此相邻。这是开始拆分文本的推荐方法 。
HTML HTMLHeaderTextSplitter, HTMLSectionSplitter HTML 特定字符 根据特定于 HTML 的字符拆分文本。值得注意的是,这添加了有关该块来自何处的相关信息(基于 HTML)
Markdown MarkdownHeaderTextSplitter, Markdown 特定字符 根据特定于 Markdown 的字符拆分文本。值得注意的是,这添加了有关该 chunk 来源的相关信息(基于 Markdown)
代码 many languages 代码 (Python、JS) 特定字符 根据特定于编码语言的字符拆分文本。有 15 种不同的语言可供选择。
Token 令 牌 many classes Tokens 令 牌 在标记上拆分文本。有几种不同的方法来测量 token。
Character 字符 CharacterTextSplitter 用户定义的字符 根据用户定义的字符拆分文本。更简单的方法之一。
语义分块器(实验性) SemanticChunker Sentences 句子 首先对句子进行拆分。然后,如果它们在语义上足够相似,则将它们彼此相邻地合并。摘自 Greg Kamradt
集成:AI21 语义 AI21SemanticTextSplitter 识别形成连贯文本片段的不同主题,并沿这些主题进行拆分。

1.4、评估

评估是评估 LLM支持的应用程序的性能和有效性的过程。它涉及根据一组预定义的标准或基准测试模型的响应,以确保其满足所需的质量标准并实现预期目的。此过程对于构建可靠的应用程序至关重要。
在这里插入图片描述

1.5、跟踪

跟踪本质上是应用程序从输入到输出所采取的一系列步骤。跟踪包含称为 runs .这些可以是来自模型、检索器、工具或子链的单个调用。跟踪为您提供链和代理内部的可观察性,对于诊断问题至关重要。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐