从初步尝试到用于SEC 10-K财报的成熟RAG系统:文本块划分、混合搜索技术。
为什么传统的RAG流程在300页长的10-K财报上会失败,以及相应的解决方案:基于章节划分的文本块处理、混合检索方式、跨编码器重排序、验证机制与元数据引用。
来源: **How I Built a RAG Pipeline on Legal & Financial Documents and What Actually Broke?**
外观正常却返回错误结果的流程
想象一下,为处理SEC 10-K报表而构建的RAG流程,耗时三周才完成,使用了LangChain进行组件整合,ChromaDB作为向量存储,还有简洁的Python数据导入脚本以及精美的Streamlit前端界面。最初的简单测试是询问哪些关键审计事项与收入确认相关,但系统却十分自信地给出了完全无关的关于高管薪酬的答案。用“幻觉”这个词并不完全准确——这些内容确实是真实的,只是来自300页文档中的错误部分,而系统却毫无察觉。
看起来像RAG系统与实际运作起来像RAG系统是两回事。下文介绍了分块决策、检索失败情况、嵌入实验、出于必要而设计的评估集,以及最终将人工审核工作量减少65%的版本。任何基于真实文档而非虚拟PDF开发大语言模型功能的人,都可以省去数周无效的学习时间。
为何难以从10-K文件中检索信息
将10-K文件视为普通PDF是一种低估。一家中型或大型上市公司的此类文件长度在150到400页之间,其中包含财务表格、引用其他脚注的脚注、占据数十页的法律标准文本、风险披露说明以及管理层评论。检索的目标是“关键审计事项”(CAMs),即审计师对其工作中最复杂或需大量判断的部分所做的说明。
读者想要找到的内容,比如风险描述和审计意见,分布并不均匀。它们集中在某些特定章节中,使用专业术语表述,且与脱离上下文就难以理解的数字相关联。如果将文档视为一堆独立的片段,检索系统大多会选错片段,这正是第一个版本所表现出来的问题。
两种并行处理方式
两个版本之间的差异蕴含着所有值得借鉴的经验。
版本1采用的是典型的教科书式设计:首先读取PDF文件,将其切割成500个词元的片段,每个片段之间有50词元的重叠部分,然后使用OpenAI的text-embedding-ada-002模型对片段进行嵌入处理,存储在ChromaDB中;针对查询结果,取出排名前5的片段,再交给GPT-4生成摘要。该方法在处理针对结构清晰文档的简单查询时效果尚可,但在处理一份300页、内容密集的文件时则出现了三种具体问题。
版本2包含了更多处理阶段:PDF导入、段落检测层、根据段落类型调整大小的智能分块、针对每个分块的元数据标注、领域感知的嵌入模型、带有丰富元数据的ChromaDB存储、查询重写、混合检索(语义检索与关键词检索结合)、重新排序、验证,最后才是生成阶段。每一个环节都有其存在的意义,具体内容将在后文阐述。
第一个问题:固定长度的分块会割裂上下文
V1最根本的缺陷在于采用500个标记为单位的固定分块方式,无论内容位于何处都会被切割。而10-K报告本身具有固定的结构,通常由标题、描述、审计师的意见以及财务说明的链接组成,其长度可达600到800个标记。按500个标记分割的话,往往会导致一个CAM报告被一分为二,描述部分在其中一个分块中,审计师的意见在另一个分块中,这样单独来看任何一个部分都失去了意义。
解决方案是在分块处理之前添加一个章节检测层。pdfplumber会解析文档结构以识别章节边界、标题、子标题以及表格边缘,随后在各个章节内部进行分块,而非跨章节分块,并且分块大小会考虑自然断点。对于内容密集的财务表格,会将其拆分为约200到300个标记的小块,以便保持行级细节的可检索性。而诸如管理层讨论与分析、风险披露之类的叙述性章节,则会被拆分为最多800到900个标记的大块,以保持每个逻辑单元的完整性。各分块之间的重叠比例控制在分块大小的10%左右。仅在做出这一项改动后,检索的相关性就有了显著提升,无需再做其他调整。
第二个问题:未能捕捉金融领域语言特征的嵌入模型
text-embedding-ada-002虽是一款性能出色的通用模型,但并未针对金融或法律文本进行专门训练,因此在处理复杂的审计文件时表现不佳。当查询与收入确认相关的判断内容时,该模型会返回有关收入以及一般性判断的片段,这些片段有的来自管理层讨论,有的来自附注,还有少数来自无关的风险披露内容,而真正的收入确认相关内容却无法被可靠地提取出来。
尝试了两种方案。OpenAI的较新模型text-embedding-3-large显著提升了检索质量。另一种混合方法则结合了密集向量搜索与稀疏BM25关键词搜索,并将两种结果合并,其效果优于单独使用任一方法。原因很简单:密集检索能找到概念上相关的内容,而稀疏检索则能找到完全一致的术语。在金融文本中,精确的用词至关重要。“关键审计事项”在法律术语中与“重大审计问题”并非同一概念,同时考虑这两种情况能大幅提高检索的精确度。稀疏检索部分使用了Python库rank_bm25,通过一个简单的互反排名融合函数在重新排序前将两个结果列表合并。
第三个失败点:缺乏验证,导致错误答案的出现
这是最隐蔽也最危险的问题。在V1版本中,从检索结果中获取的错误片段仍会引导大语言模型给出答案,而GPT-4即便在仅有少量上下文的情况下也能表现得极为自信。它的答案并非明显错误,而是对所获取片段的合理总结,即便这些片段来自文档的错误部分。
只有通过一小部分测试数据才暴露了这个问题:共30组问答对,需根据特定文档手动验证,每组都标注了应有的正确来源片段。根据这些标准衡量,V1版本大约55%的时间能检索到正确的来源片段,而剩下的45%则会产生由无关内容构成的流畅文本。
两次修改解决了问题。第一次是添加了重新排序阶段:使用名为ms-marco-MiniLM-L-6-v2的HuggingFace交叉编码器对检索到的十个片段进行重新评分,只有得分最高的三个片段才会进入生成器。交叉编码器的运行速度虽慢于双编码器,但评分精度更高,因为查询内容和候选片段会被视为一个整体输入,而非两个独立的输入。
第二次修改是在生成之前增加了验证机制。该机制会检查检索到的片段是否包含来自数据摄入时元数据中的预期部分标识符。例如,当关于CAMs的查询返回了补偿表中的片段时,验证就会失败,系统会重新编写查询并最多尝试两次,之后才会将响应标记为低可信度。这正是演示版本与生产系统之间的区别:演示版本只覆盖正常流程,而生产系统则需要处理那些不遵循该流程的45%的案例。
每个数据块的元数据
RAG教程中很少提及的一项设计考量是:为每个数据块附加丰富的元数据。ChromaDB集合中的每个数据块都包含以下字段:
section_type,表示该数据块来自10-K报告的哪个部分,如管理层讨论与分析、风险披露、财务报表、脚注等section_title,内容上方的具体标题page_range,该数据块所涵盖的源文档页码范围document_id,文件标识,格式为公司代码加财政年度has_table,用于标识是否存在表格数据fiscal_year,报告所对应的财政年度
元数据发挥了两大作用。其一,它实现了过滤检索:针对审计相关问题,搜索可在向量搜索之前预先过滤到“critical_audit_matter”这一章节类型,从而大幅缩小搜索范围。其二,它为用户界面的引用功能提供了支持,因为每篇摘要都会注明所引用的页面和章节,审计人员可以立即查看原始资料。
在财务审计工具中,引用是必不可少的。如果审计人员无法核实摘要的来源,就无法依赖该摘要;而通过元数据生成引用,使得该系统从单纯用于探索的工具转变为用户真正能够使用的实用工具。
最终的系统
这个将审计审查工作量减少65%的完整流程包含三个层次。
- 数据摄取:使用pdfplumber进行页面分割与块划分,提取元数据,通过text-embedding-3-large模型生成嵌入向量,最后将完整元数据存储在ChromaDB中。
- 查询:先通过大语言模型对查询语句进行重写以提高精确度;在适用情况下根据页面类型通过元数据预过滤;采用密集向量检索与BM25检索相结合的方式筛选出前10个结果;再通过交叉编码器对结果进行重新排序,选出前3个;随后进行验证检查,由GPT-4生成最终内容,附加引用信息并展示在用户界面中。
- 评估:使用包含30个样本的数据集,跟踪检索准确率并评估响应质量;由大语言模型作为评判者,从相关性、准确性及完整性三个方面对结果进行评分。
Streamlit前端为审核人员提供了查询框、生成的摘要、可按需展开的来源引用,以及来自验证步骤的置信度指标。
需要做出哪些不同调整
- 首先构建评估数据集。在V1版本失败后,人们出于无奈才将其拼凑起来,但实际上从项目一开始就应当建立该数据集。没有测量就无法了解改进情况,“看起来更好”并不能作为衡量标准。
- 尽早设计元数据架构。虽然确定正确的元数据架构需要时间,但每投入一小时,就能在数据检索质量以及由此带来的用户界面功能上获得数倍的回报。在系统架构中,元数据应享有优先地位。
- 要考虑到PDF解析器可能会遗漏某些内容。pdfplumber虽然性能优异,但对于金融表格中的合并单元格、旋转的表头或单元格内的脚注标记仍可能处理不当。需记录每一次提取异常,并定期查看这些日志,因为那些悄无声息的提取失败往往是日后最难追踪的错误。
结果
部署后,该系统在提取并总结关键审计事项时,所需的人工审核工作量比原有流程减少了65%。作为初步审核工具,它能自动定位相关内容并生成结构化的摘要,从而使审计人员从数据提取与整合的工作转向验证与判断。
最后这一点最为重要。其目标从来不是取代审计人员的判断力,而是去除那些机械且耗时的步骤,让专业判断力集中在真正关键的地方。设计良好的大型语言模型系统不会取代专业能力,而是引导专业能力发挥更大作用。
相关阅读
- AegisOps:具有四层记忆结构的自主式SRE代理 —— 介绍这款基于LangGraph ReAct架构的代理如何利用pgvector的情景记忆、策略控制的自主级别、十六种工具以及完整的审计追踪功能来解决Kubernetes系统故障。
- 16种RAG架构对比:不同模式适用于何种问题 —— 梳理了分为四个层级的十六种RAG架构,涵盖从标准及混合检索到代理式、图结构及联邦式设计等多种类型,并说明了逐月的构建顺序。