財務報表提取
將報告PDF轉換為經過檢查的事實:financial_fact(document, period, label, scope, value, unit, audit status)。
已在可倫坡證券交易所兩年的申報文件(2,707份報告、720,000個數字,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(隔離模式)和 CSE_OCR=0 執行 Python,除非需要 OCR(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(約 9 分鐘處理 2,700
份報告),然後針對掃描件進行單獨的 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 行和結果檔案的原因)。對於每個未載入的報表,檢查
相同的數字是否已從另一份報告(下一年度的比較數字、對應的中期報告)載入 - 通常
是的,這會將"缺口"轉變為"已涵蓋"。將結果記錄在專案文件中。
Token 與時間經濟
- 先分流;從不發送工作者去"找出問題"而沒有原因和頁面提示。
- 先文字圖層再圖片:乾淨的文字圖層可以免費確認數字;圖片用於確定佈局。渲染 1500 px;裁切
(
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 發布,並附有備份、審計乾淨(或所有標誌已解釋)、測試通過,以及在專案文件中加上結果表與帶日期的註釋。