第 14 章 · RAG 进阶:Agentic RAG 与混合检索
本章目标:分清 Agentic RAG 与传统 RAG 两种模式,掌握切块策略、元数据过滤与重排器三项提升检索质量的关键技术。
14.1 Agentic RAG vs 传统 RAG
上一章的例子是 Agentic RAG:模型拿到一个 search_knowledge_base 工具,自己决定"要不要查、查什么词",还能对查询进行改写和多轮补充检索。而传统 RAG是固定流水线——每次提问都先检索,把结果直接注入提示词:
from agno.agent import Agent
# 传统 RAG:每次请求前先检索,参考内容自动拼进用户消息
traditional = Agent(
model=model,
knowledge=knowledge,
add_knowledge_to_context=True, # 检索结果直接加入上下文
)
# Agentic RAG:模型自主决定是否/如何检索(默认行为)
agentic = Agent(
model=model,
knowledge=knowledge,
search_knowledge=True, # 注册检索工具,交给模型决策
)| 维度 | 传统 RAG | Agentic RAG |
|---|---|---|
| 检索时机 | 每次必检 | 模型按需决定 |
| 延迟与成本 | 低且可预测 | 多一轮工具调用 |
| 复杂问题 | 单次检索可能不够 | 可改写查询、多次检索 |
| 适用场景 | 简单问答、低延迟要求 | 需要多步推理的知识工作 |
选型建议
客服 FAQ 这类"一问一答"用传统 RAG 足够;研究分析类任务用 Agentic RAG。两者可以并存配置。
14.2 切块策略:决定召回上限的第一要素
向量库里存的最小单位是 chunk。切得太大,一个片段里混入无关内容稀释相似度;切得太小,语义不完整。Agno 内置多种 Chunking 策略,挂在 Reader 上生效:
from agno.knowledge.chunking.fixed import FixedSizeChunking
from agno.knowledge.chunking.recursive import RecursiveChunking
from agno.knowledge.chunking.document import DocumentChunking
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.knowledge.knowledge import Knowledge
# 固定长度:简单粗暴,适合格式规整的文本
fixed_reader = PDFReader(chunking_strategy=FixedSizeChunking(size=1500))
# 递归切块:按段落→句子→字符多级分隔符逐级回退,通用性最好
recursive_reader = PDFReader(chunking_strategy=RecursiveChunking())
# 文档结构切块:按标题/段落等文档结构边界切,保持章节完整
doc_reader = PDFReader(chunking_strategy=DocumentChunking())
knowledge = Knowledge(vector_db=vector_db)
knowledge.insert(path="handbook.pdf", reader=recursive_reader)选型经验:Markdown/HTML 用结构化切块、代码用 Code Chunking、无结构长文用递归切块起步,效果不佳再尝试基于嵌入相似度断句的 Semantic Chunking。
14.3 元数据过滤:让检索"查得准"
给入库内容打上元数据标签,查询时按标签过滤,可以把检索范围从"全库"缩小到"该用户/该年份/该类型":
# 入库时附加 metadata
knowledge.insert(
path="resumes/",
metadata={"department": "engineering", "year": 2025},
)
# 方式一:Agent 级固定过滤——这个 Agent 永远只看 engineering 的文档
agent = Agent(
model=model,
knowledge=knowledge,
search_knowledge=True,
knowledge_filters={"department": "engineering"},
)
# 方式二:调用时临时过滤,多个条件之间是 AND 关系
agent.print_response(
"总结 2025 年入职的工程师简历亮点",
knowledge_filters={"year": 2025},
)
# 直接检索同样支持 filters
results = knowledge.search(query="Python 经验", filters={"department": "engineering"})还可以开启 enable_agentic_knowledge_filters=True 让模型从用户提问中自动推断过滤条件(例如从"Jordan 的技能"里提取出 user_id=jordan),这需要配合 Contents DB 记录可用的过滤键。
14.4 重排器 Reranker:精排最后一道关
向量检索返回的是"粗排"结果。Reranker 用一个更重但更精准的交叉编码模型对候选片段逐一打分重新排序,把真正相关的顶到前面:
import os
from agno.knowledge.reranker.cohere import CohereReranker
from agno.vectordb.lancedb import LanceDb, SearchType
knowledge = Knowledge(
vector_db=LanceDb(
uri="tmp/lancedb",
table_name="docs",
search_type=SearchType.hybrid, # 混合检索:向量 + 关键词双信号
embedder=embedder,
reranker=CohereReranker(model="rerank-v3.5"), # 精排模型
),
)三者组合的分工:混合检索同时捕捉语义相似和关键词精确匹配两类信号;重排再对这些候选做细粒度相关性排序。这是目前生产级 RAG 的标准三件套。
14.5 如何评估检索质量
改参数不能靠感觉,建立最小评估闭环:
- 准备 20–50 条「问题 → 应命中内容」的标注对;
- 用
knowledge.search()批量跑,统计命中率(top-k 里是否包含正确片段); - 每次调整切块大小/换 Embedding 模型/加重排后重跑对比。
def hit_rate(kb, cases, k=5):
hits = sum(
any(c["keyword"] in r.content for r in kb.search(q=c["q"], top_k=k))
for c in cases
)
return hits / len(cases) # 例如 0.85 表示 85% 的问题召回了目标片段本章小结
- Agentic RAG(
search_knowledge=True,模型自主检索)与传统 RAG(add_knowledge_to_context=True,先检后答)各有适用场景; - 切块策略通过 Reader 的
chunking_strategy配置,递归切块是通用起点; knowledge_filters实现元数据过滤,enable_agentic_knowledge_filters=True让模型自动推断过滤条件;SearchType.hybrid+ Reranker 是生产级检索的标准组合;- 用小规模标注集量化评估每次调优的效果。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Agentic RAG 与传统 RAG 的核心区别是什么?
2. 关于切块(chunking),下列说法正确的是?
3. 设置 knowledge_filters={"year": 2025} 后又传入 {"dept": "sales"},两个过滤条件如何生效?
4. Reranker 在检索链路中的作用是?
🛠️ 动手实践
- 同一份 PDF 分别用
FixedSizeChunking(size=1000)和RecursiveChunking()入库到两张表,用同一组问题对比search()召回差异。 - 给知识库内容加上
{"lang": "zh"/"en"}元数据,实现中英文问题各自只检索对应语料。 - 为你的知识库写一个 10 条用例的
hit_rate评估脚本,记录基线分数。
下一步进入第 15 章:推理 Reasoning,让模型学会"先想后答"。