Community研究与数据分析github.com

AdhishaInd/financial-statement-extraction

从公司财务报告PDF(包括年报、季报及扫描件)中提取利润表、资产负债表和现金流量表数据,并存入关系型数据库,通过算术校验确保仅可信数据入库。

financial-statement-extraction 是什么?

该技能专注于从公司财务报告PDF中提取经校验的财务报表数据。其核心方法是:自动解析器处理大部分报告,算术校验决定哪些数据可信,分诊步骤明确指出剩余报告失败原因,然后由子代理批量从页面图像中读取这些失败报告。每次更改仅通过分阶段加载和审计才能到达已发布的数据库。该技能已在科伦坡证券交易所(CSE)两年多的文件档案中经过验证(2707份报告,72万个数据点,99.4%完全加载)。它支持多种平台(包括CSE),适用于用户需要从PDF中提取、加载、恢复或审计财务报表数据,构建或扩展财务数据库,处理部分/失败报告,从图像转录报表页面,批量运行子代理处理多份报告,或检查报告未加载原因等场景。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/AdhishaInd/agent-skills/tree/HEAD/skills/financial-statement-extraction

Installed? Explore more 研究与数据分析 skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

财务报表提取

将报告PDF转化为经过校验的事实:financial_fact(文档, 期间, 标签, 范围, 值, 单位, 审计状态)。 在科伦坡证券交易所(CSE)两年多的文件档案中构建并验证(2707份报告,72万个数据点,99.4%完全加载)。 其方法使其高效:自动解析器处理大部分报告,算术校验决定哪些数据可信, 分诊步骤明确指出每个剩余报告失败的原因,然后由子代理批量从页面图像中读取这些报告。 每次更改仅通过分阶段加载和审计才能到达已发布的数据库。

一切依赖的唯一规则

只有当数据列的算术校验通过时(资产 = 权益 + 负债;税前利润 - 税额 = 利润;归属母公司股东 + 少数股东权益 = 利润;现金流各分节之和等于净变动;期初 + 变动 = 期末),该数据才会被加载。 不会进行任何调整以使校验通过。校验仅允许印刷舍入误差(最后一位印刷数字的单位数:两项时为2,三项时为3,超过三项时为5),因此总数与其组成部分相差1-2个单位可视为通过,更大差异则为读取或印刷错误。 这就是解析器、OCR引擎、视觉转录和人工操作都能向同一数据库输入数据的原因:由校验(而非来源)决定。当校验失败时,要么是读取错误(重新读取),要么是印刷错误(记录原因,忽略该数据)。

实践中重要的推论:

  • 标签保持原样印刷,仅通过确切别名(label_alias)映射到标准行项目,从不使用模糊匹配。印刷错误的标签会使语句无法加载,而不是静默地产生错误数据。
  • 没有标签的合计行可能获得带方括号的标签(如[Total assets]),因其位置定义了它。
  • "UNCHECKED"(无法校验)不等于"FAIL"。通常表明检查器尚不认识的某种布局 - 一次性修复检查器,之后所有具有该布局的报告都能加载(参见 references/checks.md)。

文件位置

  • scripts/pipeline/ - 流水线:parse_report.py(PDF -> JSON表格),segment.py,columns.py, tokens.py,checks.py,ocr_words.py(OCR作为伪文本层),load_facts.py(JSON -> DB,仅通过校验的列), vision_tables.py(图像转录 -> JSON表格),extract_all.py,normalize.py。 映射、数据形状以及如何适配其他市场:references/pipeline.md。
  • scripts/triage.py,scripts/make_batches.py,scripts/render.py,scripts/stage_publish.py - 以下步骤的辅助工具。
  • assets/schema.sql(数据库),assets/dictionary.sql(252个标准行项目 + 471个精确标签别名), assets/worker_brief.md(转录工作者简报),assets/examples/(转录文件示例)。
  • 项目目录:数据库、PDF和输出文件存放的位置。用户在 CLAUDE.md / AGENTS.md 中指定(例如 "Financials project: ~/stocks/cse_financials");如果未指定则询问。布局:db/<name>.db,reports/<SYMBOL>/<doc>.pdf,解析器输出 extracted_final/,已发布源 extracted_final_v/, 转录 reviewed/vision/,笔记 docs/。如果项目有自己的 pipeline/,则将其视为当前代码(此技能的副本可能更旧)。

始终使用 -I(隔离模式)运行Python,除非需要OCR则设置 CSE_OCR=0(CSE_ 前缀是历史遗留)。 报告的PDF:select local_path from report_document where document_id=?(相对于项目目录),然后运行 python -I scripts/render.py <pdf> <pages> <out_dir> --text 转储文本层并渲染页面。

工作流

1. 确定范围并下载

选择期间范围;从报告标题中获取期间结束日期(交易所备案日期不可靠)。填充 report_document 行(每行一份报告:公司,类别 ANNUAL/INTERIM,标题,期间结束,来源URL),然后 并行且支持断点续传地下载。交易所特定部分(API,URL,特殊规则)属于配套技能 - cse-financials 用于科伦坡。将每个年份块作为这些步骤的独立运行。在新市场或新年份之前,请阅读 references/traps.md:它列出了最耗时的数据陷阱。

2. 使用同一解析器版本解析所有内容

python -I scripts/pipeline/extract_all.py <db> <out_dir> --workers N 并使用 CSE_OCR=0(约2700份报告 需9分钟),然后对扫描件进行单独的OCR处理(如可用则使用GPU;参见陷阱)。解析器更改后,重新解析所有 报告并逐报告进行比较 - 部分重新解析会使数据库不可重现。

3. 加载并检查状态

load_facts.py(通过 stage_publish.py,步骤6)为每份报告指定状态:EXTRACTED(利润表和资产负债表已加载)、PARTIAL 或 FAILED。预计仅解析器就能处理大约80%的报告为 EXTRACTED。

4. 分诊剩余报告 - 先于查看任何页面

python -I scripts/triage.py <db> <extracted_dir> triage.json --pdf-hints
python -I scripts/make_batches.py triage.json batches/ --target=12

分诊给出每个缺失语句的原因(not_found,UNCHECKED,FAIL,no_columns,no_months, unit_unproven,pass_refused...)以及查看位置。阅读统计:具有相同原因的大簇(例如100个UNCHECKED) 通常意味着检查器缺口或加载器规则 - 修复代码比转录数百页更有效。同时检查 FAILED 的报告是否实际上是报表: 通知、股东名单和勘误表很常见,只需记录原因。

5. 从图像转录,分批进行

每个工作者获取一个批次文件和 assets/worker_brief.md(填写路径)。它找到报表页面, 读取(先文本层,然后通过图像确定布局和任何可疑内容),写入 reviewed/vision/<doc>.txt,在其文档上运行 vision_tables.py,直到每个数据列都通过PASS,然后写入 bNN.result.json。工作者需要能够读取图像的模型。 格式和规则:references/vision-format.md。并行运行工作者、使用限制、恢复和审查结果:references/orchestration.md。 没有子代理时,按照相同简报一个接一个地处理批次。

批处理运行时,随时阅读到达的每个结果摘要:

  • 重复出现的 "UNCHECKED because the checker does not recognise X" -> 扩展 checks.py(references/checks.md), 用 vision_tables.py 重新测试受影响的文件,将规则追加到简报中以便后续批次知晓。已写入的表格随后在不重新转录的情况下通过。
  • 判断性决定(审计状态、重述列、异常期间长度、提供的标签)-> 检查是否符合简报;集中纠正(例如对COLS行使用sed) 而不是重新运行工作者。

6. 暂存、审计、发布

python -I scripts/stage_publish.py --project <dir>            # 仅暂存和审计
python -I scripts/stage_publish.py --project <dir> --publish  # 读取审计后

如果审计显示报告离开EXTRACTED状态、报告变为空、完整性/外键错误、出现新的单位异常值,或者在没有转录文件的报告中 丢失/更改了数据(意味着代码修改接触了无人重新读取的数据),则拒绝发布。非视觉报告的增加是正常的(已解决的冲突 释放了另一报告的列),但需要逐一检查。分阶段发布(先发布完成的批次)以便工作能承受中断;然后运行项目的测试(如果有)。

7. 关闭所有报表

目标是:每个缺失的报表要么已加载,要么有明确说明的原因。构建每个报表的结果表 (已加载 / 未加载 + 来自 MISSING 行和结果文件的原因)。对于每个未加载的报表,检查 相同数据是否已从另一报告加载(下一年的比较数据、匹配的中期报告)- 通常是的, 这将“缺口”转化为“已覆盖”。在项目文档中记录结果。

代币和时间经济性

  • 先分诊;切勿在没有原因和页面提示的情况下派工作者去“找出问题”。
  • 先文本层后图像:干净的文本层免费确认数字;图像确定布局。渲染1500像素;裁剪(render.py --clip) 而不是渲染整页的大图像。带有垃圾文本层("9,O4349L")的扫描件需要使用图像。
  • 按公司分批:一种布局,多个季度。每批次约10份报告或约12个成本单位,同时约6个工作者。
  • 工作者即使在校验仍为UNCHECKED时也写入完整表格,外加一行 MISSING 行:如果之后扩展了检查器, 表格无需重新读取即可加载。
  • 在代码(检查器、加载器)中一次性集中修复系统性问题;重新运行 vision_tables.py(只需几秒) 而不是重新读取页面。
  • 协调器不查看页面图像,除非解决争议;它读取摘要、运行校验和审计。
  • 工作者摘要保持在3-6行;详细信息放在文件注释中。

完成定义

每份报告:EXTRACTED,或者PARTIAL/FAILED,每个缺失报表附有命名印刷证据的原因 (例如 "printed total 3,115,995,977 vs equity + liabilities 3,115,995,968"),或者 "covered by report N"。 仅通过 stage_publish.py 发布,附带备份,审计干净(或每个标志都已解释),测试通过,并且在项目文档中 包含结果表及带日期注释。

相关技能