Claude Media
ClaudeのCitationsでスキャンPDFを引用できない理由と回避策

ClaudeのCitationsでスキャンPDFを引用できない理由と回避策

テキスト抽出できないスキャンPDFはCitationsの引用対象外です。仕組み上の理由と、OCRやテキスト抽出による回避策、citations無効時の読み取りとの違いを扱います。

スキャンPDFを渡したとき、引用はどうなるか

Citationsは、Claudeの回答に文書中の該当箇所を出典として添える機能です。紙をスキャンしただけのPDF、つまりテキストレイヤーを持たず画像だけで構成されたPDFは、この引用の対象になりません。

公式ドキュメントの記述は次のとおりです。PDFはテキストが抽出され、文単位でチャンク(引用の最小単位)に分割されます。画像からの引用はまだサポートされていないため、抽出可能なテキストを含まないスキャンPDFは引用できません。抽出できるテキストが無ければ、cited_textを返す元データがそもそも存在しません。

契約書・請求書・古い紙資料をまとめてナレッジベース化するワークフローでは、スキャンしただけのPDFが混ざり込みがちです。気づかずに運用へ乗せると、「一部の文書だけ引用が付かない」という形で表に出ます。テキストを含む通常のPDFなら、応答には次のようなpage_location型の引用が付きます。

{
  "type": "page_location",
  "cited_text": "Water is essential for life.",
  "document_index": 1,
  "document_title": "PDF Document",
  "start_page_number": 5,
  "end_page_number": 6
}

CitationsとPDFサポートのページに、一部のページだけがスキャンで残りはテキスト付きという混在PDFの扱いの記載はありません。運用に乗せる前に、少数のサンプルで引用が付くページと付かないページを試しておくと安全です。

引用できない理由は2つ重なっている

PDFの引用は「どのページ範囲を参照したか」を返す仕組みで、元のPDFから抽出したテキストに対してだけ働きます。画像の中の文字から直接位置を特定して引用する機能は、Citations側にまだありません。

つまり壁は2枚あります。スキャンPDFにテキストレイヤーが無いことと、Citationsが画像そのものからの引用に対応していないことです。片方が解消しても、もう片方が残れば引用は成立しません。

2枚目の壁は、テキストが抽出できる通常のPDFにも及びます。ページ内のグラフや図表そのものを引用元として指すことはできません。Citationsを無効にした通常のPDF処理では、ページが画像としても処理されるので、Claudeはグラフの内容を読み取って説明できます。ただし引用として指せるのは、抽出済みテキストの範囲に限られます。

手元のPDFを仕分ける手順

アップロードの前に、PDFごとにテキストレイヤーの有無を判定し、渡し方を振り分けます。大量のファイルを扱うなら、この仕分けを自動化しておくのが現実的です。

手順

スキャンPDFを引用つきで使えるようにする流れ

  1. 1

    テキストレイヤーの有無を調べる

    PDFビューアでテキストを選択・コピーできるかを試します。選択できなければ画像のみのスキャンです。大量にあるなら、下のスクリプトで機械的に判定します。

  2. 2

    既存のOCR資産で渡し方を決める

    OCR済みのPDFが手元にあれば、そのまま渡します。OCR結果をテキストや構造化データとして持っているなら、PDFに戻さずブロックとして渡します。

  3. 3

    同じリクエストの全文書でCitationsを揃える

    1つでも引用できない文書が混ざるときの扱いは、後の「複数文書を同時に渡すとき」の節で触れます。

次のスクリプトはpdfplumberで各ページから抽出できる文字数を数える、最小限の判定です。

import pdfplumber
 
def has_extractable_text(pdf_path, min_chars_per_page=20):
    with pdfplumber.open(pdf_path) as pdf:
        for page in pdf.pages:
            text = page.extract_text() or ""
            if len(text.strip()) < min_chars_per_page:
                return False
    return True

1ページでも文字数が閾値を下回れば、そのファイル全体をスキャン扱いにします。単純な判定ですが、スキャンPDFだけをOCR工程へ自動で振り分ける用途には足ります。閾値の20文字は目安なので、白紙ページや図だけのページが多い資料では調整が要ります。

回避策1: OCRでテキストレイヤーを加えてから渡す

もっとも直接的な対処は、アップロード前にOCR(光学文字認識)でスキャンPDFへテキストレイヤーを加えることです。テキストレイヤー付きのPDFは「抽出可能なテキストを含む」PDFになるので、通常のPDFと同じ経路でCitationsの対象になります。

スキャンPDFはページ数のわりにファイルが大きくなりやすく、リクエストの上限にも当たりやすい文書です。上限はPDF単体ではなく、同じリクエストに載せる他のテキストや画像も合わせたリクエスト全体にかかります。

数字

PDFを渡すときのリクエスト上限

  • リクエストサイズ

    32 MB

    Bedrockは20 MB、Google Cloudは30 MB

  • ページ数

    600ページ

    コンテキストウィンドウが1M未満なら100ページ

  • 1ページのテキスト

    1,500〜3,000トークン

    ページ画像の分のトークンは別に加わる

Messages API の値。パートナープラットフォームは別の上限を持ちます

細かい文字や複雑な表が多いPDFは、ページ数の上限に届く前にコンテキストウィンドウを使い切ることがあります。大きなPDFのリクエストは、Files APIを使っても上限前に失敗することがあり、公式ドキュメントはセクションに分けるよう勧めています。ページは画像として処理されるため、埋め込み画像の解像度を下げるのも有効とされています。

繰り返し使う文書はFiles APIへアップロードしてfile_idで参照すると、リクエストを小さく保てます。ただし、Amazon BedrockとGoogle Cloudではbase64で埋め込む形式しか選べず、Microsoft FoundryのAzureデプロイではFiles APIが使えません。これらの経路でスキャンPDFを扱うなら、OCR後のファイルサイズを抑える工夫のほうが先に必要になります。

回避策2: 抽出したテキストをプレーンテキスト・カスタムコンテンツ文書として渡す

Citationsが対応する文書形式は、PDFだけではありません。プレーンテキスト文書とカスタムコンテンツ文書も選べます。

文書タイプ適した用途チャンク分割引用の形式
プレーンテキスト適した用途シンプルな文章チャンク分割文単位で自動引用の形式文字インデックス(0始まり)
PDF適した用途テキストを含むPDFファイルチャンク分割文単位で自動引用の形式ページ番号(1始まり)
カスタムコンテンツ適した用途箇条書き・書き起こしなど、細かい引用単位が必要な場合チャンク分割追加分割なし引用の形式ブロックインデックス(0始まり)

自前のOCRパイプラインやOCRサービスでスキャンPDFからテキストを取り出せるなら、PDFに戻さず、プレーンテキスト文書かカスタムコンテンツ文書として渡す手があります。すでにテキストなので、そのまま文書として渡せます。

カスタムコンテンツ文書なら、抽出したテキストをどこで区切って引用単位にするかを自分で決められます。ページ・段落・見出しなど、OCR結果の構造に合わせてブロックを分けます。

2つの回避策は、手元の資産で選び分けます。

くらべる

OCRしてPDFのまま渡すか、抽出して渡すか

PDF資産がある場合

OCRしてPDFのまま渡す

  • すでにOCR済みのPDFがあれば、追加実装がほぼ要りません
  • 引用はpage_location型で、ページ番号がそのまま使えます
  • 引用の単位は文単位の自動分割に固定されます
構造化データがある場合

抽出してカスタムコンテンツで渡す

  • OCR結果を構造化データで持っているなら、そのままブロックにできます
  • 引用の単位を段落や見出しで自分で決められます

カスタムコンテンツ文書を使うリクエストの例です。

curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "messages": [{
      "role": "user",
      "content": [
        {
          "type": "document",
          "source": {
            "type": "content",
            "content": [
              {"type": "text", "text": "1ページ目: (OCR抽出テキスト)"},
              {"type": "text", "text": "2ページ目: (OCR抽出テキスト)"}
            ]
          },
          "title": "スキャンPDFのOCR結果",
          "citations": {"enabled": true}
        },
        {"type": "text", "text": "この文書の要点を教えて"}
      ]
    }]
  }'

例のようにブロックの先頭へ「1ページ目:」といった接頭辞を埋め込んでおけば、返ってきたブロック番号から元のページを引けます。

複数文書を同時に渡すとき

Citationsは、1リクエストに含まれる全文書で有効か無効かを揃える必要があります。テキスト付きPDFとスキャンPDFを同じリクエストに入れ、片方だけCitationsを有効にする指定はできません。

複数の資料を1回のリクエストへまとめたら、その中に1つだけスキャンPDFが混じっていた、という場面で選べるのは2通りです。スキャンPDFだけ先にOCRして他の文書と一緒に渡すか、スキャンPDFを外して別リクエストに分けます。1つのファイルのせいで全体の引用が使えなくなる事態は、組み立ての前に全文書の抽出可否を調べておけば避けられます。

Citationsを無効にすると、スキャンPDFでも読める

Citationsを無効にした通常のPDF処理では、スキャンPDFでも内容を読ませることはできます。Claudeは各ページを画像へ変換し、抽出できたテキストがあれば画像と並べて受け取ります。テキストレイヤーが無くても、画像としての理解は働くため、要約や質問応答は成り立ちます。

制約が出るのは引用のグラウンディング、つまり「この主張は文書のどこにあるか」を位置情報つきで返す機能だけです。要約や質問応答だけが目的で、出典の厳密な位置までは要らないなら、Citationsを無効のまま使う選択肢があります。通常のPDF読み込みの手順や上限はClaudeのPDF読み込み・画像解析の使い方にあります。

Amazon BedrockのConverse APIでは話が逆になる

ここまでの「Citationsを切れば画像として読める」は、Claude APIの通常の経路の話です。Amazon BedrockのConverse API(Opus 4.6以前を扱うBedrock上のClaude経由)では関係が逆転します。

公式のPDFサポートのページによると、Converse APIでPDFの視覚的な理解を使うにはCitationsを有効にする必要があります。Citationsを有効にしないと、基本的なテキスト抽出だけに切り替わります。テキストレイヤーの無いスキャンPDFは、この経路ではCitationsを切ると抽出できる文字がほぼ無くなる可能性があります。

同じページには、3ページのPDFで比べた目安のトークン数も載っています。

Converse APIのモード使われる条件3ページPDFの目安読み取れる範囲
Converse Document Chat使われる条件Citationsが無効のとき3ページPDFの目安約1,000トークン読み取れる範囲テキスト抽出のみ。図表・レイアウトは読めない
Claude PDF Chat使われる条件Citationsが有効のとき3ページPDFの目安約7,000トークン読み取れる範囲各ページをテキストと画像の両方で処理

Converse APIで視覚的な分析だけが欲しくてCitationsを有効にしている場合は、InvokeModel APIを使う選択肢があります。こちらは引用を強制せずにPDFの処理方法を選べます。

OCR後の文書に引用を付けるときの注意

OCRで文書を引用できる状態にしたあとも、いくつか引っかかりやすい点があります。

  • 構造化出力とは併用できません。Citationsを有効にした文書(documentブロックとsearch_resultブロック)を渡しつつoutput_config.formatも指定すると、APIは400エラーを返します。OCR結果をJSONスキーマで抽出したいパイプラインでは、引用つきの要約と構造化抽出を別リクエストに分けます
  • titleとcontextは引用の対象外です。 モデルには渡されますが、引用される文書の中身には数えられません。ページ番号を引用から引きたいときは、前述のとおり本文のテキストに埋め込みます。ファイル名や取得日のようなメタデータはcontextに文字列やJSONで入れられます
  • 引用ブロックそのものはキャッシュできません。 キャッシュできるのは引用元の文書です。cache_controlは、文書のコンテンツブロックのうちトップレベルに置いたものへ付けます

OCRは文書ごとに1回で済みますが、リクエストのたびに文書は入力トークンとして数えられます。同じOCR済み文書へ繰り返し質問するなら、プロンプトキャッシュを併用するかどうかで入力側のコストが変わります。

まとめ

引用の位置が要る文書だけをOCR経路かテキスト渡しに回し、位置が要らない読み取りはCitationsを無効のまま使う、という分け方が基本です。BedrockのConverse APIだけは逆で、画像として読ませるにはCitationsを有効にしておく必要があります。

トークンコストの面でCitationsがどう効くかはCitationsのトークンコストの仕組みで扱っています。

この記事を共有:XはてブLinkedIn