Soup's Blog

Back

RAG实战(三)路由与查询构建Blur image

代码开源Github地址

RAG路由与查询构建:智能检索的核心技术#

在前两章中,我们学习了RAG系统的基础和查询优化技术。但是,当面对多个数据源或需要结构化查询时,如何智能地选择正确的数据源和构建合适的查询呢?本章将介绍路由机制和查询构建技术。

为什么需要路由和查询构建?#

实际场景中的挑战

场景1: 多个数据源

# 企业知识库中的多个数据源
数据源1: 技术文档数据库
数据源2: 用户手册数据库  
数据源3: FAQ知识库
数据源4: API参考文档

用户查询: "如何使用Python SDK连接数据库?"

# 应该查询哪个数据源?
→ 单一数据源可能不够
→ 查询所有数据源效率低
→ 需要智能路由机制
python

场景2: 复杂查询条件

# 向量数据库包含:
- 文档内容 (embedding)
- 元数据: 
  - 作者
  - 发布日期
  - 文档类型
  - 标签

用户查询: "找出2023年发布的关于机器学习的文章"

# 需要同时考虑:
→ 语义相似度 (机器学习)
→ 结构化条件 (日期 >= 2023-01-01)
→ 需要查询构建技术
python

本章内容概览#

技术核心功能适用场景复杂度
逻辑路由基于规则的路由确定性路由
语义路由基于LLM的路由灵活路由⭐⭐
结构化查询构建filter条件带元数据查询⭐⭐
自查询检索器自动分离查询意图复杂查询⭐⭐⭐

环境准备与数据源设置#

创建多数据源环境#

首先,我们需要为系统准备多个数据源。我们将创建两个集合:学术论文集合和网页内容集合。

并行加载网页数据#

数据加载实战#

实际输出


Part 1: 逻辑路由 - Logical Routing#

1.1 核心概念#

逻辑路由使用基于规则的方法来决定将查询发送到哪个数据源。它通过LLM理解查询内容,然后根据预定义的规则选择合适的数据源。

工作原理

用户查询

LLM分析查询意图

匹配预定义的路由规则

选择目标数据源

执行检索
plaintext

1.2 基础路由实现#

实际输出

Processed prompts: 100%|██████████| 1/1 [00:00<00:00,  3.42it/s]
Query: 如何在数据库中查询?
Route: 数据库

Processed prompts: 100%|██████████| 1/1 [00:00<00:00, 18.21it/s]
Query: 今天的天气怎么样?
Route: web_content
plaintext

1.3 完整逻辑路由系统#

1.4 逻辑路由实战测试#

实际输出

📊 集合信息:
   langchain: 12721 个文档 - 学术论文和技术文档,包含抓取检测、滑动检测相关的研究论文、技术文档
   web_content: 5 个文档 - 网页内容,包含CSDN博客、技术教程、实践指南等网页文章

🎯 查询: 网页文档中关于抓取检测的介绍
Processed prompts: 100%|██████████| 1/1 [00:00<00:00,  1.87it/s]
🤖 LLM路由决策: 'web_content'
📍 路由到: web_content
📚 检索到 4 个文档
路由结果: web_content
文档数量: 4
  1. 来源: https://blog.csdn.net/qq_40081208/article/details/111053208
  2. 来源: https://blog.csdn.net/WhiffeYF/article/details/110829105
plaintext

1.5 带回退机制的路由优化#

1.6 多数据源查询实战#

实际输出

1.7 逻辑路由的优缺点分析#

✅ 优点

  • 可预测和可控:基于预定义规则,行为可预测
  • 易于理解和调试:规则明确,便于调试
  • 适合确定性场景:对明确分类的问题效果好
  • 快速且高效:规则匹配速度快

❌ 缺点

  • 灵活性有限:难以处理复杂或边界情况
  • 需要预定义规则:需要人工设计路由规则
  • 难以处理边界情况:模糊查询效果不佳
  • 可能需要频繁更新规则:随着数据源变化需要更新

1.8 性能优化技巧#


1.9 总结#

逻辑路由的核心价值

  1. 智能数据源选择:根据查询意图自动选择最相关的数据源
  2. 性能优化:避免查询所有数据源,提高检索效率
  3. 结果质量提升:确保查询在最适合的数据源中执行
  4. 系统可扩展性:支持轻松添加新的数据源

🎯 使用建议

场景推荐策略说明
数据源明确单一路由查询目标明确时使用
结果全面性多数据源+回退需要全面覆盖时使用
性能敏感带缓存路由高并发场景使用
数据源变化频繁动态路由数据源经常变化时使用

1.10 最佳实践#

  1. 合理设置回退机制:确保系统鲁棒性
  2. 使用路由缓存:提升高频查询性能
  3. 监控路由准确性:定期评估和优化路由规则
  4. 支持人工干预:提供手动指定数据源的能力

💡 经验总结:逻辑路由是构建多数据源RAG系统的基础,通过智能的数据源选择,显著提升了系统的检索效率和准确性。

在下一部分,我们将探讨更高级的语义路由查询构建技术,进一步提升复杂查询的处理能力。


Part 2: 语义路由 - Semantic Routing#

在Part1中,我们成功构建了一个包含多种数据源的本地知识库。现在面临一个核心挑战:当用户提出不同性质的问题时,系统应该如何智能地判断从哪个数据源中寻找答案?这就是语义路由(Semantic Routing) 要解决的关键问题。

2.1 核心概念:从”关键词”到”语义理解”#

传统的路由方式可能依赖于关键词匹配(例如,问题中包含”论文”就路由到学术库)。但这种方式非常僵化,无法处理复杂语义。

语义路由的核心理念是使用文本的嵌入向量(Embeddings) 来进行路由决策。它为每个数据源(路由)定义一段描述性文本,并将这段描述也转换为嵌入向量。当用户查询到来时,系统会计算查询的嵌入向量与所有路由描述嵌入向量的语义相似度,然后选择最相似的路由进行检索。

工作原理可以简化为以下流程:

用户查询 (例如:"如何实现抓取检测?")

计算查询的嵌入向量

计算与各路由描述的相似度

选择最相似的路由

在该路由对应的向量库中执行检索
plaintext

2.2 代码实现:构建语义路由器#

以下是一个完整的SemanticRouter类实现,它基于LangChain和ChromaDB,完美适配本地环境。

2.3 初始化与路由配置#

# 创建语义路由器
router = SemanticRouter(embeddings, persist_directory="./chroma_db")

# 配置路由规则
router.add_route(
    name="langchain",
    description="学术论文、技术文档、研究论文、抓取检测和滑动检测相关技术",
    retriever=router.langchain_collection.as_retriever()
)

router.add_route(
    name="web_content", 
    description="网页内容、技术教程、实践指南、编程教程、CSDN博客、实际操作步骤",
    retriever=router.web_content_collection.as_retriever()
)
python

2.4 实战测试与结果分析#

测试1:技术算法类问题

# 测试偏向理论算法的问题
result = router.query("如何实现一个抓取检测算法?")

print("📄 检索结果预览:")
for i, doc in enumerate(result['documents'][:2], 1):
    source = doc.metadata.get('source', '未知来源')
    content_preview = doc.page_content[:80].replace('\n', ' ')
    print(f"  {i}. 来源: {source}")
    print(f"     内容: {content_preview}...")
python

预期输出:

🎯 查询: 如何实现一个抓取检测算法?
🔍 路由分析:
   langchain: 0.501
   web_content: 0.270
📍 路由到: langchain (相似度: 0.501)
📄 检索结果预览:
  1. 来源: ./Dataset/PDF/基于视触感知协同的机器人抓取技术研究_祝会龙.pdf
     内容: 西南科技大学硕士学位论文 32 图 4-2 抓取过程 Fig.4-2 Grabbing Process (1)抓手打开阶段...
  2. 来源: ./Dataset/PDF/基于视触感知协同的机器人抓取技术研究_祝会龙.pdf
     内容: 的采集,使用 Savitzky-Golay 滤波算法进行数据滤波,并进行了测试。然后,研究了 TSF...
plaintext

测试2:实践操作类问题

# 测试偏向实践操作的问题
result = router.query("如何写一个技术博客?")

print("📄 检索结果预览:")
for i, doc in enumerate(result['documents'][:2], 1):
    source = doc.metadata.get('source', '未知来源')
    content_preview = doc.page_content[:80].replace('\n', ' ')
    print(f"  {i}. 来源: {source}")
    print(f"     内容: {content_preview}...")
python

预期输出:

🎯 查询: 如何写一个技术博客?
🔍 路由分析:
   web_content: 0.487
   langchain: 0.401
📍 路由到: web_content (相似度: 0.487)
📄 检索结果预览:
  1. 来源: 链接1
     内容: 机械臂论文笔记(二)【实时抓取点检测】Real-Time Grasp Detection Using Convolutional...
  2. 来源: 链接2  
     内容: 械臂论文笔记(三)【抓取检测】机器人抓取检测技术的研究现状 刘亚欣_基于深度图像的机械臂...
plaintext

2.5 语义路由的技术优势#

  1. 智能语义理解:系统能够理解”抓取检测算法”是研究主题(路由到学术库),而”写技术博客”是实践操作(路由到网页库)

  2. 灵活可扩展:新增数据源只需添加路由描述,无需修改核心逻辑

  3. 决策透明化:路由得分可视化,便于调试和优化描述文本

  4. 检索效率提升:避免全库搜索,针对性检索提升响应速度

2.6 多路查询与结果融合#

除了单一路由,系统还支持多路并行查询,对于复杂问题可以从多个角度获取信息。


2.7 混合路由:智能选择最佳检索策略#

在前面的章节中,我们分别介绍了逻辑路由和语义路由。这两种路由策略各有优劣,为了在实际应用中达到最佳效果,我们需要一个能够智能选择路由策略的系统,这就是混合路由(Hybrid Routing)

2.7.1 混合路由的核心思想#

混合路由的核心是根据查询的复杂度特性,动态选择最适合的路由策略:

  • 简单查询 → 使用语义路由(速度快、成本低)
  • 复杂查询 → 使用逻辑路由(准确性高、可解释性强)

工作流程示意:

2.7.2 混合路由器的实现#

以下是HybridRouter类的完整实现,它集成了前面实现的语义路由器和逻辑路由器:

2.7.3 初始化混合路由器#

# 创建混合路由器
hybrid_router = HybridRouter(embeddings, llm, persist_directory="./chroma_db")

# 添加两个路由
hybrid_router.add_route(
    name="langchain",
    description="学术论文、技术文档、研究论文、抓取检测和滑动检测相关技术",
    retriever=hybrid_router.semantic_router.langchain_collection.as_retriever()
)

hybrid_router.add_route(
    name="web_content", 
    description="网页内容、技术教程、实践指南、编程教程、CSDN博客、实际操作步骤",
    retriever=hybrid_router.semantic_router.web_content_collection.as_retriever()
)
python

输出示例:

✅ 添加路由: langchain - 学术论文、技术文档、研究论文、抓取检测和滑动检测相关技术
✅ 添加混合路由: langchain - 学术论文、技术文档、研究论文、抓取检测和滑动检测相关技术
✅ 添加路由: web_content - 网页内容、技术教程、实践指南、编程教程、CSDN博客、实际操作步骤
✅ 添加混合路由: web_content - 网页内容、技术教程、实践指南、编程教程、CSDN博客、实际操作步骤
plaintext

2.7.4 自适应路由测试#

测试1:简单查询(自动选择语义路由)

query = "如何学习抓取检测算法?"
print("\n🎯 自适应路由测试:")
result = hybrid_router.query_adaptive(query)
print(f"   路由结果: {result['route']}")
print(f"   文档数量: {len(result['documents'])}")
python

预期输出:

🎯 自适应查询: 如何学习抓取检测算法?
📊 查询复杂度: simple
🎯 混合路由查询: 如何学习抓取检测算法?
🔍 路由分析:
   langchain: 0.497
   web_content: 0.305
✅ 使用语义路由: langchain (分数: 0.497)
📚 检索到 4 个文档
   路由结果: langchain
   文档数量: 4
plaintext

测试2:复杂查询(自动选择逻辑路由)

complex_query = "请详细分析抓取检测算法在工业机器人中的应用场景、技术挑战和未来发展趋势"
print("\n🎯 复杂查询自适应路由测试:")
result = hybrid_router.query_adaptive(complex_query)
print(f"   路由结果: {result['route']}")
print(f"   文档数量: {len(result['documents'])}")
python

预期输出:

🎯 自适应查询: 请详细分析抓取检测算法在工业机器人中的应用场景、技术挑战和未来发展趋势
📊 查询复杂度: complex
🎯 混合路由查询: 请详细分析抓取检测算法在工业机器人中的应用场景、技术挑战和未来发展趋势
🤖 LLM分析: 这是一个复杂的综合分析请求,涉及应用场景、技术挑战和未来趋势...
✅ 使用逻辑路由: langchain
📚 检索到 4 个文档
   路由结果: langchain
   文档数量: 4
plaintext

2.7.5 路由策略对比分析#

下表详细对比了两种路由策略的特性:

特性逻辑路由语义路由
决策依据规则/LLM分类嵌入相似度
灵活性中等
准确性高(规则明确时)中高
速度很快 ⚡
成本需LLM调用仅需嵌入
可解释性
适用场景复杂查询、多条件查询简单查询、相似性查询
维护成本中(需维护规则)

Part 3: 查询构建 - Query Construction#

在前面,我们构建了智能的混合路由系统,能够根据查询特性选择最佳的数据源。现在,我们将深入探讨如何让检索系统理解更复杂的查询意图,这就是查询构建(Query Construction) 要解决的核心问题。

3.1 为什么需要查询构建?#

在实际应用中,用户的查询往往不仅包含对内容本身的语义描述,还包含对文档属性的明确要求。让我们通过一个具体例子来理解:

场景:带元数据的文档检索

假设我们的文档库包含丰富的元数据:

document = {
    "content": "深度学习入门教程:详细讲解了神经网络的基础概念...",
    "metadata": {
        "author": "张三",
        "date": "2023-06-15", 
        "category": "机器学习",
        "tags": ["深度学习", "神经网络"],
        "views": 1500
    }
}
python

用户提出复杂查询:

“找出张三在2023年写的关于深度学习的文章”

这个查询包含两种需求:

  1. 语义搜索需求:内容需要关于 "深度学习"
  2. 结构化过滤需求
    • author 等于 "张三"
    • date"2023-01-01""2023-12-31" 之间

查询构建的作用就是自动解析这种复杂意图,生成结合语义搜索和精确过滤的复合查询。

3.2 自查询检索器原理#

自查询检索器(Self-Query Retriever)使用LLM来解析自然语言查询,将其转换为结构化查询条件:

用户查询 → LLM解析 → 过滤条件 + 搜索词 → 向量数据库检索
plaintext

3.3 实现自定义自查询检索器#

3.4 配置与初始化#

3.5 实战测试#

测试1:结合数值过滤的查询

query = "找出点赞超过10次关于抓取检测的文章"
print(f"\n🔍 测试查询: {query}")
results = self_query_retriever.get_relevant_documents(query, k=3)

print(f"✅ 找到 {len(results)} 个文档")

for i, doc in enumerate(results, 1):
    # 清理内容显示
    clean_content = ' '.join(doc.page_content.replace('\n', ' ').split())[:80]
    print(f"\n{i}. 内容预览: {clean_content}...")
    print(f"   元数据: { {k: v for k, v in doc.metadata.items() if k in ['author', 'views', 'source']} }")
python

预期输出:

🔍 测试查询: 找出点赞超过10次关于抓取检测的文章
🎯 自查询: 找出点赞超过10次关于抓取检测的文章
Processed prompts: 100%|██████████| 1/1 [00:00<00:00,  3.69it/s]
🤖 LLM解析结果: gt:views:10;contains:content:抓取检测
🔍 解析的过滤条件: {'views': {'gt': '10'}, 'content': {'contains': '抓取检测'}}
✅ 找到 3 个文档

1. 内容预览: 机械臂论文笔记(二)【实时抓取点检测】Real-Time Grasp Detection Using Convolutional Neural Networks...
   元数据: {'source': 'https://blog.csdn.net/WhiffeYF/article/details/110829105', 'views': 15}

2. 内容预览: 械臂论文笔记(三)【抓取检测】机器人抓取检测技术的研究现状 刘亚欣_基于深度图像的机械臂抓取位姿估计...
   元数据: {'source': 'https://blog.csdn.net/WhiffeYF/article/details/111031270', 'views': 11}
plaintext

3.6 查询构建的技术优势#

  1. 自然语言理解:用户可以用最自然的方式表达复杂查询需求
  2. 精确过滤:结合元数据过滤,大幅提升检索准确性
  3. 灵活组合:支持多种条件组合(与、或、范围等)
  4. 错误恢复:解析失败时自动降级到普通检索

3.7 应用场景与局限性#

适用场景:

  • 电商产品搜索(价格范围、品牌、类别)
  • 文献检索(作者、年份、期刊)
  • 内容管理(标签、状态、日期)

当前局限性:

  • LLM解析可能存在误差
  • 复杂逻辑(OR条件)支持有限
  • 需要定义清晰的元数据schema

Part 4 自查询检索器 - Self-Query Retriever#

在前面的章节中,我们探讨了逻辑路由、语义路由以及查询构建器,它们都需要我们显式地定义路由规则或查询结构。然而,在更智能的应用中,我们期望系统能自动理解用户的自然语言查询意图,并将其分解为适合检索的组件。这就是自查询检索器的用武之地。

4.1 核心概念#

自查询检索器是LangChain提供的高级工具,它能够自动将自然语言查询分离为语义搜索部分和结构化过滤部分。其工作流程如下:

自查询处理流程:

用户自然语言查询

LLM分析查询意图

分离为两部分:
├─ 语义查询内容(用于向量搜索)
└─ 元数据过滤条件(用于结构化过滤)

执行混合检索

返回精确定位的结果
plaintext

4.2 实现原理#

自查询检索器的核心在于利用大语言模型(LLM)的语义理解能力,自动解析用户查询中的隐含过滤条件。以下是简化版实现:

4.3 实际应用演示#

以下是自查询检索器的实际测试结果:

测试输出示例:

4.4 技术优势与局限#

优势 ✅:

  • 自动化解析: 自动分离语义查询和结构化过滤条件
  • 自然语言友好: 支持复杂的自然语言查询意图理解
  • 混合检索: 结合向量搜索和元数据过滤的最佳效果
  • 易于集成: 与现有向量数据库无缝集成

注意事项 ⚠️:

  • LLM依赖: 查询解析质量依赖LLM的语义理解能力
  • 元数据定义: 需要清晰明确的元数据字段定义
  • 性能开销: LLM调用增加额外的响应时间
  • 错误处理: 需要完善的错误回退机制

4.5 性能优化策略#

1. 路由缓存

2. 批量并行处理

总结#

应用场景选择指南

选择自查询检索器当:

  • 查询条件复杂且包含隐含的过滤需求
  • 用户习惯使用自然语言表达查询意图
  • 需要结合语义搜索和精确过滤的混合场景
  • 系统需要较高的自动化程度

选择其他方案当:

  • 查询规则固定且明确 → 逻辑路由
  • 只需语义相似性搜索 → 标准检索器
  • 过滤条件简单明确 → 查询构建器

自查询检索器代表了检索系统智能化的高级阶段,通过LLM的语义理解能力,实现了从”如何查询”到”查询什么”的自然过渡,为构建更加智能和用户友好的检索系统提供了有力工具。

RAG实战(三)路由与查询构建
http://www.soupcola.top/blog/rag_blogs/rag_blogs-3
Author Soup Cola
Published at 2026年1月31日