銀行の明細書を開くことは、誰もが望むような楽しい作業ではありません。スキャンされたPDF、CSVエクスポート、あるいはOFXのような難解な略称で飾られたXMLファイルとして届きます。会計士、簿記担当者、そしてフィンテックの開発者にとって、これらの文書をクリーンで構造化されたデータに変換することは、常に頭の痛い問題です。大規模言語モデル(LLM)が登場したとき、それは救いの手のように見えました。機械にPDFを読み込ませ、JSONで出力するよう頼むだけ。一体何が悪くなるというのでしょうか?
私は、銀行明細書を実用的なデータに変換するために設計されたツール「StatementDecoder」を構築している最中に、何が起こり得るのかを身をもって学びました。多くの開発者と同様に、私は多様な文書レイアウトをシステムに読み取らせる方法を教えることが難しい部分だと考えていました。しかし、それは間違いでした。文書を読み取ることは、ほとんど些細なことでした。本当の悪夢は、機械が密かに数字を捏造したり、取引金額の2桁を入れ替えたりしたときに、それを認識することでした。
うまくいきすぎたデモ
私の最初の試みは、抗いがたいほどシンプルでした。銀行明細書を直接LLMに流し込み、その見返りに構造化されたJSONを要求しました。結果は魔法のようでした。モデルはさまざまなレイアウトを容易に処理しました。標準的なパーサーが立ち往生するようなスキャン済みPDFも読み取ることができました。明示的な指示がなくても、表、ヘッダー、そして複数ページにわたる明細を理解しているようでした。輝かしい数時間、私は問題が解決したと思いました。
しかし、実際の顧客データでテストしたところ、魔法は消え去りました。イギリスの銀行はそれぞれ独自の明細書デザインを使用しており、その違いは単なる見た目の問題ではありません。Wiseの明細には独自のフォーマットの癖があります。RevolutのCSVエクスポートは、多通貨取引やメタデータフィールドの扱い方に気づくまでは、一見単純に見えます。1990年代のものに見える古いOFXファイルは、現代的なマークアップを期待するパーサーに対して、古めかしいタグ構造やエンコーディングの問題を突きつけてきます。
モデルは依然として、市販のテンプレートシステムよりもはるかに優れたデータ抽出を行いました。しかし、お金が絡む場合、「はるかに優れている」だけでは不十分なのです。
99%の精度が失敗となる時
金融データの抽出にAIを使用することの根本的な問題はここにあります。モデルが200行の取引行を処理し、199行が正しければ、出力は完璧に見えます。JSONは整っており、キーと値も一致しています。ざっと確認しただけでは、何も怪しい点はないように見えるでしょう。しかし、そのたった一つのエラーが金額の2桁を入れ替えたり、入金を引落しに変えたり、あるいは小数点をずらしたりした場合、帳簿は台無しになります。構造化されたデータの塊を目視するだけでは、それを見つけることはできません。
生のJSONを確認する人間が、取引金額の入れ替わった数字を見つけることはめったにありません。フォーマットが完璧であるため、逆説的にその間違いはより危険なものになります。「ほとんどの場合は正しい」という金融ツールをリリースすることはできません。常に正しいか、あるいは確信が持てないことをはっきりと宣言しなければなりません。
私の最初の反応は予想通りのものでした。より優れたプロンプトを設計し、より高性能なモデルにアップグレードしました。モデルに思考プロセスを示させるために chain-of-thought 推論も試しました。しかし、どれも根本的な問題の解決には至りませんでした。私は、確率的なシステムに対して答えを生成させ、その全く同じシステムに対してその答えが正しいことを証明させていたのです。それは検証ではありません。「自己整合性の演劇(self-consistency theater)」に過ぎません。
数学に判断させる
銀行明細には、ほとんどの文書にはない特徴があります。それは、組み込まれた算術的な制約です。期首残高にすべての取引の合計を加えると、期末残高と一致しなければなりません。累計残高がある場合は、行ごとに一致していなければなりません。これらはスタイルの好みではなく、厳格なルールです。
私はこの洞察に基づいてアーキテクチャを再構築しました。現在では、抽出元がどこであれ、すべての抽出データはユーザーの目に触れる前に検証レイヤーを通過します。データが、曖昧なPDFを解釈するLLMから来たのか、スキャンされたページを読み取るOCRエンジンから来たのか、あるいは直接的なCSVパースによるものなのかは関係ありません。検証器は、すべてのソースを等しく疑わしいものとして扱います。
チェックは残酷なほどシンプルです。期首残高にすべての取引を加算します。その結果を記載されている期末残高と比較します。数字が一致しなければ、何かが間違っています。その明細をレビュー対象としてフラグを立て、抽出を拒否します。ユーザーに届けてはいけません。
この一つの変更が、製品の性質を完全に変えました。言語モデルはもはや完璧である必要はありませんでした。数学的な試練を生き残れるだけの出力を生成できれば、それで十分だったのです。プレッシャーは、「制約のない領域で不可能な精度を達成すること」から、「生成と検証の間の緊密なフィードバックループを構築すること」へと移りました。
バリデーターによって、エラーのパターンも浮き彫りになりました。特定のドキュメントタイプが計算チェックで一貫して失敗しており、どこに注力すべきかが明確になりました。全体的にプロンプトエンジニアリングを盲目的に改善するのではなく、特定の銀行のレイアウトが体系的なミスを引き起こしていることが分かりました。
コードはコードのあるべき場所に、AIは真価を発揮できる場所に
おそらく最も身に染みた教訓は、パイプラインの大部分にAIが全く必要ないという事実でした。乱雑なオーストラリアのOFXファイルに遭遇したとき、私の本能はトークンを投入して解決しようとするものでした。パースする前に、壊れたXMLをモデルに渡して構造を修復させることも一瞬考えました。しかし、代わりに20行の決定論的なコードを書きました。それがエンコーディングの癖や不正なタグを即座に修正し、ファイルあたりのコストはゼロで、完璧な再現性を実現しました。
その経験によって、抽出パイプラインをどのように構成すべきかが明確になりました。そこには3つの異なる役割があり、それらを混同すべきではありません。
- モデルは乱雑なドキュメントを理解する。 表が歪んだスキャン済みPDF、混在したフォント、手書きの
