エージェントロジックとランタイムの分離:マネージドエージェントレイヤーの提唱

Built for Speed: ~10ms Latency, Even Under Load
Blazingly fast way to build, track and deploy your models!
- Handles 350+ RPS on just 1 vCPU — no tuning needed
- Production-ready with full enterprise support
3つ目または4つ目の本番エージェントをリリースする多くのチームは、同じパターンに気づきます。それは、各エージェントファイルのコードの大部分がエージェントロジックではなくインフラストラクチャであるということです。リトライラッパー、認証情報の読み込み、ツールレジストリ、セッション状態、サンドボックス化など、これらは以前のエージェントからわずかな変更を加えてコピー&ペーストされています。ますます一般的になっている解決策の1つは、これらの懸念事項を一度に処理し、エージェントコードに狭いAPIを公開し、プラットフォームがすでに実行しているゲートウェイを介して実際の作業をルーティングするマネージドエージェントレイヤーです。この記事では、そのレイヤーを定義し、その5つのコンポーネントを説明し、AIゲートウェイとMCPゲートウェイがどのようにそれに組み込まれるかを示します。これは、Anthropicの2026年4月のManaged Agentsリリースが、この一般的な分離の一例として挙げられることで締めくくられます。
Northwindでの水曜日。 プラットフォームチームのシニアエンジニアであるデヴィは、今四半期にチームがリリースした4番目のエージェント(デプロイボットに組み込まれたリリースノートジェネレーター)のPRを開きます。差分は312行です。彼女は経験上、そのうち約30行がエージェントの実際の推論ループであることを知っています。残りの約280行は、彼女がこれまでに4回書いたのと同じ5つのことです。それは、2つの場所からMCPサーバー設定を読み込むツールレジストリ、以前のエージェントのバージョンと完全に一致しない指数バックオフ付きのリトライロジック、環境変数とシークレットマネージャーの組み合わせから認証情報を読み込む処理、推測で調整されたTTLでRedisにプッシュされるセッション状態、そしてレート制限、認証エラー、プロバイダーの障害を区別するtry/exceptの連鎖です。
本番環境に4つのエージェントがある場合、それはチーム全体に分散された約1,120行のインフラストラクチャコードであり、それぞれのコピーは微妙に異なっています。来月、プロバイダーのレート制限ポリシーが変更された場合、それを修正するには4つのPRが必要です。認証情報のローテーション手順が変更された場合、さらに4つ必要になります。エージェントロジック自体(いつどのツールを呼び出すかを決定する部分)は、各ファイルの中で最も小さく、最も触れられない部分です。その周りのインフラストラクチャこそが、エンジニアリング時間を浪費しているのです。
解決策は、より良いコピー&ペーストの規律ではありません。この記事が提唱するパターンは、エージェントコードとランタイムの間に位置し、インフラストラクチャを一度だけ所有し、エージェントコードを純粋なエージェントコードとして機能させるレイヤーです。これはいくつかのアーキテクチャの一つであり、ワークフローグラフや耐久性のある実行エンジンも有効な代替手段ですが、最近のプロバイダー管理型エージェント製品で最も明確に現れており、TrueFoundryのゲートウェイプリミティブが直接組み込まれるものです。
1. 絡み合いの問題:インフラストラクチャコードの下に埋もれたエージェントロジック
デヴィのエージェントの絡み合ったバージョンの例を、省略して示します。ファイル全体は約200行ですが、以下に示すのは最初の35行です。
Python — 素朴なエージェント、ロジックとインフラストラクチャが混在(省略版)
import os, time, random, json, yaml, redis
import anthropic, openai
from github import Github
class CodeReviewAgent:
def __init__(self):
# Credential loading — three different patterns across our four agents
self.anthropic_key = os.environ.get("ANTHROPIC_API_KEY") \
or self._load_from_vault("anthropic")
self.openai_key = os.environ.get("OPENAI_API_KEY") \
or self._load_from_vault("openai")
self.github_token = self._load_from_vault("github-pat") # different vault path
# Tool registry — drifted from review-agent-v2's version
with open("mcp_servers.yaml") as f:
self.tools = self._build_tool_registry(yaml.safe_load(f))
# State store — TTL was 30min in v2, 1h here, nobody remembers why
self.redis = redis.Redis.from_url(os.environ["REDIS_URL"])
self.session_ttl = 3600
self.anthropic = anthropic.Anthropic(api_key=self.anthropic_key)
def run(self, pr_id):
# Retry loop — slightly different in every agent file we own
for attempt in range(5):
try:
response = self.anthropic.messages.create(...)
break
except anthropic.RateLimitError:
time.sleep(2 ** attempt + random.random())
except anthropic.APIStatusError as e:
if e.status_code >= 500:
time.sleep(2 ** attempt + random.random())
else:
raise
# ... 170 more lines like this. The actual reasoning loop starts around line 130.
これらはどれもエージェントではありません。エージェントが動作する基盤です。そして、チームがリリースするすべてのエージェントは、これらすべてをわずかに変更した独自のコピーを持っています。なぜなら、それを所有するレイヤーが存在しないからです。
2. マネージドエージェントレイヤーの定義
マネージドエージェントレイヤーは、基盤を所有する抽象化です。エージェントコードには3つの狭いAPIのみを公開し、それ以外は何も公開しません。
Python — マネージドレイヤーがエージェントコードに公開するAPIサーフェス
class ManagedAgent:
# Tool invocation — routes through the MCP gateway, handles auth/RBAC/audit.
# Credentials are fetched from the secret store and injected by the runtime
# at call time; agent code references tools by logical name, never by token.
async def call_tool(self, name: str, **args) -> dict: ...
# Model inference — routes through the AI gateway, handles routing/guardrails/cost
async def llm(self, messages: list, **opts) -> str: ...
# Session state — keyed under the conversation ID, runtime owns persistence
async def save_state(self, key: str, value: object) -> None: ...
async def load_state(self, key: str) -> object | None: ...エージェントコードが責任を負うのは、目標(エージェントが何を達成しようとしているか)、推論ループ(いつどのツールを呼び出し、結果をどのように解釈するか)、および出力形式(エージェントが完了時に何を返すか)の3つだけです。それ以外のすべて、つまり認証、リトライ、サンドボックス化、状態の永続化、可観測性はランタイムに属します。特に、get_credentialメソッドはありません。シークレットは、要求があったとしてもエージェントコードを介して流れることはありません。
マネージドレイヤー上の同じコードレビューエージェントを以下に示します。PRは、以前のテストファイルよりも小さくなっています。
Python — マネージドレイヤー上の同じエージェント、純粋なロジック
from truefoundry.agent import ManagedAgent
class CodeReviewAgent(ManagedAgent):
"""Reviews a pull request against the repo's style guide."""
async def run(self, pr_id: str):
diff = await self.call_tool("github.get_pr_diff", pr_id=pr_id)
review = await self.llm([
{"role": "system", "content": REVIEW_PROMPT},
{"role": "user", "content": diff},
])
await self.call_tool("github.post_review", pr_id=pr_id, body=review)
return {"status": "ok", "review_length": len(review)}
認証情報なし。リトライループなし。状態コードなし。プロバイダーSDKなし。かつて各エージェントファイルに存在していたインフラストラクチャは、今やランタイムに一度だけ存在し、そこで独立してテスト、バージョン管理、展開が可能です。

3. 5つのコンポーネント:オーケストレーション、サンドボックス化、状態、認証情報、リトライ
オーケストレーション。 マネージドランタイムアーキテクチャでは、ランタイムがエージェントループを管理します。具体的には、エージェントの推論ステップの呼び出し、ツール呼び出しのためのモデル出力の解析、それらのディスパッチ、結果のフィードバック、そして終了の検出を行います。(ワークフローグラフや耐久性のある実行スタイルでは、この関係が逆転し、エージェントがグラフを所有し、ランタイムがプリミティブを提供します。そのトレードオフとして、より明示的な制御フローが得られる代わりに、エージェント側の機構が増えます。)どちらのモデルにおいても、停止条件は単純ではありません。例えば、モデルからの明示的な「完了」通知、ツールを要求しなくなった出力、暴走ループを制限するための厳格なターン制限、あるいはエージェントが3ターン連続で同じ引数で同じツールを呼び出している場合に作動するスタック検出器などが挙げられます。
サンドボックス化。 自身の変更を検証するためにテストを実行するコーディングエージェントは、隔離された実行環境を必要とします。具体的には、指定されたワークスペース外のホストファイルシステムへのアクセス不可、AIゲートウェイと登録済みMCPサーバー以外へのアウトバウンドネットワーク不可、厳格なCPU/メモリ/実時間制限などが挙げられます。このサンドボックスは実行ごとに作成され、使い捨てです。
状態管理。 ターン間で、会話履歴、エージェントのスクラッチパッド、中間ツール結果、およびあらゆる進捗マーカーはどこかに保存される必要があります。ランタイムは、セッションIDをキーとして、ワークロードに適したTTL(有効期限)付きのsave_stateおよびload_state機能を提供します。
認証情報注入。 エージェントコードは生のシークレットを直接見ることはありません。ランタイムはシークレットストアから認証情報を取得し、ツール呼び出しの瞬間にそれを注入します。通常、これはエージェントコードが読み取ることのできないAuthorizationヘッダーとして行われます。同じメカニズムがローテーション(更新)も処理します。認証情報がローテーションされると、エージェントコードの変更なしに、次のツール呼び出しで新しい値が使用されます。
リトライとエラー処理。 レート制限に対する指数バックオフ、プロバイダーの障害に対するサーキットブレーカー、リトライを使い果たした後に失敗した実行のためのデッドレターキューなどがあります。ランタイムは各エラー(一時的、永続的、認証など)を分類し、適切なポリシーを適用します。エージェントコードは、成功した結果か、最終的に分類された例外のいずれかを受け取り、生のリトライ状態を見ることはありません。
4. 推論ランタイムとしてのAIゲートウェイ
マネージドレイヤーのllm() APIは、プロバイダーSDKを直接呼び出すことはありません。AIゲートウェイを経由してルーティングされ、AIゲートウェイがエージェントに代わってプロバイダー結合の問題を処理します。
Python — エージェントが「行わない」ことと、ランタイムが「代わりに行う」こと
# What agent code looks like with the managed layer:
review = await self.llm(messages, model="claude-sonnet-4-6")
# What the runtime does on the agent's behalf, via the gateway:
# 1. Resolves "claude-sonnet-4-6" through the virtual-model config — could
# be routed to a different provider entirely based on cost or availability.
# 2. Applies the team's input guardrails (PII scrubbing, prompt-injection
# detection) before the call leaves the platform.
# 3. Picks an upstream account by load and rate-limit headroom; falls
# back to a configured alternate on provider outage.
# 4. Streams the response back; applies output guardrails and emits OTel
# spans with the team/app metadata from the previous post in this series.
# 5. Computes per-trace cost at span close, books it against the team budget.このデカップリングは構造的なものです。エージェントコードは、その呼び出しがOpenAI、Anthropic、あるいは自己ホスト型モデルのいずれに向けられたかを知りませんし、知る必要もありません。プラットフォームチームが新しいプロバイダーを追加したり、モデルを切り替えたり、新しいガードレールを展開したりしても、エージェント側のPR(プルリクエスト)は不要です。エージェントの契約はAPIサーフェスであり、アップストリームではありません。
5. ツールランタイムとしてのMCPゲートウェイ
同じパターンがツールにも適用されます。エージェントコードが論理名でcall_tool()を呼び出すと、MCPゲートウェイはそれを特定のMCPサーバーに解決し、適切なOAuthトークンを注入し、RBACを適用し、実行前ガードレールを実行し、呼び出しをログに記録して結果を返します。
Python — MCPゲートウェイを介してツールを呼び出すエージェントコード
diff = await self.call_tool("github.get_pr_diff", pr_id="4127")
issues = await self.call_tool("linter.check", diff=diff, ruleset="strict")
await self.call_tool("slack.post_message", channel="#code-review", text=summary)その裏側では、ゲートウェイはプラットフォームがサポートするすべてのMCPサーバーのOAuthトークンを、呼び出し元のユーザーまたはサービスIDにマッピングして保持しています。有効期限が切れるとそれらを更新します。また、エージェントのコードが何を示しているかに関わらず、「このエージェントはgithub.get_pr_diffは呼び出せるがgithub.create_prは呼び出せない」といったツール粒度でのRBACを強制します。さらに、引数、戻り値、呼び出し元エージェントおよびセッションIDを含むすべての呼び出しをログに記録し、プラットフォーム全体でのツール使用に関する集中監査ログを作成します(ただし、ログパイプラインがデータを破棄したり、サンプリングしたり、遅延したりする通常の現実が伴います)。TrueFoundryのMCPゲートウェイは、その上に仮想MCPサーバーの抽象化を追加しています。これは、複数の物理MCPサーバーからのツールを集約する単一の論理エンドポイントであり、バックエンドの実装が変更されてもエージェントのツールリストは変更されないようにします。
エージェントコードの契約はAIゲートウェイの契約と同じです。つまり、論理名でAPIを呼び出し、結果を取得し、認証情報の処理はランタイムに任せるというものです。
6. コード実行のサンドボックス化:Docker、gVisor、WebAssemblyのトレードオフ
エージェントがコードを実行する際、例えばテストを実行するコーディングエージェント、CSV上でPythonを実行するデータエージェント、任意のシェルコマンドを実行するツーリングエージェントなど、そのコードはホストから隔離された環境で実行される必要があります。2026年5月時点での主要な3つの選択肢と、コールドスタート時間のおおよその目安(実際の数値はイメージサイズ、ウォームプール、スナップショット、ノードの状態に大きく依存します)を以下に示します。
選択はテナンシーとレイテンシーによって決まります。タスクごとに長いコーディングセッションを実行するシングルテナントエージェントにはDockerが適しており、コールドスタートのコストは一度支払われ、数百回のコード実行に償却されます。信頼できないエージェントコードを提供するマルチテナントプラットフォームにはgVisorが適しています。コードが任意である場合、通常のコンテナの共有カーネルは深刻な懸念事項となるためです。ツール呼び出しごとにサンドボックスを実行するレイテンシーに敏感なワークロード(計算ツール、正規表現ツールなど)にはWasmが適しています。呼び出し自体が20ミリ秒である場合、呼び出しごとに1秒のコールドスタートを支払うことは許容できません。
これらのどれもエージェントが気にする必要はありません。ランタイムは「このコードを実行し、結果を返す」という機能を提供し、ワークロードクラスに適したサンドボックスを自動的に選択します。
7. 状態管理パターン:インコンテキスト、外部ストア、ゲートウェイセッション
エージェントの作業メモリがターン間でどこに保持されるかについては、3つの主要な選択肢があります。
ほとんどのプロダクションエージェントは、会話の集約と可観測性にはゲートウェイセッション、エージェントの明示的なスクラッチパッドと進捗マーカーには外部ストア、そして直近の数ターンにはインコンテキストというように、これらを組み合わせて使用することになります。ランタイムは、どのバックエンドにルーティングされるかに関わらず、エージェント向けAPIとして `save_state` と `load_state` を公開します。
8. Anthropicのマネージドエージェントへの接続
Anthropicの マネージドエージェントは、2026年4月9日からパブリックベータ版として提供されており(ベータヘッダー:managed-agents-2026-04-01)、エージェントロジックと実行インフラストラクチャの一般的な分離の例として捉えることができます。彼らの「メタハーネス」アーキテクチャは、この記事で説明されているほとんどの要素に対応するコンポーネントを公開しています。具体的には、オーケストレーションされたエージェントループ、サンドボックス化されたコード実行(当初はAnthropicホスト型で、その後のリリースでセルフホスト型サンドボックスが追加)、ステートフルな一時停止と再開によるセッション継続性、外部システム向けの認証情報管理、そしてトレースによる可観測性です。Anthropic自身のフレームワークは「脳と手」と表現されており、ClaudeとハーネスはAnthropicのインフラストラクチャ(脳)上で動作し、シェルコマンドとコードが実行されるサンドボックスが手となります。
単一プロバイダー上でエージェントを構築するチームにとって、Anthropicの製品は最もシンプルな選択肢です。すべてのClaude APIアカウントでハーネスがデフォルトで有効になっており、APIサーフェスは新しいmanaged-agentsベータヘッダーとなります。複数のプロバイダーでエージェントを実行するチーム、または独自のサンドボックスとツール要件を持つチームの場合、同じパターンをゲートウェイプリミティブから構築できます。具体的には、推論ランタイムとしてのAIゲートウェイ(マルチプロバイダー対応で、このシリーズの以前の投稿から引き継がれるコスト配分と予算強制機能を持つ)、ツールランタイムとしてのMCPゲートウェイ、そしてプラットフォームチームが所有するサンドボックスと状態ストア層です。TrueFoundryの AIゲートウェイ と MCPゲートウェイ は、当社が製品として提供する2つの層です。それらの上にあるオーケストレーション層とサンドボックス層は意図的にプラグイン可能に設計されています。これは、それらの層でワークロード固有の決定が行われるためです。
9. よくある質問
マネージドエージェント層は、LangGraphやAutoGenのような単なるフレームワークなのでしょうか?
LangGraphやAutoGenのようなフレームワークはエージェントループライブラリであり、この記事でマネージド層と呼ぶオーケストレーションコンポーネントを所有しています。しかし、通常、認証情報、サンドボックス化、状態の永続化、プロバイダーのルーティングはアプリケーションに任されます。ここでいうマネージドエージェント層は、これらすべてを所有し、エージェントのプロセス内のコードとしてではなく、プラットフォームインフラストラクチャとして実行します。
マネージドランタイムの代替案は何ですか?また、それらが優れているのはどのような場合ですか?
主なものを、違いの大きい順に挙げます。
- ワークフローグラフ(LangGraphなど) エージェントが明示的なステートマシングラフを所有し、ランタイムがノードを実行します。利点:明示的な制御フローで、理解しやすい。欠点:エージェント側の機構が増え、グラフ自体が保守すべきコードとなります。
- 耐久性のある実行(Temporal、Restate) エージェントのコードは、自動チェックポイントとリプレイ機能を備えた長時間実行関数として記述されます。利点:再起動をまたいでも揺るぎない信頼性。欠点:より複雑なプログラミングモデルと、重いランタイムへの依存。
- イベント駆動型 / キューワーカー 各エージェントのターンはメッセージハンドラです。利点:容易な水平スケーリング、自然なバックプレッシャー。欠点:ローカルコンテキストを共有する必要がある複数ターンの推論を表現するのが難しい。
- プロバイダー管理型(Anthropic Managed Agents、OpenAI Responses + エージェントツール) ランタイム全体がプロバイダーのものである。利点:運用負担が最小。欠点:単一プロバイダーへのロックイン、サンドボックスと認証情報の境界に対する制御が少ない。
- 組み込みライブラリ ランタイムは一切なく、各エージェントは小さなプログラムです。利点:最大限の柔軟性。欠点:本稿の冒頭で述べた絡み合いの問題。
本稿では一つのアーキテクチャを提唱しますが、これらのどれも間違いではありません。それらは、柔軟性と運用の整合性という同じ曲線上の異なる点に位置するものです。
マネージドランタイムの欠点とは何でしょうか?
率直に言って、4つの点があります。(1) ローカルな柔軟性の低下 — エージェントのコードは、ランタイムを迂回して巧妙なツールごとの認証情報処理やカスタムリトライポリシーを実行することはできません。(2) 実験の困難さ — エージェントをローカルで実行するには、Pythonインタープリタだけでなく、ランタイムが利用可能である必要があります。(3) ランタイムロックイン — エージェントをランタイムから移行させるのは、再コンパイルではなく、実際の移行作業となります。(4) 問題発生時の抽象化の漏洩 — 失敗したエージェントのデバッグでは、エージェントの推論、ランタイムのオーケストレーション、そしてその下のゲートウェイにわたる推論が必要となり、3つのログストリームを関連付けることになります。記事では、これらは通常、3番目または4番目の本番エージェント以降では支払う価値があるが、最初の1つではそうでないことが多いと主張しています。
Anthropicがマネージドエージェントを製品として提供するなら、なぜ自社で構築する必要があるのでしょうか?
Claude上で完全に動作するエージェントの場合、Anthropicのマネージドエージェント製品が通常最もシンプルな道です。チームが自社で構築するケースは、通常、マルチプロバイダールーティング(異なるワークロードに対して同じエージェントを異なるモデルで実行する)、ワークロード固有のサンドボックス化(信頼できないコード用のセルフホスト型gVisorプール)、または既存のプラットフォームインフラストラクチャとの統合(企業秘密マネージャー、既存の可観測性スタック)に帰着します。パターンは同じです。問題は、購入するか、組み立てるかです。
コードを実行しないエージェントの場合、これはどのように機能しますか?
サンドボックスコンポーネントはオプションです。純粋なツール利用型エージェント(MCP呼び出しをオーケストレーションし、構造化された出力を生成するもの)はコード実行を必要とせず、ランタイムはサンドボックスを完全にスキップできます。他の4つのコンポーネント(オーケストレーション、状態、認証情報、リトライ)は引き続き適用されます。
ストリーミング応答や部分的な出力の場合、ランタイムはどのように機能しますか?
llm() APIは、バッチ処理とストリーミングの両方のバリアントを公開しています。ストリーミングの場合、エージェントのコードはチャンクの非同期イテレータを受け取ります。ランタイムは基盤となるプロバイダーストリームを処理し、チャンクが到着するたびに出力ガードレールを適用し、ストリームが閉じるときにターンごとのOTelスパンを発行します。
これはどのようにテストされますか?LLMとサンドボックスに依存するものを単体テストすることはできません。
マネージドレイヤーは、エージェントをテスト可能にするものです。3つのAPI(call_tool、llm、save_state/load_state)はエージェントの依存関係のすべてであり、それぞれモックまたはリプレイが可能です。テストスイートは通常、決定論的なモックLLMと記録されたツール応答を組み合わせることで、プロバイダーやサンドボックスに触れることなくエージェントの推論ループを検証できます。ランタイム自体は、プラットフォームレイヤーで個別にテストされます。
TrueFoundryはどのように適合するのか?
The TrueFoundry AI Gateway and MCP Gateway は、ここで説明されているアーキテクチャの推論およびツールランタイムレイヤーを提供します。AI側ではプロバイダーフォールバックによるモデルルーティング、ガードレール、コストアトリビューション、予算管理を、ツール側ではOAuth処理、RBAC、仮想MCPサーバー、監査ロギングを担います。それらの上位にあるオーケストレーション、サンドボックス、ステートストアの各レイヤーは、ワークロード固有の決定が行われる場所であるため、意図的にプラットフォームチームが所有します。Claude上で完全に垂直統合されたマネージド製品を求めるチームにとって、AnthropicのManaged Agentsが適切な比較対象となります。
3つ目または4つ目のエージェントを作成し、DeviのPRでパターンを認識したチームにとって、最初の実用的なステップは、call_tool / llm / save_stateのインターフェースを小さな共有ライブラリに抽出し、プラットフォームが既に実行しているゲートウェイを介してそれぞれをルーティングすることです。認証情報とツールごとの認証は最初からランタイムが所有し、エージェントコードを介して渡されることはありません。その他すべては、その契約から派生します。
参考文献
- Anthropic — Claude Managed Agents(2026年4月9日公開ベータ版)
- Anthropic — 長時間実行エージェントのための効果的なハーネス
- Model Context Protocol — 仕様
- TrueFoundry AI Gateway — 概要
- TrueFoundry MCP Gateway — 概要
- gVisor — コンテナ用アプリケーションカーネル
- WebAssembly — セキュリティモデル
Northwind、Devi、および4つのエージェントコードベースは例示的なものです。アーキテクチャパターン、絡み合いの問題、およびゲートウェイベースの分解は、2026年に実際に本番エージェントプラットフォームが構築されている方法であり、その中でAnthropicのManaged Agentsは最も目立つ単一プロバイダーの例です。TrueFoundryのAI GatewayとMCP Gatewayは、推論およびツールランタイムレイヤーを提供する実際の出荷製品です。
TrueFoundry AI Gateway delivers ~3–4 ms latency, handles 350+ RPS on 1 vCPU, scales horizontally with ease, and is production-ready, while LiteLLM suffers from high latency, struggles beyond moderate RPS, lacks built-in scaling, and is best for light or prototype workloads.














.webp)
.webp)


.png)

.png)














