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以下の小さい文字。特にコードブロック内のインデントや変数名で誤読が起きる。response が reponse になっていたり、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ページを手で書き写すよりは圧倒的に速い。