エディタで眺めるかぎり、12,000 トークンのシステムプロンプトは何の問題もなく見えます。しかし Claude Sonnet 4.6 ではリクエストごとに 3.6 セントが課金され、ユーザーが一文字も入力していない時点でコンテキストウィンドウのうち 12K を消費しています。2026 年の LLM コストはトークン単位で計測されるのが当たり前で、「文字数 × 0.25」では設計判断の根拠になりません。プロバイダ本人が使っているのと同じトークナイザーで数える必要があります。
この記事では、BPE トークナイザーが実際に何をしているのか、なぜ GPT-4o と Claude が同じ文を違うトークン数に切るのか、プロバイダをまたいだコスト計算の組み立て方、そして本番のコードパス上でローカルにトークンを数える方法を扱います。リモートのトークン化サービスへ API リクエストを飛ばすのが許されない場面でも使える前提です。
API キーなしでトークンを数える
任意のプロンプトを貼り付けると、現行のフロンティアモデル 6 種 — GPT-5、GPT-4.1、GPT-4o、Claude Sonnet 4.6、Gemini 3 Pro、DeepSeek V3 — それぞれのトークン数と入力コスト見積もりがその場で返ってきます。GPT 系は OpenAI の o200k_base BPE テーブルで厳密値を計算し、それ以外は調整済みの近似係数を使います。ランクテーブルはアイドル時に一度だけ読み込まれ、その後はページから何も外に出ません。
トークンとは何か
トークンは言語モデルが実際に読む単位です。モデルは文字や単語ではなく、Byte Pair Encoding (BPE) アルゴリズムで生成された整数 ID 列を見ています。この BPE はインターネット規模のテキストで学習され、隣接するバイト対のうち頻出するものを反復的にマージし、最終的に語彙サイズ 10 万から 20 万程度のテーブルに落ち着きます。the、ing、 you(先頭スペース込み)のような英語の頻出片は 1 トークンに圧縮されますが、まれな文字列・URL・大半の CJK 文字は複数トークンに分かれます。
おおまかな目安をいくつか頭に入れておくと便利です。
| サンプル | cl100k_base トークン数 | o200k_base トークン数 |
|---|---|---|
Hello, world! | 4 | 4 |
tokenizer | 1 | 2 |
browser-based developer tools | 4 | 4 |
https://api.example.com/v1/users?limit=10 | 12 | 12 |
你好,世界! | 7 | 4 |
東京の天気は晴れ | 10 | 7 |
下二行が、GPT-4o と同時に登場した o200k_base が非英語圏で大きな話題になった理由を端的に示しています。語彙サイズを 2 倍にしたことで、中国語・日本語・韓国語・アラビア語のトークン数が 20〜40 % 削減されました。日本語のような多バイト文字主体のプロンプトでは特に効果が顕著で、長文コンテキストの呼び出しでは現実のコスト差として効いてきます。
プロバイダ間でトークン数がずれる理由
トークナイザーはプロバイダごとに自前で学習されています。語彙、バイトフォールバックの戦略、特殊トークンの予約方法、すべて異なります。同じ段落を cl100k_base、o200k_base、Anthropic のトークナイザー、Google の SentencePiece、Meta の Llama 3 トークナイザーに通せば、似てはいるが完全には一致しない 4〜6 通りのカウントが返ってきます。
| プロバイダ | トークナイザー | 語彙サイズ | 備考 |
|---|---|---|---|
| OpenAI GPT-3.5 / GPT-4 | cl100k_base (BPE) | 約 10 万 | text-embedding-3 と同じ |
| OpenAI GPT-4o / 4.1 / 5 | o200k_base (BPE) | 約 20 万 | 語彙 2 倍、多言語で有利 |
| Anthropic Claude | カスタム BPE (非公開) | 約 10 万 | テーブルは未公開、count_tokens API 経由 |
| Google Gemini | SentencePiece | 約 25.6 万 | アルゴリズム系統がそもそも違う |
| Meta Llama 3.x | tiktoken 形式の BPE | 約 12.8 万 | OpenAI 互換フォーマット、語彙は別物 |
| DeepSeek V3 | カスタム BPE | 約 10 万 | 英中バイリンガルコーパスで学習 |
ここから 2 つの帰結があります。
- トークン数は直接比較できません。 トークナイザー名を抜きにして「このプロンプトは 1,000 トークン」と言うのは無意味です。GPT-4o での 1,000 トークンは、英語なら Claude で約 970 トークン、中国語だと 850〜1,100 の幅で揺れます。
- コスト比較はトークンではなく金額で行います。 各プロバイダのトークン数に 100 万トークン単価を掛けてからドルで比較する。ZeroTool のカウンターはこの掛け算まで一括でやってくれます。
ざっくり計算: トークンからドルへ
コスト計算自体は単純ですが、コードレビュー中に暗算でやろうとすると間違えやすい類のものです。式はこれだけです。
cost_usd = (tokens / 1_000_000) * price_per_1M_usd
実例を一本通してみます。Anthropic Claude Sonnet 4.6 の入力単価は 100 万トークンあたり $3.00。これを毎リクエスト送信される 12,000 トークンのシステムプロンプトに当てるとどうなるか。
cost_per_request_usd = (12_000 / 1_000_000) * 3.00 = 0.036
cost_per_1k_requests = 0.036 * 1000 = 36.00
cost_per_1M_requests = 0.036 * 1_000_000 = 36_000
12K トークンのシステムプロンプトだけで、ユーザー入力もモデル出力もカウントする前から 100 万コール = $36,000 の請求が立ちます。出力トークンはここに重なって積み上がります。出力 100 万トークンあたり $15、平均 500 トークン応答を同じ 100 万コール分とすれば $7,500 が追加。コードレビュー時にトークンカウンターを 30 秒叩いていれば、ここで止められた話です。
ブラウザで js-tiktoken はどう動くか
ZeroTool のカウンターは js-tiktoken の上に組まれています。OpenAI 公式の tiktoken ライブラリを JavaScript に移植したもので、tiktoken はオープンソースかつ OpenAI が本番で使っているのと同じ BPE テーブルを同梱しています。JS 移植版はそのテーブルをブラウザバンドル向けにコンパイルして含んでいます。
中身を分解すると次のようになります。
Tiktokenクラス — エンコーダ / デコーダのランタイム。語彙テーブルを受け取り、.encode(text)と.decode(ids)を提供。- ランクテーブル —
cl100k_base、o200k_base、p50k_base、r50k_base、gpt2がそれぞれ別モジュール。各テーブルは gzip 後で 150〜300 KB 程度。ZeroTool のカウンターはo200k_baseのみを動的 import し、requestIdleCallbackまで遅延させて初回描画を 1.2 秒以下に抑えています。 - WASM 不使用 — lite ビルドは純粋な JavaScript なので、Service Worker、Cloudflare Workers、Deno Deploy のようなバイナリローダーが効かない環境でもそのまま動きます。
コードからトークンを数える
コードパスから同等のことをやる場合の書き方です。ランタイムでブラウザ版と違うのはバンドルサイズくらいで、サーバー側ならふつう 5 種類すべての語彙を含んだフルのトークナイザーを抱え込み、ブラウザ側だけ必要なものを選び取る形になります。
Python (tiktoken)
import tiktoken
enc = tiktoken.get_encoding("o200k_base")
text = "Count me carefully, please."
tokens = enc.encode(text)
print(len(tokens), tokens)
# 6 [3417, 668, 18455, 11, 4843, 13]
エンコーディング名ではなくモデル名で指定したい場合は tiktoken.encoding_for_model("gpt-4o") を使うと、対応するテーブルを自動で解決してくれます。
JavaScript (js-tiktoken、ブラウザ / Node)
import { Tiktoken } from "js-tiktoken/lite";
import o200k_base from "js-tiktoken/ranks/o200k_base";
const enc = new Tiktoken(o200k_base);
const text = "Count me carefully, please.";
const tokens = enc.encode(text);
console.log(tokens.length, tokens);
// 6 [ 3417, 668, 18455, 11, 4843, 13 ]
バンドルを軽くしたいなら、ユーザー操作を契機にランクテーブルを動的 import して、独立したチャンクへ切り出すのが定石です。
Bash (CI でプロンプトを単発計測)
python -c "
import sys, tiktoken
enc = tiktoken.get_encoding('o200k_base')
print(len(enc.encode(sys.stdin.read())))
" < prompt.md
CI ガードとして便利です。prompt.md が例えば 2,000 トークンを超えたらビルドを落とす、といった運用ができます。find prompts/ -name '*.md' | xargs -I {} ... と組み合わせれば、ディレクトリ単位で監査できます。
よくある落とし穴
実際にチームに金銭的損失や本番障害をもたらしているパターンを並べておきます。
-
文字数でコストを見積もる。 「英語ならだいたい 1 トークン 4 文字」は平均としては成立しますが、コードや CJK では大きく崩れます。
__init__.pyは 5 トークン / 11 文字で比 2.2、中国語の漢字はo200k_baseだと 1〜2 トークンが普通で 0.25 ではありません。本物のトークナイザーを叩く方が早い。 -
チャットの整形オーバーヘッドを忘れる。 チャット API はメッセージごとにロールマーカー (
<|im_start|>system\n...<|im_end|>) を挟み、メッセージ 1 件あたり 3〜8 トークンを消費します。100 ターンの会話なら、本文以前に 300〜800 トークンが純粋な定常コストとして乗ります。カウンターが測るのは 1 本のテキストなので、チャットセッションは各メッセージのトークン数を合計してからターンあたり 4 トークンほどを足してください。 -
1 度数えて何度も送る。 リクエストごとに送るシステムプロンプトは、リクエストごとに課金されます。キャッシュしましょう。OpenAI、Anthropic、Google のいずれもプロンプトキャッシュを提供しており、キャッシュ部分には 50〜90 % の割引が効きます。カウンターで参照している料率はキャッシュ未適用時のものです。
-
ツール呼び出しのペイロードを無視する。 関数定義やツールスキーマも入力トークンの予算に乗ります。30 個のフィールドがネストした JSON Schema で 800 トークンに膨れ上がる、というのは普通にあります。モデルを切り替えたら請求が跳ね上がった、という現象が出たら、モデル退行を疑う前にまずツール定義を監査してください。
-
ダッシュボードで厳密値と近似値を混ぜる。 ZeroTool のツールが OpenAI を
exact、それ以外をapproxとラベリングしているのには理由があります。トークン課金で顧客に請求しているなら、ログには API レスポンスから返ってきたプロバイダ側の厳密値を残すこと。ローカル推定値を入れてはいけません。 -
出力トークンを予算に入れない。 出力は入力の 3〜5 倍高いのが普通です。1,000 トークンの段落でだらだら答えるボットは、ユーザー側プロンプト全部より出力で金を食います。
ローカル vs ホスト型のトークナイザー
世の中の公開トークンカウンターはほとんど、サーバー側のトークン化エンドポイントへプロンプトを送信する作りになっています。これは以下の 3 種類の入力では問題になります。
- 自社のシステムプロンプト。 営業秘密、モデル挙動の調整、競争上の参入障壁が詰まっています。サードパーティのエンドポイントに通したら全部漏れます。
- 顧客データ。 本番のチャットログ、サポートチケット、生成済みコンテンツはプライバシーコミットメントの対象です。サードパーティのトークナイザー側がリクエストをログに残している可能性は十分あります。
- エアギャップ環境。 規制業種(医療、金融、防衛)では、そもそもプロンプトを公衆網に通せません。日本企業のセキュリティポリシーでも、外部エンドポイントへの自由なテキスト送信を許さないケースは珍しくありません。
ZeroTool のカウンターは BPE エンコーダ全体をブラウザ内で動かすことで、この 3 つを同時に解決しています。DevTools の Network タブで検証可能です。ページと遅延読み込みのランクテーブルが落ち着いた後、いくらタイプしても外向きリクエストが追加で出ないことが見えるはず。コード側でも同じ発想が使えます。Python なら tiktoken、Node やブラウザなら js-tiktoken、いずれも語彙をプロセス内に同梱しています。
ZeroTool と他のカウンター
| ツール | トークナイザー精度 | プライバシーモデル | コスト列 |
|---|---|---|---|
| OpenAI Platform Tokenizer | OpenAI のみ厳密 | サーバー側 | なし |
| gpt-tokenizer.dev | tiktoken 経由で厳密 | クライアントサイド | なし |
| tokencounter.org | OpenAI 厳密 + 他は近似 | サーバー側 | あり |
| ZeroTool AI トークンカウンター | OpenAI 厳密 + Claude / Gemini / DeepSeek は調整係数で近似 | クライアントサイド | あり |
プロバイダ純正 count_tokens API | そのプロバイダのみ厳密 | サーバー側、API キー必須 | プロバイダ単位 |
OpenAI 単独のワークロードなら gpt-tokenizer.dev や OpenAI のホスト型ツールで十分です。マルチモデルの比較をコスト列付き、API キーなしで素早くやるなら ZeroTool が最短です。
カウンターが効くワークフロー
トークンカウンターに貼り付けるだけで現実に時間が浮く場面を挙げておきます。
- デプロイ前のプロンプトダイエット。 完成形のシステムプロンプトを貼り、500 トークン削れないか見る。1 トークン削れば、デプロイの寿命のあいだ全顧客リクエストにかかってくる積み上げになります。
- ベンダー乗り換え検討。 同じペイロードを 6 プロバイダに当てて、コスト列を読む。トークナイザー差だけで勝者が 30 % 動くことはざらにあります。
- コンテキストウィンドウの設計。 GPT-4o は 128K、Claude Sonnet 4.6 は 200K、Gemini 3 Pro は 2M。検索系が 180K トークンの文脈を引いてくる構成なら、Claude (200K) と Gemini (2M) には余裕があり、GPT-4o (128K) には乗りません。どのモデルにまだ余白があるか、カウンターが見せてくれます。
- 多言語監査。 同じ内容を英語と対象市場の言語で流す。
o200k_baseはcl100k_baseと比べて CJK で大きく改善しているので、GPT-3.5 から GPT-4o への移行はモデル品質だけでなくトークン数も削減します。日本語マルチバイト主体のプロダクトなら、この差はそのまま月次請求に効きます。 - 価格改定への追従。 プロバイダがヘッドライン料率を下げたタイミング(OpenAI は 2024〜2025 年で 3 回やりました)では計算をやり直す。カウンターが同梱しているのは FAQ に日付付きで載った参照値です。出発点として使い、契約更新前にプロバイダの価格ページで照合してください。
参考資料
プロバイダ公式ドキュメントと標準資料。
- OpenAI tokenizer cookbook —
tiktokenの Python と JS の公式サンプル - Anthropic count_tokens API — API 経由で Claude の厳密値を取得
- Google AI count_tokens — Gemini API 経由で SentencePiece カウンターを叩く
- BPE 原論文 (Sennrich et al., 2016) — 基礎技術の原典
- Karpathy “Let’s build the GPT tokenizer” — BPE 内部を 3 時間で解説
関連する ZeroTool ウィジェット。
- 単語 / 文字数カウンター — 文字数や単語数のクオータが効く場面、トークンが必要ない場面で
- JSON フォーマッター — トークン計測前にツール呼び出しスキーマを整える
- cURL → コード変換 — プロバイダの API サンプルを Python / JS / Go / PHP / Node に変換
- ダミーデータ生成 — トークン数が判明している合成プロンプトを作って負荷試験へ