コンテキストが伸びるとエージェントが劣化する理由
コンテキスト腐敗(context rot)を具体的に。1 ターンはモデルへの自己完結した 1 リクエストであり、それが長くなったりツールの往復で膨らんだりすると、回答の正確さは実測で落ちます。そのリクエストに何が入っているのか、入りきらないときに何から落とすのか、そしてツール結果を要約させるのではなく回答前にリクエストを組み直す理由。
月曜日にはきちんと答えていたエージェントが、金曜日には曖昧なことを言い出す。20 通前まで守っていた指示を守らなくなる。ツールを呼び、正しいデータを受け取り、そのデータの半分を無視した回答を書く。エラーは出ず、ログにも残らず、会話は普通に見えます。これは一般に context rot(コンテキスト腐敗) と呼ばれますが、よくある説明——「コンテキストウィンドウが埋まった」——は、私たちが計測した結果とは違います。
1 ターンの全体像
ターンは 1 つのリクエストとして始まり、ツールが走るあいだに膨らみ、回答を書く前に組み直されます。見る価値があるのは真ん中の状態です。ほとんどのエージェントは、まさにそこから回答しています。
1 ターンは自己完結した 1 リクエスト
モデルはターンとターンのあいだに何も保持しません。向こう側にセッションはなく、1 時間前に何を言ったかの記憶もなく、渡されなかったものを自分で調べる手段もありません。毎ターン、プラットフォームが完全なリクエストを組み立て、丸ごと送ります。返答はそのリクエストだけから生成されます。
ネットワークの勘が働く方には、これはデータグラムに近いものです。自己完結していて、一度だけ送られ、受信側が必要とするものをすべて携え、送信側が守るべきサイズに縛られています。この喩えが破れる点が 2 つあり、どちらも重要です。
- 転送中に失われるものはありません。 送ったものはすべて届きます。変わるのは、モデルが実際にどれだけ注意を向けるかです——失われるのは受信側の内部であって、線の上ではありません。
- 配置が答えを変えます。 ルーターはペイロードを並べ替えても振る舞いを変えませんが、モデルは変えます。同じ事実、同じリクエスト、並べ方だけが違う 2 通りで、一方は正解を、他方は誤答を出しました。以降の内容のほとんどは、この結果から出ています。
リクエストの中身
順序はモデルが見る順序そのものです。保持/削除の列は、リクエストが入りきらないときにプラットフォームが適用する優先度で、順位は相対的なものであり、決まった数のブロックを落とすという意味ではありません。
履歴より上はすべて 1 つのシステムメッセージです。これは意図的です。エージェントの境界、あなたのナレッジベース、返信言語は会話ではなく、モデルはそれらがどこに置かれているかによって扱いを変えるからです。
入りきらないときに何から落とすか
予算を超えたリクエストは末尾から切り捨てられるのではなく、収まるまで優先度順にブロックが落とされます。
- 最も古い履歴から。 ただし落ちたターンは破棄されずローリング要約へ畳み込まれるので、言葉遣いは残らなくても、話された内容は残ります。
- スコアの低い記憶、次にスコアの低い検索断片。 末尾から落とすので、最も関連の深い材料が最後まで残ります。
- プロフィール、次に要約。
- エージェントの指示、あなたのナレッジベース、質問は決して落としません。 質問だけで予算を超える場合は切り詰め、切り詰めたことを明示します——文が単語の途中で切れている理由をモデルに推測させるのではなく、伝えます。
この順序はコンテキスト予算の誠実な部分です。何かは必ず出ていかねばならず、それを言わないシステムは、たまたま最後にあったものを黙って落とします。
入ることと、読まれることは別
驚いたのはここです。回答に必要な事実がどの条件でも完全に揃っているテストを作りました。何も落とされず、何も切り詰められず、どこにも情報の欠落はありません。変えたのはリクエストの配置だけです。数百件のレコードに対する複数条件の絞り込みを、自社のローカルモデルとクラウドモデルの双方で、各条件につき複数回:
| 同じ事実をどう配置したか | 正答 |
|---|---|
| 関連する行だけを載せたきれいなメッセージ 1 通 | 8 / 8 |
| まったく同じ行をツールの結果として返した場合 | 0 / 8 |
| レコード全体 + 無関係なツール結果 5 件 | 0 / 6 |
| その蓄積をきれいなメッセージ 1 通に組み直した場合 | 8 / 8 |
2 つの対照が、ありがちな解釈を退けます。きれいなリクエストを同じ総量の 2 通のメッセージに分けても 8 / 8 のままでした——つまり変数は長さではありません。抽出した行をツールの結果として返すと 0 / 8 のままでした——つまり「データが質問の近くにある必要がある」でもありません。ツールの足場と無関係な材料は、どこに置かれていてもモデルの注意を奪い合います。
クラウドモデルはより頑健でしたが、無傷ではありません。ほとんどの課題では持ちこたえた一方で、1 ターン分の蓄積されたツール結果がリクエストに入った途端、単一値の検索で 0 / 3 まで落ちました。
正直に述べておくべき限界が 2 つ。これは自社の課題での自社のテストであり、公開ベンチマークではありません。そしてモデルの上限は上げません。同じ課題を一段難しくするとどの配置でもゼロでした——そのモデルにはそもそもできなかったからです。リクエストの組み直しは、配置のせいで失っていた正確さを取り戻すだけで、モデルにない能力を買うものではありません。
つまりターンは 2 段構え:作業と、回答
これがページ冒頭のアニメーションが示している分かれ目です。
作業には散らかったリクエストが要ります。モデルは次のツールを呼ぶかどうかを決めるために、自分のツール呼び出しと生の結果を見なければなりません。この段階は変わっていません。
回答には要りません。ツールが終わったら、プラットフォームは新しいリクエストを組み立てます——同じシステム指示、会話の短い要約、このターンで実際に判明した事実、そして質問。あなたが受け取る返答はそこから生成されます。ツール呼び出し、生のペイロード、途中の推論はその中に現れません。
要約を残すのは、実際の質問がしばしば「はい」や「じゃあ 2 つ目は?」だからです。最も良い結果を出した単一メッセージの配置は単一ターンのものであり、会話はそうではありません。要約は別々のターンとして復元するのではなく同じメッセージへ畳み込まれ、計測できるコストはなく、指示語は解けたままになります。
意図的にやらないこと
ツール出力がコンテキストウィンドウを溢れさせるとき、よくある対処は各結果をモデルに要約させることです。私たちはそうしません。理由は上のテストです。満点を取った配置は生データを組み直したものであり、正確さを取り戻すのに要約の工程は要りませんでした。それを足すことは、どの事実が重要かをモデルに決めさせるということであり、コードが正確にできる仕事をモデルに渡さない、というのがこのシステム全体の設計原則です。顧客が尋ねたまさにその数字を落としてしまう要約器は、静かに失敗し、しかも良い回答に見えます。
事実が本当に入りきらないときは切り詰め、その切り詰めをリクエスト内に明記し、欠けた部分を埋めないよう指示します。「最初の 40 件しか見えませんでした」と言うエージェントのほうが、残りを自信たっぷりに作り出すエージェントより価値があります。
これがあなたにとって何を意味するか
設定するものはありません。上に書いたことは、このプラットフォーム上のすべてのエージェントの動き方です。
実務で変わるのは次の点です。
- 長い会話が使いものであり続けます。 20 ターン目も 1 ターン目と同じように組み立てられ、ウィンドウから外れたものは消えるのではなく要約として残ります。
- 大量のデータを返すツールが返答を汚しません。 結果には上限があり、最終的な回答は生のペイロードを持たないリクエストから書かれます。
- ツールを使ったターンはモデル呼び出しが 1 回増えます——短い返答でおよそ 1 秒。その間チャットは黙り込まず、何をしているかを表示します。
与えたはずのものをエージェントが無視していると感じたら、有効な問いは「コンテキストウィンドウが小さすぎるのでは」ではなく「それはどのブロックに入っていて、同じリクエストに他に何が同乗していたか」です。会話はターンとターンのあいだどこにあるのか がこのページのもう半分——モデルが何も保持しないなら、私たちの側に何があり、リクエストは何から組み立てられるのか——に答えます。根拠のある回答 は何が検索されるか、顧客ごとの記憶 は何が記憶されるか、ツールと MCP は 1 つのツール結果にウィンドウ全体を奪わせないための予算を扱います。