三、Techniques(技巧)
1、流式传输(Streaming)
在 LLM 调用中,通常会运行较长时间,这在构建需要多个推理步骤的复杂链或代理时尤为明显。幸运的是,LLM 生成输出是逐步进行的,这意味着在最终响应准备好之前,可以展示合理的中间结果。实时消费输出已经成为构建 LLM 应用程序时缓解延迟问题的重要部分,LangChain 旨在提供对流式传输的优质支持。
以下是关于 LangChain 中流式传输的一些概念和注意事项。
.stream() 和 .astream()
大多数 LangChain 模块都包括 .stream() 方法(以及在异步环境中对应的 .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)
.astream_events()
虽然 .stream() 方法直观易用,但它只能返回链的最终生成值。这对于单个 LLM 调用可能足够,但当你构建多个 LLM 调用的复杂链时,可能希望同时使用链的中间值和最终输出。例如,在构建基于文档的聊天应用时,可能需要返回源信息和最终生成内容。
LangChain 还提供了 .astream_events() 方法,它结合了回调的灵活性和 .stream() 的便捷性。当调用时,它返回一个迭代器,该迭代器会生成各种类型的事件,你可以根据项目需求进行过滤和处理。
这是一个小示例,它仅打印包含流式聊天模型输出的事件:
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(model="claude-3-sonnet-20240229")
prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
parser = StrOutputParser()
chain = prompt | model | parser
async for event in chain.astream_events({"topic": "parrot"}, version="v2"):
kind = event["event"]
if kind == "on_chat_model_stream":
print(event, end="|", flush=True)
你可以大致把它看作是一个回调事件的迭代器(尽管格式有所不同)——你可以在几乎所有 LangChain 组件上使用它!
有关如何使用 .astream_events() 的更详细信息,请参见此指南:(https://python.langchain.com/v0.2/docs/how_to/streaming/#using-stream-events),其中包括列出可用事件的表格。
Callbacks(回调)
在 LangChain 中,从 LLM 流式传输输出的最低级别方法是通过回调系统。你可以将处理 on_llm_new_token 事件的回调处理程序传递给 LangChain 组件。当该组件被调用时,其中包含的任何 LLM 或聊天模型都会使用生成的token调用回调。在回调中,你可以将这些令牌传输到其他目的地,例如 HTTP 响应中。你还可以处理 on_llm_end 事件,以执行任何必要的清理工作。
回调是 LangChain 中引入的第一种流式传输技术。尽管功能强大且具有广泛适用性,但对开发者来说可能比较复杂。例如:
-
你需要显式地初始化并管理一些聚合器或其他流以收集结果。
-
执行顺序并没有明确保证,理论上你可能在
.invoke()方法完成后运行回调。 -
提供者通常要求你传递额外参数来流式传输输出,而不是一次性返回所有结果。
-
你通常会忽略实际模型调用的结果,而更关注回调结果。
Tokens
大多数模型提供者使用的输入和输出测量单位是令牌(token)。token是语言模型在处理或生成文本时读取和生成的基本单位。token的确切定义可能会根据模型的训练方式有所不同——例如,在英语中,token可以是单个词语,如“apple”,也可以是部分词语,如“app”。
当你向模型发送提示时,提示中的单词和字符会通过分词器(tokenizer)编码为令牌。然后,模型会流式返回生成的输出token,这些令牌由分词器解码为人类可读的文本。以下示例展示了 OpenAI 模型如何将“LangChain is cool!”进行分词。

你可以看到,这段文本被分成了5个不同的tokens,并且这些tokens之间的边界并不完全与单词的边界相同。
语言模型使用tokens而不是“字符”这样更直观的单位,原因与它们处理和理解文本的方式有关。从高层次上讲,语言模型基于初始输入和之前生成的内容,迭代地预测下一个生成的输出。使用tokens进行训练使得语言模型能够处理携带意义的语言单位(如单词或子词),而不是单个字符,这使得模型更容易学习和理解语言的结构,包括语法和上下文。此外,使用tokens还可以提高效率,因为与字符级处理相比,模型处理的文本单位更少。
2、Function/tool calling(函数/工具调用)
提示
我们将工具调用和函数调用这两个术语交替使用。尽管函数调用有时特指单个函数的调用,我们将所有模型视为在每条消息中可以返回多个工具或函数调用。
工具调用允许聊天模型通过生成符合用户定义的架构的输出来响应给定的提示。
虽然名称暗示模型正在执行某些操作,但实际上并非如此!模型只是生成工具的参数,是否真正运行该工具(或不运行)由用户决定。一个常见的例子是在某些情况下你可能不希望用生成的参数来调用函数,比如你想从非结构化文本中提取符合某种架构的结构化输出。你可以为模型提供一个参数符合所需架构的“提取”工具,然后将生成的输出视为你的最终结果。

工具调用并不是通用的功能,但许多流行的LLM提供商支持此功能,包括Anthropic、Cohere、Google、Mistral、OpenAI,甚至通过Ollama支持本地运行的模型。
LangChain提供了一个跨不同模型一致的标准化工具调用接口。
该标准接口包括:
-
ChatModel.bind_tools():用于指定模型可以调用哪些工具的方法。此方法接受LangChain工具以及Pydantic对象。
-
AIMessage.tool_calls:模型返回的AIMessage上的一个属性,用于访问模型请求的工具调用。
工具使用
在模型调用工具后,您可以通过调用工具来使用它,然后将参数传递回模型。LangChain 提供了 Tool 抽象以帮助您处理此过程。
一般流程如下:
-
使用聊天模型根据查询生成工具调用。
-
使用生成的工具调用作为参数调用相应的工具。
-
将工具调用的结果格式化为
ToolMessages。

这就是工具调用代理执行任务和回答查询的方式。
3、Structured output(结构化输出)
LLMs 能够生成任意文本。这使得模型能够对各种输入做出适当的响应,但在某些用例中,约束 LLM 的输出为特定格式或结构是很有用的。这被称为结构化输出。
例如,如果输出需要存储在关系数据库中,那么如果模型生成的输出遵循定义好的模式或格式,将会更为方便。从非结构化文本中提取特定信息也是一个特别有用的情况。最常见的输出格式是 JSON,不过 YAML 等其他格式也可能有用。下面,我们将讨论几种在 LangChain 中获取结构化输出的方法。
.with_structured_output()
为了方便,某些 LangChain 聊天模型支持 .with_structured_output() 方法。该方法只需一个模式作为输入,返回一个字典或 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)
我们推荐在处理结构化输出时,从以下方法开始:
- 它在后台使用了其他模型特定的功能,无需导入输出解析器。
- 对于支持工具调用的模型,无需特别的提示。
- 如果支持多种底层技术,可以通过传递一个方法参数来切换使用哪一种技术。
Raw prompting(原始提示)
获取模型生成结构化输出的最直观方法是直接请求。在查询之外,您可以提供描述所需输出类型的说明,然后使用输出解析器将模型生成的原始消息或字符串输出转换为更易于操作的格式。
原始提示的最大优点是其灵活性:
-
原始提示不需要任何特殊的模型功能,只需足够的推理能力来理解传递的模式。
-
您可以提示任何您希望的格式,而不仅仅是 JSON。如果您使用的模型更多地训练于某种类型的数据(如 XML 或 YAML),这可能很有用。
但也存在一些缺点:
-
LLM 是非确定性的,提示 LLM 一致地以完全正确的格式输出数据以便平滑解析可能意外地困难且模型特定。
-
个别模型根据其训练数据具有独特的表现,优化提示可能非常困难。有些模型可能更擅长解释 JSON 模式,其他可能在 TypeScript 定义方面表现更好,还有一些可能更喜欢 XML。
-
虽然模型提供商提供的功能可能会提高可靠性,但无论您选择哪种方法,提示技术仍然对于调整结果非常重要。
JSON mode
一些模型,如 Mistral、OpenAI、Together AI 和 Ollama,支持一种称为 JSON 模式的功能,通常通过配置启用。
启用 JSON 模式后,它会将模型的输出约束为始终为某种有效的 JSON。通常需要一些自定义提示,但这通常比完全的原始提示要轻松得多,提示内容通常是“您必须始终返回 JSON”。这种模式下的输出通常也更易于解析。
它也通常比工具调用更简单直接且更普遍可用,提供了比工具调用更大的灵活性来提示和塑造结果。
示例:
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.output_parsers.json import SimpleJsonOutputParser
model = ChatOpenAI(
model="gpt-4o",
model_kwargs={ "response_format": { "type": "json_object" } },
)
prompt = ChatPromptTemplate.from_template(
"Answer the user's question to the best of your ability."
'You must always output a JSON object with an "answer" key and a "followup_question" key.'
"{question}"
)
chain = prompt | model | SimpleJsonOutputParser()
chain.invoke({ "question": "What is the powerhouse of the cell?" })
{'answer': 'The powerhouse of the cell is the mitochondrion. It is responsible for producing energy in the form of ATP through cellular respiration.',
'followup_question': 'Would you like to know more about how mitochondria produce energy?'}
Tool calling(工具调用)
对于支持工具调用的模型,它可以非常方便地生成结构化输出。它通过内置的模型功能消除了如何最佳提示模式的猜测。
它的工作方式是首先通过 .bind_tools() 方法将所需的模式直接绑定到聊天模型或通过 LangChain 工具。然后,模型将生成一个包含 tool_calls 字段的 AIMessage,其中包含与所需格式匹配的参数。
以下是将工具绑定到 LangChain 模型的几种可接受格式之一的示例:
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
class ResponseFormatter(BaseModel):
"""Always use this tool to structure your response to the user."""
answer: str = Field(description="The answer to the user's question")
followup_question: str = Field(description="A followup question the user could ask")
model = ChatOpenAI(
model="gpt-4o",
temperature=0,
)
model_with_tools = model.bind_tools([ResponseFormatter])
ai_msg = model_with_tools.invoke("What is the powerhouse of the cell?")
ai_msg.tool_calls[0]["args"]
{'answer': "The powerhouse of the cell is the mitochondrion. It generates most of the cell's supply of adenosine triphosphate (ATP), which is used as a source of chemical energy.",
'followup_question': 'How do mitochondria generate ATP?'}
你可以使用以下翻译:
工具调用是一种普遍一致的方式来让模型生成结构化输出,并且是 .with_structured_output() 方法在模型支持时使用的默认技术。
4、Few-shot prompting(少量示例提示)
一种提高模型性能的有效方法是:向模型提供示例,以展示你希望它执行的任务。将示例输入和预期输出添加到模型提示中的技术称为“少量示例提示”。这种技术基于《语言模型是少量学习者》(Language Models are Few-Shot Learners)论文。进行少量示例提示时,需要考虑以下几个方面:
- 生成示例
生成有效示例的第一步也是最重要的步骤。好的示例应该在运行时相关、清晰、信息丰富,并提供模型之前未曾了解的信息。
生成示例的基本方法有:
- 手动生成:由人类生成他们认为有用的示例。
- 更好的模型:使用更高级(通常更昂贵/更慢)模型的响应作为较差(通常更便宜/更快)模型的示例。
- 用户反馈:用户(或标注者)对与应用程序的交互留下反馈,并根据反馈生成示例(例如,所有具有正面反馈的交互可以转化为示例)。
- LLM反馈:与用户反馈类似,但通过让模型自我评估来自动化这个过程。
哪种方法最好取决于你的任务。对于需要深刻理解少数核心原则的任务,手动制作一些高质量示例可能更有价值。对于正确行为的范围较广且更复杂的任务,可以通过自动化生成大量示例,以提高在运行时获取相关示例的可能性。
单轮与多轮示例
生成示例时,另一个要考虑的维度是示例实际展示的内容。
最简单的示例只包含用户输入和预期的模型输出,这些是单轮示例。
另一种复杂的示例是整个对话示例,通常模型最初的响应不正确,然后用户告诉模型如何纠正答案。这被称为多轮示例。多轮示例对于更复杂的任务很有用,因为它可以展示常见错误,并详细说明错误的原因和应做的正确处理方法。
- 示例数量
一旦拥有示例数据集,我们需要考虑每个提示中应包含多少示例。关键权衡在于更多示例通常可以提高性能,但较大的提示会增加成本和延迟。超过某个阈值后,示例过多可能会开始使模型困惑。找到合适的示例数量高度依赖于模型、任务、示例质量以及你的成本和延迟约束。一般来说,模型越好,它需要的示例就越少,并且添加更多示例的回报递减会更快。但是,可靠回答这个问题的最好方法是通过实验不同数量的示例来测试。
- 选择示例
假设我们不是将整个示例数据集添加到每个提示中,我们需要有选择示例的方式。我们可以:
- 随机选择
- 基于(语义或关键词)相似性选择
- 基于其他约束,如token大小
LangChain 提供了多个 ExampleSelectors,方便使用这些技术。
通常,基于语义相似性选择会导致最好的模型性能。但这有多重要仍取决于模型和任务,是值得实验的。
- 格式化示例
目前最先进的模型多为聊天模型,因此我们将重点讨论如何为这些模型格式化示例。我们的基本选项是将示例插入:
- 系统提示中作为字符串
- 作为单独的消息
如果我们将示例插入系统提示中作为字符串,我们需要确保模型清楚每个示例的开始位置以及哪些部分是输入,哪些是输出。不同的模型对不同的语法响应更好,例如 ChatML、XML、TypeScript 等。
如果我们将示例作为消息插入,其中每个示例表示为一系列 Human 和 AI 消息,我们可能还需要为这些消息分配名称,如“example_user”和“example_assistant”,以明确这些消息对应于不同的角色,而不是最新的输入消息。
格式化工具调用示例
在格式化工具调用示例时,可能会遇到一些挑战,因为不同的模型对生成工具调用的消息序列有不同的约束。
- 一些模型要求任何包含工具调用的 AIMessage 紧跟着 ToolMessages 。
- 一些模型还要求任何 ToolMessages 紧跟着 AIMessage 之后,才能出现下一个 HumanMessage。
- 一些模型要求如果聊天历史中有工具调用/ToolMessages,必须将工具传递给模型。
这些要求是模型特定的,应根据你使用的模型进行检查。如果你的模型要求在工具调用后添加 ToolMessages 和/或在 ToolMessages 后添加 AIMessages,并且你的示例仅包含预期的工具调用而没有实际的工具输出,你可以尝试在每个示例的末尾添加带有通用内容的虚拟 ToolMessages/AIMessages 以满足 API 约束。在这种情况下,尤其值得实验将示例插入为字符串还是消息,因为虚拟消息可能会对某些模型产生不利影响。
5、Retrieval(检索)
语言模型(LLMs)在一个大型但固定的数据集上进行训练,这限制了它们对私人或最新信息的推理能力。通过特定事实的微调是缓解这一问题的一种方法,但它通常不适合事实回忆,并且可能成本较高。检索是为语言模型提供相关信息的过程,以改进其对给定输入的响应。检索增强生成(RAG)是通过使用检索到的信息来使语言模型的生成(输出)更具实际依据的过程。
RAG的效果取决于检索文档的相关性和质量。幸运的是,出现了一系列新兴技术,可以用于设计和改进RAG系统。我们已经专注于对这些技术进行分类和总结(见下图),并将在以下部分分享一些高级战略指导。你可以并且应该尝试将不同的技术组合使用。
1)Query Translation
首先,考虑一下用户输入到你的RAG系统中的内容。理想情况下,RAG系统能够处理各种输入,从措辞不当的问题到复杂的多部分查询。使用语言模型来审查和(可选地)修改输入是查询翻译的核心思想。这作为一个通用的缓冲区,用于优化原始用户输入以适应你的检索系统。例如,这可以简单到提取关键词,也可以复杂到为复杂查询生成多个子问题。
| 名称 | 何时使用 | 描述 |
|---|---|---|
| 多查询 | 当你需要涵盖问题的多个视角时。 | 从多个视角重写用户问题,为每个重写的问题检索文档,返回所有查询的唯一文档。 |
| 分解 | 当一个问题可以分解为更小的子问题时。 | 将问题分解为一组子问题/问题,可以顺序解决(使用第一个问题的答案 + 检索来回答第二个问题)或并行解决(将每个答案汇总成最终答案)。 |
| 回退 | 当需要更高层次的概念理解时。 | 首先让LLM提出一个关于更高层次概念或原则的通用回退问题,并检索相关事实。使用这些基础信息来帮助回答用户问题。 |
| HyDE | 如果你在使用原始用户输入检索相关文档时遇到挑战。 | 使用LLM将问题转换为假设文档,以回答问题。使用嵌入的假设文档来检索真实文档,假设文档间相似性搜索可以产生更相关的匹配。 |
2)Routing(路由)
其次,考虑您RAG系统可用的数据源。您可能需要查询多个数据库或同时查询结构化和非结构化数据源。使用LLM来审查输入并将其路由到适当的数据源是一种简单而有效的跨源查询方法。
| 名称 | 使用时机 | 描述 |
|---|---|---|
| Logical routing | 当您可以使用规则提示LLM来决定路由输入的去处时。 | 逻辑路由可以利用LLM来推理查询并选择最合适的数据存储。 |
| Semantic routing | 当语义相似性是一种有效的确定输入路由位置的方法时。 | 语义路由会将查询和通常的一组提示嵌入,然后根据相似性选择合适的提示。 |
3)Query Construction(查询构建)
第三步,考虑你的数据源是否需要特定的查询格式。许多结构化数据库使用SQL,而向量存储通常有特定的语法来应用关键词过滤器。使用LLM将自然语言查询转换为查询语法是一种流行且强大的方法。特别是,文本到SQL、文本到Cypher和元数据过滤器的查询分析分别对结构化数据库、图数据库和向量数据库的交互非常有用。
| 名称 | 使用时机 | 描述 |
|---|---|---|
| 文本到SQL | 当用户提问需要从关系数据库中获取信息,并且该数据库可以通过SQL访问时。 | 使用LLM将用户输入转换为SQL查询。 |
| 文本到Cypher | 当用户提问需要从图数据库中获取信息,并且该数据库可以通过Cypher访问时。 | 使用LLM将用户输入转换为Cypher查询。 |
| 自查询 | 当用户提问时,更适合通过根据元数据获取文档,而不是与文本相似性进行检索。 | 使用LLM将用户输入转换为两部分内容:(1)用于语义检索的字符串,(2)与之配套的元数据过滤器。这种方法适用于问题涉及文档的元数据(而不是内容本身)。 |
4)Indexing(索引)
第四,考虑文档索引的设计。一个简单而有效的想法是将用于检索的文档与用于生成的文档分开。索引通常使用嵌入型和向量存储,将文档中的语义信息压缩为固定大小的向量。
许多RAG方法关注将文档拆分成块,并根据与输入问题的相似性检索一些块,以供LLM使用。然而,块的大小和数量可能很难设置,如果它们未能提供LLM回答问题所需的完整上下文,则会影响结果。此外,LLMs越来越能够处理数百万个tokens。
有两种方法可以解决这种紧张关系:
-
(1)多向量检索器使用LLM将文档转换为适合索引的形式(例如,通常转换为摘要),但将完整文档返回给LLM进行生成。
-
(2)ParentDocument检索器嵌入文档块,但也返回完整文档。这个想法是兼顾两者的优势:使用简洁的表示(摘要或块)进行检索,但使用完整文档进行回答生成。
| 名称 | 索引类型 | 使用LLM | 适用场景 | 描述 |
|---|---|---|---|---|
| Vector store | 向量存储 | 否 | 如果你刚开始入门并寻找简单快捷的方法。 | 这是最简单的方法,最容易上手。涉及为每段文本创建嵌入。 |
| ParentDocument | 向量存储 + 文档存储 | 否 | 如果你的页面包含很多较小的、独立的信息块,它们最好单独索引,但一起检索效果最佳。 | 涉及为每个文档索引多个块。然后在嵌入空间中找到最相似的块,但检索整个父文档并返回(而不是单个块)。 |
| Multi Vector | 向量存储 + 文档存储 | 有时在索引时 | 如果你能从文档中提取出比文本本身更相关的信息来索引。 | 涉及为每个文档创建多个向量。每个向量可以以多种方式创建,例如文本摘要或假设问题。 |
| Time-Weighted Vector store | 向量存储 | 否 | 如果你的文档有时间戳,并且你希望检索最新的文档。 | 根据语义相似性(如正常的向量检索)和时效性(查看索引文档的时间戳)的组合来获取文档。 |
5)提高相似性搜索的质量
第五,考虑如何提高相似性搜索的质量。嵌入模型将文本压缩为固定长度的向量表示,捕捉文档的语义内容。这种压缩对搜索和检索非常有用,但也对单个向量表示施加了很大的压力,要求它能够捕捉文档的语义细微差别和详细信息。在某些情况下,不相关或冗余的内容可能会削弱嵌入的语义有效性。
ColBERT 是一种有趣的方法,通过更高粒度的嵌入来解决这个问题:
(1) 为文档和查询中的每个 token 生成一个受上下文影响的嵌入;
(2) 计算每个查询 token 与所有文档 token 之间的相似度;
(3) 取最大值;
(4) 对所有查询 token 执行此操作;
(5) 将所有查询 token 的最大分数(步骤 3 中)相加,得到查询文档的相似度分数。
这种基于 token 的评分方法可以产生强大的效果。

要提高检索质量,还有一些额外的技巧可以使用。嵌入模型在捕捉语义信息方面表现出色,但在处理基于关键词的查询时可能会遇到挑战。为此,许多向量存储提供了内置的混合搜索功能,它将关键词匹配与语义相似度结合起来,从而融合了两种方法的优势。此外,许多向量存储还支持最大边际相关性(Maximal Marginal Relevance,MMR),这一方法旨在多样化搜索结果,避免返回相似和冗余的文档,从而提高检索结果的质量和覆盖面。 以下是改进检索质量的技术总结:
| 名称 | 适用场景 | 描述 |
|---|---|---|
| ColBERT | 需要更高粒度的嵌入时 | ColBERT 使用上下文影响的嵌入来对文档和查询中的每个词元进行嵌入,以获得更精细的查询-文档相似度得分。 |
| Hybrid search | 需要结合关键词搜索和语义相似性时 | 混合搜索结合了关键词和语义相似性,融合了这两种方法的优点。 |
| Maximal Marginal Relevance (MMR) | 需要多样化搜索结果时 | MMR 试图多样化搜索结果,避免返回相似和冗余的文档。 |
Post-processing
6)对文档进行过滤或排序
第六点,考虑对检索到的文档进行过滤或排序。这在您合并多个来源返回的文档时非常有用,因为它可以降低不相关文档的排名,或者压缩相似的文档。
以下是关于Post-processing技术的总结:
| 名称 | 索引类型 | 是否使用 LLM | 适用场景 | 描述 |
|---|---|---|---|---|
| 上下文压缩 | 任意类型 | 有时 | 如果您发现检索到的文档包含过多无关信息,并且会干扰 LLM。 | 这一步是在另一个检索器的基础上进行后处理,仅提取检索到的文档中最相关的信息。可以通过嵌入或 LLM 完成。 |
| 集成 | 任意类型 | 否 | 如果您有多种检索方法,并且想尝试将它们结合起来。 | 从多个检索器获取文档,然后将它们组合在一起。 |
| 重排序 | 任意类型 | 是 | 如果您希望基于相关性对检索到的文档进行排序,尤其是当您希望结合多种检索方法的结果时。 | 根据查询和文档列表,重排序器按文档与查询的语义相关性从高到低进行排序。 |
7)Generation
最后,考虑如何在您的RAG系统中构建自我纠正机制。RAG系统可能会因为低质量的检索(例如,用户问题超出了索引的范围)和/或生成中的幻觉而出现问题。一个简单的检索-生成管道无法检测或自我纠正这些类型的错误。在代码生成的背景下引入了“流工程”概念:通过单元测试迭代地构建代码问题的答案,以检查和自我纠正错误。多个研究将这一概念应用于RAG系统,如Self-RAG和Corrective-RAG。在这两种情况下,都会在RAG答案生成流程中进行文档相关性、幻觉和/或答案质量的检查。
我们发现图表是可靠表达逻辑流程的好方法,并使用LangGraph实现了几篇论文中的一些想法,如下图所示(红色 - 路由,蓝色 - 回退,绿色 - 自我纠正):
- 路由:Adaptive RAG(论文)。将问题路由到不同的检索方法,如上所述。
- 回退:Corrective RAG(论文)。如果文档与查询不相关,则回退到网络搜索。
- 自我纠正:Self-RAG(论文)。修正有幻觉或未能解决问题的答案。

| 名称 | 使用时机 | 描述 |
|---|---|---|
| Self-RAG | 当需要修正有幻觉或无关内容的答案时。 | Self-RAG在RAG答案生成流程中进行文档相关性、幻觉和答案质量的检查,通过迭代地构建答案并自我纠正错误来提升质量。 |
| Corrective-RAG | 当需要为低相关性文档提供回退机制时。 | Corrective-RAG包括回退机制(例如,回退到网络搜索),如果检索的文档与查询不相关,可以确保更高质量和更相关的检索结果。 |
6、Text splitting(文本切分器)
LangChain 提供了多种不同类型的文本切分器,这些切分器都在 langchain-text-splitters 包中。
表格列说明:
- Name(名称):文本切分器的名称
- Classes(类):实现该文本切分器的类
- Splits On(切分依据):该文本切分器依据什么来切分文本
- Adds Metadata(是否添加元数据):该文本切分器是否会添加关于每个文本块来源的元数据
- Description(描述):对切分器的描述,包括何时使用的推荐建议
7、文本切分器
| Name(名称) | Classes(类) | Splits On(切分依据) | Adds Metadata(是否添加元数据) | Description(描述) |
|---|---|---|---|---|
| Recursive | RecursiveCharacterTextSplitter, RecursiveJsonSplitter | 一系列用户定义的字符 | 否 | 递归地切分文本。该方法尝试将相关的文本片段保持在一起。建议作为开始切分文本的方法。 |
| HTML | HTMLHeaderTextSplitter, HTMLSectionSplitter | HTML 特定字符 | ✅ | 基于 HTML 特定字符切分文本。特别地,这会添加有关文本块来源的相关信息(基于 HTML)。 |
| Markdown | MarkdownHeaderTextSplitter | Markdown 特定字符 | ✅ | 基于 Markdown 特定字符切分文本。特别地,这会添加有关文本块来源的相关信息(基于 Markdown)。 |
| Code | 多种语言类 | 编程语言特定字符(Python, JS) | 否 | 基于编程语言特定字符切分文本。支持 15 种不同语言的选择。 |
| Token | 多种类 | 令牌(Tokens) | 否 | 基于令牌切分文本。存在几种不同的令牌度量方式。 |
| Character | CharacterTextSplitter | 用户定义的字符 | 否 | 基于用户定义的字符切分文本。方法较为简单。 |
| Semantic Chunker (Experimental) | SemanticChunker | 句子 | 否 | 首先按句子切分。然后如果句子之间语义足够相似,则将相邻的句子组合在一起。由 Greg Kamradt 提出。 |
| Integration: AI21 Semantic | AI21SemanticTextSplitter | |||
| ✅ | 识别形成连贯文本的不同主题并沿着这些主题切分文本。 |
8、Evaluation(评估)
Evaluation是评估您的 LLM 驱动的应用程序的性能和有效性的过程。它涉及将模型的响应与一组预定义的标准或基准进行比较,以确保它满足所需的质量标准并实现预期的目的。这个过程对于构建可靠的应用程序至关重要。

LangSmith 通过以下几种方式帮助这个过程:
- 数据集创建与策划:通过其跟踪和注释功能,使创建和策划数据集变得更容易。9
- 评估框架:提供了一个评估框架,帮助您定义指标并将应用程序在您的数据集上进行测试。
- 结果追踪:允许您跟踪结果并自动运行评估器,您可以按照时间表运行评估器或将其作为 CI/代码的一部分。
最后分享
AI大模型作为人工智能领域的重要技术突破,正成为推动各行各业创新和转型的关键力量。抓住AI大模型的风口,掌握AI大模型的知识和技能将变得越来越重要。
学习AI大模型是一个系统的过程,需要从基础开始,逐步深入到更高级的技术。
这里给大家精心整理了一份
全面的AI大模型学习资源,包括:AI大模型全套学习路线图(从入门到实战)、精品AI大模型学习书籍手册、视频教程、实战学习、面试题等,资料免费分享!

1. 成长路线图&学习规划
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。
这里,我们为新手和想要进一步提升的专业人士准备了一份详细的学习成长路线图和规划。可以说是最科学最系统的学习成长路线。

2. 大模型经典PDF书籍
书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。(书籍含电子版PDF)

3. 大模型视频教程
对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识。

4. 2024行业报告
行业分析主要包括对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。

5. 大模型项目实战
学以致用 ,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

6. 大模型面试题
面试不仅是技术的较量,更需要充分的准备。
在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。

全套的AI大模型学习资源已经整理打包,有需要的小伙伴可以
微信扫描下方优快云官方认证二维码,免费领取【保证100%免费】

2万+

被折叠的 条评论
为什么被折叠?



