Managed Agentsのセルフホストサンドボックスを構築する — Environment worker実装
Managed Agentsのツール実行を自社インフラに置くセルフホストサンドボックスを、Environment workerの構築手順とカスタムツール提供まで実装します。
Managed Agentsは既定でAnthropicが管理するクラウドサンドボックス上でツールを実行します。セルフホストサンドボックスは、この実行だけを自社インフラに移し、エージェントのコード・ファイルシステム・ネットワークegressを自分の環境から出さない構成です。オーケストレーション(モデルが動く場所)はAnthropic側に残ったままなので、切り替えても既存のエージェント設計自体は変わりません。
本稿ではEnvironment workerを実際に構築する手順を、常時稼働のワーカーからカスタムツールの提供までコード例つきで扱います。すでにエージェントを作成済みで、これから実行環境だけをクラウドから自社インフラへ移したい人を対象にしています。
セルフホスト化で変わる範囲と事前準備
セルフホストに切り替えても、ツールの入出力はAnthropicの制御プレーンに流れ続けます。モデルが結果を見て次の判断をする以上、これは避けられません。変わるのは、ファイルシステムの読み書き・プロセスの起動・到達可能なネットワークの3つが自社ホストの管理下に入る点です。エージェントのskillsやmemory storesの中身はAnthropicが保管したまま、セッションの間だけワーカーがサンドボックスにコピーします。
事前に用意するものは次の4つです。
- 既存のエージェント。無ければ先にQuickstartでエージェントIDを取得しておきます
/bin/bashをその正確なパスに持つLinuxホスト。ワーカーのbashツールはPATHを参照せずこのパスを直接呼び出します。TypeScript SDKを使う場合はNode.js 22以上とunzip/tarも必要ですantCLI、またはPython / TypeScript / GoのいずれかのAnthropic SDK- Consoleで発行する環境キー。キー発行はConsole限定で、APIから環境を作った場合でも発行だけはConsoleで行います
サンドボックス内のファイル配置も把握しておきます。/workspaceがツール実行とskillダウンロードの既定ディレクトリで、antの--workdirはカレントディレクトリが既定なので/workspaceを明示指定します。成果物はセルフホストではエージェントがサンドボックス内に書いた場所(通常は作業ディレクトリ配下)にそのまま残ります。memory storesを添付したセッションでは/mnt/memory/以下にストアごとのディレクトリが作られ、セッション終了時にワーカーが削除します。
ステップ1 — セルフホスト環境を作成しキーを発行する
Console(Workspace > Environments > New > Self-hosted)からでも、APIからでも作成できます。
curl -sS --fail-with-body https://api.anthropic.com/v1/environments \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{
"name": "self-hosted",
"config": {"type": "self_hosted"}
}'環境を作成したらConsoleでその環境を開き、「Generate environment key」をクリックします。発行された値をワーカーホストにエクスポートします。
export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..."
export ANTHROPIC_ENVIRONMENT_ID="env_..."ステップ2 — Environment workerを動かす
self_hosted環境は1つのワークキューとして振る舞います。セッションがこの環境に割り当てられると、Anthropicがそのセッションをワークアイテムとしてキューに積み、ワーカーがそれを取得(claim)して実行コンテキストを立ち上げ、skillをダウンロードし、ツール呼び出しを実行して結果を投げ返します。取得の方式は常時ポーリングのalways-onか、session.status_run_startedイベントで起きるwebhook-triggeredのどちらかです。
always-on(antCLI)はもっとも手数が少ない構成です。
ant beta:worker poll --workdir "/workspace"このコマンドはANTHROPIC_ENVIRONMENT_KEYとANTHROPIC_ENVIRONMENT_IDを環境変数から読み、ワークアイテムの取得・skillダウンロード・ツール実行・結果送信までを1プロセスで担います。SIGTERMやSIGINTを受けると、実行中のツール呼び出しをキャンセルしてエラー結果を投げ、ワークアイテムを解放してから終了します。
セッションごとに独立したファイルシステムやリソース制限が要る場合は、セッションごとに別サンドボックスを起動する構成にします。antを組み込みant beta:worker runをエントリポイントにしたイメージを用意し、ポーラーからのスポーンスクリプトで起動します。
spawn.sh の例(セッションごとに別コンテナを起動)
#!/bin/bash
# spawn.sh: called once per claimed work item
mkdir -p "/host/outputs/$ANTHROPIC_SESSION_ID"
exec docker run --rm \
-e ANTHROPIC_SESSION_ID -e ANTHROPIC_ENVIRONMENT_KEY \
-e ANTHROPIC_WORK_ID -e ANTHROPIC_ENVIRONMENT_ID -e ANTHROPIC_BASE_URL \
-v "/host/outputs/$ANTHROPIC_SESSION_ID":/workspace \
your-imageポーラーはANTHROPIC_SESSION_ID / ANTHROPIC_WORK_ID / ANTHROPIC_ENVIRONMENT_ID / ANTHROPIC_ENVIRONMENT_KEYをスクリプトの環境変数へ注入し、取得したワークアイテムをJSONで標準入力に渡します。Anthropicがセッション用のsecretを発行していれば、そのJSONにも含まれます。ホスト側の/host/outputsをサンドボックスの/workspaceにバインドマウントしておけば、セッション終了後に成果物とダウンロード済みskillをそのまま回収できます。ただしant beta:worker runのエントリポイントはmemory storesをマウントしません。memory storesを使うセッションがある環境では、SDKワーカーをベースにイメージを作り直す必要があります。
起動はポーラーにスクリプトを渡すだけです。
ant beta:worker poll --on-work ./spawn.shalways-on(SDK)はTypeScriptなら次の形になります。
import Anthropic from "@anthropic-ai/sdk";
import { EnvironmentWorker } from "@anthropic-ai/sdk/helpers/beta/environments";
const environmentKey = process.env.ANTHROPIC_ENVIRONMENT_KEY!;
const environmentId = process.env.ANTHROPIC_ENVIRONMENT_ID!;
const client = new Anthropic({ authToken: environmentKey });
const controller = new AbortController();
process.once("SIGINT", () => controller.abort());
process.once("SIGTERM", () => controller.abort());
await new EnvironmentWorker({
client,
environmentId,
environmentKey,
workdir: "/workspace",
signal: controller.signal
}).run();プロセスをkillするのではなくAbortControllerで中断させているのは、これによってワーカーが変更済みのmemory storeファイルをアップロードしてから終了できるためです。webhook-triggeredを選ぶ場合は、Consoleでsession.status_run_startedを購読するWebhookエンドポイントを定義し、署名検証用のWebhook Signing Keyを別途エクスポートします(Python実装ではpip install "anthropic[webhooks]"が必要です)。
ステップ3 — サンドボックスからカスタムツールを提供する
カスタムツールは自分のコードが実行するツールで、エージェントがagent.custom_tool_useイベントを出し、対応するuser.custom_tool_resultを待ちます。ワーカー自身をこの実装先にすると、ツールはサンドボックスに設定した内部サービス・認証情報・ネットワークegressにだけ到達し、それ以外には出ません。結果の送信は環境キーで認可されるため、Claude APIキーをワーカーホストに置く必要もありません。
まずエージェント側のtoolsにcustomエントリを追加し、ワーカーが登録する名前と一致させます。
{
"type": "custom",
"name": "get_order_status",
"description": "Look up an order in the internal fulfillment system by order ID.",
"input_schema": {
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "The order ID" }
},
"required": ["order_id"]
}
}次にワーカーのtoolsファクトリへ、標準ツールセットと並べて実装を渡します。
import Anthropic from "@anthropic-ai/sdk";
import { EnvironmentWorker } from "@anthropic-ai/sdk/helpers/beta/environments";
import { betaTool } from "@anthropic-ai/sdk/helpers/beta/json-schema";
import { betaAgentToolset20260401 } from "@anthropic-ai/sdk/tools/agent-toolset/node";
const getOrderStatus = betaTool({
name: "get_order_status",
description: "Look up an order in the internal fulfillment system by order ID.",
inputSchema: {
type: "object",
properties: { order_id: { type: "string", description: "The order ID" } },
required: ["order_id"]
},
run: async ({ order_id }) => `Order ${order_id}: shipped`
});
await new EnvironmentWorker({
client,
environmentId,
environmentKey,
workdir: "/workspace",
tools: (ctx) => [...betaAgentToolset20260401(ctx), getOrderStatus]
}).run();エージェントに宣言だけあってワーカー・クライアントのどちらにも実装が登録されていないカスタムツールは、何かが結果を投げ返すまでrequires_actionの停止理由でセッションが止まったままになります。動かないときは、まずこの登録漏れを疑うのが早道です。
自社ネットワーク内のMCPサーバーを使いたい場合は、MCP tunnelsでAnthropic側から到達させる方法のほかに、ワーカー自身をMCPクライアントにしてサーバーのツールをカスタムツールとして宣言する方法があります。この場合MCPサーバーは外部からのインバウンド接続を一切必要とせず、ワーカーがサンドボックス内から自分のMCPセッション越しに呼び出しを転送します。両者は独立した仕組みなので、実行場所とツール到達経路の両方を自社境界内に収めたいときは併用します。カスタムツールの宣言形式そのものはAgent SDKのカスタムツール実装と同じ考え方です。
よくあるつまずき
/bin/bashがそのパスに無い。ワーカーのbashツールはPATHを経由せず直接そのパスを呼ぶため、シェルをどこか別の場所にインストールしていると素通りでエラーになります--workdirを指定し忘れる。antの既定はカレントディレクトリで、システム既定の/workspaceではありません。skillは<workdir>/skills/<name>/にダウンロードされるので、別のディレクトリを使うならエージェントのシステムプロンプト側でも参照先を合わせる必要がありますant beta:worker runにmemory storesを期待する。このエントリポイントはmemory storesをマウントしません。マウントが要るならSDKワーカーをベースにイメージを作り直します- カスタムツールをant CLIワーカーに登録しようとする。ant CLIには登録手段自体が無いため、この用途ではSDKワーカー一択です
- Claude Platform on AWSでConsole発行の環境キーを使う。この構成の認証はIAMまたはAWS Console発行のAPIキーで、Console発行の環境キーはそのままでは通りません
always-on・webhook-triggered・サンドボックス単位、どれを選ぶか
| 構成 | 向く場面 | 留意点 |
|---|---|---|
| always-on(ant CLI) | 向く場面まず動かして構成を確かめたい | 留意点セットアップが最小。カスタムツールは登録できない |
| always-on(SDK) | 向く場面カスタムツールやmemory storesを使う | 留意点シグナルハンドリングを自分で書く必要がある |
| webhook-triggered(SDK) | 向く場面アイドルなポーラーを常駐させたくない | 留意点Webhookエンドポイントと署名検証の実装が別途要る |
| サンドボックス単位(spawn.sh) | 向く場面セッションごとに独立したファイルシステム・リソース制限が要る | 留意点memory storesを使うならSDKワーカーへの作り直しが要る |
まとめ
セルフホストサンドボックスは、モデルの実行主体をAnthropicに残したまま、ツールが触るファイルシステム・プロセス・ネットワークだけを自社インフラに引き戻す構成です。最短経路はant CLIのant beta:worker pollで、カスタムツールやセッション単位の分離が必要になった時点でSDKワーカーへ切り替えるのが実装の流れとして自然です。どこまで自社が責任を持つことになるかは、セキュリティ責任分界で切り分けて確認してから本番投入するのが安全です。