朝、検証用CSVの input_tokens と cache_read_tokens を何気なく足していて、手を止めた。後者を前者の内訳として出すAPIなら、足すほど利用量を多く見積もってしまう。
8月13日にはWriterがトークンコストを抑えるハーネスを含む新しいAIモデルを発表した。新モデルの単価に目が行くけれど、運用で先に壊れるのはキャッシュが効いていた前提だったりする。プロンプトの先頭へ日付や実行IDを足した、デプロイでキャッシュが温まり直した、会話履歴が閾値を越えた。こういう変更で入力の大半が急に通常入力へ戻る。
料金表を毎回書き換える前に、キャッシュ率が落ちた時刻をログから拾う。標準ライブラリだけで動く。
まずログの意味を固定する
このコードは、CSVの cache_read_tokens が input_tokens の部分集合である、と決めている。APIごとにusageフィールドの意味は違うので、生のレスポンスは収集側で次の4列へ正規化する。
| 列 | 意味 |
|---|---|
timestamp |
リクエスト完了時刻 |
input_tokens |
入力トークン総数 |
cache_read_tokens |
そのうちキャッシュから読んだ入力トークン数 |
output_tokens |
出力トークン数 |
キャッシュ率は cache_read_tokens / input_tokens。モデルやリージョンが混じるログは、分けるかフィルタしてから実行する。異なる系列を一緒にした中央値は、だいたい役に立たない。
直前5回の中央値から落下を拾うPython
直前5件の中央値が50%以上で、今回がその40%以下なら警告する。中央値なら、単発の長い入力で基準線が動きすぎない。
import csv
import statistics
import sys
def cache_ratio(row: dict[str, str]) -> float:
input_tokens = int(row["input_tokens"])
cache_read_tokens = int(row["cache_read_tokens"])
if input_tokens <= 0:
raise ValueError("input_tokens must be positive")
if not 0 <= cache_read_tokens <= input_tokens:
raise ValueError("cache_read_tokens must be within input_tokens")
return cache_read_tokens / input_tokens
with open(sys.argv[1], newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
ratios: list[float] = []
for line, row in enumerate(rows, start=2):
current = cache_ratio(row)
if len(ratios) >= 5:
baseline = statistics.median(ratios[-5:])
if baseline >= 0.50 and current <= baseline * 0.40:
print(f"cache-drop line={line} time={row['timestamp']} "
f"baseline={baseline:.1%} now={current:.1%}")
ratios.append(current)
今日、次の6行で動かした。最後だけキャッシュ読取をゼロにしている。
timestamp,input_tokens,cache_read_tokens,output_tokens
2026-08-14T09:00:00Z,11000,9000,412
2026-08-14T09:05:00Z,11000,9100,398
2026-08-14T09:10:00Z,11000,8900,421
2026-08-14T09:15:00Z,11000,9200,407
2026-08-14T09:20:00Z,11000,9000,416
2026-08-14T09:25:00Z,11000,0,405
python cache_drop.py usage.csv の出力はこれだった。
cache-drop line=7 time=2026-08-14T09:25:00Z baseline=81.8% now=0.0%
ここまでで、見るべき順序が決まる。
警告が出たら、モデルを替える前に先頭を比べる
この警告は失効の断定ではない。時刻の近いリクエストを2件だけ取り出し、先頭からどこまで同じだったかを見る。システムプロンプト、ツール定義、固定の検索結果、会話要約の挿入位置。このあたりで差が出ることが多い。
キャッシュは、同じ文字列がどこかに含まれていれば効くものではない。多くの実装では先頭側の安定したプレフィックスが効きどころになる。毎回変わる日時やUUIDを先頭へ置くと、後ろの固定指示が長くても比較しづらい。自分は可変情報を末尾か別メッセージへ逃がしている。落ちた時だけ、その並び順を疑えば十分だった。
出力トークンが増えた障害と、キャッシュ読取が消えた障害は修正場所が違う。前者は生成設定やタスク分割、後者はリクエスト組み立てと有効期限を見る。usageを合算した一つの数字だけ追うと、この区別が消える。
おわりに
単価の安いモデルへ切り替えても、キャッシュ率が81%から0%へ落ちれば見積もりは外れる。usageログに入力の内訳を残し、落下した時刻だけを開く。料金の変動をモデル選定の問題へ誤って寄せずに済む。
このスクリプトは料金を計算しない。課金単位はAPIごとに違うが、キャッシュ率の急落は、調べるべきリクエストを先に教えてくれる。
出典: Writer introduces new AI model and upgraded harness to contain token costs