エージェントの「長期記憶」は幻想だった
2026年のLLMはエージェントとして動くことが前提になりつつある。ツールを呼び、結果を受け取り、次のアクションを決め、また呼ぶ。Claude Code、Cursor、Devin——どれも「長時間ループ」を基本戦略としている。
しかしこの戦略は、8GB VRAMのローカル環境では物理的に成立しない。
私はRTX 4060 Laptop (8GB) + Qwen2.5-7B Q4_K_Mでllama.cppベースのツール呼び出しエージェントを試した。結果は単純だ。ツール呼び出し5回を超えたあたりから、応答品質が目に見えて劣化する。 10回を超えると、直前に自分が呼んだツールの結果を無視し始める。
この記事では、この劣化がなぜ起きるのかをKVキャッシュとContext Rotの観点から分解し、8GBで使える3つの迂回策を検討する。
ツール呼び出しはどれだけKVキャッシュを食うか
ツール呼び出し1回のトークンコストを考えてみる。
1回のツール呼び出しサイクル:
システムプロンプト : ~500 tok (固定)
ユーザー指示 : ~200 tok (固定)
過去の会話履歴 : 可変 (蓄積)
ツール定義 (function schema): ~300 tok × ツール数
LLMの応答 (tool_call) : ~100 tok
ツール実行結果 : ~500-2000 tok (可変)
5つのツールを定義し、各呼び出しで平均800トークンの結果が返るとする。ステップごとのKVキャッシュ蓄積を概算すると:
| ステップ | 累積トークン | KVキャッシュ (fp16) | VRAM残り (7B Q4_K_M) |
|---|---|---|---|
| 0 (初期) | ~2,200 | 0.12 GB | 2.60 GB |
| 3 | ~4,900 | 0.26 GB | 2.46 GB |
| 5 | ~6,700 | 0.36 GB | 2.36 GB |
| 10 | ~11,200 | 0.60 GB | 2.12 GB |
| 20 | ~20,200 | 1.08 GB | 1.64 GB |
| 30 | ~29,200 | 1.56 GB | 1.16 GB |
def agent_vram_estimate(steps, tokens_per_step=900, base_tokens=2200,
model_gb=4.68, overhead_gb=0.6,
n_layers=28, n_kv_heads=4, head_dim=128, dtype_bytes=2):
"""エージェントループのVRAM消費推定"""
total_tokens = base_tokens + steps * tokens_per_step
kv_gb = (2 * n_layers * total_tokens * n_kv_heads * head_dim * dtype_bytes) / (1024**3)
total_gb = model_gb + kv_gb + overhead_gb
return {
"steps": steps,
"tokens": total_tokens,
"kv_cache_gb": round(kv_gb, 2),
"total_vram_gb": round(total_gb, 2),
"remaining_gb": round(8.0 - total_gb, 2)
}
for s in [0, 5, 10, 20, 30, 50]:
r = agent_vram_estimate(s)
print(f"Step {s:2d}: {r['tokens']:,} tok, KV={r['kv_cache_gb']}GB, 残り={r['remaining_gb']}GB")
30ステップあたりでVRAM残りが1GBを切り、50ステップで理論上OOMが見えてくる。Q4 KVキャッシュ量子化(--cache-type-k q4_0 --cache-type-v q4_0)を使えば約3.5倍に圧縮できるが、それでも100ステップ超の長期ループは現実的ではない。
ただし、OOMより先にもっと深刻な問題が来る。
Context Rot — 長いコンテキストが品質を殺す
VRAMに収まっていても、コンテキストが長くなると応答品質が崩壊する。これは「Context Rot」として知られる現象だ。
Chroma Researchの報告によれば、LLMの情報再現精度はトークン数に反比例して低下する。特にエージェントのような「中間結果の蓄積」パターンでは劣化が顕著だ。
MicrosoftとSalesforceの共同研究 "LLMs Get Lost In Multi-Turn Conversation" (arXiv:2505.06120) はさらに具体的な数字を出している。ベンチマークプロンプトをマルチターン会話(エージェントワークフロー類似)に変換したところ、6つの生成タスクで平均39%の性能低下を報告した。o3やDeepSeek-R1のような推論特化モデルでさえこの傾向から免れなかった。
7Bモデルではこの劣化がさらに早く始まる。私がQwen2.5-7Bで確認した挙動:
- ステップ3-5: 正常動作。ツール結果を正確に参照し次のアクションを選択
- ステップ5-8: 初期の指示を忘れ始める。同じツールを冗長に再呼び出し
- ステップ8-10: 直前のツール結果を無視。ハルシネーション混入率が上昇
- ステップ10+: 会話の方向性を失う。ツール呼び出しが目的と無関係になる
これは「Lost in the Middle」(Liu et al., TACL 2024)で指摘された中間情報の忘却と同じ構造だ。エージェントの場合、ステップ3-4のツール結果がちょうど「中間」に押し出され、先頭のシステムプロンプトと末尾の最新結果だけが参照される。
大型モデルなら解決するのか
反論として重要なデータがある。
GPT-4.1はツール重視の対話で劣化しなかった。 Parloaの検証で、大型モデルは長い会話でも安定した性能を維持している。
MemAgentは8Kコンテキストから3.5Mトークンのタスクに外挿し、性能損失10%未満を達成した(OpenReview)。RLM(Recursive Language Model)は1000文書・10M+トークンでも91.33%の精度を維持している。
ただし、これらはすべて数十〜数百GBのメモリを持つ大型モデルかクラウド推論の話だ。
8GB VRAMで動く7Bモデルの場合:
- コンテキストウィンドウ自体が物理的に制限される(前述の通り)
- Attentionヘッド数が少ないため長距離依存関係の保持力が弱い
- GQA (Grouped Query Attention) でKVキャッシュは節約できるが、モデルの「記憶力」自体は改善しない
「モデルサイズが十分なら問題は緩和される」のは事実だが、「8GBでは設計で迂回するしかない」もまた事実だ。
迂回策① 短ループ×コンテキストリセット
最もシンプルで効果的な方法。エージェントのループを短く区切り、各ループの開始時にコンテキストをリセットする。
def short_loop_agent(task: str, tools: list, max_steps_per_loop: int = 5):
"""短ループ×リセット戦略のエージェント"""
memory = [] # ループ間で引き継ぐ要約のみ
while not is_task_complete(memory):
# コンテキストを最小限で再構成
context = build_context(
system_prompt=SYSTEM_PROMPT,
task=task,
memory_summary=summarize(memory[-3:]), # 直近3件の要約のみ
tools=tools
)
# 短いループを実行
for step in range(max_steps_per_loop):
response = llm.generate(context)
if response.tool_call:
result = execute_tool(response.tool_call)
context.append(result)
memory.append({"step": step, "tool": response.tool_call, "result_summary": summarize_result(result)})
else:
break
# ループ終了時: コンテキストをリセット、要約のみ持ち越し
# KVキャッシュは解放される
ポイントはmemory_summaryだ。ループ間で渡すのは生のツール結果ではなく、その要約。これでKVキャッシュの蓄積を防ぎつつ、必要な情報は保持する。
5ステップ×6ループ=30ステップ相当のタスクを、各ループ約6,700トークン(KVキャッシュ0.36GB)で処理できる。30ステップを一気に回す場合の1.56GBと比べて、VRAM消費は4分の1以下だ。
迂回策② Persistent Q4 KV Cache
arXiv:2603.04428 "Agent Memory Below the Prompt" (2026) は、エージェントのKVキャッシュをディスクにQ4量子化して永続化し、必要時にAttentionレイヤーに直接ロードする手法を提案している。
この論文がApple M4 Proで検証した結果:
- FP16では10.2GBのKVキャッシュ予算で3エージェント分しか保持できない
- Q4量子化で4倍のエージェントコンテキストが同じメモリに収まる
- キャッシュ復元によるTTFT (Time-to-First-Token) 改善: 最大136倍 (Gemmaで22〜136倍、DeepSeekで11〜76倍)
この手法の本質は「再計算の回避」だ。通常、コンテキストを復元するには全トークンのprefill計算をやり直す必要がある。Persistent KV Cacheはこの計算を丸ごとスキップして、保存済みのKVキャッシュを直接ロードする。
この論文はApple M4 Proで検証されたものだが、原理はRTX 4060でも同じだ。llama.cppには現時点でKVキャッシュの保存/復元APIが実験的に実装されている(--save-state, --load-state)。NVMe SSD上にエージェントごとのKVキャッシュスナップショットを保存し、タスク切り替え時にロードすることで、prefill再計算を回避できる。8GBでは同時に保持できるエージェントコンテキストが1つに限られるため、この「スワップ」戦略の恩恵がM4 Pro以上に大きい。
迂回策③ 動的ツール選択(Tool Loadout)
ツール定義が増えるとモデルの選択精度が落ちる。Berkeleyのfunction-calling leaderboardでも、ツール数が増えるほど説明が重複し正しいツールの選択が困難になる傾向が確認されている。経験的には、7Bモデルでは5〜10ツールが実用的な上限だ。ツール定義自体がコンテキストを消費し、KVキャッシュを圧迫するためだ。
解決策は「全ツールを常時定義しない」こと。
def dynamic_tool_selection(query: str, all_tools: list, max_tools: int = 5):
"""クエリに応じてツールを動的に選択"""
# 軽量な分類器でクエリカテゴリを判定
category = classify_query(query) # "search", "code", "data", etc.
# カテゴリに応じてツールサブセットを選択
tool_groups = {
"search": ["web_search", "file_search", "grep"],
"code": ["run_python", "read_file", "write_file"],
"data": ["sql_query", "csv_parse", "chart_generate"],
}
selected = tool_groups.get(category, all_tools[:max_tools])
return selected
20ツールの定義をすべてコンテキストに入れると約6,000トークン。5ツールに絞れば約1,500トークン。差の4,500トークンは、Q4 KVキャッシュでも0.04GBの節約になる。小さく見えるが、30ステップのループではこの差が蓄積して1.2GB以上の差になる。
8GBエージェントの設計原則
3つの迂回策を組み合わせた設計原則をまとめる。
原則1: ループは5ステップ以内
7Bモデルのコンテキスト品質が維持できるのは約6,000〜8,000トークンまで。ツール呼び出し1回あたり900トークンとすると、5ステップが上限だ。
原則2: 記憶は「要約」で持ち越す
生のツール結果をコンテキストに残さない。各ループ終了時に結果を要約し、次のループではその要約のみを参照する。
原則3: ツール定義は5つまで
動的ツール選択で、各ステップに必要なツールだけをロードする。「万能エージェント」は8GBでは成立しない。
原則4: 「コンテキスト品質」を監視する
ツール呼び出しの「的中率」(呼んだツールが目的に合致していたか)を追跡し、低下したらループをリセットする。自動リセットのトリガーに使える。
8GBの制約はエージェント設計を改善する
128Kコンテキストの記事でも書いたが、8GBの制約は「ハンデ」ではなく「設計の強制力」だ。
クラウドの大型モデルは100ステップのエージェントループを力技で回せる。しかし、MicrosoftとSalesforceの研究が示すように、回せることと品質が維持されることは別問題だ。o3ですら39%劣化している。
8GBの制約は「5ステップで品質が落ちる」という事実を隠さない。だからこそ、短ループ設計・要約持ち越し・動的ツール選択という「本質的に正しい設計」に到達できる。これらの設計原則はクラウド環境でもそのまま適用できる——むしろ適用すべきだ。
エージェントの性能を決めるのは、コンテキストの長さではなく、コンテキストの品質だ。
参考
- "Context Rot: How Increasing Input Tokens Impacts LLM Performance" (Chroma Research): https://research.trychroma.com/context-rot
- "Lost in the Middle: How Language Models Use Long Contexts" (Liu et al., TACL 2024): https://arxiv.org/abs/2307.03172
- arXiv:2603.04428 — Agent Memory Below the Prompt: Persistent Q4 KV Cache for Multi-Agent LLM Inference on Edge Devices
- "LLMs Get Lost In Multi-Turn Conversation" (Microsoft Research & Salesforce, arXiv:2505.06120): https://arxiv.org/abs/2505.06120
- Parloa Labs — Long Conversations and LLM Performance: https://www.parloa.com/labs/insights/long-calls-LLM-performance/
- Berkeley Function-Calling Leaderboard: https://gorilla.cs.berkeley.edu/leaderboard.html
- "Cutting Through the Noise: Smarter Context Management for LLM-Powered Agents" (JetBrains Research, NeurIPS 2025 Workshop): https://blog.jetbrains.com/research/2025/12/efficient-context-management/
- llama.cpp: https://github.com/ggerganov/llama.cpp