一个金融研报 RAG 应用:「离线解析」和「在线问答」双链路详解
复盘一套真实的金融 RAG 项目:离线链路怎么把多栏排版、扫描件和跨页表格解析成干净的知识,在线链路怎么通过 Query 优化、混合检索和重排把答案找准。
TL;DR
金融、保险这类专业领域的 RAG,难点全在非结构化数据上:多栏排版、扫描件模糊、跨页表格, 再加上用户模糊的提问意图。这篇跳出代码细节,把 RAG 拆成两条异步流水线来看。 离线链路是消化系统:用 OCR 和布局识别让机器看懂版面,用表格结构还原保住表头与数值的对应, 用 naive_merge 算法按 Token 预算动态装箱来切块,再用元数据把每个切片的层级坐标找回来。 在线链路是大脑:Query 重写扩写和 HyDE 扩充漏斗口,BM25 与向量检索并行做粗筛, Cross-Encoder 重排做精选。 对 PM 来说,知道数据在管道里怎么流动,才能在出 Bad Case 时判断是解析出了问题还是检索走了神。
目录
在 AI 产品的落地中,RAG 是知识库的标配,且是 Agent 应用中的核心组件。尤其是在金融、保险等专业领域,多栏排版、扫描件模糊、跨页表格,以及用户模糊的提问意图,这些非结构化数据带来的挑战,是每一个 AI 产品经理和技术团队必须攻克的深水区。
这篇文章复盘自一套真实的金融 RAG 实战项目。跳出代码细节,从「离线解析」与「在线问答」这两条核心链路出发,拆解构建 RAG 系统的底层逻辑。
对于 AI 产品经理而言,拥有系统性的优化思维至关重要:知道了数据在管道中是如何流动的,才能在产品出现 Bad Case 时,精准定位是解析出了问题,还是检索走了神。
全链路架构
做一个 RAG 产品,首先要建立上帝视角,不能只停留在前端的对话框,还要看透后端的「双流」架构。RAG 系统本质上由两条异步的流水线组成。
离线处理流程(数据入库),这是系统的消化系统:
- 提取:从 PDF、PPT、TXT 等异构数据源中提取内容。
- 向量化:通过 Embedding 模型将文本转化为向量。
- 存储:将文本和向量存入向量数据库(如 Milvus / ElasticSearch)和关系型数据库(如 PostgreSQL)。
在线查询流程(实时问答),这是系统的大脑:
- 查询处理:对用户 Query 进行编码、重写和意图识别。
- 检索:通过语义检索和关键词检索获取相关片段。
- 生成:LLM 基于增强的上下文生成最终回答。
离线阶段是提前做好知识库的解析;当用户来问问题的时候,进入第二阶段的在线检索。通过这两步操作,最终将用户的问题扩充为「检索回来的精确文本块 + 用户的原始问题」,再一起送给 LLM 作为 user prompt。
离线解析:精细化治理
RAG 领域的名言 Garbage In, Garbage Out,在金融研报这种专业领域体现得淋漓尽致,解析质量直接决定了系统的上限。可以从解析、分块、层级结构这三个维度进行优化。
1、深度解析:从「看到」到「看懂」
在金融 RAG 中,PDF 解析不能是单纯的文字提取,需要对文档结构进行逆向工程,集成 OCR、深度学习布局识别和机器学习文本合并的复杂流水线。
(1)视觉化与 OCR:让机器「看」到
金融研报中大量存在扫描件或图片格式的图表。解析的第一步是将 PDF 页面转化为高分辨率图像:使用 pdfplumber 将页面转为图像,同时提取原始字符坐标。如果检测到是扫描件而非电子原生 PDF,系统会启动 OCR 引擎(如 Tesseract),识别图像中的文字并生成新的文本框,与原始信息融合。
(2)布局识别:让机器「分类」
系统需要知道哪块是正文,哪块是页眉,哪块是图表。调用基于深度学习目标检测模型的 LayoutRecognizer,对页面进行区域划分,给不同区域打上 Title、Text、Table、Figure 等标签。
这解决了多栏排版读乱序的问题——因为这里是按区域读取,而不是按行读取。
(3)表格结构还原
金融数据都在表格里,因此不仅要提取表格里的字,还要还原行列关系。TableStructureRecognizer 会裁剪出表格区域,识别单元格边界,将图片中的表格重构为 HTML 或结构化数据,确保「表头—数值」的对应关系不丢失。
(4)智能文本合并:让机器「连贯」
这是最体现技术深度的环节。OCR 出来的往往是破碎的单字或单行,需要把它们拼成完整的段落:
- 横向合并:将同一行内距离极近的文本框拼接。
- 纵向合并:基于行距和对齐方式,将同一自然段的多行文本拼接。
- 基于 XGBoost 的上下文合并:这是一个高级特性。训练一个 XGBoost 模型,根据文本特征(是否以句号结尾、行间距、字体大小变化)来预测「下一行是否属于当前段落」,这比写死的规则要准确得多。
2、智能切块:数据入库前的精修
切分不是简单的「每 500 字切一刀」。错误的切分会斩断语义,导致检索失效。在 RAG 系统中,文档切块是最容易被忽视、却直接决定检索效果的环节:切得太碎,模型看不懂上下文;切得太长,检索噪音大且浪费 Token。
需要一种从宏观到微观再回归宏观的处理策略,原始文档变成机器可读的向量要经历三个阶段。
第一步:格式识别与初步解析。系统首先充当一个分拣员,根据文件后缀调用不同的解析器(如 PdfParser、DocxParser),将文档拆解为自然的段落(Sections)。
第二步:碎片化拆分。为了保证后续合并的灵活性,不能直接用段落当 Chunk,因为有的段落长达几千字,有的只有几个字。因此需要先打碎:利用分隔符(换行符、句号、感叹号)将 Section 进一步拆解为更小的文本碎片,产物是一堆细粒度的句子或短语。
第三步:基于 Token 预算的动态合并。这是最核心的一步,我们像装箱子一样,设定一个标准箱子大小(例如 128 Tokens),然后把刚才打碎的句子一个个装进去。
这里涉及的核心算法是 naive_merge,它的目标是在保持语义连贯性(不把一句话切断)的前提下,最大化利用每个 Chunk 的容量:
- 初始化:创建一个空的 Chunk 容器,设定阈值(例如
chunk_token_num = 128)。 - 循环填装:遍历文本碎片列表,计算「当前 Chunk 已有的 Token 数 + 新碎片的 Token 数」。小于等于阈值就装入;大于阈值就封箱,把当前 Chunk 保存,创建一个新 Chunk 并把这个碎片作为第一个元素放进去。
- 最终输出:生成 Chunks 列表。
举个例子:假设阈值是 128,当前 Chunk 里已经装了 120 个 Token,下一个句子有 15 个 Token。120 + 15 = 135 > 128,于是系统把这 120 个 Token 打包成 Chunk A,然后开启 Chunk B,把这 15 个 Token 的句子放进去。
经过上述处理,我们得到的不仅仅是一段段文本字符串,而是一个结构化的对象列表。每个最终的 Chunk 都包含:
- Content:文本块的字符串内容,用于给大模型阅读。
- Tokens:分词后的 Token 列表,用于计算成本和上下文窗口。
- Metadata:位置信息(页码、在原文中的偏移量),这对于产品界面上展示引用来源至关重要。
为什么不直接按字符数或段落切分? 直接切字符可能会把「人工智能」切成「人工」和「智能」两个 Chunk,导致语义崩坏;直接切段落则有的极短导致向量过于稀疏检索匹配不到,有的极长超过 Embedding 模型的窗口限制。
naive_merge 是一种动态平衡:先利用标点符号保护句子级的语义完整性,再利用 Token 计数控制 Chunk 的大小颗粒度。
3、重建层级结构与元数据
单纯的文本切片是孤立的,我们需要为每个切片找回它的坐标。
比如,当模型检索到「第三条:赔付金额为 50 万」这个切片时,如果丢失了它所属的「一级标题:重大疾病险」,模型可能会张冠李戴,把它当成意外险的条款。
解决方案是元数据增强。在解析时维护一个层级栈,给每一个 Chunk 打上标签:
{ "source": "理赔手册.pdf", "page": 5, "section_path": "总则/第二章/第三条", "type": "text"}那么在线问答检索时,就可以让 LLM 利用这些元数据进行精准过滤(例如「只看第二章的内容」),或者在回答时准确引用出处。
在线检索:组合优化
有了高质量的数据,下一步是让系统听懂人话,并精准找到答案。
1、Query 理解与优化
用户的问题往往是模糊的(如「怎么报销?」),直接检索效果会比较差。
- 意图识别:判断用户是在查流程、查数据还是闲聊,可通过规则匹配或 BERT 分类模型实现。
- Query 重写:将口语化的「怎么报销保险费用?」改写为规范的「保险费用报销流程是什么?」,去除冗余词,补全上下文。
- Query 扩写:引入同义词。用户搜「理赔」,系统自动扩展搜索「索赔」「赔付」,扩大召回范围。
- HyDE(假设文档嵌入):对于复杂问题,先让 LLM 生成一个假设答案,再用这个假设答案去检索真实文档,这能显著提升长尾问题的召回率。
2、混合检索
单一的检索方式在金融场景下往往捉襟见肘。向量检索擅长语义匹配(如「推销」匹配「销售」),但对专有名词(如「A 款产品」)不够敏感;关键词检索(BM25)擅长精确匹配,但无法理解语义。
解决方案是 BM25 与向量检索并行,将两者的得分进行归一化和加权融合(例如 0.6 × 向量分 + 0.4 × BM25 分),取长补短。
3、领域微调
通用的 Embedding 模型(如 BGE)可能不懂「保单现金价值」是什么。策略是使用金融领域的私有数据(问答对、专业术语)对 Embedding 模型进行微调,拉近专业术语在向量空间中的距离,显著提升召回准确率。
4、重排序:精度的最后一道防线
初步检索为了不漏掉信息,通常会召回 Top 50 甚至 Top 100 个片段,但这其中包含大量噪声。
Cross-Encoder 重排引入一个更精细的模型,将用户 Query 和候选文档拼在一起进行深度打分,能识别细微的语义差异。例如查询「最新车险流程」,初步检索可能混入了「2020 年旧流程」,重排模型能精准识别出「2023 年修订版」更相关,将其排在第一位。
总结来看,检索是一个漏斗模型:Query 优化(扩充漏斗口)→ 混合检索(粗筛)→ 重排序(精选)→ LLM 生成。需要关注每个环节的转化率。通过建立评估指标(如 MRR、NDCG、Precision@K),就可以量化出「引入重排后,Top 3 召回率提升了 15%」这样的结论。
最后
- 离线解析的颗粒度,决定了知识库的纯度。
- 在线检索的重排策略,决定了用户体验的精度。
也许一个 AI 产品的护城河,就是藏在这些最不起眼的脏活累活、某一个参数的调优里。看似枯燥的技术细节优化,最终都会转化为用户感知到的「这个 AI 真的懂我」。
核心结论
标注「判断」「假设」的是我的看法而非事实;标注「已推翻」的保留在这里,不删除。
RAG 系统本质上由两条异步流水线组成:离线处理(提取、向量化、存储)是消化系统,在线查询(Query 处理、检索、生成)是大脑。建立这个上帝视角,才能在出 Bad Case 时判断问题出在哪一条链路。#
判断 · 把握较大
文档切块是 RAG 系统中最容易被忽视、却直接决定检索效果的环节。切得太碎模型看不懂上下文,切得太长检索噪音大且浪费 Token。#
判断 · 把握较大
按 Token 预算动态合并(naive_merge)是工程落地中高性价比的切块方案——先用标点符号保护句子级的语义完整性,再用 Token 计数控制 Chunk 的颗粒度,避免了纯字符切分和纯段落切分各自的缺陷。#
判断
孤立的文本切片会导致张冠李戴。检索到「第三条:赔付金额为 50 万」时如果丢失了它所属的一级标题,模型可能会把重疾险条款当成意外险。层级栈加元数据是找回坐标的解法。#
检索是一个漏斗模型:Query 优化扩充漏斗口、混合检索做粗筛、重排序做精选、最后交给 LLM 生成。每个环节的转化率都要单独看,才能量化出「引入重排后 Top 3 召回率提升多少」这样的结论。#
判断 · 把握较大
引用本文
晨旭,《一个金融研报 RAG 应用:「离线解析」和「在线问答」双链路详解》,晨光里的AI,2025-12-05
[晨旭:《一个金融研报 RAG 应用:「离线解析」和「在线问答」双链路详解》](https://chenxu.xin/writing/financial-rag-dual-pipeline)带进你的 AI 继续追问
这篇文章有一份干净的 Markdown 原文,可以直接交给任何模型读,不用复制粘贴。