AI

企业级 RAG 系统搭建:基于 LangChain 的查询路由 + 子问题拆解,告别 “Naive RAG”

转载:小红书 AI产品赵哥

前言🔖


上一篇咱聊了向量数据库的选型,有朋友回去把自己公司的文档切片存进 FAISS 或 Chroma 之后,马上就遇到了新的小问题。

这也是目前 AI 产品经理上手 LangChain 最常见的一个现象:“Demo 猛如虎,实战二百五”。

  • 当你只存了 10 个文档时,你问什么 AI 都能答对。
  • 当你存了 10,000 个文档,涵盖了人事制度、技术文档、销售话术、财务报表时,你问:“怎么请假?”,AI 可能会根据销售话术回答你:“请假需要向客户提前报备”。

这就是 朴素 RAG(Naive RAG)的局限性。它只是简单粗暴地根据相似度检索,不管你的问题是什么类型,也不管你的数据有多少个领域。它把所有数据混在一个大池子里捞针。

今天咱们来聊的是企业级 RAG 必须跨越的门槛:Advanced RAG(高级 RAG)

我们将利用 LangChain 强大的编排能力,实现两个核心逻辑:查询路由(Routing)和子问题拆解(Query Decomposition)

这篇笔记稍微有点技术范儿,涉及架构逻辑,但大家不要担心,拔拔高,才能更强大。准备好了吗?咱走着!

  

痛点:为什么 “大一统” 的检索行不通?🔖


在企业环境里,知识库通常是异构的。

  • 数据源不同:有的是 PDF 文档,有的是 SQL 数据库,有的是 API 接口。
  • 领域不同:同样的关键词 “增长”,在产品或运营部门指用户增长,在市场部门可能指收入增长。
  • 问题复杂度不同:“昨天的销售额是多少?” 是查数问题;“怎么配置 VPN?” 是流程问题;“比较一下 A 产品和 B 产品的优劣” 是推理问题。

如果你把这些都扔进同一个 Vector Store,用同一个 Prompt 去处理,结果必然是灾难性的。

LangChain 的解决思路是:分而治之。我们要构建一个智能调度层,在检索之前,先让 LLM 思考:“这个问题该去哪里查?该怎么查?”

  

核心技术一:查询路由(Query Routing)🔖


路由的核心思想是:专库专用。与其建立一个包含万物的巨大索引,不如建立多个小而精的索引。

  • Index A:专门存技术文档。
  • Index B:专门存 HR 员工手册。
  • Index C:专门存法务合同。

当用户提问时,我们先经过一个 Router(路由器),判断意图,然后把问题分发给对应的索引。

🔹1.逻辑架构

用户的 Query →→ LLM 路由层(分类器)→→ 命中具体的 Retriever →→ 获取 Context →→ 生成答案。

  

🔹2.LangChain 实现代码(具象化)

LangChain 提供了非常优雅的路由实现方式。即使是产品经理,看懂下面的逻辑也不难。我们利用 LLM 的 Function Calling 能力来做精准分类。

from typing import Literal
from langchain_core.pydantic_v1 import BaseModel, Field

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# 1. 定义路由的数据结构
# 我们告诉 LLM,你必须从 "technical" 或 "hr" 中选一个
class RouteQuery(BaseModel):
    """根据用户的问题,将查询路由到合适的数据源。"""
    datasource: Literal["technical", "hr"] = Field(
        ...,
        description="如果是技术问题、代码问题,选 technical;如果是请假、福利、入职问题,选 hr"
    )

# 2. 初始化强力模型(建议 GPT-5 或 GPT-4)
llm = ChatOpenAI(model="gpt-4", temperature=0)

# 3. 绑定工具(让模型强制输出结构化分类)
structured_llm_router = llm.with_structured_output(RouteQuery)

# 4. 定义路由提示词
system = """你是一个专业的分类员。你的任务是将用户的问题路由到正确的数据源。
不要回答问题,只负责分类。"""
route_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "{question}"),
    ]
)

# 5. 构建路由链
router_chain = route_prompt | structured_llm_router

--- 测试效果 ---
Case A
print (router_chain.invoke ({"question": "怎么安装 Python 环境?"}))
输出: datasource='technical'
Case B
print (router_chain.invoke ({"question": "年假没休完可以折现吗?"}))
输出: datasource='hr'

产品经理 Insight:

在 PRD 中,你需要定义清楚 “路由规则”。也就是上面代码中 description 的部分。这部分描述写得越清楚,AI 分类就越准。不要只依赖开发去写,作为 PM,你要梳理业务边界,告诉开发:“涉及到报销的,全部分给财务库;涉及到合同的,全部分给法务库”。

  

核心技术二:子问题拆解(Query Decomposition)🔖


路由解决了 “去哪查” 的问题,但它解决不了复杂查询的问题。

场景描述:

用户问:“比较一下 LangChain 和 LlamaIndex 在 RAG 实现上的区别。”

朴素 RAG 的做法:

直接拿整句话去向量库搜。向量库可能会搜到介绍 LangChain 的文档,也可能搜到介绍 LlamaIndex 的文档。但很少有一篇文档是专门写这俩区别的。

结果就是:

检索回来的碎片信息拼凑不起来,回答不仅缺胳膊少腿,还容易瞎编。

LangChain 的解决思路:

把一个大问题,拆解成多个可以独立检索的小问题。

  • “LangChain 的 RAG 实现特点是什么?”
  • “LlamaIndex 的 RAG 实现特点是什么?”
  • 拿到这两份资料后,再让 LLM 进行对比总结。

🔹1. 逻辑架构

用户 Query →→ LLM 拆解层 →→ 生成子问题列表 [Q1, Q2] →→ 分别检索 →→ 汇总 Context →→ 生成最终答案。

  

🔹2. LangChain 实现代码(MultiQueryRetriever)

这里我们不需要手写复杂的循环,LangChain 有一个现成的组件叫 MultiQueryRetriever,但为了让大家看懂逻辑,我展示一下核心的 Prompt 驱动拆解过程。

from langchain_core.output_parsers import StrOutputParser

# 1. 定义拆解 Prompt
# 这里的核心技巧是 Few-Shot (少样本),给 AI 几个拆解的例子
template = """你是一个智能助手。你的任务是根据用户的问题,生成
通常原本的问题太复杂,无法直接检索。请把它们拆解成更简单的问题。
请每行输出一个子问题。

示例 1:
输入:比较 iPhone 15 和 华为 Mate 60 的屏幕参数。
输出:
iPhone 15 屏幕参数是什么?
华为 Mate 60 屏幕参数是什么?

示例 2:
输入:{question}
输出:
"""

prompt_decomposition = ChatPromptTemplate.from_template(

# 2. 构建拆解链
# 模型 -> 解析成多行文本 -> 转换成列表
generate_queries_chain = (
    prompt_decomposition
    | ChatOpenAI(temperature=0)
    | StrOutputParser()
    | (lambda x: x.split("\n"))
)

# --- 测试效果 ---
user_question = "LangChain 和 Semantic Kernel 哪个更适合"
sub_questions = generate_queries_chain.invoke({"question"

print(sub_questions)
# 输出结果将是一个列表:
# [
# "LangChain 对 Java 开发的支持情况如何?",
# "Semantic Kernel 对 Java 开发的支持情况如何?"
# ]

拿到这个列表后,代码会遍历这个列表,分别去向量库检索,把找回来的 6 段话(假设每个问题找 3 段)拼在一起,最后扔给 llm 说:“基于以上信息,回答用户关于两者比较的问题。”

产品经理 Insight:

如果你的产品涉及对比分析、多步推理、长文总结,一定要在技术方案中要求加上 Decomposition 策略。否则,面对复杂问题,你的 AI 除了会说 “抱歉我不知道”,就是在一本正经地胡说八道。

  

进阶一下子:自查询检索器(Self-Querying)🔖


除了路由和拆解,还有一个非常高频的场景:结构化过滤。

场景描述:

用户问:“找一下 2023 年的老王写的关于 AI 的文章。”

朴素 RAG:

向量搜索是基于语义的。它会去库里找和 “2023 年”、“老王” 语义相近的词。但实际上,这里的 “2023 年” 和 “老王” 不应该参与向量计算,它们应该是过滤器(Filter)。

正确的 SQL 逻辑应该是:

SELECT * FROM docs WHERE year=2023 AND author=’ 老王 ‘ AND semantic_match (‘AI’)。

LangChain 的 SelfQueryRetriever:

它能自动把自然语言把其中的 Metadata(元数据)剥离出来。

伪代码逻辑展示
metadata_field_info = [AttributeInfo (name="author", description="文档的作者"),AttributeInfo (name="year", description="文档发布的年份")]

当用户问:找一下 2023 年老王的文章
SelfQueryRetriever 会自动将其转化为:
filter = {
"author": {"$eq": "老王"},
"year": { "$eq": 2023 }
}
query = "AI 文章"

这对对于合同检索(按金额、按签署日期)、简历库(按学历、按工作年限)等场景是刚需。

  

企业级 RAG 的完整形态🔖


把上面讲的结合起来,一个能在生产环境 “耐撕” 的 RAG 系统,它的数据流转应该是这样的:

  1. Input Guardrail(输入护栏):先判断用户是不是在问敏感词,或者是否在攻击系统。
  2. Query Analysis(查询分析 – 重点)
    • Router:它是问 HR 还是问技术?
    • Extractor:有没有时间、作者等过滤条件?
    • Decomposer:问题太复杂吗?要不要拆?
  1. Retrieval(检索层):并行执行多个检索任务。
  2. Rerank(重排序):把找回来的几十个片段,用高精度的 Rerank 模型再排个序,取前 5 个。
  3. Generation(生成):带着这 5 个精准的片段,去问 GPT-5。
  4. Output Guardrail(输出护栏):检查 AI 有没有回答幻觉。

  

落地实战中的 3 个坑🔖


作为产品经理,在推行这种高级架构时,要注意以下代价:

🔹1.延迟(Latency)的增加

坑点:朴素 RAG 只需要请求一次 Embedding,查一次库,请求一次 LLM。速度很快。高级 RAG(路由 + 拆解)可能需要请求 3-4 次 LLM 才能生成答案。

解决方案:必须上流式输出(Streaming)。在后台进行拆解和检索时,前端要展示 “正在思考拆解步骤……”、“正在检索技术文档库……”,缓解用户焦虑。

  

🔹2.Token 成本的激增

坑点:每次拆解、每次路由,都是在消耗 Token。特别是 MultiQuery,把一个问题拆成 3 个,意味着你要做 3 次检索,上下文长度可能会翻 3 倍。

解决方案:路由和拆解层,尽量使用便宜的小模型(如 gpt-4.1-nano),只有最后的生成层才用 GPT-5。

  

🔹3.错误的路由比不路由更可怕

坑点:如果 Router 把一个 “技术问题” 错误地分到了 “HR 库”,那结果就是 0 分。

解决方案:设置兜底链(Default Chain)。如果 Router 觉得这问题既不像 HR 也不像技术,或者置信度低,就去查一个通用的大索引,或者直接利用 LLM 自身的知识库回答,并标注 “未检索到内部文档,以下基于通用知识回答”。

  

来,咱们总结一下吧🔖


这篇笔记的核心其实就一句话:不要让 LLM 傻傻地去检索,要先让它 “思考” 一下检索策略。

  • Routing(路由)解决了数据源混乱的问题。
  • Decomposition(拆解)解决了复杂逻辑推理的问题。
  • Self-Query(自查询)解决了精确条件筛选的问题。

当你能跟开发团队聊出:“这个场景我们需要加一个 Router Chain,用 Function Calling 做分类,然后针对复杂 Case 上一个 MultiQuery Retriever” 时,你在团队里的技术话语权就立住了。

LangChain 最强大的地方,不仅仅是它封装了 API,而是它封装了这些 Cognitive Architectures(认知架构)。它让开发人员能够像搭积木一样,把这些高级的推理模式组装进产品里。