受け取った資料をまとめて要約させる小さな仕組みを、個人開発の片手間で動かしております。入力は毎回 PDF です。
月初に立てた見積もりと、月末に出てくる入力トークンが、どうしても噛み合わない週がありました。文字数は数えていたのです。抽出した本文の文字数に単価を掛けて、これくらいだろう、と。ところが実績はその何倍かで着地します。
最初に疑ったのは自分のプロンプトでした。けれども原因はそこではありませんでした。PDF は、本文だけが渡っているわけではなかったのです。
ページは、文字と絵の両方で渡っています
PDF support の公式ドキュメントは、受け取った PDF に何が起きるかを三段階で書いています。各ページが画像に変換され、そのページから抽出されたテキストが画像と一緒に添えられ、Claude は両方を見て答える——という流れです。
つまり入力は二本立てです。公式はコストの節にこう補足しています。テキスト側はページあたりおおむね 1,500〜3,000 トークン、画像側は通常の画像とまったく同じ計算が当てはまる、と。
文字数だけを数えていた私の見積もりからは、この画像側が丸ごと抜け落ちておりました。抜けているものが「少し」ではなく「もう一本分」だったので、何倍かになるのは当然でした。
画像側には、ページ 1 枚ごとの上限があります
Claude は画像をピクセルではなく 28×28 ピクセルの升目で見ています。1 枚の画像が使う視覚トークンは ⌈幅 / 28⌉ × ⌈高さ / 28⌉ 個です。
ここに上限が二つ掛かります。長辺の上限と、視覚トークン数の上限です。どちらかを超えると、縦横比を保ったまま両方に収まる最大の大きさまで縮められてから読まれます。上限の値はモデルの解像度ティアで変わります。
| ティア | 対象モデル | 長辺の上限 | 視覚トークンの上限 |
|---|---|---|---|
| 標準 | Claude 4.7 より前のモデル | 1568 px | 1568 |
| 高解像度 | Claude 4.7 以降のモデル | 2576 px | 4784 |
画像なら、こちらで縮めてから送るという手が打てます。PDF ではそれができません。座標とバウンディングボックスの項に、PDF のページはこちらが決められない大きさでサーバー側でラスタライズされると明記されています。返ってきた座標を元のページに重ねられない理由として書かれている一文ですが、コストの話にもそのまま効きます。
こちらにできるのは、ページ 1 枚がどこまで行くかを先に知っておくことだけです。公式の縮小規則をそのまま実装して、A4 縦を解像度ごとに数えてみました。
| 描画解像度 | ピクセル | 標準ティア | 高解像度ティア |
|---|---|---|---|
| 72 dpi | 595×842 | 682 | 682 |
| 96 dpi | 794×1123 | 1,189 | 1,189 |
| 150 dpi | 1240×1754 | 1,551 | 2,835 |
| 200 dpi | 1654×2339 | 1,551 | 4,756 |
| 300 dpi | 2480×3508 | 1,551 | 4,756 |
200 dpi から先は、どれだけ細かく描いても値が動きません。A4 1 ページの画像トークンは、標準ティアで 1,551、高解像度ティアで 4,756 で頭打ちになります。 同じページが、モデルの世代が変わるだけで約 3.07 倍です。
ここで肩の荷が下りました。ラスタライズの解像度は分からないままでよかったのです。分からない値は当てにいかず、動かない上限のほうで挟みます。 見積もりの置き方を、そこで入れ替えました。
A4 9 ページの文書で、二通りに数えてみました
手元の A4 縦 9 ページ・本文 8,021 文字(空白を除く)の日本語文書で突き合わせました。1 ページあたり 891 文字ですので、文字としてはむしろ薄いほうの書類です。
文字側にもう一つ増分があります。トークンカウントの公式ドキュメントが、Claude 4.7 以降のモデルは新しいトークナイザを使っていて、同じ文章がおよそ 30% 多く数えられると書いています。8,021 文字が、旧世代で測ったつもりの数え方より 3 割ほど重くなる、ということです。
| 数え方 | 入力トークン | Opus 5 で 1 回(100 万トークン 5 ドル) |
|---|---|---|
| 文字数だけ | 約 8,021 | 約 0.040 ドル |
| 本文(+30%)+ 標準ティアの画像上限 | 約 24,386 | 約 0.122 ドル |
| 本文(+30%)+ 高解像度ティアの画像上限 | 約 53,231 | 約 0.266 ドル |
現行世代のモデルで送ると、文字数から出した見積もりの 6.6 倍でした。1 通を 1 日 20 回、30 日回す仕組みなら、月 24 ドルのつもりが 160 ドルです。噛み合わなかったのは、この差でした。
内訳を見ると、増えた分のほとんどは画像側の 42,804 トークンです。文字側の 3 割増しは、金額で言えば添え物でした。
送る前に挟む見積もりを、40 行ほどで
公式の縮小規則を実装して、PDF のページ寸法だけから上限を出す関数です。API は一度も呼びません。フォルダごと数えても一瞬で終わります。
import math
from pypdf import PdfReader
PATCH = 28 # Claude が画像を見る升目の一辺
TIERS = {"standard": (1568, 1568), # (長辺の上限, 視覚トークンの上限)
"high": (2576, 4784)} # high = Claude 4.7 以降
def visual_tokens(w, h):
return math.ceil(w / PATCH) * math.ceil(h / PATCH)
def fits(w, h, max_edge, max_tokens):
# 端は升目の切りのよいところまで詰められるので、切り上げてから長辺を見ます
padded_edge = max(math.ceil(w / PATCH), math.ceil(h / PATCH)) * PATCH
return padded_edge <= max_edge and visual_tokens(w, h) <= max_tokens
def page_ceiling(pt_w, pt_h, tier="high", dpi=300):
"""ページ 1 枚がどれだけ細かく描かれても超えない視覚トークン数を返します。"""
max_edge, max_tokens = TIERS[tier]
w, h = round(pt_w * dpi / 72), round(pt_h * dpi / 72)
if fits(w, h, max_edge, max_tokens):
return visual_tokens(w, h)
# 縦横比を保ったまま、両方の上限に収まる最大の倍率を二分探索します
lo, hi = 0.0, 1.0
for _ in range(40):
mid = (lo + hi) / 2
if fits(max(round(w * mid), 1), max(round(h * mid), 1), max_edge, max_tokens):
lo = mid
else:
hi = mid
return visual_tokens(max(round(w * lo), 1), max(round(h * lo), 1))
def estimate_pdf(path, tier="high", text_per_page=(1500, 3000)):
pages = PdfReader(path).pages
image = sum(page_ceiling(float(p.mediabox.width),
float(p.mediabox.height), tier) for p in pages)
n = len(pages)
return {"pages": n, "image_tokens": image,
"total_min": n * text_per_page[0] + image,
"total_max": n * text_per_page[1] + image}
print(estimate_pdf("sample.pdf", tier="high"))
# {'pages': 9, 'image_tokens': 42804, 'total_min': 56304, 'total_max': 69804}dpi=300 は「これ以上細かく描かれても値が変わらない」ところまで上げておくための既定値で、実際の変換解像度を言い当てているわけではありません。上限を出すための踏み台です。
手を動かして確かめておいた点が一つあります。この関数に 1920×1080、2000×1500、3840×2160 を通すと、公式のビジョン解説にある表の 1560・1564・1560(標準)と 2691・3888・4784(高解像度)にそのまま一致しました。規則を自分で書き写すときは、この突き合わせまでやっておくと安心できます。
なお先ほどの 9 ページ文書の実測 53,231 は、この関数の下限 56,304 をわずかに下回っております。公式の「ページあたり 1,500〜3,000」は密度の高いページを想定した幅ですので、薄い書類では少し過大に出ます。挟んだ幅は、次の手順で締めます。
count_tokens で答え合わせをするときに引っかかること
トークンカウントのエンドポイントは PDF も数えてくれますし、料金はかかりません。Messages とは別枠のレート制限で、Start ティアでも毎分 5,000 リクエストあります。
ただし、ここで一度つまずきました。document ブロックの url ソースと file ソースは数えられません。 base64 で送ったものだけが対象です。大きい PDF ほど Files API に置いて file_id で参照したいのに、数えるときだけは手元で base64 に戻す必要がありました。
移行のときの注意も一つ添えられています。古いモデルで測った値を流用せず、送る予定のモデル ID で数え直すこと。先ほどの 30% 増は、まさにここで表に出ます。
私自身は、この二つを組み合わせる形に落ち着きました。
- フォルダ全体は
page_ceilingで挟み、月の上限として先に予算を決めます - 代表的な 1 通だけ base64 にして
count_tokensを 1 回呼び、その書類種別の「1 ページあたり実測値」を取ります - 以後はその実測値 × ページ数で回し、書類のレイアウトが変わったときだけ 2 をやり直します
上限は動きませんので、1 は作り置きが効きます。2 は無料で、月に一度で足ります。
決めたのは、絵として読む必要のあるページだけ PDF で渡すことでした
内訳を見てから、入力の渡し方そのものを分けました。
表やグラフ、レイアウトそのものを読んでほしいページは PDF のまま渡します。ここは画像側の費用を払う価値があります。本文が読めれば足りるページは、こちらでテキストを抜いて、テキストとして渡します。プレーンテキストは text/plain として Files API に置いて document ブロックから参照できますので、流れを大きく変えずに済みました。
先ほどの 9 ページの文書は、後者で足りる種類のものでした。画像側の 42,804 トークンが、そっくり消えます。
絵として読む必要のないページには、絵の代金を払いません。 上限を数えて分かったのは、この一行でした。
まずはお手元の PDF を 1 通だけ page_ceiling に通して、ページ 1 枚の上限を見てみてください。そこが 1,551 なのか 4,756 なのかで、予算の桁が変わります。画像をまとめて送る側の上限については画像は20枚ずつ送っています — 21枚目から全画像に別の上限がかかると知った日に、PDF の解析そのものの組み方はClaude Vision API 実装パターン — 画像解析・PDF 処理・OCR を本番品質で動かすまでに書き残しております。
見積もりが合わない理由を探していた時間は、いま思えば数え方を一つ足すだけで済む話でした。同じところで手が止まっている方の助けになれば嬉しく思います。お読みいただきありがとうございました。