使用 LangGraph 构建一个检索代理,该代理能够决定是搜索向量存储还是直接回答用户。 LangChain 提供了基于 LangGraph 原语构建的内置代理实现。当您需要更深层次的自定义时,可以直接在 LangGraph 中实现代理。本教程将介绍一种检索代理的模式。 在本教程中,您将学到:
  1. 为检索获取和预处理文档。
  2. 为这些文档建立索引以进行语义搜索,并为代理创建检索工具。
  3. 构建一个能够决定何时使用检索工具的智能 RAG 系统。
Hybrid RAG

概念

本教程涵盖以下概念:

设置

安装所需的包并设置您的 API 密钥:

设置 LangSmith

RAG 应用程序按顺序运行检索和生成。当您运行本教程中的示例时,LangSmith 会为每个查询记录一条跟踪日志,以便您检查检索、工具调用和模型响应。 在您注册 LangSmith 之后,设置您的环境变量以开始记录跟踪日志:
如果您正在构建生产环境代理,我们还建议您设置 LangSmith Engine,它可以监控您的跟踪日志、检测问题并提出修复建议。

预处理文档

1

获取文档

使用 Lilian Weng 博客 中的三篇近期文章。使用一个基于 fetch 和 cheerio 构建的简单辅助函数来获取页面内容:
2

分割文档

将获取的文档分割成更小的块,以便索引到向量存储中:

创建检索工具

将分割后的文档索引到向量存储中以进行语义搜索。
1

索引文档并创建工具

使用内存向量存储和 OpenAI 嵌入模型,然后使用 LangChain 预构建的 createRetrieverTool 创建检索工具:
2

测试工具

生成查询或响应

检索工具准备好后,开始将代理构建为 LangGraph 图。在 Graph API 中,一个图由以下部分组成:
  • 状态:节点读取和更新的共享数据。本教程使用 MessagesAnnotation,它存储一个包含聊天消息列表的 messages。
  • 节点:函数,接受当前状态,运行一个步骤(例如,调用模型或工具),并返回状态更新。
  • 边:定义接下来运行哪个节点的连接,包括基于状态进行分支的条件边。
第一个节点是代理的决策点。根据到目前为止的对话,模型要么直接回答用户,要么在问题需要博客上下文时调用检索工具。这种选择使得系统是智能的,而不是固定的“检索然后生成”管道:仅当模型请求时才运行检索。
1

构建节点

构建一个 generateQueryOrRespond 节点,该节点在当前消息上调用模型,并使用 .bindTools 绑定 tools:
2

尝试一个简单的问候

输出:
3

提出一个需要检索的问题

提出一个需要语义搜索的问题:
输出:

文档评分

普通的边总是将图导向同一个下一个节点。条件边通过在当前状态上运行一个函数,在运行时选择下一个节点。检索之后,使用该模式来评估文档是否相关:如果相关,则继续进行答案生成;如果不相关,则重写问题并重试。
1

添加文档评分

添加一个 gradeDocuments 节点,该节点使用带有结构化输出(Zod)的模型,如果结构化解析失败,则回退到普通的“是”或“否”响应。根据结果(generate 或 rewrite)使用条件边进行路由:
2

使用不相关文档测试

使用工具响应中包含不相关文档的输入运行此函数:
3

使用相关文档测试

确认相关文档被正确分类:

重写问题

如果评分器将检索到的文档标记为不相关,图不应基于该上下文进行回答。相反,应将原始用户问题重写为更清晰的搜索查询,然后将控制权送回 generate-query-or-respond 节点,以便代理可以再次检索。这个重试循环是代理从一次失败的初次检索中恢复的方式,而不是停止或产生幻觉答案。
1

构建重写节点

构建 rewrite 节点,用于在检索失败时改进原始用户问题:
2

尝试一下

输出:

生成答案

当评分器接受检索到的文档后,图移动到答案生成。此节点是经典的 RAG 步骤:将原始用户问题与包含检索到的上下文的工具消息结合起来,然后要求模型生成一个基于上下文的回复。保持提示简洁,以便模型根据提供的上下文回答,而不是编造细节。
1

构建答案节点

构建 generate 节点,根据问题和检索到的上下文生成最终回复:
2

尝试一下

输出:

组装图

将节点和边组装成一个完整的图:
  • 从 generateQueryOrRespond 开始,并确定是否调用检索工具。
  • 使用条件边路由到下一步:
    • 如果 generateQueryOrRespond 返回了 tool_calls,则调用检索工具来检索上下文。
    • 否则,直接响应用户。
  • 对检索到的文档内容进行相关性评分 (gradeDocuments) 并路由到下一步:
    • 如果不相关,使用 rewrite 重写问题,然后再次调用 generateQueryOrRespond。
    • 如果相关,进入 generate 并使用包含检索到的文档上下文的 ToolMessage 生成最终响应。

运行智能 RAG

通过使用一个问题运行来测试完整的图:

另请参阅