TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

smolagents 检索智能体

把检索器包成 smolagents 工具交给智能体:查询重构、多轮检索、结果校验全由模型规划,检索从固定环节变成可迭代动作。

传统 RAG 的流程是一条直线:用户查询丢给检索器,取回的片段连同问题塞给模型,模型据此作答。这条直线有两个先天弱点——它只有一次检索机会,第一轮没找到的东西后面就再也没有了;它对齐的是查询的字面语义相似度,用户问法稍有偏差,真正相关的资料就可能落在召回窗口之外 [1]。

Agentic RAG 的思路是把检索过程交给智能体:自主构造搜索查询、批判取回的结果、按需执行多轮检索。检索不再是流水线上的一个固定环节,而成为模型可以规划、可以迭代的一组动作 [1]。

最简版本:联网搜索智能体

用 smolagents 搭一个能联网检索的智能体,只需要一个搜索工具加一个模型:

from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

# Initialize the search tool
search_tool = DuckDuckGoSearchTool()

# Initialize the model
model = InferenceClientModel()

agent = CodeAgent(
    model=model,
    tools=[search_tool],
)

# Example usage
response = agent.run(
    "Search for luxury superhero-themed party ideas, including decorations, entertainment, and catering."
)
print(response)

这个智能体跑起来分四步:先分析请求,从查询里识别出关键要素(豪华风格的超级英雄派对、装饰、娱乐、餐饮);再执行检索,用 DuckDuckGo 拉取最新相关信息;然后综合信息,把零散结果整理成一份完整可执行的计划;最后存档备用,把检索到的信息留存下来,供以后规划同类活动时复用 [1]。

自定义知识库:把检索器包成工具

联网搜索解决的是开放信息,企业场景更常见的是把自有文档做成知识库。做法是把检索器包成一个 smolagents Tool:向量数据库存的是文本经机器学习模型转出的数值表示(嵌入),靠高维空间里「含义相近」来做语义搜索 [1]。示例用 BM25 做检索、RecursiveCharacterTextSplitter 做切分:

from langchain_community.docstore.document import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from smolagents import Tool
from langchain_community.retrievers import BM25Retriever
from smolagents import CodeAgent, InferenceClientModel

class PartyPlanningRetrieverTool(Tool):
    name = "party_planning_retriever"
    description = "Uses semantic search to retrieve relevant party planning ideas for Alfred’s superhero-themed party at Wayne Manor."
    inputs = {
        "query": {
            "type": "string",
            "description": "The query to perform. This should be a query related to party planning or superhero themes.",
        }
    }
    output_type = "string"

    def __init__(self, docs, **kwargs):
        super().__init__(**kwargs)
        self.retriever = BM25Retriever.from_documents(
            docs, k=5  # Retrieve the top 5 documents
        )

    def forward(self, query: str) -> str:
        assert isinstance(query, str), "Your search query must be a string"

        docs = self.retriever.invoke(
            query,
        )
        return "\nRetrieved ideas:\n" + "".join(
            [
                f"\n\n===== Idea {str(i)} =====\n" + doc.page_content
                for i, doc in enumerate(docs)
            ]
        )

工具类是 smolagents 工具机制的典型样板:name 和 description 是给模型看的,模型靠它们决定什么时候调用;inputs 声明参数 schema;真正干活的是 forward 方法——从 BM25 索引取回前 5 条文档,拼成带分隔符的文本返回。然后准备语料、切分、装配:

party_ideas = [
    {"text": "A superhero-themed masquerade ball with luxury decor, including gold accents and velvet curtains.", "source": "Party Ideas 1"},
    {"text": "Hire a professional DJ who can play themed music for superheroes like Batman and Wonder Woman.", "source": "Entertainment Ideas"},
    {"text": "For catering, serve dishes named after superheroes, like 'The Hulk's Green Smoothie' and 'Iron Man's Power Steak.'", "source": "Catering Ideas"},
    {"text": "Decorate with iconic superhero logos and projections of Gotham and other superhero cities around the venue.", "source": "Decoration Ideas"},
    {"text": "Interactive experiences with VR where guests can engage in superhero simulations or compete in themed games.", "source": "Entertainment Ideas"}
]

source_docs = [
    Document(page_content=doc["text"], metadata={"source": doc["source"]})
    for doc in party_ideas
]

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    add_start_index=True,
    strip_whitespace=True,
    separators=["\n\n", "\n", ".", " ", ""],
)
docs_processed = text_splitter.split_documents(source_docs)

# Create the retriever tool
party_planning_retriever = PartyPlanningRetrieverTool(docs_processed)

# Initialize the agent
agent = CodeAgent(tools=[party_planning_retriever], model=InferenceClientModel())

# Example usage
response = agent.run(
    "Find ideas for a luxury superhero-themed party, including entertainment, catering, and decoration options."
)

print(response)

切分参数值得留意:chunk_size 500、chunk_overlap 50,加了起始索引、去首尾空白,分隔符从段落级一路细到字符级。经验上,块太大稀释检索精度,块太小丢失上下文,50 左右的重叠能保住跨块边界的句子。装配好的智能体先查文档拿相关信息,再结合知识库的洞见,并在内存里维持对话上下文 [1]。

BM25 和向量检索是互补关系:BM25 是词频统计方法,不需要嵌入模型,对术语的精确匹配很强;向量检索捕捉同义改写和语义相近。生产系统常用混合检索把两路召回合并,再统一重排。

让检索更聪明

Agentic RAG 的进阶玩法集中在七类策略:查询重构,不改用户原话而是构造更贴合目标文档的检索词;查询分解,复合问题拆成多个子查询分别检索;查询扩展,同一意图换成多种措辞各查一遍;重排序,用 Cross-Encoder 对「查询-文档」对打更细的相关性分;多步检索,用前一轮结果指导下一轮查询;多源整合,网络搜索和本地文档一起用;结果校验,内容进答案之前先过一遍相关性和准确性审查 [1]。

这些策略在工程上各有位置。Cross-Encoder 打分准但贵——每对查询文档都要过一遍完整模型,所以标准做法是先用便宜的第一阶段召回 top-k,再对短名单重排。查询分解适合多实体多约束的复合问题,子查询独立检索后合并去重。结果校验可以让模型判断检索片段与问题的事实一致性,不一致的片段宁可不用。

系统层面同样有讲究:智能体应按查询类型和上下文在可用工具之间做选择;内存系统维持对话历史、避免重复检索同样的问题;要有回退策略,主检索失效时系统仍能给出有价值的东西;校验步骤兜住最后的质量关 [1]。

本文改编自 Hugging Face agents-course 的 Building Agentic RAG Systems 一节[1],原文以 Apache-2.0 许可发布。

来源

  1. 1
    Building Agentic RAG Systems | Hugging Face Agents CourseHugging Face agents-course (Apache-2.0)

引用本页: smolagents 检索智能体. TellWorld, https://www.tellworld.net/zh/practice/practice-smolagents-retrieval. 更新于 2026-11-14

相关动态