4. 系统架构设计
金融研报与财报的RAG问答系统,难点不在“能不能调用大模型”,而在于如何把一份几十页、含大量表格与脚注的PDF,稳定地变成可检索、可追溯、可复现的知识资产。因此架构设计的核心思路是:把整条链路拆成职责单一的模块,每个模块有明确的输入输出契约,并允许独立替换与评测。下面按端到端数据流展开。
一、核心原理:分层解耦与契约驱动
整条链路可划分为六层:采集层、解析层、索引层、检索层、重排层、生成层。层与层之间只通过结构化数据交互,例如解析层输出统一的Document对象,索引层只认这个对象,不关心PDF来自哪里。这样做的好处是:更换PDF解析器不影响检索逻辑,更换向量模型不影响采集逻辑。
关键契约包括:解析层必须保留页码、章节标题、表格结构、来源文件四类元数据;检索层必须返回chunk_id与score;生成层必须能引用chunk_id回溯原文。没有元数据的RAG,在金融场景中几乎不可用,因为合规与投研都要求“答案可溯源”。
二、方法流程:六个模块的输入输出
- 采集层:从交易所、公司IR页面、研报平台拉取PDF/HTML,记录
source_url、publish_date、doc_type。建议用增量哈希去重,避免同一份财报重复入库。 - 解析层:将PDF转为结构化文本。表格必须单独抽取为Markdown或HTML,脚注与正文分离。输出统一
Document对象。 - 索引层:按语义边界切分,金融文档建议按章节+表格行切分,而非固定512字符。切分后调用Embedding模型生成向量,写入向量库。
- 检索层:采用混合检索,向量召回负责语义相似,BM25负责财务术语与数字精确匹配。
- 重排层:用Cross-Encoder对Top-K结果精排,解决“向量召回把相似但不相关的段落排前”的问题。
- 生成层:将重排后的上下文注入Prompt,要求模型输出答案并标注引用编号。
关键参数建议:向量召回Top-20,重排后保留Top-5;chunk大小300~600中文字符,表格chunk可放宽到800;重叠窗口50~80字符,防止跨段语义断裂。
三、案例场景:一份年报的问答链路
用户提问:“宁德时代2023年研发费用同比变化多少?”系统执行:采集层定位到2023年年报PDF;解析层抽取“研发费用”表格行,保留页码与表名;索引层将该行与上下文切为一个chunk并向量化;检索层同时命中“研发费用”关键词与语义向量;重排层将含具体数字的表格行排到第一;生成层输出“同比增长约X%,见第Y页研发费用表”,并附chunk_id。
| 模块 | 输入 | 输出 | 常见失败 |
|---|---|---|---|
| 采集 | URL/文件 | 原始PDF | 重复入库、版本混淆 |
| 解析 | Document对象 | 表格错行、脚注混入正文 | |
| 索引 | Document | 向量+元数据 | 切分过碎、丢失表头 |
| 检索 | Query | Top-K候选 | 数字召回不准 |
| 重排 | 候选集 | 精排Top-N | 延迟高、显存不足 |
| 生成 | 上下文+Query | 答案+引用 | 幻觉、引用错位 |
四、误区与复盘
误区一:只做向量检索。金融问答中大量问题涉及精确数字与专有名词,纯向量检索容易把“营业收入”和“营业成本”混淆。必须加入BM25或关键词过滤。
误区二:解析层偷懒。把整页PDF直接丢给切分器,表格会被切成无意义的碎片。表格必须结构化抽取,并保留表头与单位。
误区三:忽略元数据。没有页码和来源的答案无法审计,投研与合规场景直接不可用。
复盘建议:为每个模块建立独立评测集。解析层看表格还原准确率,检索层看Recall@20,重排层看NDCG@5,生成层看引用正确率与幻觉率。只有分层评测,才能定位问题到底出在“没检索到”还是“检索到了但生成错了”。
# 典型配置片段(伪代码)
retriever:
vector_top_k: 20
bm25_top_k: 20
fusion: rrf
reranker:
model: bge-reranker-large
top_n: 5
chunker:
size: 500
overlap: 60
respect_section: true
table_as_chunk: true
综上,端到端架构的价值在于把不确定性关进模块里:采集保证来源可信,解析保证结构可信,检索与重排保证召回可信,生成保证表达可信。每一层都可替换、可评测、可回滚,这才是金融RAG系统能够长期迭代的基础。