メインコンテンツへスキップ
#AI活用 約9分で読めます

Claude Opus 4.6 の Vision で技術ドキュメントのスクリーンショット分析を自動化する

Claude Opus 4.6 の Vision API を使って技術ドキュメントのスクリーンショットを自動で読み取り、構造化データに変換する実装手順と実務で気づいた注意点

きっかけは、外部ベンダーから納品された設計書がPDFではなく画像だったこと。全50ページ、すべてスクリーンショットのPNG。テキストのコピーもできない。手で書き写すのは論外なので、Claude の Vision API に投げてみたらかなり使えた。そこから技術ドキュメントのスクリーンショット解析を自動化するパイプラインを組んだので、やり方と引っかかったポイントを書く。

Opus 4.6 の Vision で何ができるか

Claude Opus 4.6 は Messages API で画像を受け取れる。JPEG、PNG、GIF、WebP の4形式に対応していて、1リクエストあたり最大600枚まで送れる。画像は base64 エンコードして image コンテンツブロックとして渡すか、URL指定かFiles API経由で送る。

技術ドキュメントの画像に対してやれることは多い。表の読み取り、コードブロックの抽出、アーキテクチャ図の説明、エラー画面のスタックトレース解析など。自分が一番使っているのは「スクリーンショットからMarkdownへの変換」で、これが実用レベルで動く。

最低限のコードはこれだけ。

import anthropic
import base64
from pathlib import Path

client = anthropic.Anthropic()

def analyze_screenshot(image_path: str, prompt: str) -> str:
    image_data = base64.standard_b64encode(
        Path(image_path).read_bytes()
    ).decode("utf-8")

    # 拡張子からmedia_typeを判定
    suffix = Path(image_path).suffix.lower()
    media_types = {".png": "image/png", ".jpg": "image/jpeg",
                   ".jpeg": "image/jpeg", ".webp": "image/webp"}
    media_type = media_types.get(suffix, "image/png")

    message = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": media_type,
                        "data": image_data,
                    },
                },
                {"type": "text", "text": prompt},
            ],
        }],
    )
    return message.content[0].text

ポイントは画像をテキストより先に置くこと。公式ドキュメントでも「images come before text」が推奨されていて、手元で試した感じでも、画像を後ろに置いたときより読み取り精度が安定する。

画像サイズとトークンコストの感覚

Vision API の課金は画像のピクセル数に比例する。Claude は画像を28×28ピクセルの「パッチ」に分割して、それぞれを1ビジュアルトークンとして数える。計算式は ⌈幅÷28⌉ × ⌈高さ÷28⌉ で、これが入力トークンに加算される。

Opus 4.6 は「Standard」解像度ティアに分類されていて、長辺の上限が1568px。これを超える画像は自動でリサイズされる。高解像度ティア(長辺2576px)は Opus 4.7 以降のモデルが対象なので、4.6 を使う場合は1568px以内に自分でリサイズしておくのが無駄がない。

実際の費用感。Opus 4.6 の入力トークン単価は $5/MTok。1920×1080のフルHDスクリーンショットは、Standard ティアではリサイズされて約1560トークン。つまり1枚あたり約$0.0078。1000枚処理しても$8程度。出力トークン($25/MTok)のほうがむしろ支配的で、画像1枚につき500トークンの出力を返させると$0.0125。入力より出力のほうが高い。

Batch API を使えば入出力ともに半額になるので、大量処理するなら必ず使う。

画像サイズビジュアルトークン数入力コスト(1枚)
1000×1000約1,300約$0.0065
1920×1080約1,560(リサイズ後)約$0.0078
800×600約620約$0.0031

技術ドキュメント分析で引っかかった点

フォントが小さいと読み間違える

設計書のスクリーンショットでありがちなのが、12px以下の小さい文字。特にコードブロック内のインデントや変数名で誤読が起きる。responsereponse になっていたり、0(ゼロ)と O(オー)の区別がつかなかったり。

対策として、解析前に画像を1.5〜2倍に拡大してから送るようにした。1568pxの上限に収まる範囲で拡大すれば、トークン数は増えるが読み取り精度がだいぶ上がる。

from PIL import Image

def upscale_for_vision(image_path: str, max_long_edge: int = 1568) -> str:
    """Vision API用に画像を拡大する。Standard ティアの上限内に収める"""
    img = Image.open(image_path)
    w, h = img.size
    long_edge = max(w, h)

    if long_edge < max_long_edge:
        scale = min(max_long_edge / long_edge, 2.0)  # 最大2倍
        new_w, new_h = int(w * scale), int(h * scale)
        img = img.resize((new_w, new_h), Image.LANCZOS)

    output_path = image_path.replace(".png", "_upscaled.png")
    img.save(output_path)
    return output_path

表の読み取りはプロンプトで精度が変わる

技術ドキュメントには表がつきもの。「この画像の内容を読み取って」だけだと、表の罫線が薄い場合にセルの区切りを見落とす。プロンプトで「Markdownの表形式で出力して。列のヘッダーを省略しないで」と明示すると、だいたいきれいに変換される。

自分がドキュメント変換用に使っているプロンプトはこれ。

この技術ドキュメントのスクリーンショットを正確にMarkdown形式に変換してください。
以下のルールに従ってください:
- 見出しレベルは画像内のフォントサイズから推測して h2〜h4 で付ける
- 表は Markdown テーブルとして出力する。列のヘッダーは必ず含める
- コードブロックは ```言語名 で囲む。言語が判別できなければ ```text にする
- 箇条書きは - で統一する
- 画像内のテキストをそのまま転記する。要約や言い換えはしない

「要約しない」と書かないと、Claude が親切心で内容を圧縮してしまうことがある。ドキュメント変換では原文の忠実な再現が大事なので、この一文は外さないほうがいい。

複数ページの一括処理

50ページの設計書を1枚ずつAPIに投げると、前後のページとの文脈が途切れる。見出しの番号体系がリセットされたり、前ページから続く表が途中から始まったりする。

これは1リクエストに複数画像をまとめて送ることで改善できる。Opus 4.6 のコンテキストウィンドウは1Mトークンあるので、画像が数十枚でも余裕で収まる。ただし、20枚を超えると1枚あたりの画像サイズ制限が厳しくなる(公式ドキュメントに “many-image requests” の制限として記載されている)ので、各画像の辺を2000px以下にリサイズしておく必要がある。

def batch_analyze(image_paths: list[str], prompt: str) -> str:
    content = []
    for i, path in enumerate(image_paths):
        image_data = base64.standard_b64encode(
            Path(path).read_bytes()
        ).decode("utf-8")
        content.append({"type": "text", "text": f"Page {i + 1}:"})
        content.append({
            "type": "image",
            "source": {
                "type": "base64",
                "media_type": "image/png",
                "data": image_data,
            },
        })
    content.append({"type": "text", "text": prompt})

    message = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=8192,
        messages=[{"role": "user", "content": content}],
    )
    return message.content[0].text

各画像の前に Page 1: のようなラベルを付けておくと、出力でも「Page 3の表に記載されている〜」のように参照してくれるので、後から確認しやすい。

Prompt Caching で繰り返し処理を安くする

同じ設計書のスクリーンショット群に対して、「Markdown変換」「API仕様の抽出」「テスト項目の洗い出し」と複数の分析を回すことがある。このとき Prompt Caching を使えば、2回目以降の画像入力コストが10分の1になる。Opus 4.6 の場合、キャッシュヒット時の入力は $0.50/MTok(通常の10%)。

最初のキャッシュ書き込みは通常の1.25倍(5分キャッシュ)か2倍(1時間キャッシュ)かかるが、同じ画像セットに2回以上分析をかけるなら確実に元が取れる。

自分の場合、50ページの設計書を3パターンの分析にかけたとき、キャッシュなしだと画像入力だけで約$0.40かかるところが、キャッシュありだと初回$0.50 + 2回目以降$0.04×2 = 合計約$0.58。微増に見えるが、画像枚数が増えるほどキャッシュの恩恵が効いてくる。

PDF を直接送るという選択肢

ここまでスクリーンショット(画像)の話をしてきたが、もしドキュメントがPDFで手に入るなら、Claude API にはPDFをそのまま送る機能がある。document コンテンツブロックで application/pdf を指定すればいい。埋め込みテキストも画像も表もまとめて処理してくれる。

ただ、実際の業務では「PDFが手に入らない」場面が意外と多い。Confluenceのページ、Notionのスクリーンショット、Slackに貼られた画像、レガシーシステムの管理画面キャプチャ。こういう「画像しかない」状況で Vision API が活きる。

結局のところ、画像からの情報抽出は完璧ではない。フォントが小さい、コントラストが低い、多段組レイアウトで読み順が曖昧——こういう画像では誤読が混じる。出力は必ず目視で確認して、特にコード片や数値は手動でチェックしたほうがいい。それでも、50ページを手で書き写すよりは圧倒的に速い。

参考リンク

#Claude #Vision API #ドキュメント分析 #自動化 #スクリーンショット
シェア