LLMを活用したエージェントは、デモでは完璧に動作しても、数回のやり取りの後に動作が重くなり、請求額が膨れ上がることがあります。その隠れた原因は、モデルの不安定さではなく、「トークンドリフト(token drift)」、つまりモデルが毎回処理しなければならないプロンプトが徐々に肥大化していく現象です。
トークンドリフトは、やり取りが行われるたびにモデルの入力コンテキストにテキストが追加されることで発生します。会話履歴、ツールのスキーマ、APIレスポンス、取得されたドキュメントなどがすべて積み重なり、その結果、呼び出しを繰り返すごとにペイロードが大きくなっていきます。モデルの処理時間と料金は入力トークン数に応じて増加するため、コストは線形ではなく二次関数的に上昇します。
なぜデモではなく本番環境で問題が表面化するのか
本番環境へのデプロイでは、ユーザーの発言、ツールの出力、取得された知識の断片など、すべてが保持されます。この蓄積は、レイテンシが急増し、請求書が届くまで表面化しません。
トークンドリフトの主な原因
- 繰り返されるトランスクリプト – 古いメッセージを要約したり破棄したりせずに、すべてプロンプトに残してしまうこと。
- 重いツールスキーマ – ターンごとにツールの機能に関する巨大なJSON定義を送信すること。
- 肥大化したツール結果 – エージェントが実際に必要とする以上のデータを含む、APIレスポンスの全文やデータベースの行を含めてしまうこと。
- RAGの肥大化 – 検索拡張生成(RAG)において、古かったり無関係だったりするドキュメントのチャンクを大量に追加してしまうこと。
- 重複するメモリ – 要約、ステートオブジェクト、生のトランスクリプトをまとめて詰め込み、同じ情報を3回繰り返してしまうこと。
これらはすべて、新たな推論能力には寄与しないトークンを追加し、プロンプトのサイズを膨らませる原因となります。
トークン予算を管理する方法
1. レイヤード・コンテキスト設計を採用する
- 安定した指示 – システムプロンプトや安全ルールを最上部に保持し、毎ターン再送するのではなく、それらを参照するようにします。
- 構造化されたステート – エージェントが素早く読み取れるよう、目標、決定事項、識別子をコンパクトな形式で保存します。
- 圧縮された履歴 – 古いやり取りを人間が読める短い段落に要約し、しきい値に達したときのみ更新します。
- 直近のやり取り – 文脈の連続性を保つため、直近の数件のメッセージはそのまま含めます。
固定テキストと要約可能なコンテンツを分離することで、同じ言葉を何度も再送してしまうのを防げます。
2. ツール出力を削ぎ落とす
- エージェントが実際に使用するフィールドのみを抽出し、冗長な説明は削除します。
- 巨大な結果は簡潔な要約またはリファレンスIDに置き換え、フルペイロードはデータベース、キャッシュ、またはBlobストレージに保存します。
- ツールがリストを返す場合は、現在の決定に重要な上位N個のアイテムのみを送信します。
3. スマートな要約を適用する
- 毎ターン要約を行うのは避けましょう。余分な処理がオーバーヘッドとなります。
- 古いやり取りの累積トークン数が設定された制限を超えたときにのみ、要約を更新します。
- ID、金額、タイムスタンプなどの重要な事実は、散文の中に埋め込むのではなく構造化されたストアに保持することで、要約を短く保ちます。
4. 正しいメトリクスを追跡する
- ユーザーのリクエストごとではなく、モデルの呼び出しごとのトークン使用量をログに記録します。これにより、入力側の隠れた増加が明らかになります。
- ターンごとに追加される入力トークン数を監視します。急激な増加はドリフトの原因を示しています。
- キャッシュされたトークン(前回の呼び出しから再利用されたもの)と、新しく生成されたトークンを区別します。ドリフトを引き起こすのは前者のみです。
プロンプトを無限のトランスクリプトではなく、有限のリソースとして扱いましょう。意図的に測定、要約、削減を行うことで、LLMエージェントの高速性、低コスト、そして本番環境のスケールへの対応力を維持できます。
まとめ: トークンドリフトは、知らぬ間にコストを増大させ、エージェントの動作を遅らせます。プロンプトの中で肥大化している部分を特定し、それらを圧縮または外部化し、呼び出しごとのトークン使用量を監視しましょう。規律あるアプローチによって、予測不可能な請求額の急増を、管理可能で予算内に収まる運用へと変えることができます。
