プロンプトキャッシュとコンテキスト設計 — 前方一致という一本の制約が、請求額を桁で変える
同じシステムプロンプトを毎回読み直させて、毎回その分を払っていませんか。プロンプトキャッシュは前方一致でしか効かない——この一点だけで、コンテキストに何をどの順で置くべきかが決まります。先頭に現在時刻を1つ入れただけで全滅する理由と、TTLを読み違えて逆に25%高くなる事故まで。
毎朝、同じマニュアルを音読させている
新しい部下に仕事を頼むたび、300ページのマニュアルを最初のページから声に出して読み上げさせ、読み終わってからようやく用件を伝える。そんな職場があったら、誰でもおかしいと思うはずです。
LLM APIへのリクエストは、既定ではそれをやっています。APIはステートレスなので、前回の会話も、システムプロンプトも、ツール定義も、参照させたい社内ドキュメントも、毎回丸ごと送り直している。受け取ったモデルは、その全部を先頭から読み直します。ここでいう「読む」とは全トークンをAttentionに通して内部表現を作ることで、その計算に対して入力トークン料金を払っています。
プロンプトキャッシュは、この読み直しを省く仕組みです。「前回ここまで読んだ結果を取ってあるので、続きからどうぞ」とモデルに言えるようにする。ただし、この仕組みにはきわめて硬い制約が1つだけあります。それを理解しているかどうかで、同じアプリの請求額が桁で変わります。
なぜ「前方一致」でしか効かないのか
制約の正体は、Transformerの構造そのものにあります。
言語モデルの注意機構は因果的(causal)です。位置 のトークンは、自分より後ろを見ることができません。つまり、そのトークンの内部表現は、先頭から自分までの入力だけで決まります。
式(1)が言っているのは一言だけです。 番目のトークンのKとVは、1番目から 番目までの入力だけから作られる。 番目以降に何が来ようと、値は1ビットも変わらない。
ここから結論が自動的に出ます。2つのリクエストが先頭から トークン目まで完全に同一なら、その 個分のKとVは必ず同じ値になります。だから計算し直す必要がない。取っておいたものをそのまま使えます。
逆も自動的に決まります。先頭から数えて1トークンでも違いがあれば、その位置以降のすべての が変わるので、再利用できるのはその1つ手前まで。これが前方一致(prefix matching)です。中間一致も後方一致も、原理的に存在しません。プロンプトの真ん中のドキュメントを1本差し替えたら、その後ろは全部作り直しになります。
1回のリクエストの中でKVを使い回す話はKVキャッシュを1から理解するで扱いました。プロンプトキャッシュは、そのKVキャッシュをリクエストをまたいで生き延びさせたものだと思ってください。原理は同じで、寿命だけが違います。
料金表は3段になる
キャッシュを有効にすると、入力トークンの単価が3種類に分かれます。Anthropic APIの現行の倍率で言うと、こうです(正確な値は公式の料金表で確認してください)。
| 種別 | 単価 | いつ発生するか |
|---|---|---|
| 通常の入力 | 1× | キャッシュ対象外の部分 |
| キャッシュ書き込み | 1.25×(既定TTL)/ 2×(長寿命TTL) | 初回、またはミスしたとき |
| キャッシュ読み出し | 0.1× | ヒットしたとき |
「桁で変わる」の正体は最後の行、0.1×です。ヒットしている限り、その部分は10分の1で買えます。つまり削減の上限は10倍、それ以上にはなりません。
書き込みが1.25×と少し割高なのが効いてきそうに見えますが、損益分岐はすぐ来ます。同じprefixを2回使うだけで、 となり、キャッシュなしの より安い。寿命の内に2回使われるなら、もう元は取れているわけです。
会話が伸びると、費用は2乗で伸びる
キャッシュが本当に効いてくるのは、会話が積み上がる場面です。ここで何が起きているかを数えてみます。
システムプロンプトが2,000トークン、1ターンごとに(ユーザー発話+モデルの返答で)履歴が1,000トークンずつ伸びる、20ターンの会話を考えます。 ターン目の入力量は トークンです。20ターン分を足し上げると、
つまり「毎ターンの入力量を20ターン分ぜんぶ足すと23万トークンになる」ということです。会話が終わった時点で画面に載っている文章は21,000トークンぶんしかないのに、その11倍を買っていることになります。ターン数を とすると累計入力量は に比例するので、会話が長引くほどこの差は開きます。
コメント
コメントにはログインが必要です