面向 FAQ、流程文档、规则文档的 RAG 处理方案

面向 FAQ、流程文档、规则文档的 RAG 处理方案

一文了解LangChain4j RAG的概念、阶段、流程、Query压缩、Query路由、RAG+Tool等实战 介绍了LangChain4j的RAG的使用,尚未深入处理具体的业务文档,本文对具体业务类型文档RAG。

概览

如果知识库里的内容同时来自 Markdown、Word、PDF ,而且还包含 表格、图片、超链接、公式 等复杂元素,直接切分和向量化,通常会遇到两个问题:

  1. Chunk 不完整:同一段规则、同一张表格、同一条流程被拆散,检索命中后信息不全。
  2. 语义被污染:尤其是图片 URL、无意义路径、格式噪声,会降低向量检索质量。

这类文档特别适合先做一次 统一语义结构转换,再进入 RAG(Retrieval-Augmented Generation,检索增强生成)流程。

读完本文,你可以获得 4 个核心结论:

  • 为什么异构文档不能直接切分向量化
  • 如何设计一套统一的文档语义结构
  • 如何处理 Word/PDF 中的图片,避免 URL 带来语义噪声
  • 检索命中后,如何再把占位图片恢复成真实可访问 URL

一、这类文档为什么要单独处理

FAQ、流程型文档、规则型文档有一个共同特点:结构比自然语言更重要

例如:

  • FAQ 依赖"问题-答案"的配对关系
  • 流程文档依赖"步骤顺序"和"条件分支"
  • 规则文档依赖"章节层级""编号""表格约束"

如果直接把 Word、PDF 文本抽出来再粗暴切分,常见问题包括:

  • 标题和正文被拆开
  • 表格被打散成无序文本
  • 列表项失去编号关系
  • 图片只剩下链接,丢失图意
  • 公式、链接、批注等信息在抽取中丢失

因此,更稳妥的做法是:

先把不同格式文档转换成统一的"语义树",再生成适合 LLM 和向量检索的标准化 Markdown。


二、整体处理流程

整个方案可以分成两个阶段。

1. 索引阶段

text 复制代码
异构文档
→ 统一语义结构
→ 输出标准 Markdown
→ 生成适合 LLM 的 Markdown
→ 文档切分与向量化

2. 检索阶段

text 复制代码
查询检索
→ 命中 Chunk
→ 恢复图片真实 URL
→ 组装上下文
→ 交给 LLM 生成回答

这套流程的核心目标只有一个:

让进入向量库的内容尽可能"语义完整、噪声更低、结构可恢复"。


三、统一语义结构:先把文档变成"可理解的树"

为了兼容 Word、PDF、Markdown,建议先抽象出一层中间结构。

1. 基础结构

java 复制代码
@Data
public class DocumentNode {
    private NodeType type;
    private String content;
    private Map<String, Object> attrs; 
    // 记录文档结构信息,例如:
    // "level": 3, "numbering": "1.1", "style": "Heading3", "page": 4
    private List<DocumentNode> children;
}
java 复制代码
public enum NodeType {
    HEADING,
    PARAGRAPH,
    LIST,
    TABLE,
    IMAGE,
    CODE,
    LINK
}

2. 这层结构有什么意义

这不是简单的文本抽取,而是把文档还原为"有层次的内容单元"。

例如:

  • HEADING:表示标题,能保留层级关系
  • PARAGRAPH:表示普通段落
  • LIST:表示列表,便于保留步骤顺序
  • TABLE:表示表格,避免列与列之间被打乱
  • IMAGE:表示图片,后续可绑定图片语义和元数据
  • LINK:表示超链接,必要时保留锚文本与目标地址

这样做的好处是:

  • 后续切分时可以按节点边界切,而不是按字符硬切
  • 检索时更容易保留上下文完整性
  • 回答时能更接近原始文档结构

3. 建议补充的节点类型

如果你的文档里公式较多,建议在 NodeType 中增加:

  • FORMULA:公式节点
  • QUOTE:引用节点
  • NOTE:备注或提示框
  • SECTION:大章节容器节点

尤其是规则型文档,公式和注释往往会直接影响答案准确性。


四、索引阶段的关键设计

1. 第一步:异构文档解析

针对不同来源文档,先做基础解析:

  • Markdown:读取标题、列表、表格、图片、代码块
  • Word:读取段落样式、编号、表格、内嵌图片、超链接
  • PDF:按版面结构提取段落、表格、页码、图片区域

这里的重点不是"把字读出来",而是尽量保留:

  • 标题层级
  • 段落关系
  • 编号关系
  • 表格结构
  • 图片位置
  • 页码信息

其中 attrs 很关键,它能保存结构线索,例如:

json 复制代码
{
  "level": 2,
  "numbering": "3.1",
  "style": "Heading2",
  "page": 12
}

这些信息在后续切分、检索排序、结果展示时都很有价值。


2. 第二步:转换为统一语义结构

当 Word、PDF、Markdown 都被转换为 DocumentNode 树之后,后续流程就统一了。

此时你可以做几件重要的标准化操作:

标题归一化

把 Word 的 Heading1/Heading2、PDF 的字号层级、Markdown 的 # / ## / ###,统一成标准层级。

列表归一化

将:

  • 1.
  • (1)
  • a.
  • -

统一识别为列表项,并保留原始编号。

表格归一化

表格不要直接压扁成连续文本,建议保留:

  • 表头
  • 行列关系
  • 单元格内容
  • 跨行跨列信息(如果可提取)

图片归一化

每张图片至少保留:

  • 图片 ID
  • 图片说明文字(caption)
  • 原始位置
  • 页码
  • 宽高、类型等元数据

3. 第三步:输出标准 Markdown

统一语义结构生成 Markdown 的目的,不只是为了"好看",而是为了形成一个:

既适合人工检查,也适合机器处理的中间产物。

例如:

  • 标题转为 # / ## / ###
  • 列表转为标准 Markdown 列表
  • 表格转为 Markdown 表格,或必要时转为结构化文本
  • 图片转为 Markdown 图片语法
  • 链接保留锚文本

这一层输出后,通常可以作为"归档版 Markdown"。


4. 第四步:生成适合 LLM 的 Markdown

标准 Markdown 不一定最适合向量化和大模型理解,因此建议再做一层"LLM 友好化"处理。

例如:

补足上下文

把孤立的小标题和正文合并,避免只向量化一个短标题。

表格语义展开

对于重要表格,可以额外转为自然语言描述,例如:

费用审批规则表:A 类金额上限 5000 元,审批人为部门负责人;B 类金额上限 20000 元,审批人为总监。

保留图片语义,不保留噪声 URL

图片不要直接保留真实 CDN 链接,而应保留语义占位符。

加入结构提示

在必要时补充轻量标签,例如:

text 复制代码
[章节] 请假审批规则
[步骤] 2
[页面] 4

这类提示有助于提升召回和回答稳定性。


5. 第五步:按语义边界切分并向量化

这里的关键不是"切多大",而是"按什么切"。

推荐切分原则

优先按以下边界切分:

  1. 标题块
  2. 列表步骤块
  3. 表格块
  4. 图片说明块
  5. 规则条款块

为什么不能只按字数切

因为 FAQ、流程、规则类文档的核心信息,常常依赖完整结构:

  • 一个问题必须和它的答案放在一起
  • 一个流程步骤必须和前后步骤关联
  • 一条规则必须保留编号、条件、例外说明

因此,建议采用:

结构切分优先,长度限制兜底。

术语说明

  • Chunk:切分后的文本片段
  • Embedding:把文本映射为向量,用于语义检索
  • 向量化:生成 Embedding 的过程

五、Word / PDF 图片处理:真正容易出问题的环节

在这类文档中,图片经常很重要,比如:

  • 审批流程图
  • 系统操作截图
  • 架构图
  • 规则示意图

但图片处理不当,会直接影响检索效果。


1. 问题:真实图片 URL 会带来语义噪声

例如在 Markdown 中写成:

markdown 复制代码
![员工请假审批流程图](https://cdn.xxx.com/img102.png)

对 Embedding 模型和 LLM 来说,下面这些内容几乎没有语义价值:

  • https
  • cdn
  • 哈希串
  • 文件路径
  • 文件名编号

这些字符会占据文本空间,却不能帮助模型理解内容,反而会降低有效语义密度。

简单说就是:

模型需要的是"这是一张什么图",而不是"图放在哪个 URL"。


2. 解决方案:语义占位 + 延迟注水

这是处理图片最实用的一种方式。

向量化前,不写真实 URL,而写语义占位符

例如:

plain 复制代码
![员工请假审批流程图](image://img_102)

或者写得更明确一些:

plain 复制代码
[IMAGE_REF:img_102]
caption: 员工请假审批流程图

这里的 image://img_102 是一种内部引用,不是真实访问地址。

这个方案的好处

1)Embedding 仍能感知图片语义

模型能看到"员工请假审批流程图"这几个字,这是真正有用的语义信息。

2)不会引入 URL 噪声

避免把一长串无意义路径送进向量模型。

3)回答阶段还能恢复真实图片

当检索命中这段内容后,再把 image://img_102 解析成真实 URL 即可。

这就是"延迟注水"的含义:

先保留语义,等真正展示或回答时,再把可访问资源补回来。


六、图片处理的推荐实现流程

1. 在文档解析阶段

从 Word 或 PDF 中读取图片时,先提取图片二进制内容。

如果技术链路方便,可以临时转成 Base64 进行传递,但要注意:

Base64 只适合作为中间传输格式,不应该进入向量化文本。

因为 Base64 同样会制造大量噪声。


2. 在生成 Markdown 阶段

把图片上传到云端或对象存储后,得到真实 URL,例如:

markdown 复制代码
![员工请假审批流程图](https://example.com/img_102.png)

这一版 Markdown 适合作为"展示版"或"存档版"。


3. 在向量化前做一次替换

在送入向量模型前,把真实 URL 替换为占位符:

markdown 复制代码
![员工请假审批流程图](image://img_102)

同时在元数据(metadata)中保存图片真实信息:

json 复制代码
{
  "imageUrl_img_102": "https://example.com/img102.png",
  "mime": "image/png",
  "width": 1200,
  "height": 800
}

这样做之后:

  • 文本里保留的是语义
  • 元数据里保留的是资源定位信息

二者分工明确。


七、检索阶段:命中后再恢复真实图片 URL

当用户提问后,系统会先检索出相关 Chunk。

如果 Chunk 中存在:

markdown 复制代码
![员工请假审批流程图](image://img_102)

那么在把内容交给前端展示或交给 LLM 之前,可以通过内容注入器恢复:

markdown 复制代码
![员工请假审批流程图](https://example.com/img_102.png)

1. 实现思路

可以自定义 ContentInjector,继承默认实现,在格式化内容时完成替换逻辑。

伪代码思路如下:

java 复制代码
content = content.replace("image://img_102", "https://example.com/img_102.png");

更通用的方式是:

java 复制代码
imageResolver.resolve("img_102")

由解析器从 metadata 中查出真实 URL,再注入内容。


2. 为什么要在检索后恢复,而不是索引前恢复

因为两个阶段目标不同:

索引阶段

目标是提高检索质量,所以要尽量减少噪声。

检索/展示阶段

目标是让回答更完整、结果更可读,所以要恢复真实资源。

这也是整个方案的关键思想:

索引时追求语义纯度,展示时追求内容完整。


八、一个更完整的流程示意

1. 索引阶段

text 复制代码
Word/PDF/Markdown
→ 解析结构化内容
→ 转为 DocumentNode 语义树
→ 生成标准 Markdown
→ 图片上传对象存储
→ 将真实图片 URL 替换为 image:// 占位符
→ 保存图片 metadata
→ 按语义边界切分 Chunk
→ 向量化并写入向量库

2. 检索阶段

text 复制代码
用户提问
→ 向量检索召回 Chunk
→ 发现 image://img_102
→ 从 metadata 解析真实 URL
→ 还原 Markdown 图片链接
→ 组装上下文
→ 交给 LLM 生成答案

九、这套方案能带来什么收益

采用统一语义结构后,FAQ、流程、规则类知识库通常会得到几个明显收益:

1. Chunk 完整性更高

标题、步骤、表格、规则条款不容易被错误拆散。

2. 检索召回更稳定

模型能更好理解"这段内容讲的是什么",而不是被格式噪声干扰。

3. 回答准确率更高

LLM 拿到的是结构化、上下文完整的内容,幻觉会更少。

4. 图片可检索、可展示

不仅能通过图片说明文字参与召回,还能在结果中恢复真实图片。

5. 技术链路更可维护

统一语义结构相当于建立了一层"文档中台",后续接入更多格式也更容易扩展。


十、实践建议

如果你准备落地这套方案,建议优先做好以下几点:

1. 把"文档解析"和"向量化"解耦

不要从原始 Word/PDF 直接一步到向量库,中间最好有统一语义层。

2. 图片、表格、标题优先保结构

这三类内容最容易影响召回质量。

3. 不要把 Base64、真实 URL 直接送入 Embedding

这类内容通常只会制造噪声。

4. Chunk 切分要基于语义节点

不是简单按字符数切块。

5. metadata 要足够完整

至少建议保存:

  • 文档 ID
  • 页码
  • 标题路径
  • 图片映射
  • 表格标识
  • 原始章节编号

十一、财务流程RAG实战效果

问:如何录入发票信息?

结果:

bash 复制代码
[httpclient-dispatch-1] INFO tech.pplus.cases.ai.advanced.rag.FaqRagMain - text:如何录入发票信息?->aiMessage:
根据提供的参考资料,录入发票信息的具体操作如下:

1.  **基本原则**:对外支付遵循"先开发票后付款"的规定。申请人在填写对公支付申请时,必须依据**已收到**的发票录入相关信息。
2.  **关键选项设置**:
    *   在"发票是否归档"处,必须选择"**是**"。
    *   在"发票选择"处,需从系统中选择已录入的对应发票信息。
3.  **关联规则**:
    *   **单行限制**:每行只能选择一张发票。
    *   **拆分关联**:如果同一张发票涉及多个部门或不同业务类型的费用支出,可以在同一个流程中多次关联该张发票。此时需将金额分开填写,但所有关联金额的总和必须等于发票总金额。

![发票录入图](https://example.com/img_102.png)

注意:有些大模型在使用RAG提供资料回答时,不会返回markdown格式图片,需要在SystemPrompt中要求返回图片。


总结

对于 FAQ、流程型、规则型文档,RAG 的难点不在"能不能抽出文本",而在:

能不能保住结构,减少噪声,并在回答时恢复完整信息。

一套更稳妥的处理方式是:

  1. 先把 Markdown、Word、PDF 转成统一语义结构
  2. 再生成标准化 Markdown
  3. 向量化前,把图片真实 URL 替换为语义占位符
  4. 检索命中后,再恢复真实图片 URL
  5. 最终把结构完整、语义干净的内容交给 LLM

这样做,既能提高检索质量,也能明显改善最终回答的准确性与可读性。

相关推荐
打小就很皮...11 小时前
基于 Python + LangChain + RAG 的知识检索系统实战
前端·langchain·embedding·rag
奋斗的老史1 天前
LangChain4j 进阶实战系列
java·langchain4j·ai应用开发
wuxinyan1231 天前
大模型学习之路009:问题解决-RAG 知识库系统能上传文档,但检索不到内容
人工智能·学习·rag
wuxinyan1231 天前
大模型学习之路008:RAG 零基础入门教程(第五篇):完整 Naive RAG 系统搭建与评估
人工智能·学习·rag
快跑bug来啦1 天前
RAGFlow部署教程:Ubuntu24.04
ai·大模型·知识图谱·知识库·rag
狐狐生风2 天前
LangGraph 重构个人知识库问答系统(稳定 + 可扩展版)
python·langchain·rag·langgraph·agentai
@atweiwei2 天前
LangChainRust Agent 引擎:Graph 构建到执行
rust·langchain·llm·agent·rag·langchaingraph
suixinm2 天前
Agent 设计模式:从 ReAct、CodeAct 到 Agentic Rag 与多智能体
设计模式·ai·react·rag·ai agent·agent智能体·multi-agent
木子七3 天前
RAG质量评估&实施RAG工程核心步骤
ai·rag