Claude Agent SDK は、呼び出すたびに claude CLI のサブプロセスを起動し、そのプロセスがシェル・作業ディレクトリ・ディスク上のセッションファイルを持ちます。そのため、状態を持たない API ラッパーと同じ感覚ではホスティングできません。この記事では公式ドキュメント「Hosting the Agent SDK」に沿って、自前のインフラで SDK を動かすときのセッション構成、コンテナに必要なもの、永続化・監視・認証・スケーリング、マルチテナント分離を整理します。SDK の全体像は「Claude Agent SDK でエージェントを作る」を参照してください。
なお、エージェントループ自体を自分のインフラで動かす必要がなければ、Anthropic がループをホストする Managed Agents という選択肢もあります。こちらはアプリがイベントを送り、結果をストリームで受け取る形で、ツール実行は Anthropic 管理のクラウドサンドボックスか、自前のセルフホスト型サンドボックスで行われます。
サブプロセスモデルとローカルに残る状態
コードから query() を呼ぶと、SDK は別プロセスとして claude CLI を起動し、標準入出力(stdio)でやり取りします。シェル、作業ディレクトリ、JSONL 形式のセッション記録(トランスクリプト)はこのサブプロセスが持ちます。サブプロセス自身はネットワークで待ち受けず、外向きに api.anthropic.com へ HTTPS で通信するだけです。
1セッションは1サブプロセスに対応します。N 件のセッションを同時に動かせば N 個のサブプロセスができ、それぞれが自分のプロセスツリーとトランスクリプトを持ちます。既定ではすべてアプリと同じ作業ディレクトリを引き継ぐので、セッションごとにファイルを分けたいときは query() のオプションで cwd を個別に渡します。
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Summarize the files in this directory",
options: { cwd: "/work/session-a" },
})) {
console.log(message);
}
Python では ClaudeAgentOptions(cwd="/work/session-a") を渡します。TypeScript の例はトップレベル await を使うので、.mts で保存するか package.json に "type": "module" を設定します。
既定では次の3種類の状態がコンテナのファイルシステムに置かれ、コンテナの再起動・スケールダウン・別ノードへの移動ではどれも残りません。
| 状態 | 既定の場所 |
|---|---|
| セッションのトランスクリプト | ~/.claude/projects/(CLAUDE_CONFIG_DIR を設定していればその下の projects/) |
CLAUDE.md メモリファイル | ユーザー階層は ~/.claude/CLAUDE.md、プロジェクト階層はセッションの作業ディレクトリ |
| 作業ディレクトリの成果物 | セッションの作業ディレクトリ |
トランスクリプトをホストをまたいで残すには SessionStore アダプタを設定します。メモリファイルや作業ディレクトリの成果物は対象外なので、ボリュームのマウントやオブジェクトストレージとの同期など、別の保存方法を用意します。再開・フォークの API 上の扱いは「Agent SDK のセッション管理」を参照してください。
4つのセッション構成から選ぶ
公式は、コンテナの寿命とセッションの関係を次の4パターンに分けています。コンテナをどこで動かすか(ローカル Docker・Modal・Kubernetes)は、公式の hosting cookbook(claude-cookbooks リポジトリの claude_agent_sdk/hosting)にデプロイ可能なコードがあり、パターンとデプロイ先を組み合わせて選びます。
| パターン | 動かし方 | 向いている用途の例 |
|---|---|---|
| エフェメラル | タスクごとにコンテナを作り、終わったら破棄する | バグの調査と修正、請求書・領収書の抽出、文書の翻訳、メディア変換 |
| 長時間稼働 | 常駐コンテナで、多くは1コンテナに複数の SDK プロセスを載せる | メールの仕分けと返信、ユーザーごとの編集可能なサイト、Slack などの継続トラフィックを受けるチャットボット |
| ハイブリッド | エフェメラルなコンテナが起動時に SessionStore から復元し、更新を書き戻す | ときどき確認する個人用プロジェクト管理、数時間かけて中断・再開する調査、履歴を引き継ぐサポート |
| マルチエージェント | 1コンテナ内で複数の SDK サブプロセスを動かす | 共有環境でエージェント同士がやり取りするシミュレーション |
エフェメラルでは、環境変数 TASK_PROMPT からタスクを読み、SDK を呼んで終了する一回きりのエントリポイントを置きます。
import asyncio
import os
from claude_agent_sdk import ClaudeAgentOptions, query
async def main():
async for message in query(
prompt=os.environ["TASK_PROMPT"],
options=ClaudeAgentOptions(max_turns=20),
):
print(message)
asyncio.run(main())
上限内で終われば結果メッセージの subtype は success です。20ターンの上限に達すると error_max_turns になり、そのメッセージを返したあと query() が例外を投げるので、コンテナをきれいに終わらせたい場合はループを try で囲みます(結果の種類は「Agent SDK のエージェントループ」参照)。
長時間稼働では、コンテナが HTTP か WebSocket のエンドポイントを公開し、アクティブなセッションごとに長寿命の query とその裏のサブプロセスを対応させます。TypeScript は streamInput() で進行中のセッションにターンを足し、startup() で事前にサブプロセスを温めておけます(最初のリクエストまで作業ディレクトリが決まらない場合は prewarm())。Python は ClaudeSDKClient でセッションを開いたままにします。コンテナは同時セッションの最大数をメモリに載せられる大きさにします。
ハイブリッドは、アイドル中はコンテナを落とし、利用者が戻ってきたら立ち上げ直す構成です。SessionStore 無しでコンテナを落とすとトランスクリプトも消えるので、このパターンではストアが必須です。データベースなどから引いたセッション ID を resume に、共有ストアを sessionStore(Python は session_store)に渡して再開します。プロバイダのアイドルタイムアウトは、利用者が戻ってくる頻度に合わせて調整します。
マルチエージェントでは、エージェントごとに作業ディレクトリを分けてファイルの上書きを防ぎ、エージェントごとの CLAUDE.md が混ざらないよう設定の読み込みも分離します(方法は最後の節を参照)。
コンテナの準備:ランタイム・リソース・ネットワーク
プロセス分離、リソース制限、ネットワーク制御、使い捨てのファイルシステムを得るため、SDK はサンドボックス化したコンテナで動かします。提供元を選ぶときに確認する点として、公式は次を挙げています。
- 誰がサンドボックスを運用するか: サービスとして運用してもらうか、ソフトウェアを自分のインフラで動かすか
- コールドスタートの速さ: エフェメラル構成では1秒未満が必要。長時間稼働ならもっと許容できる
- 永続ストレージ: 永続ボリュームがあるか。ハイブリッド構成はどこかに永続ストレージが要る
- 料金体系: 秒単位課金は突発的なエフェメラル用途に、時間単位は長時間稼働に向く
- ネットワーク: 独自の外向きルール、外向きプロキシ、規制環境向けのプライベート VPC ピアリングに対応しているか
Docker・gVisor・Firecracker などのセルフホスト型の分離技術と細かな設定は、公式の Secure deployment にまとまっています。サンドボックスの考え方は「Claude Code のサンドボックス環境を選ぶ」も参考になります。
ランタイムは、Python SDK なら Python 3.10 以上、TypeScript SDK なら Node.js 18.14 以上です。どちらの SDK も多くのインストール形態でネイティブの Claude Code バイナリを同梱しており、起動される CLI のために Node.js を別途入れる必要はありません。同梱バイナリは SDK パッケージのバージョンに固定されているので、CLI の更新は SDK の更新で行います。SDK はセマンティックバージョニングに従うため、パッチリリースは継続的に取り込み、マイナーリリースは CHANGELOG を確認してから上げるよう公式は勧めています。
リソースは、起動直後のインスタンスで「エージェント1つあたり RAM 1GiB・ディスク 5GiB・CPU 1」が出発点の目安です。メモリはセッションの長さとツールの使用量に応じて増えるので、アイドル時ではなく実際に必要なセッション長と同時実行数で見積もります。
ネットワークは、api.anthropic.com(Amazon Bedrock や Google Cloud で動かす場合は各リージョンのエンドポイント)への外向き HTTPS が必要です。MCP サーバーや外部ツールを使うならその接続先も開けます。本番では、ドメインの許可リスト・認証情報の注入・リクエストのログ記録を行う外向きプロキシ経由にするのが推奨です。内向きは、コンテナで HTTP か WebSocket のポートを公開し、アプリがそのリクエストを受けて内部で SDK を呼びます。
本番運用で決めておくこと
セッションと状態の永続化: 利用者が再開を期待するセッションは、SessionStore アダプタでトランスクリプトを永続ストレージへミラーします。SDK のリポジトリにはオブジェクトストア(S3)・キーバリューストア(Redis)・データベース(Postgres)向けの参照実装と、自作アダプタ用の適合テストがあります。挙動として押さえておく点は次の3つです。
- 対象はトランスクリプトだけ:
CLAUDE.mdや作業ディレクトリの成果物は含まれません。 - 置き換えではなくミラー: サブプロセスはまずローカルディスクに書き、SDK が同じバッチをストアへ転送します。新規セッションではローカルのトランスクリプトが残りますが、ストアから再開した実行では終了時にローカルのコピーが消え、ストアだけが永続的なコピーになります。
mirror_errorメッセージ: ストアへの書き込みに失敗したバッチは、合計3回まで試行したあと破棄され、{ type: "system", subtype: "mirror_error" }が流れてクエリは続行します。ストアの耐久性が重要なら、これを監視・通知の対象にします。
監視(オブザーバビリティ): SDK は環境変数から OpenTelemetry の設定を引き継ぎます。コンテナやオーケストレータの階層で OTEL の変数を設定しておけば、すべての query() がスパン・メトリクス・ログイベントをコレクタへ送ります。CLAUDE_CODE_ENHANCED_TELEMETRY_BETA はトレースにだけ必要です。プロンプト本文やツール入力は既定では送られません。詳しくは「OpenTelemetry で Claude Code を監視する」も参照してください。
CLAUDE_CODE_ENABLE_TELEMETRY=1
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.example.com:4318
認証とシークレット: 気にすべき点は3つです。
- Anthropic API: サブプロセスは環境変数
ANTHROPIC_API_KEYを読みます。シークレットマネージャから渡すか、ANTHROPIC_BASE_URLでコンテナの外でキーを付けるプロキシを経由させます。 - 内向き: 利用者の認証はエージェントの手前のゲートウェイで行い、エージェントは認証済みのリクエストだけを受け取るようにします。cookbook のサーバー例も、既定では認証が無いのでゲートウェイの背後に置き、ポートを直接インターネットに出さないよう注意書きしています。
- 外向きのツール: ツールの認証情報はエージェントの環境に置かず、コンテナを出たあとにプロキシが API キーを付ける形にします。
スケーリングと同時実行: セッションごとにサブプロセスが1つできるので、1ホストの同時実行数はメモリで決まります。公式の目安式は次のとおりです。
agents per host = (host RAM - overhead) / (per-session RAM ceiling)
1セッションあたりの上限は、代表的なセッションを想定の長さとツール負荷で実際に流し、ピーク時の RSS を測って決めます。前述の 1GiB は下限であって上限ではありません。長時間稼働パターンで水平に増やすときは、ロードバランサの背後にコンテナを並べ、sessionId のコンシステントハッシュで各セッションを同じコンテナに固定します。
コスト: 一般にトークン費用がコンテナ費用を1桁以上上回ります。最小構成のコンテナは1時間あたり約0.05ドルですが、長いセッション1つでトークンに数ドルかかることがあります。セッション単位の集計は「Agent SDK のコスト追跡」を参照してください。
マルチテナント分離と既知の制約
SDK は既定でファイルシステムから設定と CLAUDE.md を読みます。複数のテナント(利用者・顧客)を1つのコンテナで扱うと、あるテナントの文脈が別のテナントのセッションに漏れるおそれがあります。公式が挙げる分離策は次のとおりです。
settingSources: [](Python はsetting_sources=[])で、ユーザー・プロジェクト・ローカルの設定を読まないenvにCLAUDE_CODE_DISABLE_AUTO_MEMORY=1を設定する。自動メモリはsettingSourcesに関係なくシステムプロンプトに読み込まれるためCLAUDE_CONFIG_DIRをテナントごとのディレクトリに向け、グローバル設定~/.claude.jsonを共有しない- テナントごとの作業ディレクトリを、すべての
query()でcwdとして明示する - 外向きプロキシでテナントごとのルール(送信元 IP・認証情報・ドメイン許可リスト)を分ける
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt,
options: {
cwd: tenantDir,
settingSources: [],
env: {
...process.env,
CLAUDE_CONFIG_DIR: configDir,
CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
},
},
})) {
// ...
}
TypeScript の env はサブプロセスの環境変数を置き換えるので、PATH や ANTHROPIC_API_KEY を残すために ...process.env を展開します。Python の env は引き継いだ環境変数に上書きで足される形です。tenantDir と configDir は、ほかのテナントから読めないパスにします。権限まわりの設定は「Agent SDK の権限設定」もあわせて確認してください。
設計時に織り込んでおくべき既知の制約は次のとおりです。
| 制約 | 対処 |
|---|---|
| セッション全体のタイムアウトが無い | maxTurns(Python は max_turns)でターン数の上限を決める |
| 長いセッションでメモリが増える | セッションの長さに上限を設けるか、サブプロセスを定期的に作り直す |
| 大量のサブエージェントを並列で出すとレート制限に当たることがある | 一度に広く出さず、小さなバッチに分ける |
| サブエージェントごとの実時間の締め切りが無い | AgentDefinition の maxTurns で各サブエージェントを制限する。CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS は出力が止まったときの見張りで、総実行時間の締め切りではない |
手元では動くのにデプロイ先で失敗するときは、公式のトラブルシューティングが3つの典型を挙げています。サービス起動時に CLI が見つからない(Python ではサービスの PATH がシェルと違う、TypeScript ではイメージのビルドで SDK の optional dependencies が省かれた、または pathToClaudeCodeExecutable の指す先が無い)、CLI はあるが起動しない(コンテナのアーキテクチャや libc と合わないバイナリ、実行権限が落ちたファイル)、実行途中で Claude Code のプロセスが終了する、の3つです。
本ページは Hosting the Agent SDK を中心に、Persist sessions to external storage と claude-cookbooks の hosting サンプル の記述を踏まえた非公式の日本語解説です(確認日 2026-10-29)。コード例は一部を簡略化しています。最新の仕様は公式ドキュメントを確認してください。