Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

领域特异性 RAG 的通用设计模式

电力系统的运维人员问“220kV 线路保护配置“,系统返回的是 1000kV 特高压规程的段落——措辞专业、结构工整,外行人根本审不出错。这种“看起来正确的错误“,比答不上来危险得多。

同样的病,我在财报系统里也犯过:问 2025 Q4 的数据,返回 2020 Q4,相似度分数还很高。第 6 篇讲时间错配时展开过,这里不重复。两个行业隔着十万八千里,bug 长得一模一样,根因也是同一个——embedding 分不清“语义相似“和“条件等价“(“条件等价“是我自己的叫法,不是学界术语,指的是“满足同一份约束条件”)。

第 17、18 两篇分别划了 Agent 和 GraphRAG 的边界,主题都是“什么时候别急着上“。边界划完,该往回收一层:这些判断背后,有没有跨领域通用的东西?我在财报和电力两个领域各踩了一遍坑,最后收敛出五个模式。不敢说它们是普遍规律,但至少在两个相距很远的领域,指向了同一套设计。这篇一个一个拆。

模式一:强约束 metadata 优先

垂直领域的文档天然带强约束维度:财报有年份和口径,电力有电压等级和设备型号。这些维度恰恰是 embedding 的盲区。1000kV 和 220kV 的文档,用词、结构高度重叠,都在讲变电站、线路保护、断路器,向量化之后几乎是孪生。语义相似不等于条件等价——指望向量自己把它们分开,不现实。

我的做法很固定:花一天时间,把这个领域的强约束维度全部列出来,逐一定成 metadata 字段,检索时做硬过滤。财报那边是年份、口径字段,第 6 篇讲过;电力这边直接看代码:

# 入库:每条 chunk 打上电压等级标签
doc.metadata["voltage_level"] = "1000kV"

# 检索:先提取 query 里的电压等级,再作为硬约束
voltage = extract_voltage(user_query)  # 规则或轻量 LLM 均可
retriever = vectorstore.as_retriever(
    search_kwargs={"filter": {"voltage_level": voltage}}
)

关键在 filter 这一层。把电压等级注入 chunk 文本、召回后加 reranker,都是软信号:只能提高正确文档排在前面的概率,不能保证错误文档不出现。硬过滤是在向量库层面把另一个电压等级的文档直接排除——它们连参与排序的资格都没有。Milvus、Pinecone、Weaviate、Qdrant 这些主流向量库都支持 metadata filter,实现上没有门槛。

代价也要讲清楚:硬过滤把信任押在两处,标注质量和 query 约束提取。标签标错,过滤会把正确答案也一起滤掉;query 里没带电压等级时,提取逻辑还得有兜底。第一个问题,模式四回来讲。

这是我做垂直领域 RAG 雷打不动的第一步,先做掉它再谈别的。

模式二:分片由文档结构驱动,不由 token 数驱动

通用 RAG 按 token 数切 chunk,垂直领域按文档的语义结构切。

财报里,管理层讨论是段落,财务数据是表格。表格得当原子块处理:表头、口径、数值行留在同一个 chunk 里,切散了,模型就只看得见数字、不知道数字是谁的。这套先识别 TextBlock 再合并的做法第 4 篇写过,不展开。

电力这边的对应动作是入库前的结构化预处理:每条 chunk 继承文档级上下文,比如它出自《1000kV 变电站运行规程》的哪一节。按同一逻辑反推,设备规范应该跟着“技术参数 / 运行条件 / 保护配置“这种参数组切,而不是跟着 512 个 token 切。再类推开去,法律文档大概按“条“切,药品说明书大概按 section 切——这两个领域我没实操过,只是类比;但结构驱动这个思路,我判断是通的。

原则值得单独一行:

识别文档的语义结构边界,在边界内分片,永不在边界中间切。

边界长什么样因领域而异,找边界、守住边界的动作是同一个。

模式三:下游问题驱动上游 ingestion 设计

常见的顺序是先建一套管线,再问它能答什么。我反过来:先列出系统要回答的 20 个最典型 query,逐个反推答案需要的信息在原文的什么位置、什么格式,ingestion 从“怎么准确抽出这些信息“开始设计。

财报就是这么推出来的。用户问收入、利润、现金流——数字在表格里,所以表格抽取的优先级高于段落。用户问“为什么变了“——主表只给结果,附注才解释口径变化和拆分原因,所以附注的召回权重被我调高。管线最后长什么样,是 query 分布决定的,不是审美决定的。

电力按同一逻辑反推:典型 query 带着电压等级和设备名进来,所以 ingestion 阶段,电压等级和设备型号的结构化优先级最高——模式一那两个 metadata 字段,就是这么来的。【待补:电力侧典型 query 的具体列表,现有素材未覆盖】

说白了:不建完美的通用管线,建对你的 query 最优的管线。

模式四:关键页面的人工审查永远可靠

垂直领域的错误成本,比通用场景高一个量级。通用 RAG 答错一条 FAQ,用户翻个白眼;财报把 2020 年的数字当 2025 年答,电力把电压等级张冠李戴,那是决策级错误。

所以有些人工成本不能省。先定义关键页面:用户最常问的 10 个问题涉及的源文档区域。在这些页面上花 30 分钟逐页检查 ingestion 质量,比花 3 天调 embedding 参数划算。财报里这大约是 15 到 20 页,具体怎么审,第 2 篇写过。

电力侧的人工审查有个更具体的对象:metadata 标签本身。模式一说过,硬过滤的可靠性取决于标注质量——一个电压等级标签标错,过滤当场打穿,reranker 顶多兜底漏标注的情况。自动化检查能覆盖大多数页面,但关键页面上那双眼睛,目前没有被替代。

模式五:优化顺序——先把免费的花完

领域 RAG 的优化有优先级。我的清单分八级:

  1. metadata 强约束过滤(免费)
  2. chunk 策略(免费)
  3. query 改写 + BM25 补充(少量成本)
  4. 噪声清洗(免费)
  5. reranker(少量 API 成本)
  6. 更强的 embedding(fine-tune 或 API 成本)
  7. GraphRAG(构建和维护成本高,第 18 篇算过这笔账)
  8. Agentic RAG(延迟和成本翻倍,第 17 篇划的边界)

第 1、2、4 级是免费午餐:不要额外推理成本,不要 GPU,不要调模型。第 3 级我原来也归在免费里,后来把它降了半档:query 改写要调 LLM,按次计费;BM25 要一套倒排索引设施——Elasticsearch、OpenSearch 或者向量库自带的稀疏通道,FAISS、Chroma 这类纯向量库并不原生支持。成本不大,但不是零。这里的“免费“和“贵“是定性分档——按要不要新增模型和基础设施来划,不是精确账单。前四级没做完就跳去五到八,等于轮子还没装好,就先买赛车胎。

这个排序在两个领域独立验证过。财报侧,时间错配是靠免费的 metadata 过滤解决的,不需要换 embedding——第 6 篇专门讲过,为什么不建议只靠换模型。电力侧的结论更直接:“最关键的一步是 metadata filter”,reranker 只是兜底。两个互不相干的领域,得出了同一个排序。

收尾

五个模式可以当检查表用。新接一个垂直领域,按一到五过一遍:列强约束维度,找文档结构边界,用 query 反推 ingestion,盯住关键页面,按免费到贵的顺序优化。前两步通常就解决八成问题。

领域知识决定下限——不懂电压等级和口径,连 metadata 字段都列不出来;通用模式决定上限。

这五个模式不是一天想出来的,是三年里一个接一个踩出来的。下一篇拉长时间轴:第 20 篇《RAG 技术演进 2023→2026:从外挂到操作系统》,看这些模式是怎么在三年里长出来的。