AIエージェントを長時間動かしていると、最初は正常だったのに、途中から回答が遅くなったり、以前決めたルールを忘れたり、関係のないツールを呼び出したりすることがあります。
原因のひとつが、コンテキストの肥大化です。
AIエージェントではユーザーとの会話だけでなく、System Prompt、ツール定義、Tool Callingの結果、検索結果、RAGで取得した文章、サブエージェントの出力なども毎ターンモデルへ渡されます。これらを積み上げていくと、コンテキストは想像以上の速度で大きくなります。
コンテキストウィンドウが大きいモデルを使えば解決するように見えますが、単純に上限を増やせばよいわけではありません。
Anthropicも、コンテキストが増えるにつれて応答品質が低下するため、長時間動作するAgentでは古いコンテンツを要約するCompactionを利用してアクティブなコンテキストを小さく保つ方法を案内しています。OpenAI Agents SDKにも、履歴の取得件数を制限する仕組みや、長い会話を自動的に圧縮するOpenAIResponsesCompactionSessionが用意されています。
この記事では、AIエージェントのコンテキストが長くなりすぎる原因と、会話履歴を圧縮・削除・分割して長時間安定して動かすための設計を解説します。
- AIエージェントの「コンテキスト」とは
- コンテキストウィンドウと長期記憶は別物
- 会話履歴を毎回すべて送るとコンテキストが増え続ける
- OpenAI Agents SDKのSessionも履歴が増えていく
- Tool Callingの結果がコンテキストを急激に増やす
- Tool Resultは必要な情報だけ返す
- 過去のTool Resultをいつまでも残さない
- RAGで大量の文書を毎回入れている
- RAGは取得件数より関連性を重視する
- 大量のツール定義もコンテキストを消費する
- タスクごとに利用可能なツールを分ける
- System Promptが長くなりすぎている
- プロンプトキャッシュだけではコンテキスト肥大化を解決できない
- 長いコンテキストは上限に達する前から問題になる
- 古い履歴を一定件数で切る
- 古い履歴を要約して圧縮する
- OpenAI Agents SDKではCompactionSessionを使える
- AnthropicにもサーバーサイドCompactionがある
- 要約するときに残す情報を決める
- 要約だけに依存すると重要な情報が失われる
- 会話とタスク状態を分離する
- 長いタスクはフェーズごとにセッションを分ける
- サブエージェントに作業を分割する
- サブエージェントの全文をMain Agentへ返さない
- ファイル内容を会話履歴へ保存し続けない
- ログも全文を渡さない
- コンテキスト使用量を監視する
- 上限ギリギリまで使わない
- 古い履歴を削除するだけではなく「再取得できる」設計にする
- 実用的なのは「要約+直近履歴+外部メモリ」
- コンテキスト圧縮と分割は併用したほうがよい
- AIエージェントのコンテキストの長期化に関するよくある質問
- まとめ
AIエージェントの「コンテキスト」とは
コンテキストとは、モデルが現在のレスポンスを生成するときに参照できる情報です。
単純なチャットであれば、System Promptとユーザー、Assistantの会話履歴が中心になります。
AIエージェントでは、そこへツール関連の情報が追加されます。
たとえば商品を検索するAgentなら、内部では次のような情報を何度もモデルへ渡すことになります。
System Prompt User: RTX 5070のPCを探して Assistant: search_productsを呼び出す Tool: 20件の商品データ Assistant: 候補を比較する User: もう少し安いものは? Assistant: search_productsを再度呼び出す Tool: さらに20件の商品データ
ユーザーから見ると短い会話でも、モデル側では大量の商品情報がコンテキストへ追加されている可能性があります。
さらに数十種類のFunction Toolが登録されていれば、それぞれの名前、description、JSON Schemaもコンテキストを消費します。
AnthropicのTool Context Managementでも、ツール定義と蓄積したtool_resultの両方がコンテキストウィンドウを消費し、多数のツールや長時間のAgent処理ではタスク完了前に利用可能なコンテキストを使い切る可能性があると説明されています。
コンテキストウィンドウと長期記憶は別物
AIエージェントを設計するときに混同しやすいのが、コンテキストとメモリです。
コンテキストウィンドウは、モデルがその瞬間の推論で参照する「作業領域」に近いものです。
一方、データベースへ保存したユーザー情報や過去の会話要約などは長期記憶として扱えます。
たとえば、
ユーザーの名前 ユーザー設定 プロジェクトの仕様 過去に決定した重要事項
を永久に毎回のプロンプトへ入れる必要はありません。
データベースへ保存しておき、必要になったときだけ現在のコンテキストへ取り出せばよいからです。
つまり、
覚えておく必要がある
ことと、
毎ターンモデルへ送る必要がある
ことは別です。
長時間動くAIエージェントでは、この2つを分離することが非常に重要になります。
会話履歴を毎回すべて送るとコンテキストが増え続ける
もっとも分かりやすい原因が、会話履歴を毎回すべてモデルへ送る実装です。
たとえば次のような配列を管理しているとします。
const messages = [];
messages.push({
role: "user",
content: userMessage
});
const response = await client.responses.create({
model: "gpt-5.6",
input: messages
});
messages.push(...response.output);
このmessagesを削除せず使い続ければ、会話が続くたびに入力が増えていきます。
10ターン程度なら問題にならなくても、100ターン、500ターンと続けば大量の履歴を毎回送ることになります。
しかも古い会話ほど、現在の質問には必要ない情報になっている可能性があります。
「今日は東京の天気を調べて」という質問に対して、数日前の商品検索結果や大量のログを一緒にモデルへ渡しても意味がありません。
履歴を保存することと、毎回すべてを推論に利用することは分けて考える必要があります。
OpenAI Agents SDKのSessionも履歴が増えていく
OpenAI Agents SDKではSessionを使うと、複数ターンの会話履歴を自動管理できます。
たとえば次のように利用できます。
from agents import Agent, Runner, SQLiteSession
agent = Agent(
name="Assistant"
)
session = SQLiteSession(
"conversation_123"
)
result = await Runner.run(
agent,
"前回の続きをしてください",
session=session
)
非常に便利ですが、Sessionを使えばコンテキスト肥大化を自動的に防げるわけではありません。
OpenAI Agents SDKのSessionでは、各実行前に保存された会話履歴を取得して入力へ追加し、実行後には新しいユーザー入力、Assistantレスポンス、Tool CallなどをSessionへ保存します。
つまり長期間同じSessionを使えば、保存される履歴も増えていきます。
そのため長時間動作するAgentでは、Sessionを利用するだけでなく「どの履歴を次回のモデル呼び出しへ含めるか」も設計する必要があります。
Tool Callingの結果がコンテキストを急激に増やす
AIエージェントで特に問題になりやすいのがTool Resultです。
たとえば検索APIが100件の商品を返したとします。
{
"products": [
{
"id": 1,
"name": "...",
"description": "...",
"price": 100000
}
]
}
実際には、このようなオブジェクトが100件含まれる可能性があります。
モデルが必要としているのは商品名と価格だけなのに、商品説明、内部ID、画像URL、更新日時、在庫履歴などまで返していると、大量のトークンを消費します。
さらに次の検索でも100件返せば、その結果も追加されます。
AIエージェントのコンテキストが急激に増える場合、会話履歴よりTool Resultのほうが原因になっているケースも珍しくありません。
Tool Resultは必要な情報だけ返す
Tool Callingでは、APIレスポンスをそのままモデルへ渡すのではなく、必要な情報だけに変換する方法が有効です。
たとえば商品APIが次の情報を返すとします。
{
"id": 12345,
"name": "Gaming PC A",
"price": 149800,
"description": "非常に長い商品説明...",
"image_url": "...",
"created_at": "...",
"updated_at": "...",
"internal_status": "...",
"warehouse_id": "..."
}
モデルが比較に必要なのが商品名と価格だけなら、
{
"name": "Gaming PC A",
"price": 149800
}
まで削れます。
ツール設計では「APIから何を取得するか」だけでなく、「モデルへ何を返すか」も重要です。
内部システムのJSONをそのままTool Resultにするのではなく、LLM向けのデータ形式へ変換する層を作っておくとコンテキストを大幅に削減できます。
過去のTool Resultをいつまでも残さない
さらに重要なのが、古くなったTool Resultの扱いです。
たとえば最初に東京の天気を取得します。
get_weather("Tokyo")
結果:
25℃、晴れ
その後、ユーザーが大阪について質問したとします。
get_weather("Osaka")
結果:
28℃、曇り
さらに福岡、札幌、名古屋と検索を続ければ、それぞれのTool Resultが履歴へ積み上がっていきます。
しかし現在の回答に東京の古い天気情報が不要なら、アクティブなコンテキストへ残す意味はほとんどありません。
Anthropicでは、この問題に対してContext Editingによる古いTool Resultのクリアを提供しています。公式ドキュメントでも、長時間動作するAgentで蓄積するTool Resultを削除することがコンテキスト管理の方法として案内されています。
自前のAgentでも同じ考え方を利用できます。
重要なのは「過去にToolを使ったという事実」と「Toolが返した巨大な生データ」を同じように保存しないことです。
RAGで大量の文書を毎回入れている
RAGもコンテキスト肥大化の大きな原因になります。
ユーザーの質問に関連する文書をVector Databaseから取得して、
検索結果1 検索結果2 検索結果3 検索結果4 検索結果5
をモデルへ渡す仕組みは一般的です。
しかし、Top 20やTop 50のChunkを毎回そのままコンテキストへ追加すると、すぐに巨大になります。
特に1Chunkが長い場合は注意が必要です。
たとえば1Chunkが1000トークンで、20件取得すれば、それだけで約2万トークンになります。
その履歴を次のターンにも残したまま、さらに20件検索すれば、コンテキストが急増します。
RAGでは検索結果を永続的な会話履歴として扱わず、その質問に必要な一時的コンテキストとして扱う設計も検討する必要があります。
RAGは取得件数より関連性を重視する
「情報を多く与えれば回答精度が上がる」と考えて大量のChunkを渡すのは逆効果になる場合があります。
現在の質問に本当に関係する情報だけを選ぶほうが、モデルが重要な内容へ集中しやすくなります。
たとえば最初にVector Searchで候補を20件取得しても、rerankして上位5件だけモデルへ渡す設計にできます。
さらに、必要ならAgent自身が追加検索できるようにしておけば、最初からすべての情報を詰め込む必要はありません。
コンテキストウィンドウはデータ置き場ではなく、現在の推論に必要な作業領域として扱ったほうが安定します。
大量のツール定義もコンテキストを消費する
Tool CallingではTool Resultだけでなく、Function Definition自体もコンテキストを使います。
たとえば1つのFunctionに、
name description parameters properties required enum
などが含まれます。
これが5個程度なら大きな問題にならなくても、100個のFunctionを毎回モデルへ渡せば相当な量になります。
特に各FunctionのdescriptionやJSON Schemaが長い場合は影響が大きくなります。
AnthropicもTool Context Managementで、大規模なTool SetではTool Searchを利用して、必要なツールだけを動的に読み込む方法を紹介しています。
AIエージェントでTool Callingを安定させるという意味でも、毎ターンすべてのFunctionをモデルへ見せる必要があるか確認するとよいでしょう。
タスクごとに利用可能なツールを分ける
たとえばECサイト向けAgentに、
商品検索 注文検索 顧客検索 在庫確認 メール送信 返金 注文キャンセル 商品登録 レビュー検索 売上分析
など数十種類のToolがあるとします。
ユーザーが「RTX 5070の商品を探して」と質問している段階では、返金やメール送信のFunctionは必要ありません。
そこでタスク分類後に、現在必要なToolだけモデルへ渡すことができます。
商品検索フェーズでは、
const tools = [ searchProductsTool, getProductTool ];
だけを公開します。
注文サポートへ移ったら、
const tools = [ getOrderTool, cancelOrderTool ];
へ切り替えます。
このようにするとコンテキストを減らせるだけでなく、誤ったTool Callingを減らす効果も期待できます。
System Promptが長くなりすぎている
AIエージェントを改良していると、System Promptにもルールが追加され続けます。
最初は、
ユーザーをサポートしてください。
だけだったものが、
この場合はA この場合はB 例外の場合はC ツールXを使う場合はD ツールYの結果が空ならE
と増えていきます。
さらに失敗が起きるたびに、
絶対に○○しないこと
というルールを追加すると、数千行のSystem Promptになることもあります。
System Promptは毎ターン必要になることが多いため、肥大化すると恒常的にコンテキストを消費します。
プロンプトを長期記憶の代わりに使うのではなく、業務ロジックとしてコード側で制御できるものはコードへ移したほうがよい場合があります。
たとえば、
管理者以外はdelete_userを使用してはいけない
というルールをSystem Promptだけに書くのではなく、アプリ側の権限チェックとして実装します。
モデルへ説明しなくてもコードで確実に制御できるものは、コンテキストへ入れないという考え方も重要です。
プロンプトキャッシュだけではコンテキスト肥大化を解決できない
長いSystem Promptがある場合、Prompt Cachingを使えばコストを減らせることがあります。
しかし、キャッシュしたからといってコンテキストウィンドウを消費しなくなるわけではありません。
Anthropicも、キャッシュされたPrompt Prefixは引き続きコンテキストウィンドウを占有し、Prompt Cachingは料金の計算方法を変えるものであって、コンテキストへカウントされるかどうかを変えるものではないと説明しています。
つまり、
Prompt Cacheを使っているから長いSystem Promptでも問題ない
というわけではありません。
コスト対策とコンテキスト管理は分けて考える必要があります。
長いコンテキストは上限に達する前から問題になる
コンテキスト管理というと、
最大トークン数を超えなければ問題ない
と考えがちです。
しかし実際には、上限へ到達する前から問題になることがあります。
大量の古い情報が残っていると、モデルが現在重要な情報を判断しにくくなります。
その結果として、
以前の指示を現在の指示と勘違いする 古いTool Resultを使う 関係のない文書を引用する 間違ったFunctionを選ぶ すでに完了した作業を再実行する
といった問題が発生しやすくなります。
AnthropicもCompactionについて、単にコンテキスト上限を超えないようにするためだけでなく、会話が長くなるにつれて低下する応答品質を維持するため、古い内容を短い要約へ置き換える仕組みとして説明しています。
つまりコンテキスト管理は「エラー回避」だけでなく「精度維持」のためにも必要です。
古い履歴を一定件数で切る
もっとも簡単な対策は、直近の会話だけモデルへ渡す方法です。
たとえばPythonなら概念的には次のようにできます。
recent_history = history[-20:]
result = await run_agent(
input=recent_history
)
直近20件しか必要ない会話なら、それ以前の履歴はモデルへ送らなくて済みます。
OpenAI Agents SDKではSessionSettingsのlimitを利用して、Sessionから取得する履歴件数を制限できます。
from agents import (
Agent,
RunConfig,
Runner,
SessionSettings,
SQLiteSession,
)
agent = Agent(
name="Assistant"
)
session = SQLiteSession(
"conversation_123"
)
result = await Runner.run(
agent,
"続きをお願いします",
session=session,
run_config=RunConfig(
session_settings=SessionSettings(
limit=50
)
),
)
OpenAI公式ドキュメントでも、SessionSettings(limit=N)を使って最新N件だけ取得でき、長い会話で取得サイズを制限する用途に利用できると説明されています。
ただし単純に古い履歴を切るだけでは、重要な決定事項まで失われる可能性があります。
そこで次に必要になるのが要約です。
古い履歴を要約して圧縮する
長期間の会話で有効なのがCompactionです。
たとえば100ターンの履歴がある場合、古い80ターンをそのまま保持する代わりに、
これまでの決定事項: ユーザーはTypeScriptを使用する。 データベースはPostgreSQL。 認証にはOAuthを使用する。 商品検索APIは実装済み。 現在は注文検索機能を実装中。
のような短い状態へ変換します。
その後のモデル入力は、
過去の要約 + 直近20ターン + 現在のユーザー入力
という形にできます。
これなら重要な情報を維持しながら、古い会話そのものをコンテキストから外せます。
OpenAI Agents SDKではCompactionSessionを使える
OpenAI Agents SDKには、Responses APIのCompaction機能を利用するOpenAIResponsesCompactionSessionが用意されています。
たとえばSQLiteSessionをラップできます。
from agents import Agent, Runner, SQLiteSession
from agents.memory import OpenAIResponsesCompactionSession
underlying = SQLiteSession(
"conversation_123"
)
session = OpenAIResponsesCompactionSession(
session_id="conversation_123",
underlying_session=underlying,
)
agent = Agent(
name="Assistant"
)
result = await Runner.run(
agent,
"続きをお願いします",
session=session,
)
OpenAI Agents SDKでは、条件を満たした場合にSession履歴を自動的にCompactionし、長時間の会話を管理できます。
必要であれば自動Compactionを無効にし、任意のタイミングで手動実行することもできます。
session = OpenAIResponsesCompactionSession(
session_id="conversation_123",
underlying_session=underlying,
should_trigger_compaction=lambda _: False,
)
result = await Runner.run(
agent,
"続きをお願いします",
session=session,
)
await session.run_compaction({
"force": True
})
たとえばユーザー操作がない時間や、ひとつの作業フェーズが終わったタイミングで圧縮できます。
AnthropicにもサーバーサイドCompactionがある
Claude APIでも、長時間のAgent処理向けにサーバーサイドCompactionが提供されています。
Compactionを有効にすると、設定したトークンしきい値へ到達したときに以前の会話を要約し、その要約を含むcompactionブロックから処理を継続します。
Pythonでは概念的に次のように利用できます。
response = client.beta.messages.create(
betas=["compact-2026-01-12"],
model="claude-opus-5",
max_tokens=4096,
messages=messages,
context_management={
"edits": [
{
"type": "compact_20260112"
}
]
},
)
Anthropicでは、長時間の会話や大量のTool Callingを伴うAgent Workflowにおける主要なContext Management戦略としてCompactionを案内しています。
要約するときに残す情報を決める
履歴を要約するとコンテキストは小さくなりますが、何でも短くすればよいわけではありません。
たとえばコーディングAgentなら、
変更済みファイル 現在の実装状態 ユーザーが決めた仕様 失敗した方法 残っているTODO テスト結果
などは重要です。
逆に、
過去の雑談 すでに不要になった検索結果 大量の成功ログ 一時的なデバッグ出力
などは残す必要がないことがあります。
要約には「会話内容」ではなく「次の作業を再開するために必要な状態」を残すと使いやすくなります。
要約だけに依存すると重要な情報が失われる
Compactionにも欠点があります。
要約は元の履歴を完全に保存するわけではありません。
たとえばユーザーが30ターン前に、
絶対に本番DBへ書き込まないこと
と指定していたとします。
Compaction時の要約からこの情報が抜ければ、後続のAgentはその制約を知らなくなります。
そのため重要なルールや状態は、会話要約だけに依存しないほうが安全です。
たとえば、
project_state.json user_preferences database memory store task state
などへ構造化して保存します。
特に安全性、ユーザー権限、業務ルールなどは、会話履歴ではなくコードや永続データとして管理すべきです。
会話とタスク状態を分離する
長時間動くAIエージェントでは、すべての状態をチャット履歴へ保存する設計を避けると安定します。
たとえばコーディングAgentなら、
{
"current_task": "認証機能の実装",
"completed": [
"DBスキーマ作成",
"ログインAPI作成"
],
"remaining": [
"ログアウトAPI",
"テスト"
],
"decisions": {
"auth": "OAuth",
"database": "PostgreSQL"
}
}
のような状態を外部へ保存できます。
モデルへは毎ターン、この状態の必要な部分だけ渡します。
この方法なら、100ターン前の会話をすべて読み返さなくても現在の状態を復元できます。
長いタスクはフェーズごとにセッションを分ける
ひとつのSessionを永遠に使い続ける必要もありません。
たとえばWebアプリを開発するAgentなら、
要件定義 DB設計 API実装 フロントエンド実装 テスト
を別Sessionにできます。
DB設計が終了したら、その成果物をSchemaファイルとして保存します。
API実装のAgentには巨大なDB設計会話を渡すのではなく、完成したSchemaだけ渡します。
こうすると「どうやってその結論へ至ったか」という過去の議論をコンテキストから削除できます。
現在のAgentが必要なのは、多くの場合「議論の過程」ではなく「確定した結果」です。
サブエージェントに作業を分割する
大きなタスクを1つのAgentへすべてやらせるとコンテキストが肥大化します。
たとえば、
リポジトリ全体を調査 バグ原因を調査 修正案を作成 コード変更 テスト レビュー
まで同じAgentに担当させる構成です。
そこで調査だけをSubagentへ任せます。
Main Agentから、
このエラーの原因を調査してください。
と依頼し、Subagent内部では大量のファイルやログを読み込ませます。
最後にMain Agentへ返すのは、
原因はauth.tsのrefreshToken処理。 42行目で期限切れTokenを再利用している。 修正候補はrefreshTokenの再取得。
程度にまとめます。
Subagentが読んだ数万トークンのコードやログをMain Agentへそのまま返さなければ、Main Agentのコンテキストを小さく保てます。サブエージェントごとに独立した予算(Tool Call数・トークン・時間)を持たせる設計はAIエージェントの無限ループを防ぐ方法の「サブエージェントにも予算を持たせる」で解説しています。
サブエージェントの全文をMain Agentへ返さない
Subagentを使っていても、最終出力として巨大なレポートを返せば意味がありません。
Main Agentに必要なのは、
結論 重要な根拠 変更対象 次に行うべき処理
といった情報です。
調査用コンテキストと意思決定用コンテキストを分離する考え方が重要になります。
Subagentは「別のモデルを呼ぶ機能」というより、「一時的に巨大な作業コンテキストを隔離する仕組み」としても利用できます。
ファイル内容を会話履歴へ保存し続けない
コーディングAgentでは、ファイル内容もコンテキストを圧迫します。
たとえばAgentが1000行のファイルを読み込み、その内容をTool Resultとして会話履歴へ保存します。
その後ファイルを編集し、再び1000行読み込めば、古いバージョンと新しいバージョンの両方が履歴へ残る可能性があります。
さらに数回編集すれば、
初期版1000行 修正版1000行 再修正版1000行 最新200行
のように大量の重複情報が蓄積します。
ファイルはディスク自体がSource of Truthです。
過去のファイル全文を会話履歴へ保持する必要はありません。
現在必要な部分だけ再読み込みする設計のほうがコンテキストを節約できます。
ログも全文を渡さない
デバッグAgentでも同じ問題があります。
たとえばアプリケーションログ10万行をToolで取得し、そのままモデルへ渡すのは非効率です。
まずコード側で、
ERROR WARN 対象時刻 対象Request ID Stack Trace周辺
などに絞ります。
さらに必要ならAgentが追加検索します。
AIに「全部読ませて探させる」のではなく、通常の検索、grep、SQL、ログ集計などを先に使って候補を減らし、最後の判断だけLLMへ任せるほうが効率的です。
コンテキスト使用量を監視する
コンテキスト管理は、問題が起きてから対応するより、使用量を監視したほうが安全です。
OpenAI Agents SDKでは、Run中のモデル呼び出しについてToken Usageを取得できます。Tool CallやHandoffを生成したモデル呼び出しもUsageへ集計されます。この使用量をリクエスト単位でDBへ保存し、料金として集計する設計はAI APIの料金をリクエスト単位で計算する方法で解説しています。
入力トークンが急激に増えているなら、
会話履歴 Tool Result RAG Function Definition System Prompt
のどこが増えているか調査します。
「コンテキスト上限に到達したら圧縮する」だけでなく、一定のしきい値を超えた段階で不要データを削除する設計にすると余裕を持って運用できます。
上限ギリギリまで使わない
モデルのコンテキスト上限が100万トークンだからといって、常に99万トークンを入力する設計が最適とは限りません。
コンテキストにはモデルが回答を生成するための余裕も必要です。
さらに長い入力になるほどAPIコスト、処理時間、重要情報を見つける難しさも増えます。
そのため、
最大まで入るか
ではなく、
現在の判断に本当に必要か
を基準に情報を入れることが重要です。
コンテキストウィンドウはハードディスクではありません。
大量のデータを保存する場所ではなく、モデルが現在の作業で使う作業メモリとして扱うほうがよいでしょう。
古い履歴を削除するだけではなく「再取得できる」設計にする
コンテキストを小さくするために何でも削除すると、後から必要になった情報を取得できなくなります。
そこで、
アクティブコンテキスト 長期ストレージ
を分けます。
たとえば過去の会話全文はDatabaseへ残しておきます。
現在のモデルへは直近の会話と要約だけ渡します。
ユーザーが、
先月話したAPIの設計はどうなっていた?
と聞いた場合だけ、過去のConversation Storeを検索します。
つまり「モデルへ常に覚えさせる」のではなく、「必要なら思い出せる」構造にします。
この設計なら、保存量が増えてもモデルのコンテキストを同じ速度で増やす必要はありません。
実用的なのは「要約+直近履歴+外部メモリ」
長時間動くAIエージェントでは、すべての履歴を毎回送るより、
System Prompt 現在のタスク状態 過去の重要事項をまとめた要約 直近の会話 今回必要なRAG検索結果 今回必要なTool Definition
という構成にすると管理しやすくなります。
古い会話全文、過去の巨大なTool Result、使用していないTool Definitionなどはアクティブコンテキストから外します。
必要になれば外部ストレージから再取得します。
これにより、会話を長期間保存しながらモデルへ渡すコンテキスト量は一定範囲に抑えられます。
コンテキスト圧縮と分割は併用したほうがよい
Compactionだけですべてを解決しようとする必要もありません。
長時間の会話では古い履歴をCompactionします。
巨大なTool Resultは削ります。
RAGは必要なChunkだけ取得します。
独立した調査はSubagentへ分割します。
重要な状態は外部Memoryへ保存します。
利用するFunctionも現在のタスクに合わせて絞ります。
このようにコンテキスト肥大化の原因ごとに対策を変えるほうが効率的です。
単純に「履歴を100ターンごとに要約する」だけでは、毎ターン巨大なTool Resultを返しているAgentでは十分な効果が得られません。
まず何がトークンを消費しているのかを確認することが重要です。
AIエージェントのコンテキストの長期化に関するよくある質問
Qコンテキストウィンドウが大きいモデルに変えれば解決しますか
A根本的な解決にはなりません。上限を増やしても、古い情報が多く残っているとモデルが現在重要な情報を判断しにくくなり、精度が低下する可能性があります。トークン数だけでなく、現在の判断に本当に必要な情報だけを渡す設計が重要です。
QCompactionを使えば会話履歴を全部残せますか
ACompactionは古い履歴を要約して短くする仕組みであり、元の会話をそのまま無制限に保持できるわけではありません。要約からは重要な決定事項が抜ける可能性があるため、安全性やユーザー権限に関わる重要なルールは会話要約だけに依存せず、構造化データとして別途保存することをおすすめします。
QPrompt Cachingを使えばコンテキスト管理は不要になりますか
A不要にはなりません。Prompt Cachingは同じ内容を再送したときの料金計算を変える仕組みで、キャッシュされたPrompt Prefixも引き続きコンテキストウィンドウを占有します。コスト対策とコンテキストサイズの管理は別の問題として考える必要があります。
QTool Resultはどこまで削ってよいですか
Aモデルが実際に利用する情報だけを残すのが基本です。商品名と価格だけが必要なら、内部ID・画像URL・更新日時などは削れます。ただし今後の判断に必要な情報まで削ってしまうと、その場では動いても後の会話で不整合が起きる可能性があるため、何を残すかはツールの用途ごとに検討します。
QSession(会話履歴)とMemory(長期記憶)は同じものですか
A別のものとして扱うのがおすすめです。コンテキストウィンドウはその瞬間の推論で使う作業領域、長期記憶はデータベースなどへ保存しておき必要なときだけ取り出す情報です。すべてを毎ターンのコンテキストへ含める必要はありません。
Qサブエージェントを使うとコンテキストは本当に減りますか
A調査などの一時的な作業をSubagentへ任せ、その結論だけをMain Agentへ返す設計にすれば効果があります。Subagentが読んだ大量のコードやログをそのままMain Agentへ返してしまうと、コンテキストを隔離した効果が薄れるため、返す情報を結論・根拠・次のアクションなどに絞ることが重要です。
Qコンテキスト管理はどの順番で対策すればよいですか
Aまず何がトークンを消費しているのか(会話履歴・Tool Result・RAG・Function Definition・System Promptのどこか)を確認します。原因ごとに対策は異なるため、闇雲にCompactionだけを導入するのではなく、実際の使用量を調べてから、古い履歴の圧縮・Tool Resultの整理・RAGの取得件数見直し・サブエージェント分割などを組み合わせます。
まとめ
AIエージェントのコンテキストが長くなりすぎる原因は、単にユーザーとの会話が長いからとは限りません。
Tool Callingの結果、RAGで取得した文書、大量のFunction Definition、System Prompt、サブエージェントの出力、ファイル内容やログなどが積み重なることで、コンテキストは急速に肥大化します。
上限の大きなモデルへ変更するだけでは根本的な解決になりません。
長いコンテキストは入力コストやレイテンシを増やすだけでなく、現在重要な情報へモデルが集中しにくくなる原因にもなります。
OpenAI Agents SDKではSessionの取得件数を制限したり、OpenAIResponsesCompactionSessionで古い履歴を圧縮したりできます。Anthropicも長時間実行するAgent向けにサーバーサイドCompactionや古いTool Resultを削除するContext Editingを提供しています。
重要なのは、すべての情報を永久にモデルへ渡し続けるのではなく、現在必要な情報だけをアクティブコンテキストへ置くことです。
古い履歴は要約し、巨大なTool Resultは削り、独立した処理は別Agentへ分割し、重要な状態はDatabaseやMemoryへ保存します。
長時間安定して動くAIエージェントを作るなら、コンテキストを「記録を全部保存する場所」と考えるのではなく、現在の推論に必要な情報だけを載せる限られた作業領域として設計することが重要です。

