5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントのコンテキスト管理を ReACT と Ralph Loop の対比から考えてみた

5
Last updated at Posted at 2026-02-23

はじめに

突然ですが、コンテキストの管理には、2つの流儀があるのをご存じでしょうか?
ひとつは、ツール呼び出しも含めてすべての対話を履歴に残すところから始める流儀、そして、もうひとつは、まずは捨て去るところから始める流儀です。
このブログは、そんな2つの流儀についての考察です。

AI エージェントを使っていると、「最初は賢かったのに、しばらく走らせていたら急にポンコツになった」という経験があるのではないでしょうか。指示を忘れる、同じミスを繰り返す、的外れな ツール呼び出しを連発する。これらの症状の多くは、コンテキストウィンドウの扱い方に根本な原因があることは明らかですよね。

最近は、汎用的なエージェント、コーディング・エージェント、便利なエージェント・フレームワーク、コンテキストやメモリの管理を支援してくれるマネージドサービスも増えています。

しかし、このコンテキストウィンドウの扱い方を理解した上でこれらの機能を活用しないと便利なエージェントも期待したような成果を上げてくれませんし、便利なフレームワークやマネージドサービスを使っても良いエージェントは作れません。

短期記憶にはこの機能、コンパクションにはこの機能、長期記憶にはこの機能.....それはそれで大事なんですが、コンピューターのレジスター、キャッシュ、メモリーは高速だけど揮発性、SSDやハードディスクは遅いけど永続化できる...ということとは文脈が全然違います。

そんなことを考えているとき、遅ればせながら、Geoffrey Huntley が提唱する「Ralph Loop」というパターンを知りました。LLM に推論(Thought)→ ツール呼び出し(Action)→ 結果の観察(Observation)を繰り返させるエージェントの基本パターンである ReAct(Reasoning and Acting)と比較しながら整理してみると、両者の違いは「コンテキストをどう管理するか」という一点に集約できることが見えてきました。この視点はどちらのアーキテクチャを選ぶにしてもエージェント設計の土台になると感じたのが、この記事を書いた動機です。

この記事では、ReAct と Ralph Loop をコンテキスト管理という切り口で雑にまとめてみます。厳密な論文レビューのようなものではなく、自分の理解を整理するためのメモに近いものです。

また、この記事で ReAct の問題点として扱っているものは、ReAct: Synergizing Reasoning and Acting in Language Modelsで提唱された ”ReAct に本質的に内在しているものではなく、ReAct パターンに則った多くの実装に共通する問題点という意味” ですので、その点はご了承ください。

Claude Code にも Ralph Loop プラグインが存在しますが、これは、Geoffrey Huntley が提唱する Ralph Loop とはかなり実装が異なっています。本ブログの内容は、あくまでも Geoffrey Huntley が提唱するオリジナルの Ralph Loop に関するものです。
両者の違いについては、こちらの YouTube 動画 Stop Using The Ralph Loop Plugin で解説されています。

ReAct と Ralph Loop におけるコンテキストの扱い

ReAct

ReAct は、ツールの呼び出しの結果(成功、失敗、有益だったかどうかに関わらず)やLLMの推論結果を基本的にはすべてコンテキストに積み上げていきます。そして、コンテキストが汚染されたら掃除(一定のターン数で切り捨てる、要約・圧縮する)をするというシンプルなコンテキストの管理を行います。コンテキストエンジニアリングとかメモリーエンジニアリングを細かく考えずに手っ取り早く始めたい場合に向いているといえるかもしれません。しかし、コンテキストウィンドウが希少資源で、LLM はコンテキストのノイズに弱く、Lost in the Middle というコンテキストの中間部分に置かれた情報ほど LLM が見落としやすいという既知の現象も緩和されたとはいえ、本質的には未解決な現状ではすぐに問題が顕在化してしまいます。

※コンテキストが上限に近づいたときに、要約・切り捨て・マスキングなどの手段でコンテキストを圧縮する処理を総称してコンパクションと呼びます

Ralph Loop

Ralph Loop は、ひとつのイテレーションが終わったらコンテキストを破棄してクリーンな状態から次のイテレーションを実行するものです。イテレーション間で引き継ぐべき状態はファイルシステムと git の履歴に書き出し、次のイテレーションでそれを読み出す方針のため、何をファイルに書き出して次のイテレーションへ引き継ぐべきかはエンジニアリングが必要となります。最初からエンジニアリングが必要なので取っつきにくさはあるのですが、コンテキストの汚染(pollution)に対して構造的に強いといえます。

※「タスク間」ではなく「イテレーション間」と書いたのは、あるタスクが未完了だった場合、次のイテレーションへ引き継いで完了させることもあり得るためです。

本質的な違いは、単一のループか多段のループか

ここが両者の本質的な分岐点だと思います。

ReAct は「ひとつのセッションの中で推論と行動を繰り返す」単一のループです。Thought → Action → Observation のサイクルがすべて同一のコンテキストウィンドウ内で回ります。

一方、Ralph Loop は「セッションそのものを使い捨てにして外側から繰り返す」外部ループを持つ多段ループ構造です。特に、この外部ループを Ralph Loop と呼びます。

Geoffrey Huntley が示す Ralph Loop の最も純粋な形は、驚くほどシンプルな bash ループです。

while :; do
  cat PROMPT.md | <ANY_CODING_AGENT>
done

毎回同じプロンプトを投入し、エージェントには前回のイテレーションがファイルシステムや git に残した痕跡だけが見える。この「外側から回す」という構造が Ralph Loop の核心です。

なお、この外部ループと内部ループは排他的なものではありません。Ralph Loop の各イテレーションの「内側」では、ReAct 的な tool loop(LLM ↔ tools の繰り返し)が回ることが一般的です。外でコンテキストの衛生を保ちながら、中では ReAct の柔軟性を活かす、という二重構造になるわけですね。

ところが、この内部ループと外部ループを区別しないですべて ReAct 的な実装でコンテキストを引き継いでループを回してしまうと問題が起きます。

ReAct を蝕むコンテキスト汚染(Context Pollution)

ReAct の問題は、ループが回れば回るほど会話履歴が肥大化し、無関係な情報・中途半端な思考結果・エージェント間でのノイズがどんどん混入していき、LLM の注意が初期の指示から離れていくことにあります。これがいわゆる コンテキスト汚染(Context Pollution) です。

Dex Horthy(HumanLayer) は、コンテキストウィンドウの容量を40%以上使い始めるとモデルの精度が目に見えて落ちる領域つまり、「Dumb Zone」(だめなゾーン、ばかなゾーン)があると指摘しています。

具体的に何が起きるかというと、ReAct 型では仕様や計画プロンプトを最初に1回読み込んだ後、Thought / Action / Observation が履歴として積み上がっていきます。コンテキストが長くなれば、LLM が初期の指示に注意を向けられなくなったり、Lost in the Middle によってすべてのコンテキストを有効に活用できなくなります。

さらに、コンテキスト長が限界に近づくと、要約・切り捨て・Observation masking などのコンパクションが行われます。このとき初期の「計画プロンプト」「仕様」「重要な設計判断」が要約や切り捨てで劣化・消失することがあるのも問題です。なお、Observation masking とは、ツールの実行結果についてのみ、直近 N ターンだけ残して、それ以前の履歴をプレースホルダーに置き換える方式です。

また、LLM に自分自身のコンテキストを要約させることは、多くの場合、効率性、有効性の観点で Observation Masking と大差なく、現状では、その複雑さに見合うだけのアドバンテージはないとする報告もあります👇

The Complexity Trap: Simple Observation Masking Is as Efficient as LLM Summarization for Agent Context Management, Tobias Lindenbauer et al.

これは、RAG においてもコンテキストに載せるチャンクが多すぎるとノイズとなって精度が下がるという性質と通じるものがありますね。コンテキストは多ければ良いというものではなく、質が重要だという点は共通しています。

Ralph Loop のコンテキストに対する考え方

Ralph Loop はこの問題を「コンテキストを溜めない」という方針で回避します。

各イテレーションが新品のセッションなので、コンテキストウィンドウの使用率は常に低く保たれます。仕様、progress ファイルなどの「必要だと判断した情報」を 「毎回最初から投入」 し、過去の会話履歴はイテレーションごとに基本的に捨てます。過去の文脈は LLM へ投入する会話履歴ではなくディスク上のファイルとして存在し、エージェントは必要に応じてそれを読みに行きます。

言い換えると、「状態管理の責任を会話履歴からファイルシステムに移した」ということです。

git の履歴が累積的に残るため、エージェントは git loggit diff で自分の前回の試行を確認でき、同じ失敗を繰り返すことを避けられます。Geoffrey Huntley はこの「環境そのものを累積的なメモリとして使う」点を重視しており、エージェントが発見した学びを書き込むファイルに学習結果を蓄積させる運用を推奨しています。発見したパターンやハマりどころがファイルに記録され、次のイテレーション(あるいは将来の人間の開発者)がそれを参照できるという仕組みですね。

Ralph のコスト

ここで「それなら Ralph はただ効率的なだけでデメリットはないのでは?」と思いますよね?
しかし、ReAct 型と比較したときに Ralph が(というか設計者が)払っている明確なコストがあります。

ReAct 型では、仕様やプロンプトは最初に1回コンテキストへ載せれば、以降は会話履歴として保持されます。新しいステップごとに必要な処理は「直前の Observation や推論結果を追加する」だけであり、コンテキストの組み立ては極めてシンプルです。一方 Ralph は、イテレーションごとにセッションを破棄するため、毎回「仕様を読み直し、progress ファイルを解析し、何を渡すべきかを判断する」というコンテキスト組み立て処理をゼロから行う必要があります。

Geoffrey Huntley はこれを「malloc を毎回やり直す」ような設計だと表現しています。ここで Geoffrey Huntley の言う「allocation」はコンテキストウィンドウという有限空間への書き込みを指しています。LLM は呼び出しのたびにコンテキスト全体を受け取るため、ReAct でも Ralph でも「毎回全コンテキストを送る」点は変わりません。両者の違いは、その都度送るコンテキストを組み立てる方法にあります。

ReAct Ralph Loop
一度 malloc した大きな配列にどんどん append していくイメージ。
コンテキストの組み立ては単純だが、配列がどんどん肥大化する。LLM への毎回の送信量はステップが進むごとに増大し、性能が劣化、やがてコンパクション(GCのイメージ)が走って重要な情報が落ちるリスクがある
ループごとに新しい配列を malloc し直すイメージ。
毎回コンテキストを組み立てる手間はかかるが、配列サイズは常にコントロールされていて、ギリギリまで詰め込まない

「割り当てる量が多いほど、悪い結果になる可能性が高まります。Ralphは、コンパクションイベントが発生しないように、割り当てを最小限に抑えるための意図的な試みです。」
"The more you allocate the, the more you, you're gonna get bad outcomes. Ralph is a deliberate attempt to minimize allocation so I never get a compaction event."

Inventing the Ralph Wiggum Loopより

「ループごとに新しい配列を malloc し直すイメージ」というのは、「仕様などのコンテキストを組み立てて再読み込みするという処理を毎回繰り返す」ことを指しています。ReAct なら会話履歴に残っている仕様を、Ralph はわざわざディスクから読み直す。この組み立て処理の繰り返しと、それに必要なファイル設計のエンジニアリング上の負担が、Ralph が払っているコストです。その代わりに、コンパクションなし・Dumb Zone なしという構造的保証を買っているわけです。初期仕様がコンパクションで落ちることがなく、毎回フルに再共有される、つまり「非効率に見えるが構造的に合理的」ということです。

ファイル設計という新たなエンジニアリング

とはいえ、Ralph Loop の難しさが消えるわけではありません。「何をファイルに書き出して、何を書き出さないか」の設計判断がエージェントの性能を大きく左右します。progress ファイルに書く粒度が粗すぎれば同じ失敗を繰り返しますし、細かすぎれば読み込み時のコンテキストを圧迫して本末転倒です。

Geoffrey Huntley は「コンテキストウィンドウへの割り当てを最小化する」ことを繰り返し強調しており、主コンテキストはスケジューラーとして機能させ、重い処理(テスト結果の要約など)はサブエージェントに委譲すべきだと述べています。

各イテレーションで必ず入れるもの(仕様、進捗サマリ)と、二度と入れないもの(各イテレーション内の失敗トライの細かいログなど)を明確に切り分ける。そして仕様自体も、丸ごと貼るのか、必要な部分だけ抜粋して投入するのかを、ドメインに合わせてチューニングする。失敗から学んだことをべからず集、ガードレールとしてまとめる。ここがまさに「コンテキストエンジニアリング」の腕の見せ所であり、ReAct のように成り行き任せにできない部分です。

終了条件の設計思想の違い

もうひとつ重要な違いがあります。終了条件の設計思想です。

ReAct はモデル自身が「もう十分だ」と判断したら止まります。つまり LLM の自己評価に依存 します。

Ralph Loop では、品質保証と終了制御が異なるレイヤーで機能します。

  • 品質保証(backpressure)
    テスト、ビルド、型チェック、リントなどの検証は、各イテレーションの内部でエージェント自身が実行します。PROMPT.md に「テストが通ってから commit せよ」と指示することで、エージェントはテスト不通過のコードを commit しないよう誘導されます。これが Huntley のいう「backpressure」であり、不正なコード生成を拒否する圧力として機能します。

  • 終了制御(外部ループ)
    一方、外部の bash ループ自体にはテスト結果を判定するゲートはありません。ループは無条件に次のイテレーションへ進みます。終了は、人間が Ctrl+C で止めるか、max_iterations に到達するか、タスク計画書の未完了タスクが無くなってエージェントが自然終了するか、のいずれかです。

つまり Ralph Loop の品質保証の仕組みは「外部からの合否判定」ではなく「内部への圧力」です。Huntley はこの backpressure がなければエージェントが低品質なコードを commit し続けてしまうと繰り返し警告しており、テスト・型チェック・リントなどを適切に設定することの重要性を強調しています。

考えてみれば、これは人間の開発プロセスにおける CI/CD パイプラインとは少し異なる発想です。CI/CD が「マージ前のゲート」として機能するのに対し、Ralph の backpressure は「開発者(エージェント)自身にテストを走らせ、通るまで commit させない」という、より「開発者の規律」に近い仕組みです。

トレードオフのまとめ

結局のところ、ReAct と Ralph Loop はトレードオフの関係にあります。

観点 ReAct Ralph Loop
コンテキスト管理 セッション内に蓄積。溢れたらコンパクション(要約/切り捨て) イテレーション毎に破棄。必要な状態はファイルと git に永続化
コンテキスト組み立て 単純(直前の結果を追加するだけ) 毎回再構成が必要(仕様・進捗をディスクから読み直す)
コンテキストの肥大化 ステップごとに増大し、Dumb Zone に入るリスク 毎回クリーン。コンテキスト使用率は常に低く保たれる
導入の手軽さ 手軽(すぐ始められる) 複雑(ファイル設計やフィードバックループの整備が必要)
終了条件 LLM の自己評価 人の介入(Ctrl+C)/ max_iterations / タスク枯渇
品質保証 なし(コンテキスト内の自己判断のみ) backpressure(テスト・ビルド・型チェック等をイテレーション内部で実行)
適したタスク 動的な QA、探索的な推論 検証可能なゴールがある機械的タスク

ReAct は柔軟で汎用的だが長時間走らせるとコンテキストが腐る。Ralph Loop はコンテキストの劣化に強いが、設計コストが高く、明確に検証可能なゴールがないタスクには向かない。
Huntley 自身もスコープの重要性を一貫して強調しています。Ralph のプロンプトには「最も重要な項目をひとつだけ選んで、それだけをやれ(choose the most important thing)」という指示が組み込まれており、各イテレーションでエージェントが扱う範囲を絞ることでコンテキストの肥大化を防いでいます。また、タスクの前段階として要件をトピックごとに分解し、それぞれを独立した仕様ファイルにまとめるワークフローを設けています。トピックの粒度が適切かどうかの判断基準として、「"and" を使わずに一文で説明できるか(One Sentence Without 'And')」というテストも示しています。
つまり、Ralph Loop が機能するためには、エージェントに投入する前にスコープが十分に分解・構造化されていることが前提になります。例えば「ダッシュボード全体を作って」のような曖昧で大きな指示をそのまま与えてしまうと、backpressure となるテストや完了基準を適切に定義できず、イテレーション内部でエージェントが堂々巡りしたり、品質保証のないまま commit を繰り返したりすることになりかねません。

最後に

コンテキストは LLM エージェントにとって酸素のようなものだと思っています。なくてはならないが、多過ぎたり汚れると毒になる。ReAct はその酸素を使い続けてときどき換気する方式、Ralph Loop は部屋ごと入れ替える方式。

どちらが正解かはタスクの性質次第ですが、コンテキストの管理がエージェントの性能を根本的に左右するという認識は、どちらのアーキテクチャを選ぶにしても持っておくべきですね。

RAG でもコンテキストに載せるチャンクの数や並び順が回答精度を大きく左右するように、エージェントにおいてもコンテキストに何を載せて何を載せないかが性能の鍵を握っています。昔、同僚のエンジニアが「石の気持ちになって考えろ」と言っていましたが、ここでも「LLM の気持ちになって考えること」が必要なのかもしれませんね。

参考資料

Ralph Loop についての参考資料

関連技術 - Deep Agents

より良いエージェントを作るためには Deep Agents についても知っておくと役に立ちます。ご興味がありましたらこちらのブログも訪ねてみてください。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?