[登壇資料] Anthropic「Long-running agents」を Google ADK + Gemini 3.1 Pro で再現してみた #GoogleCloudNext

[登壇資料] Anthropic「Long-running agents」を Google ADK + Gemini 3.1 Pro で再現してみた #GoogleCloudNext

2026.04.30

はじめに

お疲れさまです。とーちです。
弊社主催のGoogle Cloud Next 2026 の reCap イベントで「Anthropic『Long-running agents』を Gemini で再現してみた」というテーマで登壇しました。
本記事ではその際に使用した資料の概要をご紹介します。

登壇資料

登壇した際に使用した資料です。

資料概要

セッションreCap部分

前半は、Google Cloud Next 2026 で聴いてきた Anthropic の Harsh Patel 氏のセッション「Long-running agents: Lessons from the enterprise frontier」のリキャップで、長時間動くエージェントを本番投入するときに陥りがちな「5 つのつまずきパターン」と、それを補う「ハーネス設計の 4 つの柱」を紹介しています。前半の内容についてはこちらのブログ記事に詳しくまとめていますので、そちらをご参照ください。

Google ADK + Gemini 3.1 Pro で再現してみた

reCap ということで、Anthropic セッションで紹介されていた planner / builder / evaluator のハーネス構成を、Google スタックで組んでみました。採用技術は Google ADK (Agent Development Kit) と Gemini 3.1 Pro Preview です。

コードの全体像

実装したコードは以下のリポジトリに公開しています。

https://github.com/ice1203/gemini-coding-agents

オーケストレーション構造

最終的な組み立て部分のコードは以下のとおりで、SequentialAgent の中に LoopAgent をネストさせる二段構造になっています。

https://github.com/ice1203/gemini-coding-agents/blob/5603a02d75a9142c6ba12bb7cb7c10811a7dc275/agents/plan_build_eval/agent.py#L255-L267

イメージで描くとこんな感じのツリーです。

root_agent (SequentialAgent)        # 直列実行
├─ planner (LlmAgent)               # 1回だけ走る
└─ refine_loop (LoopAgent)          # 最大5回繰り返す
   ├─ builder (LlmAgent)
   └─ evaluator (LlmAgent)

ここで使っている Agent クラスは大きく 3 種類で、役割がはっきり分かれています。

  • LlmAgent: LLM を呼ぶ「葉」のエージェント
  • SequentialAgent: sub_agents を上から順番に実行する制御フロー
  • LoopAgent: sub_agentsmax_iterations 回 or escalate まで繰り返す制御フロー

SequentialAgentLoopAgent はあくまでも、「どのサブエージェントをどんな順序やルールで動かすか」を定義するだけのもので、LLM が実際に動くのは子の LlmAgent の中だけです。

3 つの LlmAgent の役割分担

LlmAgent は 3 つで、それぞれ instruction (システムプロンプトに相当) とツールを変えて専門化させています。例として planner の定義は以下のとおりで、builder と evaluator も同じ書き方で instruction とツールが違うだけです。

https://github.com/ice1203/gemini-coding-agents/blob/5603a02d75a9142c6ba12bb7cb7c10811a7dc275/agents/plan_build_eval/agent.py#L164-L188

3 つの役割は以下のとおりです。

  • planner: rubric (採点基準) を含む仕様だけを workspace/spec.md に書き出す。コードは書かない
  • builder: 仕様を読み込んで workspace/ 配下に生成物 (今回の例だと index.html) を出力する
  • evaluator: read_file で生成物を読み込み、verify_html (内部で Playwright を使ったヘッドレスブラウザ検証) で実機チェックする。全 PASS なら exit_loop を呼ぶ

output_key と session.state で状態だけを引き継ぐ

エージェント間の状態受け渡しは builder の定義を見るのがわかりやすいです。{spec} のテンプレート変数と output_key="builder_log" の組み合わせがポイントになっています。

https://github.com/ice1203/gemini-coding-agents/blob/5603a02d75a9142c6ba12bb7cb7c10811a7dc275/agents/plan_build_eval/agent.py#L191-L214

output_key は「LLM が最後に出力したテキスト全体を session.state[output_key] にそのまま保存する」仕組みです (例: output_key="spec" なら session.state["spec"] に格納)。中身は別の場所で明示的に定義しているわけではなく、そのエージェントの instruction で「最後に何を出力させるか」を書くことで間接的に決めている形です。たとえば builder の instruction には「4. 最後に『何をどのパスに書いたか』を箇条書きでサマリする」と書いてあり、その箇条書きサマリがそのまま session.state["builder_log"] に入ります。

そして次のエージェントの instruction 内に {spec} と書いておくと、実行直前に ADK が session.state["spec"] の値で文字列展開してから LLM に渡してくれます。{evaluation?} のように ? を付けると state にキーが未存在でも KeyError にならず空文字に置き換わる仕様で、初回ループで evaluator がまだ走っていないタイミングでも builder の instruction が壊れません。

エージェント間で会話履歴を共有するのではなく、output_key という構造化チャンネル経由でだけ通信する形になっているのがポイントです。これがまさに Anthropic 登壇で言われていた「uncorrelated context window (互いに独立したコンテキストウィンドウ)」の ADK 実装になっていて、evaluator が builder の長いログを丸ごと抱え込まずに済みます。

外部 artifact パターン (write_file)

write_file ツールは Python の関数として以下のようにシンプルに定義しています。

https://github.com/ice1203/gemini-coding-agents/blob/5603a02d75a9142c6ba12bb7cb7c10811a7dc275/agents/plan_build_eval/agent.py#L63-L74

ここでの「外部 artifact」は、具体的には planner が書き出す workspace/spec.md のことを指します。planner は仕様 (rubric 込み) を一度ファイルに永続化しておき、後続の builder や evaluator はそのファイルを read_file で読み戻して「真実 (source of truth)」として参照する、という流れです。

これが特に効いてくるのが context rot 対策の面です。長時間ランで会話が長くなってくると、LLM はコンテキスト内の古い情報への注意がだんだん薄れていき、当初の仕様を取り違えたり「自分が今やっているタスク」を見失ったりします。仕様を毎回 read_file でファイルから読み直す形にしておけば、エージェントは何ループ目でも常に同じ「原本」に立ち返れるので、長く走っても仕様がブレません。Anthropic 登壇で紹介されていた「features.json に仕様を外出しして source of truth にする」というパターンと同じ発想です。

ちなみに ADK では tools=[write_file] のように Python の関数をそのまま渡せば自動で FunctionTool にラップされ、関数の docstring が tool description として LLM に渡されます。直感的でいいですね。

LoopAgent と exit_loop でループを抜ける

exit_loop ツールの実装はとてもシンプルです。

https://github.com/ice1203/gemini-coding-agents/blob/5603a02d75a9142c6ba12bb7cb7c10811a7dc275/agents/plan_build_eval/agent.py#L158-L161

evaluator が成果物を「合格」と判定したらループを止めたいわけですが、ADK ではこれを exit_loop ツールで実現しています。中身は tool_context.actions.escalate = True をセットして返すだけです。

escalate=True がセットされると、ADK は「このサブエージェントが上位に伝播する終了シグナルを出した」と判断し、LoopAgent がループから抜けます。evaluator の instruction に「全項目 PASS のときだけ exit_loop を呼べ」と書いておけば、LLM の判断 → Python の決定論的な制御フローへの橋渡しが成立する、というわけです。

なお LoopAgent(max_iterations=5) を併用しているので、evaluator がいつまでも PASS を出さないケースでも 5 回で強制脱出する保険が効きます。

感想

planner / builder / evaluator の分離はモデルに依存しない汎用的なパターンなので、Claude Code のスキル設計にも転用してみたいなと感じています。

この記事が誰かのお役に立てばなによりです。
以上、とーちでした。

この記事をシェアする

関連記事