コンテキストウィンドウ トークン管理 コスト最適化 Responses API LLM API

長い会話のためのLLMコンテキストウィンドウ管理

コンテキストウィンドウには2つのコストがある。オーバーフローエラーとターンごとの課金だ。トークンの数え方、履歴のトリミングと要約、安定プレフィックスのキャッシュ、サーバー側コンパクションの使い方。

AveMujica API 1 分で読めます

成長する会話は2つの異なる方法で失敗し、それらを混同すると間違った修正に至ります。うるさい失敗はオーバーフローです。プロンプトと要求された出力がモデルのコンテキストウィンドウを超え、リクエストがきっぱり拒否されます。静かな失敗はコストです。コンテキストに保持するすべてが毎ターン再課金されるため、決してオーバーフローしないチャットでも、履歴が積み重なるにつれて線形に高価になりえます。コンテキストウィンドウの管理とは、両方に対して防御することを意味します。トークン数を硬い上限の下に保つことと、同じ履歴を永遠に再送信する代金を払わないほど小さく保つことです。この記事では、予算の測り方、それを抑えるトリミングと要約の戦略、そして長い会話のコストを削減するキャッシングとサーバー側コンパクションを扱います。

1つの予算、入力・出力・推論で共有

コンテキストウィンドウは、リクエスト全体が引き出す単一のトークン予算です。「上限までの入力トークン、その上に出力」ではありません。入力トークン、モデルに生成を求めるトークン、そして(推論モデルでは)隠れた推論トークンのすべてが、同じ天井を奪い合います。2つの帰結が直ちに従います。第一に、長いプロンプトは回答に残される余地を縮めるため、詰め込みすぎたコンテキストは硬いオーバーフローエラーがなくても打ち切られた、あるいは空の応答を強いることがあります。第二に、推論モデルでは目に見えない推論トークンもその天井を食い込むため、推論モデルのAPIパラメーター が出力の余地を確保することを強調するのです。ウィンドウを全体として予算化しましょう。履歴に占めさせるトークン数、推論のために確保するトークン数、可視の回答のために保証するトークン数を、前もって決めます。

ウィンドウを超えるのは硬いエラーです。プロバイダーはトークンを静かに落とすのではなくコンテキスト長エラーを返すため、オーバーフローはモデルが吸収するのではなくあなたが防ぐべき問題です。防御は、送信する前にトークン数を知ることです。

送信前に測る

測らない予算は管理できません。すべての履歴管理の判断は、これから行うリクエストの正確なトークン数に依存し、「トークンあたりおよそ4文字」は、コード、非ラテン文字、構造化データでは大きくずれる目安です。可能なところではプロバイダー自身のトークナイザーで数え、最新のユーザーターンだけでなくリクエスト全体、すなわちシステム指示、ツール、すべてのメッセージ、添付されたドキュメントを数えましょう。

数えることは、モデルが何を見るかについて正直でなければなりません。ツール定義、構造化出力 のためのJSONスキーマ、画像はすべてトークンを消費し、「会話」らしく見えないため忘れやすいのです。大きなツールカタログは、呼び出しごとにウィンドウの意味のある一片を静かに占有しえます。信頼できるカウントを得たら、以下の戦略はその数値を望むところに保つためのポリシーにすぎません。

トリミング戦略、粗いものから巧妙なものまで

履歴を予算内に保つ一般的な方法が4つあり、実際のシステムはそれらを組み合わせます。

最も古いターンを切り捨てる。 最も単純なポリシーです。システムプロンプトに加えて直近のNターンを保持し、残りを落とします。安価で予測可能であり、古いコンテキストがめったに重要でないサポート型のチャットには正しいデフォルトです。その弱点は健忘です。モデルは長いセッションの早い段階で確立された事実を忘れます。

要約して置き換える。 古いターンを落とす代わりに圧縮します。定期的にモデルに会話の最も早い部分をコンパクトなメモに要約させ、それらのターンをその要約で置き換えます。これは追加の呼び出しと多少の忠実度の損失と引き換えに、要点をトークンの一部で保存します。1時間前に下した決定を覚えていなければならない長いアシスタントには、たいていこのトレードは価値があります。

保持せず取得する。 対話ではなくドキュメントに存在する知識については、コンテキストにまったく持ち込みません。外部に保存し、各ターンで現在の質問に関連する箇所だけを取得してそれを注入します。取得は、ソース素材がどれだけあってもウィンドウを小さく一定に保ちます。マニュアル全体の代金を毎ターン払うのと、重要な2段落の代金を払うのとの違いです。

安定したプレフィックスを保ち、末尾を変える。 変わらない部分(システムプロンプト、ツール、参照素材)が前に座り、直近のターンだけが末尾で変わるように会話を構造化します。それ自体が良い衛生であり、次に論じるキャッシングの前提条件です。

最も耐久性のあるチャットシステムはこれらを重ねます。固定のシステムプレフィックス、中間の走行要約、直近の数ターンをそのまま、そして事実的なものには取得です。正確な組み合わせは、あなたの優先事項が想起、レイテンシ、コストのどれかによります。

再送信を避けられないプレフィックスをキャッシュする

一部のコンテキストは本当に毎ターン存在しなければなりません。詳細なシステムプロンプト、固定のツールセット、議論中のドキュメントなどです。タスクを壊さずにトリミングすることはできませんが、それを再処理する満額を払うのをやめることはでき、それこそまさにプロンプトキャッシングが行うことです。安定したプレフィックスがキャッシュされると、プロバイダーはそれを再利用し、それらのトークンを満額の入力レートではなく大幅な割引で課金します。管理上の一手は、キャッシュ可能な領域が最大かつバイト安定になるよう会話を配置すること、すなわちその内側にタイムスタンプや並べ替えたツールを入れず、揮発性の末尾をキャッシュ境界の後に落とすことです。プロバイダーごとの仕組みは OpenAI、Claude、Geminiにまたがるプロンプトキャッシング にあります。ここでの要点は、キャッシングとコンテキスト管理は2つの角度から見た同じ規律だということです。トリミングは送るトークン数を減らし、キャッシングは避けられないトークンのコストを減らします。

サーバー側の状態は履歴を無料にしない

Responses API は、履歴全体を再送信する代わりに previous_response_id でスレッドを継続させてくれます。これは本物の使い勝手の勝利です。新しいターンだけを送れば、サーバーがそれを保存されたコンテキストに縫い合わせます。これを「以前のコンテキストは今や無料」と読みたくなります。そうではありません。連鎖内のすべての以前の入力トークンは、各継続で依然として入力トークンとして課金されます。連鎖はバイトをワイヤーで送ることから救うのであって、モデルがなお処理するコンテキストの代金を払うことから救うのではありません。つまりサーバー側の会話状態は、誰が履歴を保持するかを変えるのであって、それに対して払うかどうかを変えるのではありません。コストを制御するには上記のトリミングとキャッシングがなお必要です。previous_response_id はそれと直交しています。

コンパクション:サーバー上で会話を縮める

会話が、あなた側での要約すら扱いにくいほど長く成長したとき、一部のResponsesスタイルのワークフローは、走行中のコンテキストを、そこから継続できるより小さな表現に凝縮するサーバー側のコンパクションステップを公開します。このゲートウェイはそれを専用のエンドポイント POST /v1/responses/compact として表出させ、それをサポートする上流へコンパクションリクエストを中継します。リクエストは凝縮する会話を運びます。model、input、instructions、そして任意で previous_response_id、tools、reasoning オブジェクトです。応答は凝縮された output に加えて、コンパクション自体が消費した入力と出力のトークンを含む usage オブジェクトを返します。

curl https://your-gateway.example.com/v1/responses/compact \
  -H "Authorization: Bearer AVE_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.6",
    "previous_response_id": "resp_abc123",
    "instructions": "Condense the conversation so far, preserving decisions and open tasks."
  }'

留意すべきことが2つあります。コンパクションはそれ自体がモデル呼び出しです。会話を読み、凝縮版を書くため、他のどのリクエストとも同様に使用ログへ現れるトークンを消費します。1回限りのコンパクションコストを、より安い後続ターンと引き換えにします。そしてそれは、コンパクションエンドポイントを実装する上流に対してのみ意味を持つため、パススルー能力であって、それを欠くプロバイダーのためにゲートウェイが合成できるものではありません。利用可能なところでは、それは「要約して置き換える」戦略のサーバー側の類似物であり、圧縮作業を自前のクライアントで走らせる代わりに上流へ移します。

回答のために余地を確保する

どの組み合わせを選ぶにせよ、常に可視の回答に十分なスペースを残しましょう。明示的な出力の上限を設定し、context_used + reserved_output がウィンドウの下に余裕をもって収まることを確かめ、推論モデルではその確保が隠れた推論トークンもカバーしなければならないことを覚えておきましょう。これを省くと、リクエストが成功し、大きな入力の代金を請求し、終える余地が残っていなかったために打ち切られた回答を返す、というもどかしい失敗になります。同じ原則をタイムアウトにも適用し、意図的に余裕を残してください。

まとめ

長時間走るアシスタントのためのコンテキストポリシーは、たいていこう見えます。キャッシュ可能なシステムプレフィックスを固定し、直近の数ターンをそのまま保ち、古い中間の走行要約(またはサーバー側コンパクション)を維持し、事実は持ち歩く代わりに外部ストアから取得し、毎回の送信前にトークンを数え、固定の出力予算を確保します。各ピースは2コスト問題、すなわちオーバーフローかターンごとの課金の特定の部分を狙っており、合わせて会話を硬い上限の下に、かつ絶えず上昇するコスト曲線から外して保ちます。ゲートウェイの役割はその効果を可視にすることです。リクエストごとのトークン数を観察し、履歴ポリシーの変更が実際に数値を動かしたことを確認する1つの場所であり、それは LLM APIコストの削減 というより広い作業に結びつきます。

FAQ

リクエストがコンテキストウィンドウを超えると何が起こりますか?

デフォルトでは、プロバイダーはコンテキスト長エラーで拒否し、収めるためにプロンプトを静かに切り捨てることはしません。そのため、送信前にトークンを数えてオーバーフローを防ぐ責任はあなたにあります。例外はResponsesのオプトイン式 truncation: auto で、大きすぎるリクエストを収めるために入力の中央にある項目を削除します。便利ですが、選んでいない内容が静かに失われるため、安全網としてではなく意図的に有効化してください。入力、出力、推論のトークンが1つの予算を共有するため、長すぎるプロンプトは硬い上限の下でも、生成する余地が足りず回答を打ち切らせることがあります。

previous_response_id を使うとトークンの請求は減りますか?

いいえ。履歴をワイヤーで再送信することから救いますが、連鎖内のすべての以前の入力トークンは各継続で依然として入力として課金されます。サーバー側の状態は使い勝手とレイテンシの機能であって、コスト削減ではありません。コンテキストコストを実際に削るには、なおトリミング、要約、そして安定プレフィックスのプロンプトキャッシングが必要です。

履歴を要約するか切り捨てるか、どちらを使うべきですか?

切り捨て(直近のNターンを保つ)は最も単純で、古いコンテキストがめったに重要でないときは問題ありませんが、早い事実を忘れます。要約は古いターンをコンパクトなメモに圧縮し、圧縮ごとの追加コストで要点を保存します。以前の決定を覚えていなければならない長いアシスタントは、たいてい両方を走らせます。直近のターンをそのまま、加えて古い中間の走行要約、そして事実的なものには取得です。

/v1/responses/compact エンドポイントは何をしますか?

Responsesの会話を、そこから継続できるより小さな表現に凝縮することをサポートする上流へ、コンパクションリクエストを中継します。会話を(input/instructions および任意で previous_response_id 経由で)渡すと、凝縮された output と、コンパクションが消費したトークンの usage カウントを返します。それはパススルー能力であり、上流が実装している場合にのみ利用可能で、要約して置き換えるのサーバー側版です。

曲線を描き、平らにする

長時間続く会話を1つ計測し、毎ターンの合計トークン数を記録して曲線を描いてください。着実に上昇するなら、閾値でローリング要約またはコンパクションを追加し、使用ログ上で曲線が平らになるのを確認します。次にシステムプレフィックスをキャッシュ可能にし、ターンごとの入力料金が下がることを確かめます。トークン数と入力コストという2つの前後曲線が、コンテキスト方針が理論上ではなく自分のトラフィックで実際に機能しているかを示します。

参考文献