コンテキストが溢れたら、削る技術
「The agent can forget strategically and keep working forever.」戦略的な忘却は工学的な能力だ。
なぜ compact が必要なのか
agent を長時間動かすと messages[] が肥大化する:read_file のたびに数千 token、bash のたびに数百 token、さらに各ターンのモデルの思考テキストが積み上がる。50ターン動かせば context は100K 超えになりうる。2つの結果:
- モデルの上限に当たる:ウィンドウサイズを超えるとクラッシュするか、API 呼び出しコストが線形に増加する。
- 注意力の希薄化:今手がけているタスクが30ターン前の無関係な tool_result に埋もれ、モデルが迷走し始める。
s06 のアプローチ:agent に重要でない内容を積極的に忘れさせ、重要な状態だけ保持する。3層の仕組み、軽い順に解説する。
Layer 1 · micro_compact(毎ターン静かに実行)
最も安い層だ。毎回の LLM 呼び出し前に一度走り、3つより古い tool_result をプレースホルダーに置き換える:
# 10ターン前より古いほとんどの tool_result はこうなる: { "type": "tool_result", "tool_use_id": "toolu_01A", "content": "[Previous: used bash]" # 数千文字から数十文字へ }
例外が一つある:read_file の結果は圧縮しない。なぜか?read の出力は参照資料であり、圧縮したらモデルが再度読み直す必要が生じて、かえってコストが高くなるからだ。
PRESERVE_RESULT_TOOLS = {"read_file"} # 永遠に圧縮しない
micro_compact がターンごとに古い結果を消化する様子
以下で10ターンのインタラクションをステップごとにシミュレートし、各ターン前に micro_compact を一度実行する。messages[] 内の古い tool_result が [Previous: ...] に置き換わり、最新の3つだけ完全な形で残るのを確認しよう。
Layer 2 · auto_compact(閾値超えで発火)
micro を走らせ続けても、ある規模を超えると溢れる。s06 には閾値(デフォルト 50000 token)が設定されている:
- token 数を見積もる
len(str(messages)) // 4(粗いが十分)。 - 閾値超過 → 完全な transcript を
.transcripts/transcript_TIMESTAMP.jsonlにディスクに書き出す(バックアップ)。 - LLM に会話全体の summary を生成させる。
messages全体を一行の"[compressed] SUMMARY..."に置き換える。
代償は明白——具体的なツール出力や会話のトーンが失われ、概要だけ残る。しかし agent は作業を続けられる——これが核心的なメリットだ。
Layer 3 · モデル自身が compact ツールを呼ぶ
auto_compact は harness が自動的に発火させるもので、モデルは知らない。Layer 3 は逆だ:モデルに compact ツールを与え、それを能動的に呼ばせる——前段の探索がもう不要と判断したときや、新しいフェーズを始めたいときなどに。
モデルが呼ぶ:
tool_use("compact", focus="keep the API design decisions")
auto と同じ処理が発火するが、focus パラメータを渡して要約時に重点的に保持すべき内容を指定できる。実際の場面で非常に役立つ——モデルは「前半フェーズが終わった」とわかるので、harness のヒューリスティックより正確だ。
どの層が適切か:判定問題
以下のシナリオで、micro / auto / manual のどの発火が最も適切かを選ぼう。