前七天講的是架構、成本、資料庫、資料模型、定價。從今天起要講的是它最花時間的部分:把一份財報 PDF 變成 LLM 讀得懂、而且數字不會騙人的 Markdown。
今天先把地圖畫出來。四個坑,每個都給一句「為什麼難」,先不展開——展開是接下來六天的事。
一般的程式錯誤會丟例外、會有 stack trace、會讓排程亮紅燈。財報 PDF 的坑不是這樣。它們絕大多數是「看起來成功了」:
在 PDF 解析裡,「有沒有報錯」跟「對不對」幾乎是兩件無關的事。 你必須自己建立判別機制。
下面每個坑,我都會標「能不能自動判別」。
資產負債表、損益表、現金流量表看起來是表格,但在 PDF 裡多半不是。
PDF 的內容是一連串繪圖指令,它沒有「表格」這個概念。所謂的表格,是「很多條水平線 + 垂直線 + 一堆各自獨立的文字片段」在視覺上湊出來的。那些文字片段只知道自己的座標,不知道自己在第幾列第幾欄。
為什麼難:結構不存在,只能從視覺線索反推;而且線索本身不統一——畫同一條線,PDF 至少有兩種寫法。
能不能自動判別:可以,但要自己寫偵測器,而且規則會一直漏。我們實測的表格偵測命中率,從第一版的 47% 做到 96%(Day 9、Day 10 細講)。
這是四個坑裡最陰的一個。
有些 PDF 嵌入字型時用的是 Identity-H 編碼,裡面存的是**字形編號(GID)**而不是字元;字型檔裡有字形外框,卻沒有「這個編號對應哪個 Unicode」的對照表(ToUnicode CMap)。沒有對照表就沒有反推的依據,PyMuPDF 這類函式庫只能把編號原樣當成碼位回傳——抽出來就是一片落在西里爾、泰文區段的亂碼。
為什麼難:程式不會報錯。文字層存在、頁數正確、字數甚至比正常文件還多,只是一個字都不能讀。而且不是整批中招——同一批來源裡,用 A 字型的完全正常,用 B 字型的全毀。
能不能自動判別:可以。亂碼的異常碼位佔比動輒三、四成,正常文件在 0.5% 以下。我們實測某一類文件亂碼率 42%;台灣 MOPS 那批華康字型造成約 29% 不可用(約 13% 亂碼 + 約 16% 空白)。(Day 11 細講)
有些比較舊的文件是真的掃描件——整頁一張圖,沒有文字層,也就沒有東西可以抽。
為什麼難:一是比例不大(約 4%),但跳過它,使用者一搜就會發現「這家公司的報告怎麼是空的」;二是 OCR 慢——1.36 秒/頁,跟規則引擎的毫秒級差三個數量級,只能補洞不能當主力。
這也是我最後沒有全量換視覺模型的原因:視覺模型逐頁跑神經網路,比規則引擎慢大約 100 倍,GPU 只能補回 3 到 5 倍。全量替換永遠不划算。(Day 13 細講)
元、千元、百萬元,同一批文件裡混著用,不同公司、不同市場習慣都不一樣。日本的「円 / 千円 / 百万円」也是同一回事。LLM 不會自己判斷——你餵它「1,234(萬元)」,它會當成 1,234 元。
為什麼難:這是四個坑裡唯一一個解析完全成功、資料還是錯的。它不產生亂碼、不少行、不留白,只讓你的數字錯三個數量級。
能不能自動判別:只能把文件開頭那句「單位:元」抽出來,顯式標進 Markdown 頭部,讓下游不用猜:
> 單位:元 幣種:人民幣
這個坑沒有技術解法,只有流程解法。(Day 14 細講)
| 坑 | 表象 | 為什麼難 | 能自動判別嗎 |
|---|---|---|---|
| 一、向量線條表格 | 表格不見了 | 結構不存在,只能憑視覺線索反推 | 可以,但要自己寫偵測器 |
| 二、無 ToUnicode | 整篇亂碼 | 沒有對照表,且程式不報錯 | 可以,看異常碼位佔比 |
| 三、掃描件 | 整篇空白 | 沒有文字層,OCR 慢三個數量級 | 可以,但只能靠 OCR 補 |
| 四、單位混用 | 數字錯三個數量級 | 解析成功,錯得隱蔽 | 只能靠顯式標註 |
除了第四個,其他三個都是「內容變少或變亂」,只有第四個是「內容看起來完全正常」。 也因為這樣,第四個反而最好處理:只要多讀一行字。真正難的是一、二、三。
PARSER_VERSION 與重轉的取捨。另外還有兩個坑在採集端——更正公告的版本處理,以及「擋流量時回 200 卻塞錯誤頁」的靜默失敗,留到第三週。
明天從第一個坑開始:你的表格不是表格,是向量線條畫出來的。
本文為 2026 iThome 鐵人賽連載第 8 篇。系列主題:把東亞財報 PDF 煉成 LLM 讀得懂的 Markdown。