• 文档
  • Agentic RAG 与 LangGraph

Agentic RAG 与 LangGraph 和 Qdrant

传统的检索增强生成(RAG)系统遵循一条直接的路径:查询 → 检索 → 生成。当然,这在许多场景中都能很好地工作。但坦白地说,这种线性方法在处理需要多个步骤或汇集多种类型信息的复杂查询时常常力不从心。

Agentic RAG 通过引入能够协调多个检索步骤并智能决定如何收集和使用所需信息的 AI Agent,将事情提升了一个层次。这样想:在 Agentic RAG 工作流程中,RAG 只是一个更大、更通用的工具集中的一个强大工具。

通过将 LangGraph 强大的状态管理与 Qdrant 尖端的向量搜索相结合,我们将构建一个不仅能回答问题,还能巧妙处理复杂、多步骤信息检索任务的系统。

我们将构建什么

我们将使用 LangGraph 构建一个 AI Agent 来回答关于 Hugging Face 和 Transformers 文档的问题。我们 AI Agent 的核心是 LangGraph,它就像管弦乐队的指挥一样。它指导着各个组件之间的流程——决定何时检索信息、何时执行网页搜索以及何时生成响应。

这些组件包括:两个 Qdrant 向量存储和 Brave 网页搜索引擎。然而,我们的 Agent 不会盲目地遵循一条路径。相反,它会评估每个查询,并决定是利用第一个向量存储、第二个向量存储,还是搜索网页。

这种选择性方法为您的系统提供了灵活性,可以根据任务选择最佳数据源,而不是像传统 RAG 那样每次都锁定在相同的检索流程中。虽然我们在本教程中不会深入探讨查询细化,但您在这里学到的概念是将来添加此功能的坚实基础。

工作流程

image1

步骤描述
1. 用户输入您首先通过一个界面(例如聊天机器人或网页表单)输入一个查询或请求。此查询直接发送给 AI Agent,它是整个操作的核心。
2. AI Agent 处理查询AI Agent 分析您的查询,弄清楚您在问什么以及哪些工具或数据源最适合回答您的问题。
3. 工具选择根据其分析,AI Agent 选择适合任务的工具。您的数据分散在两个向量数据库中,Agent 会根据查询选择合适的数据库。对于需要实时或外部网页数据的查询,Agent 会调用由 BraveSearchAPI 驱动的网页搜索工具。
4. 查询执行AI Agent 然后使其选择的工具开始工作
- RAG 工具 1 查询向量数据库 1。
- RAG 工具 2 查询向量数据库 2。
- 网页搜索工具 使用搜索 API 深入互联网。
5. 数据检索结果如下
- 向量数据库 1 和 2 返回与您的查询最相关的文档。
- 网页搜索工具提供最新或外部信息。
6. 响应生成使用文本生成模型(如 GPT),AI Agent 会根据您的查询精心制作详细准确的响应。
7. 用户响应经过润色的响应通过界面发送回给您,随时可用。

技术栈

该架构利用尖端工具来支持高效的 Agentic RAG 工作流程。以下是其组件和所需技术的快速概述:

  • AI Agent:系统的核心,此 Agent 解析您的查询,选择合适的工具,并整合响应。我们将使用 OpenAI 的 gpt-4o 作为推理引擎,由 LangGraph 无缝管理。
  • 嵌入 (Embedding):查询使用 OpenAI 的 text-embedding-3-small 模型转换为向量嵌入。
  • 向量数据库:嵌入存储在此,并用于相似性搜索,Qdrant 是我们选择的数据库。
  • LLM:响应使用 OpenAI 的 gpt-4o 生成,确保答案准确且基于上下文。
  • 搜索工具:为了扩展 RAG 的能力,我们添加了一个由 BraveSearchAPI 驱动的网页搜索组件,非常适合实时和外部数据检索。
  • 工作流程管理:整个协调和决策流程是使用 LangGraph 构建的,提供了处理复杂工作流程所需的灵活性和智能。

准备好从头开始构建这个系统了吗?我们开始吧!

实现

在深入构建 Agent 之前,先做好所有准备工作。

导入

以下是所需的关键导入列表:

import os
import json
from typing import Annotated, TypedDict
from dotenv import load_dotenv
from langchain.embeddings import OpenAIEmbeddings
from langgraph import StateGraph, tool, ToolNode, ToolMessage
from langchain.document_loaders import HuggingFaceDatasetLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.llms import ChatOpenAI
from qdrant_client import QdrantClient
from qdrant_client.http.models import VectorParams
from brave_search import BraveSearch

Qdrant 向量数据库设置

我们将使用 Qdrant Cloud 作为文档嵌入的向量存储。以下是设置方法:

步骤描述
1. 创建账户如果您还没有账户,请前往 Qdrant Cloud 并注册。
2. 设置集群登录您的账户,在仪表板上找到创建新集群按钮。按照提示配置:
- 选择您的首选区域
- 选择免费套餐进行测试。
3. 安全地保存您的详细信息集群准备就绪后,记下这些详细信息:
- 集群 URL (例如:https://xxx-xxx-xxx.aws.cloud.qdrant.io)
- API 密钥

请安全保存这些信息以备将来使用!

OpenAI API 配置

您的 OpenAI API 密钥将用于支持嵌入生成和语言模型交互。请访问 OpenAI 平台并注册账户。在仪表板的 API 部分创建一个新的 API 密钥。我们将使用 text-embedding-3-small 模型进行嵌入,并使用 GPT-4 作为语言模型。

为了增强搜索能力,我们将集成 Brave Search。请访问 Brave API 并完成其 API 访问请求流程以获取 API 密钥。此密钥将为我们的 Agent 提供网页搜索功能。

为了增强安全性,请将所有 API 密钥存储在 .env 文件中。

OPENAI_API_KEY = <your-openai-api-key>
QDRANT_KEY = <your-qdrant-api-key>
QDRANT_URL = <your-qdrant-url>
BRAVE_API_KEY = <your-brave-api-key>

然后加载环境变量

load_dotenv()
qdrant_key = os.getenv("QDRANT_KEY")
qdrant_url = os.getenv("QDRANT_URL")
brave_key = os.getenv("BRAVE_API_KEY")

文档处理

在创建 Agent 之前,我们需要处理并存储文档。我们将使用 Hugging Face 的两个数据集:其通用文档和 Transformers 特定文档。

以下是我们的文档预处理函数:

def preprocess_dataset(docs_list):
    text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
        chunk_size=700,
        chunk_overlap=50,
        disallowed_special=()
    )
    doc_splits = text_splitter.split_documents(docs_list)
    return doc_splits

此函数通过将文档分割成易于管理的块来处理文档,通过重叠确保在块边界保留重要的上下文。我们将使用 HuggingFaceDatasetLoader 将数据集加载到 Hugging Face 文档中。

hugging_face_doc = HuggingFaceDatasetLoader("m-ric/huggingface_doc","text")
transformers_doc = HuggingFaceDatasetLoader("m-ric/transformers_documentation_en","text")

在此演示中,我们从数据集中选择前 50 个文档并将它们传递给处理函数。

hf_splits = preprocess_dataset(hugging_face_doc.load()[:number_of_docs])
transformer_splits = preprocess_dataset(transformers_doc.load()[:number_of_docs])

我们的分割已准备就绪。让我们在 Qdrant 中创建一个集合来存储它们。

定义状态

在 LangGraph 中,状态 (state) 指的是在过程或一系列操作执行期间存储和维护的数据或信息。状态捕获系统需要跟踪的中间或最终结果,以便管理和控制任务流,

LangGraph 使用基于状态的系统。我们这样定义状态:

class State(TypedDict):
    messages: Annotated[list, add_messages]

让我们构建工具。

构建工具

我们的 Agent 配备了三个强大的工具:

  1. Hugging Face 文档检索器
  2. Transformers 文档检索器
  3. 网页搜索工具

首先,我们定义一个检索器函数,它接受文档和集合名称作为参数,然后返回一个检索器。查询使用 OpenAIEmbeddings 转换为向量。

def create_retriever(collection_name, doc_splits):
    vectorstore = QdrantVectorStore.from_documents(
        doc_splits,
        OpenAIEmbeddings(model="text-embedding-3-small"),
        url=qdrant_url,
        api_key=qdrant_key,
        collection_name=collection_name,
    )
    return vectorstore.as_retriever()

Hugging Face 文档检索器和 Transformers 文档检索器都使用此相同函数。通过此设置,为每个检索器创建独立的工具非常简单。

hf_retriever_tool = create_retriever_tool(
    hf_retriever,
    "retriever_hugging_face_documentation",
    "Search and return information about hugging face documentation, it includes the guide and Python code.",
)

transformer_retriever_tool = create_retriever_tool(
    transformer_retriever,
    "retriever_transformer",
    "Search and return information specifically about transformers library",
)

对于网页搜索,我们使用 Brave Search 创建了一个简单而有效的工具:

@tool("web_search_tool")
def search_tool(query):
    search = BraveSearch.from_api_key(api_key=brave_key, search_kwargs={"count": 3})
    return search.run(query)

search_tool 函数利用 BraveSearch API 执行搜索。它接受一个查询,使用 API 密钥检索前 3 个搜索结果,并返回结果。

接下来,我们将设置工具并将其与语言模型集成:

tools = [hf_retriever_tool, transformer_retriever_tool, search_tool]

tool_node = ToolNode(tools=tools)

llm = ChatOpenAI(model="gpt-4o", temperature=0)

llm_with_tools = llm.bind_tools(tools)

此处,ToolNode 类处理和协调我们的工具:

class ToolNode:
    def __init__(self, tools: list) -> None:
        self.tools_by_name = {tool.name: tool for tool in tools}

    def __call__(self, inputs: dict):
        if messages := inputs.get("messages", []):
            message = messages[-1]
        else:
            raise ValueError("No message found in input")

        outputs = []
        for tool_call in message.tool_calls:
            tool_result = self.tools_by_name[tool_call["name"]].invoke(
                tool_call["args"]
            )
            outputs.append(
                ToolMessage(
                    content=json.dumps(tool_result),
                    name=tool_call["name"],
                    tool_call_id=tool_call["id"],
                )
            )

        return {"messages": outputs}

ToolNode 类通过初始化工具列表并将工具名称映射到相应的函数来处理工具执行。它处理输入字典,提取最后一条消息,并检查来自 LLM 工具调用能力提供商(如 Anthropic、OpenAI 等)的 tool_calls。

路由和决策

我们的 Agent 需要确定何时使用工具以及何时结束循环。此决策由路由函数管理:

def route(state: State):
    if isinstance(state, list):
        ai_message = state[-1]
    elif messages := state.get("messages", []):
        ai_message = messages[-1]
    else:
        raise ValueError(f"No messages found in input state to tool_edge: {state}")

    if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0:
        return "tools"

    return END

整合所有组件:图

最后,我们将构建连接所有组件的图:

graph_builder = StateGraph(State)

graph_builder.add_node("agent", agent)
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "agent",
    route,
    {"tools": "tools", END: END},
)

graph_builder.add_edge("tools", "agent")
graph_builder.add_edge(START, "agent")

图示如下:

image2

图 3:Agentic RAG 与 LangGraph

运行 Agent

设置完成后,我们可以使用一个简单的函数运行 Agent:

def run_agent(user_input: str):
    for event in graph.stream({"messages": [("user", user_input)]}):
        for value in event.values():
            print("Assistant:", value["messages"][-1].content)

现在,您可以开始提问关于 Hugging Face 和 Transformers 的问题了!我们的 Agent 会智能地将文档中的信息与需要的网页搜索结果结合起来。

例如,您可以询问:

In the Transformers library, are there any multilingual models?

Agent 将深入 Transformers 文档,提取关于多语言模型的相关详细信息,并提供清晰、全面的答案。

响应可能如下所示:

Yes, the Transformers library includes several multilingual models. Here are some examples:

BERT Multilingual: 
Models like `bert-base-multilingual-uncased` can be used just like monolingual models.

XLM (Cross-lingual Language Model): 
Models like `xlm-mlm-ende-1024` (English-German), `xlm-mlm-enfr-1024` (English-French), and others use language embeddings to specify the language used at inference.

M2M100: 
Models like `facebook/m2m100_418M` and `facebook/m2m100_1.2B` are used for multilingual translation.

MBart: 
Models like `facebook/mbart-large-50-one-to-many-mmt` and `facebook/mbart-large-50-many-to-many-mmt` are used for multilingual machine translation across 50 languages.

These models are designed to handle multiple languages and can be used for tasks like translation, classification, and more.

结论

我们已成功实现了 Agentic RAG。但这仅仅是个开始——您可以探索更多内容,将您的系统提升到新的水平。

Agentic RAG 正在改变企业连接数据源与 AI 的方式,实现了更智能、更动态的交互。在本教程中,您学习了如何构建一个 Agentic RAG 系统,该系统将 LangGraph、Qdrant 和网页搜索的力量整合到一个无缝的工作流程中。

该系统不仅限于从 Hugging Face 和 Transformers 文档中检索相关信息。在需要时,它还能智能地回退到网页搜索,确保没有查询遗漏。以 Qdrant 作为向量数据库核心,您可以获得快速、可扩展的语义搜索,即使在海量数据集中也能出色地检索精确信息。

要真正理解此方法的潜力,何不将这些概念应用于您自己的项目?定制我们分享的模板以适应您的独特用例,释放 Agentic RAG 的全部潜力来满足您的业务需求。可能性是无限的。

此页面是否有用?

感谢您的反馈!🙏

很抱歉未能满足您的期望。😔 您可以在 GitHub 上编辑此页面,或创建一个 GitHub issue。