smolagents 文档

Agentic RAG

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

Agentic RAG

检索增强生成 (RAG) 简介

检索增强生成 (RAG) 将大型语言模型的能力与外部知识检索相结合,以产生更准确、真实和上下文相关的响应。RAG 的核心是“使用 LLM 回答用户查询,但答案基于从知识库中检索到的信息。”

为何使用 RAG?

与使用普通或微调 LLM 相比,RAG 具有显著优势:

  1. 事实依据:通过将响应锚定在检索到的事实中来减少幻觉。
  2. 领域专业化:无需模型再训练即可提供领域特定知识。
  3. 知识时效性:允许访问超出模型训练截止日期的信息。
  4. 透明度:能够引用生成内容的来源。
  5. 控制:对模型可以访问的信息提供精细控制。

传统 RAG 的局限性

尽管有其优点,但传统 RAG 方法面临一些挑战:

  • 单次检索步骤:如果初始检索结果不佳,最终生成也会受到影响。
  • 查询-文档不匹配:用户查询(通常是问题)可能与包含答案(通常是陈述)的文档不匹配。
  • 有限推理:简单的 RAG 管道不允许进行多步推理或查询优化。
  • 上下文窗口限制:检索到的文档必须在模型的上下文窗口内。

代理 RAG:一种更强大的方法

我们可以通过实现一个**代理 RAG** 系统来克服这些限制——本质上是一个配备检索功能的代理。这种方法将 RAG 从僵硬的管道转变为交互式、推理驱动的过程。

代理 RAG 的主要优势

配备检索工具的代理可以:

  1. 制定优化的查询:代理可以将用户问题转换为适合检索的查询。
  2. 执行多次检索:代理可以根据需要迭代检索信息。
  3. 对检索内容进行推理:代理可以分析、综合并从多个来源得出结论。
  4. 自我批评和完善:代理可以评估检索结果并调整其方法。

这种方法自然地实现了高级 RAG 技术:

  • 假设文档嵌入 (HyDE):代理不是直接使用用户查询,而是制定优化检索的查询(论文参考)。
  • 自查询优化:代理可以分析初始结果并使用优化后的查询执行后续检索(技术参考)。

构建代理 RAG 系统

让我们一步步构建一个完整的代理 RAG 系统。我们将创建一个代理,通过从 Hugging Face Transformers 库的文档中检索信息来回答有关该库的问题。

您可以跟着下面的代码片段进行操作,也可以查看 smolagents GitHub 仓库中的完整示例:examples/rag.py

步骤 1:安装必要的依赖项

首先,我们需要安装必要的软件包:

pip install smolagents pandas langchain langchain-community sentence-transformers datasets python-dotenv rank_bm25 --upgrade

如果您打算使用 Hugging Face 的推理 API,则需要设置您的 API 令牌。

# Load environment variables (including HF_TOKEN)
from dotenv import load_dotenv
load_dotenv()

步骤 2:准备知识库

我们将使用包含 Hugging Face 文档的数据集,并将其准备好进行检索。

import datasets
from langchain.docstore.document import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.retrievers import BM25Retriever

# Load the Hugging Face documentation dataset
knowledge_base = datasets.load_dataset("m-ric/huggingface_doc", split="train")

# Filter to include only Transformers documentation
knowledge_base = knowledge_base.filter(lambda row: row["source"].startswith("huggingface/transformers"))

# Convert dataset entries to Document objects with metadata
source_docs = [
    Document(page_content=doc["text"], metadata={"source": doc["source"].split("/")[1]})
    for doc in knowledge_base
]

# Split documents into smaller chunks for better retrieval
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,  # Characters per chunk
    chunk_overlap=50,  # Overlap between chunks to maintain context
    add_start_index=True,
    strip_whitespace=True,
    separators=["\n\n", "\n", ".", " ", ""],  # Priority order for splitting
)
docs_processed = text_splitter.split_documents(source_docs)

print(f"Knowledge base prepared with {len(docs_processed)} document chunks")

步骤 3:创建检索器工具

现在,我们将创建一个自定义工具,我们的代理可以使用它从知识库中检索信息。

from smolagents import Tool

class RetrieverTool(Tool):
    name = "retriever"
    description = "Uses semantic search to retrieve the parts of transformers documentation that could be most relevant to answer your query."
    inputs = {
        "query": {
            "type": "string",
            "description": "The query to perform. This should be semantically close to your target documents. Use the affirmative form rather than a question.",
        }
    }
    output_type = "string"

    def __init__(self, docs, **kwargs):
        super().__init__(**kwargs)
        # Initialize the retriever with our processed documents
        self.retriever = BM25Retriever.from_documents(
            docs, k=10  # Return top 10 most relevant documents
        )

    def forward(self, query: str) -> str:
        """Execute the retrieval based on the provided query."""
        assert isinstance(query, str), "Your search query must be a string"

        # Retrieve relevant documents
        docs = self.retriever.invoke(query)

        # Format the retrieved documents for readability
        return "\nRetrieved documents:\n" + "".join(
            [
                f"\n\n===== Document {str(i)} =====\n" + doc.page_content
                for i, doc in enumerate(docs)
            ]
        )

# Initialize our retriever tool with the processed documents
retriever_tool = RetrieverTool(docs_processed)

我们使用 BM25,一种词汇检索方法,以简化和提高速度。对于生产系统,您可能需要使用带有嵌入的语义搜索,以获得更好的检索质量。请查看 MTEB 排行榜以获取高质量的嵌入模型。

步骤 4:创建高级检索代理

现在,我们将创建一个可以使用我们的检索工具回答问题的代理。

from smolagents import InferenceClientModel, CodeAgent

# Initialize the agent with our retriever tool
agent = CodeAgent(
    tools=[retriever_tool],  # List of tools available to the agent
    model=InferenceClientModel(),  # Default model "Qwen/Qwen2.5-Coder-32B-Instruct"
    max_steps=4,  # Limit the number of reasoning steps
    verbosity_level=2,  # Show detailed agent reasoning
)

# To use a specific model, you can specify it like this:
# model=InferenceClientModel(model_id="meta-llama/Llama-3.3-70B-Instruct")

推理提供商允许访问数百个模型,这些模型由无服务器推理合作伙伴提供支持。支持的提供商列表可以在这里找到。

步骤 5:运行代理回答问题

让我们使用我们的代理来回答一个关于 Transformers 的问题。

# Ask a question that requires retrieving information
question = "For a transformers model training, which is slower, the forward or the backward pass?"

# Run the agent to get an answer
agent_output = agent.run(question)

# Display the final answer
print("\nFinal answer:")
print(agent_output)

代理 RAG 的实际应用

代理 RAG 系统可应用于各种用例:

  1. 技术文档辅助:帮助用户浏览复杂的技术文档。
  2. 研究论文分析:从科学论文中提取和综合信息。
  3. 法律文件审查:在法律文件中查找相关的判例和条款。
  4. 客户支持:根据产品文档和知识库回答问题。
  5. 教育辅导:根据教科书和学习材料提供解释。

结论

代理 RAG 代表了对传统 RAG 管道的重大进步。通过将 LLM 代理的推理能力与检索系统的事实依据相结合,我们可以构建更强大、更灵活、更准确的信息系统。

我们演示的方法:

  • 克服了单步检索的局限性。
  • 实现了与知识库更自然的交互。
  • 通过自我批评和查询优化提供了一个持续改进的框架。

在构建自己的代理 RAG 系统时,请考虑尝试不同的检索方法、代理架构和知识来源,以找到最适合您特定用例的最佳配置。

< > 在 GitHub 上更新