中級 11 分金融

コンタクト:抽出の factures

端的な回答

ローカルで請求書から情報を抽出するには、Ollamaで視覚モデル(Qwen 3.5 9B、6.6 GB、またはGemma 4)を提供し、応答をJSONスキーマに従わせたうえで、コードで検証してください。確認するのは、税抜金額(HT)+付加価値税(TVA)=税込金額(TTC)、SIRET、日付です。検証に失敗した請求書は、人間による再確認に回します。2026年9月以降、構造化された電子請求書はAIを使わずに受け取れるため、このパイプラインは通常のPDFやスキャンを対象とします。

手で領収書を入力するのは遅く、誤りが生じやすいですが、オンラインサービスに会計資料を委託するとプライバシーの問題が生じます。ローカルなビジョンモデルはページを読み取り、スキーマが結果の形式を定め、決定論的なチェックが誤りをフィルタリングします。本ガイドでは、PDFの分類から会計データのインポートまでを完全に構築し、2026年9月から電子領収書がどのように変化するかを再確認します。

著者 Mohamed Meguedmi·更新 2026-09-30·Windows・macOS・Linuxでテスト済み

#自動化する内容と、その信頼性のレベル

仕入先の請求書PDFから、会計ソフトに一括インポートできる構造化JSON(仕入先、請求書番号、日付、税抜金額、VAT額、税込金額、明細行)を取得することを目指します。Ollama経由で提供されるビジョンモデルが、別途OCRを使わずにページの画像を直接読み取ります。この構成の信頼性を支えるルールは、「モデルが読み取り、コードが検証する」の一言に尽きます。抽出結果は、計算と識別子の検証に合格しない限りインポートされません。検証に失敗したものはすべて、人による確認待ちのキューに送られます。

このガイドは具体的なケースを対象としています。月に数百枚の請求書を受け取る事務所や中小企業で、1台のワークステーションまたは小さなローカルサーバーを使用する場合です。ここでは精度の数値は約束されていません。精度はサプライヤー、スキャンの品質、モデルに依存します。測定方法は、ご自身の請求書のサンプルを用いて、後述します。

#電子請求書:2026年および2027年に変更される点

ローカルAIキット

お使いのマシンで、プライベートかつ無料のChatGPTを1時間で構築 — LM Studio、Ollama、Open WebUI、ご自身のドキュメント、クラウド不要。

  • 永久に利用できるオンラインスペース
  • PDF + ファイル
  • 30日間返金対応

読み取りパイプラインを構築する前に、制度改革が受け取る請求書にどのような影響を与えるかを確認する必要があります。impots.gouv.frによると、2026年9月1日以降、大企業と中堅企業は認定プラットフォームを通じて請求書を発行する必要があり、すべての企業は電子請求書を受け取れる体制を整える必要があります。中小企業、零細企業、マイクロ企業については、税務当局の実務ガイドによると、発行義務は2027年9月1日から適用されます。

実務上の帰結は二つあります。第一に、受け取る請求書のうち、AIで読み取る必要のない構造化済みの形式で届くものの割合が増えていきます。第二に、フランスとドイツの混合請求書標準であるFactur-Xは、読み取り可能なPDFと自動処理用のXMLデータを同じファイルに埋め込み、複数のデータプロファイルを備えます。PDFにこのXMLが含まれる場合は、モデルに推測させるより直接読み取る方が安全です。したがって、以下のパイプラインは、構造化データがあればそれを優先し、次にPDFのネイティブテキスト、最後に画像認識を使います。

i
このガイドがカバーしていない内容
認可されたプラットフォームを通じた受領、電子報告(e-reporting)、発行義務への対応は、選択するプラットフォームと会計ソフトの提供元が関わる領域です。このパイプラインが対象とするのは、海外の仕入先や小規模なサービス提供者からの請求書、レシート、紙をスキャンしたものなど、今も通常の PDF やスキャン画像で届く請求書です。

#モデルを選ぶ:画像の処理に必要なメモリ量

請求書の読み取りには、Ollamaライブラリの2つのモデルファミリーが適しています。Qwen 3.5の90億パラメータ版は6.6 GBで、テキストと画像を受け付け、公称コンテキストウィンドウは256,000トークンです。27B版は17 GBです。Gemma 4のe4b版は、Ollamaのモデル情報によると6.6〜9.5 GBを占め、テキストと画像に対応し、公称コンテキスト長は128,000トークンです。したがって、12 GBのGPUがあれば9B版またはe4b版を動かすのに十分で、ページ画像を扱う余裕もあります。

どのようなPDFにどのツールを使うか
ファイルの種類ツールなぜ
PDF Factur-Xまたは埋め込みXMLXMLの直接読み込み正確なデータ、読み取り誤りのリスクはありません
テキストを選択できるネイティブPDFテキストを抽出し、その後テキスト用LLMで処理高速で画像処理は不要
スキャンされたPDFまたはきれいな写真ビジョンモデル(Qwen 3.5 9B、Gemma 4)ページのレイアウトおよび表を読み取ります
品質の低いスキャンTesseract による OCR の後、人が内容を確認画像認識はノイズで誤った判断をしやすいため、人間が最終判断を行います

サイトメモリ目安: 9BパラメータのモデルをQ4で量子化すると、約5〜6 GBの容量を占め、さらにコンテキストキャッシュとページ画像が加算されます。複数ページにわたる請求書ページは他のページよりもコストが高くなります。1リクエストで10ページ分のバッチを処理すると、デフォルトのOllamaウィンドウを超える可能性があります。このデフォルト値は、ユーザーが引き上げない限り、モデルが公称する256 000トークンに大きく届きません。

#読み取る前にPDFを分類する:ネイティブ、スキャン、構造化

ファイルの種類を判別すれば、画像を不必要にビジョンモデルに送らずに済みます。PyMuPDFライブラリで抽出したテキストが数百文字を超えるPDFは、電子的に作成されたPDFです。テキストがほとんどないPDFはスキャンです。Factur-XファイルにはXMLの添付ファイルが含まれており、ほかの処理を行う前に添付ファイルの一覧を確認できます。

PDFの種類に応じてルーティングする
import fitz  # PyMuPDF

def type_pdf(chemin):
    doc = fitz.open(chemin)
    pieces = doc.embfile_names()  # pièces jointes intégrées
    if any(n.lower().endswith('.xml') for n in pieces):
        return 'structure'
    texte = ''.join(page.get_text() for page in doc)
    return 'natif' if len(texte.strip()) > 300 else 'scan'

品質の低いスキャンには、Tesseractが無料でオフラインでも使える予備の手段として役立ちます。その場合は、フランス語の言語ファイルをインストールし、300 dpiでスキャンする必要があります。Tesseractのガイドでは設定を詳しく説明しています。精度の低いOCRで得られたテキストを、確認せずに次の工程へ進めてはいけません。人による確認待ちのキューに回して、確認を引き継ぎます。

フランス語対応を含めてTesseractをインストールする
# macOS
brew install tesseract tesseract-lang

# Ubuntu / Debian
sudo apt install tesseract-ocr tesseract-ocr-fra

#ビジョンモデルと指定されたスキーマを使用して抽出する

Ollamaでは、モデルの回答をJSONスキーマに従うよう制約できます。ドキュメントによると、formatフィールドにスキーマを指定し、回答をそのスキーマに沿わせるため、プロンプト内にも同じスキーマを記載することが推奨されています。キーと型が強制されるため、自由形式の文章で「JSON」を求めるよりも、はるかに堅牢です。画像については、REST APIはメッセージのimagesフィールドにbase64でエンコードした画像を指定することを要求します。

請求書の構造化抽出(Ollama、ビジョン)
import base64, json, requests
from io import BytesIO
from pdf2image import convert_from_path

SCHEMA = {
  'type': 'object',
  'properties': {
    'fournisseur': {'type': 'object', 'properties': {
      'nom': {'type': 'string'},
      'siret': {'type': ['string', 'null']},
      'tva_intra': {'type': ['string', 'null']}}},
    'facture': {'type': 'object', 'properties': {
      'numero': {'type': 'string'},
      'date': {'type': 'string'},
      'echeance': {'type': ['string', 'null']}}},
    'montants': {'type': 'object', 'properties': {
      'ht': {'type': 'number'}, 'tva': {'type': 'number'},
      'ttc': {'type': 'number'}, 'devise': {'type': 'string'}}},
    'lignes': {'type': 'array', 'items': {'type': 'object', 'properties': {
      'description': {'type': 'string'}, 'quantite': {'type': 'number'},
      'pu_ht': {'type': 'number'}, 'total_ht': {'type': 'number'}}}}
  },
  'required': ['fournisseur', 'facture', 'montants']
}

PROMPT = ("Tu extrais les données d'une facture française. "
  "Réponds uniquement par un JSON conforme à ce schéma : " + json.dumps(SCHEMA) +
  ". Si une donnée est absente ou illisible, mets null. Les montants sont des nombres (1234.56), "
  "les dates au format AAAA-MM-JJ. N'invente rien.")

def pages_b64(pdf, dpi=200):
    sortie = []
    for img in convert_from_path(pdf, dpi=dpi):
        buf = BytesIO(); img.save(buf, format='PNG')
        sortie.append(base64.b64encode(buf.getvalue()).decode())
    return sortie

def extraire(pdf):
    r = requests.post('http://localhost:11434/api/chat', json={
      'model': 'qwen3.5:9b', 'stream': False, 'format': SCHEMA,
      'messages': [{'role': 'user', 'content': PROMPT, 'images': pages_b64(pdf)}],
      'options': {'temperature': 0, 'num_ctx': 16384}})
    return json.loads(r.json()['message']['content'])

三つの設定について説明しておきます。温度をゼロにすると、出力に再現性が得られます。num_ctx パラメータでコンテキストウィンドウを広げるのは、複数ページの画像が、Ollama の小さなデフォルトのウィンドウをすぐに使い切ってしまうためです。この仕組みは、コンテキストウィンドウのガイドで詳しく説明しています。最後に、「情報を捏造しない」という指示を与え、null 値を許可することで、最も重大なリスクを減らせます。それは、モデルが読み取れない項目をもっともらしい値で埋めてしまうことです。厳格なスキーマが強制するのは回答の形式であり、内容の正確さではありません。

#実際のケースに合わせてスキーマを拡張する

基本スキーマは、一般的な仕入先の請求書の大部分に対応しています。必要な拡張は、事業内容によって異なります。一つずつ追加し、それぞれの効果をサンプルで測定してください。項目を詰め込みすぎたスキーマは、重要なフィールドの読み取り精度を低下させます。

前払い金と残金
acompte_payeフィールドを追加してください。このフィールドがないと、税込合計額は支払い残額と一致しません。
注文参照番号
bon_commande_refというフィールドを提供することで、お客様の注文と照合が可能になります。
配送費および割引
専用の明細行かport_htフィールドに記録してください。そうしないと、明細行の合計が税抜総額と一致しません。
VATの内訳
税率、課税標準額、税額の一覧。請求書に複数の税率が混在する場合に不可欠です。
勘定科目への割り当て
モデルに勘定科目を推測させないでください。候補を作成し、候補であることを明示したうえで、業務ルールまたは人による確認を経て確定してください。

#インポート前に検証する:エラーを見つけるチェック

この段階が、仕組み全体の品質を左右します。各チェックは決定論的なので、モデルよりも信頼できます。1つ目は計算のチェックです。税抜金額(HT)に付加価値税(TVA)を加えた金額が税込金額(TTC)と一致する必要があり、端数処理については数セントの誤差を許容します。2つ目は明細行のチェックで、その合計が税抜金額と一致する必要があります。3つ目は日付のチェックで、妥当な下限より古い日付でも、未来の日付でもないことを確認します。4つ目はSIRETのチェックです。

SIRETの検証には特別な注意が必要です。Wikipediaによると、この番号は14桁で、最後の桁はLuhnの式で計算されるチェックディジットです。例外が存在します。SIRENが356000000であるLa Posteの事業所は、14桁の合計が5の倍数であるという別のルールに従います。したがって、素朴なLuhn検証では、La Posteの請求書が誤って拒否されてしまいます。以下のコードは、両方のケースを処理します。

抽出した請求書の整合性チェック
from datetime import date

def luhn_ok(s):
    total = 0
    for i, c in enumerate(reversed(s)):
        d = int(c)
        if i % 2 == 1:
            d *= 2
            if d > 9:
                d -= 9
        total += d
    return total % 10 == 0

def siret_valide(s):
    if not (s.isdigit() and len(s) == 14):
        return False
    if s.startswith('356000000'):  # La Poste : somme multiple de 5
        return sum(int(c) for c in s) % 5 == 0
    return luhn_ok(s)

def valider(f):
    erreurs = []
    m = f['montants']
    if abs(m['ht'] + m['tva'] - m['ttc']) > 0.02:
        erreurs.append('HT + TVA différent du TTC')
    lignes = f.get('lignes') or []
    if lignes and abs(sum(l['total_ht'] for l in lignes) - m['ht']) > 0.05:
        erreurs.append('somme des lignes différente du HT')
    siret = (f['fournisseur'].get('siret') or '').replace(' ', '')
    if siret and not siret_valide(siret):
        erreurs.append('SIRET invalide : ' + siret)
    try:
        d = date.fromisoformat(f['facture']['date'])
        if d.year < 2000 or d > date.today():
            erreurs.append('date suspecte : ' + str(d))
    except ValueError:
        erreurs.append('date illisible')
    return erreurs
!
不整合を絶対にインポートしない
合計額が一致しない請求書は「要確認」の処理待ちキューに入れる必要があります。すべてのチェックに合格した抽出結果も、正しいとは限りません。一方、チェックに不合格だった抽出結果は確実に見直しが必要なので、人による確認・振り分けはそこに集中させます。

#自分の請求書で正確性を測定する

公開されている精度の数値は、ご自身のコーパスでの測定に代わるものではありません。卸売業者の請求書を取り扱う事務所と、協会では取り扱う文書が異なるためです。代表的な請求書約 50 枚のサンプルを作成し、主要フィールドを手動で入力したうえで、フィールドごとに比較してください。

  1. 01
    サンプルを用意する
    スキャンしたもの、ネイティブPDF、複数ページのもの、さまざまな取引先のものなど、多様な実際の請求書を用意してください。結果を共有する場合は、匿名化してください。
  2. 02
    正解データを入力する
    自動化する項目を手作業で記録してください:番号、日付、税抜金額、付加価値税額(TVA)、税込金額、SIRET。
  3. 03
    フィールドごとの比較
    全体ではなく、フィールドごとに正解率を計算してください。モデルは日付を完璧に読み取れても、付加価値税(VAT)の項目では誤ることがあります。
  4. 04
    自動化のしきい値を設定
    人による再確認なしでインポートできる項目を決めてください。金額は計算チェックに合格すれば有力な候補ですが、勘定科目への割り当ては決してその対象にしてはいけません。
  5. 05
    変更ごとに再実行
    モデル、解像度、またはプロンプトを変更した場合は、本番運用に移る前にサンプルを使った処理を再実行してください。

#フォルダー内の請求書を一括処理する

バッチ処理では、仕分け、抽出、検証を順に実行し、その結果に応じて各請求書を整理します。元のPDFと抽出したJSONの二つのファイルは、常に一緒に保存してください。

再確認用キューを備えたバッチ処理
from pathlib import Path
import json

def traiter(dossier_in, dossier_ok, dossier_revue):
    for pdf in Path(dossier_in).glob('*.pdf'):
        try:
            f = extraire(str(pdf))
            erreurs = valider(f)
        except Exception as e:
            f, erreurs = {}, ['échec extraction : ' + str(e)]
        dest = Path(dossier_ok if not erreurs else dossier_revue)
        (dest / (pdf.stem + '.json')).write_text(
            json.dumps({'donnees': f, 'erreurs': erreurs}, ensure_ascii=False, indent=2))
        pdf.rename(dest / pdf.name)
        print(('OK ' if not erreurs else 'A VERIFIER ') + pdf.name)

#会計ソフトにデータを取り込む

各エディタは独自のインポートフォーマットと仕様を持ち、それらは進化しています:ツールのドキュメントを基にし、一般化されたモデルに頼らず、実際のツールのドキュメントを参照してください。会計ソフトウェアは一般的に構造化ファイルによるインポートまたはAPIインターフェースを提供しています。最も安全な方法は、インポートファイルを正しく作成し、テストフォルダに読み込み、生成された記録を実際に入力した記録と比較することです。

审计ログを残してください:オリジナルのPDF、抽出されたJSON、モデルのバージョン、および処理日付。検査の際に、会計記録から証憑ファイルまでたどれるようにしてください。請求書には個人情報や商業情報が含まれていますので、第三者に委託しないでローカルで扱うことが望ましいですが、ファイルの保存は依然として貴社の保存およびセキュリティポリシーに従う必要があります。

FAQ
ローカル LLM はスキャンされた請求書を読み取れますか?+
はい。Qwen 3.5 9BやGemma 4のような視覚モデルは、ページの画像を読み取り、各項目を抽出します。信頼性はスキャンの品質とレイアウトに左右されます。そのため、計算上の整合性をチェックして結果を検証し、チェックに通らない請求書はすべて人に回す必要があります。
ビジョンモデルに追加でOCRが必要ですか?+
必ずしも必要ではありません。視覚モデルは画像を直接読み取るため、別途OCRを使うことによる情報の損失を避けられます。Tesseractは、著しく劣化したスキャンや、テキストをそのまま抽出できるネイティブPDFに対する補助手段として、引き続き役立ちます。推測で決めるのではなく、ご自身のサンプルで両方を試してください。
出力として有効なJSONを保証する方法は?+
Ollamaでは、APIのformatフィールドにJSONスキーマを渡すと、応答をその構造に制約できます。これにより形式は保証されますが、値の正しさは保証されません。そのため、税抜合計額にTVA(付加価値税)を加えた金額、SIRET番号、日付、明細行の合計について、コードで必ず整合性をチェックしてください。
このタイプのツールに対する電子請求書の義務化はどのような影響を与えますか?+
2026年9月1日以降、すべての企業は電子請求書を受け取れるようにする必要があり、2027年9月には中小企業(PME)にも発行が義務付けられます。Factur-Xのような構造化された請求書は、AIを使わずに読み取れます。このパイプラインは主に、今も通常のPDFや紙の請求書を送ってくる仕入先への対応に使います。
請求書の情報を抽出するには、どのグラフィックスカードが必要ですか?+
90 億パラメータのモデルを Q4 で使用すると、ページイメージやコンテキストを除き、約 6 GB になります。12 GB のカードが適しており、コンテキストを削減すれば 8 GB でも 1 ページの請求書処理に十分です。GPU なしでも処理は可能ですが、速度が遅くなります。夜間の処理を計画してください。
モデルが提案する勘定科目への振り分けは信頼できますか?+
いいえ、確認なしに信頼することはできません。モデルは仕入先の表記から勘定科目を提案できますが、その提案は業務ルールまたは会計担当者による確認が必要です。勘定科目の割り当てミスは気づかれないまま、すべての算術チェックを通過してしまいます。常に人間の確認が必要な唯一の領域として扱ってください。
このガイドは役に立ちましたか?

ご意見、誤りのご指摘、補足はありますか?ぜひお知らせください。皆さんにとってより良いガイドにするために役立ちます。