ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

金融财报问答大模型落地:从PDF解析到RAG的完整工程链路

金融财报问答大模型落地:从PDF解析到RAG的完整工程链路 简介金融财报问答大模型LLM.zip 是一套面向金融领域智能问答场景的大模型应用资源适合需要快速解析财报、构建问答系统的开发者与研究人员。压缩包内共42个文件涵盖22个Python源码、6个Shell脚本、3个JSON配置、2个YAML配置及README、LICENSE等文档整体大小约54KB。Python文件覆盖意图识别、信息抽取、相关性打分、实体识别、开放问答与答案生成等模块Shell脚本用于数据下载、模型下载、服务启停与微调训练配置文件对应训练、服务与推理环境目录结构清晰便于本地部署和二次开发。资源结合多模态理念可处理财报文本与图表等非结构化信息并对接chatglm2、text2vec等模型方案。目前已有390人学习使用适合入门大模型微调、金融文本解析及检索增强生成的开发者快速上手作为理解完整处理链路与工程化脚本的参考。1. 金融财报问答大模型LLM.zip这个压缩包装的不是模型是读财报的完整工程链路做企业服务的人遇到“金融财报问答大模型LLM.zip”这种交付物第一反应往往是“又是个打包好的模型文件解压就能用”。真解压过几个类似工程包之后我的判断完全不同财报问答不是一个模型能解决的问题它是一套从PDF解析、指标对齐、检索增强到生成约束的工程链路。压缩包里装的如果只是权重文件那是最省事的版本但凡带数据脚本、评测集或服务代码你面对的其实是别人把踩过的坑打包好了。这篇笔记就把这条链路拆开讲数据怎么从财报里抽出来、问答用什么架构落地、哪些环节最容易隐藏翻车点以及怎么验证这包东西真的值得你投入进去。2. 先搭数据基座把财报PDF转成能喂给大模型的问答语料2.1 财报解析的硬骨头不在文本在表格和数字任何金融财报问答系统第一步都是把PDF里的内容变成结构化文本。做过的人都知道招股书和年报的PDF排版千奇百怪双栏、页眉页脚、跨页表格、合并单元格、数字后面的单位注释每一项都能让解析脚本崩溃。通用PDF解析库比如PyPDF2、pdfplumber对纯文本段落的效果尚可但到了三大报表和附注里的表格问题就来了——表格结构丢失、列错位、数字被换行拆开。我一般会用pdfplumber做表格抽取它是少数能输出表格结构信息的Python库。核心参数有两个table_settings里的vertical_strategy和text_shotgun。前者控制竖线检测策略默认lines只认真实画出来的线碰到无框线表格就废改成text模式则靠文本对齐推断列边界适合免费版财报PDF那种“伪表格”。后者控制是否把被拆散的文本碎片合并财报里最常见的“营业收入 1,234,567”被拆成两个单元格的案例开这个就能救回不少。import pdfplumber with pdfplumber.open(annual_report_2024.pdf) as pdf: for page in pdf.pages: tables page.extract_tables({ vertical_strategy: text, # 无框线表格用文本对齐推断列 horizontal_strategy: lines, text_shotgun: True, # 合并被拆散的同一逻辑单元 snap_tolerance: 3, # 列对齐容差(px)PDF渲染差异大时可调大 }) for table in tables: # 每行是一个list先过滤空行再落库 for row in table: if any(cell and cell.strip() for cell in row): cleaned [cell.replace(\n, ) if cell else for cell in row] print(|.join(cleaned))这段代码的逻辑是这样逐页提取表格vertical_strategy用text而不是lines是应对年报里大量使用空格排版而非真正画线的表格snap_tolerance控制列边界合并的像素容差不同PDF渲染器比如东财、巨潮的公告偏差很大我通常从3开始试解析出的列错位明显就加到5。cleaned这一步把单元格内的换行符去掉否则后续喂给LLM时会出现“营业收入”和“1,234,567”中间隔着\n影响embedding切分效果。2.2 把表格变成问答对规则生成比手工标注靠谱表格抽出来只是第一步。问答系统需要的是“问题-答案-来源页码”这样的三元组。手工标注几千条金融问答对的成本极高所以常见做法是用规则从结构化表格里批量生成。财报表格的结构高度规律行是报表项目营业收入、净利润、经营活动现金流列是报告期2023年、2024年或本期、上期。这种二维结构天然适合模板化生成。import re def generate_qa_from_table(table, page_num, fiscal_year): 从单张财报表格生成问答对返回(list of dict) qa_pairs [] # 假设第一行是表头[项目, 2023年, 2024年] header table[0] year_cols {} for i, col in enumerate(header): m re.search(r(20\d{2}), col or ) if m: year_cols[m.group(1)] i for row in table[1:]: item_name row[0] if not item_name: continue # 只保留数值型单元格去掉带“%”和“—”的噪声 for year, col_idx in year_cols.items(): raw row[col_idx] if col_idx len(row) else if raw and re.match(r^[\d,\.\(\)\-]$, raw): qa_pairs.append({ question: f{fiscal_year}年财报中{item_name}的{year}年金额是多少, answer: raw, source_page: page_num, meta: {item: item_name, year: year}, }) return qa_pairs这段代码的关键在于用正则从表头里识别年份列而不是硬编码列位置——因为不同券商的财报PDF表头顺序不一样。数值单元格的过滤条件^[\d,\.\(\)\-]$看着简单实际能挡掉大量“-”无数据、“0.5%”比率这类非绝对金额的噪声。生成的问答对会带上source_page这个字段在后面做RAG检索时非常有用它既是召回答案的证据链也是做人工抽检时定位原文的索引。做完这一步你手里应该有一批几千条级别的“问题-答案-页码”三元组。这批数据有两个用途一是直接作为微调语料二是作为RAG评测集。我建议先别急着决定用哪个往下看架构选型再定。3. 选型与RAG架构财报问答不是只有微调一条路3.1 微调还是RAG先看你的查询属于哪种类型拿到语料之后最常见的灵魂拷问是“要不要微调”。我的经验是先给问题分类再决定。财报问答的查询大致能归成三类事实检索型“2024年营业收入是多少”、计算推理型“毛利率同比变化了几个百分点”、总结归纳型“管理层对明年行业格局怎么看”。事实检索型占绝大多数这类问题用RAG就能解决得很好答案直接来自原文自带证据链计算推理型需要模型具备数值运算能力RAG配上思维链提示词也能应付总结归纳型才真正需要调优模型来增强语义理解。微调的成本和风险都在数据侧金融语料标注贵而且一旦微调数据里混入错的数字模型会一本正经地胡编——这在金融场景里是不可接受的。所以常见做法是先RAG后微调RAG解决事实回答微调只用来优化回答风格和数值计算格式。这也是为什么很多交付包里同时有retrieval/和finetune/两个目录的原因它们本来就是两层东西。3.2 最小可用的RAG管线嵌入、召回、重排、生成四段式一个能跑通的最小RAG管线包含四段文档切分、向量化、召回、生成。财报文档的切分粒度很讲究——按固定字符数硬切会切断表格行的完整性我一般会按“章节-表格”结构切先用正则识别“第十节 财务报告”这类章节标题再在章节内部按表格边界切分。这样每个切片的语义是完整的不会出现“营业收入”在上一块、“1,234,567”在下一块的情况。from langchain.text_splitter import RecursiveCharacterTextSplitter # 按章节/表格边界切分优先保结构完整 splitter RecursiveCharacterTextSplitter( separators[\n\n第十节, \n\n第.*节, r\n\|, \n\n], chunk_size800, chunk_overlap100, is_separator_regexTrue, ) chunks splitter.split_text(report_text)切分后的每个chunk送入embedding模型转成向量。股票财报场景不建议用通用中文embedding模型因为财报术语“归母净利润”“扣非净利润”“少数股东损益”在不同模型上的表征差异很大。我在生产环境里常用bge-large-zh-v1.5做base它在金融语料上的测试效果稳定支持中文长文本输出维度1024Faiss索引的构建和检索压力可控。召回阶段要特别注意财报里同一个指标出现在多个地方合并利润表、母公司利润表、附注向量检索会把它们全部召回来不做区分就会造成答案张冠李戴。我的做法是在召回后加一个规则过滤器——把问题里出现的年份和报表项目名提取出来跟chunk的元数据做精确匹配过滤掉年份不匹配的段落。这个过滤器的价值不亚于向量检索本身它能直接用元数据兜底向量召回的误差。生成阶段最重要的一点是“让模型老实”。我用的提示词会强制模型两个行为一是只根据给定的上下文作答二是回答结尾必须附上上下文来源页码。前者抑制幻觉后者让系统具备可追溯性——这在金融场景里是刚需分析师问完问题第一反应是“你引用的原文在哪一页”。4. 避坑记录数字幻觉、表格断裂与上下文越界的血泪经验4.1 数字张冠李戴合并报表和母公司报表的同一指标被混用现象问“2024年归属母公司股东的净利润”模型答出的数字来自母公司利润表而非合并利润表两个数字相差几倍。原因切分后的chunk里合并利润表和母公司利润表都包含“归属于母公司股东的净利润”行向量相似度打分区分不了这两张表的语义差别两个chunk都被召回了。模型在生成时选择了相似度排名靠前但实际语义错误的那一个。解决在切分chunk时把表格所属的报表类型合并/母公司写进元数据生成阶段的提示词里明确要求“优先采用合并报表数据除非问题明确指向母公司”。这种元数据级的干预比在prompt里说一百遍“请注意区分”都管用。4.2 表格跨页断裂同一个表格被PDF解析切成两半现象检索到的chunk只有一张表的上半部分营收项在页尾“1,234,567”这个金额排在下一页页首LLM最终答出“找不到对应数据”。原因财报PDF中长表格跨页是常态pdfplumber逐页提取时把同一张逻辑表拆成了两个物理表格切分脚本又没有做合并导致语义断裂。解决解析阶段结束后对相邻两页的表格做“表头一致性检测”——如果下一页表格的表头与上一页相同就把两页数据拼成一张表再切分。这个检测用表头行的归一化文本是否相等判断就够了不用引入复杂模型。踩过这坑之后我再也没信任过任何“逐页提取、直接切分”的流水线。4.3 上下文越界财报太长截断策略把关键数字截掉了现象问一家大型银行的资本充足率模型说“根据上下文未提及该指标”但全文明明有。原因年报动辄几百页送入LLM的上下文窗口有限截断策略是“从头开始取前N个字符”。而资本充足率这种监管指标往往在后面的“风险管理”章节前面的章节全是公司治理和业务综述关键信息被截掉了。解决不按文档顺序截断按召回结果组织上下文。先向量检索出与问题相关的chunk按相关性排序后拼接再截断到模型窗口大小。这个顺序很重要——先召回、后拼接、再截断而不是先截断、后检索。很多RAG框架默认了后者在长文档场景里就是个坑。4.4 模型对负号和小数的处理不一致现象问“2023年净利润同比变化”模型回答“增长15.2%”实际是“下降15.2%”——负号丢了。原因PDF解析时负号可能以全角“−”出现正则过滤时[\d,\.\(\)\-]没匹配全角字符导致负数变成正数或者LLM在生成时对“−15.2%”和“15.2%”的处理态度不够严肃。解决解析阶段做一次全角转半角把−U2212、全角减号统一替换成-生成阶段在提示词里加一条“如果数值为负在答案中显式写出‘下降’或‘负增长’”。这类字符规范化问题不解决后面做评测时你会发现数字精确率始终卡在90%上不去找半天原因在字符编码上。5. 把问答质量量化用LLM as Judge守住每一次迭代的底线前面把RAG管线搭起来了但“能用”和“能上线”之间还差一个评测闭环。没有评测集的大模型项目迭代几次之后就不知道改成什么样了。我从第一个财报问答项目开始就养成的习惯是每次改动换embedding模型、调切分参数、改提示词都跑一遍固定的评测集。评测集不用大100条足够——50条事实检索型、30条计算推理型、20条总结归纳型覆盖三大报表的核心指标和附注里的关键披露项。每条评测样本预先标注好“标准答案”和“答案所在页码”后者是判断召回质量的关键。打分环节我用LLM as Judge的方案让一个大模型当裁判给答案打三个维度的分——数值精确性是否和标准答案一致允许数值四舍五入的偏差、来源可溯性答案引用是否来自正确的页码、无幻觉率是否只根据上下文作答没有编造上下文外的内容。judge_prompt 你是金融财报问答的质检员。请对以下回答进行评分每项0-5分 [标准答案]{ground_truth} [模型回答]{model_answer} [引用页码]{cited_page} 评分维度 1. 数值精确性回答中的数字与标准答案是否一致 2. 来源可溯性引用页码指向的原文是否包含回答中的关键信息 3. 无幻觉率回答内容是否超出给定上下文 输出JSON格式{{numeric_accuracy: 0-5, traceability: 0-5, no_hallucination: 0-5}}用这个prompt跑完100条评测样本你会得到三个指标的均值。我自己的经验阈值是数值精确性低于4.5说明解析或召回有问题而不是生成的问题——先去查PDF解析和切分别急着改提示词来源可溯性低于4.0说明元数据页码、报表类型维护不到位优先排查切分时有没有把页码写进chunk。跑过一次这种全流程评估后你再看包里的评测脚本和样例数据会比我刚拿到手时的理解深刻得多。做这个方向的最后一件事是给评测集留一份“黄金版本”每条样本的标准答案、来源页码、所属报表类型都人工核对过任何人改动RAG管线后用它回归。这样你就不是在做一次性的项目交付而是在建一个能持续演进的产品底座。希望这份walkthrough能帮你把那个zip里的东西变成自己手里真正能迭代的体系。本文还有配套的精品资源点击获取
返回列表