自托管沙箱

在您自己的自托管沙箱环境中运行智能体会话。


默认情况下,托管智能体在 Anthropic 托管的云容器内执行工具和代码。自托管沙箱将编排保留在 Anthropic 侧,但将工具执行转移到您控制的基础设施中,因此智能体的代码、文件系统和网络出口永远不会离开您的环境。

自托管沙箱尚未在 Claude Platform on AWS 上可用。

与云环境的区别

云环境自托管沙箱
工具运行位置Anthropic 托管的容器您的基础设施
网络访问Anthropic 的出口控制您的网络策略
文件和 GitHub 仓库挂载Anthropic 管理您管理
生命周期Anthropic 管理您管理

当智能体需要操作不能离开您网络边界的数据、访问不可公开路由的内部服务,或在您组织自己的合规性和审计控制下运行时,自托管是一个很好的选择。

有关零数据保留和 HIPAA BAA 资格,请参阅 API 和数据保留

何时与 MCP 隧道结合使用

自托管控制智能体代码在哪里执行MCP 隧道控制Anthropic 如何访问您网络中的 MCP 服务器。它们是独立的:在 Anthropic 云容器中运行的会话仍然可以通过隧道访问私有 MCP 服务器,自托管会话可以使用隧道或公共 MCP 服务器。当您希望执行和工具访问都保持在您的边界内时,请同时使用两者。

环境工作者

Tip

以下指南描述了如何使用任何通用沙箱平台构建工作者。还有针对 CloudflareDaytonaModalVercel 的平台特定指南。

环境工作者是您在自己基础设施上运行的进程,它接收来自 Anthropic 的工具执行请求并在本地运行。self_hosted 环境是一个工作队列,将 Anthropic 的编排连接到您的工作者:当会话被分配到环境时,Anthropic 将其作为工作项入队。您的工作者从该队列中获取项,为每个会话生成执行上下文,下载智能体技能,在本地运行工具调用,并将结果发回。

工作通过轮询环境的队列来获取:由持续轮询的常驻工作者,或在 session.status_run_started 时唤醒并开始轮询的 webhook 触发处理器

CLI 和 SDK 都包含预构建的工作者来编排您的会话。ant CLI 仅支持常驻模式;SDK 支持常驻和 webhook 触发架构。

CLI 和 SDK 都是可配置的(参见参考),但如果您需要更多控制,可以直接使用环境工作端点并实现自己的工作者。

Note

SDK 辅助程序需要 /bin/bash 在该确切路径。TypeScript SDK 还需要 unziptar 和 Node.js 22 或更高版本。这些依赖项在固定路径解析,不尊重 PATH 覆盖。

沙箱文件系统

  • /workspace:工具执行和技能下载的默认工作目录。技能下载到 /workspace/skills/<name>/。如果您将 --workdir 从默认值更改,请更新智能体的系统提示,让 Claude 知道在哪里找到它们。
  • /mnt/session/outputs:智能体将最终输出文件写入此路径。在容器中运行时,在此处挂载主机目录以检索它们。
  1. 创建自托管环境

    控制台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"}
      }'
    
    ant beta:environments create \
      --name self-hosted \
      --config '{"type": "self_hosted"}'
    
    client = anthropic.Anthropic()
    
    environment = client.beta.environments.create(
        name="self-hosted", config={"type": "self_hosted"}
    )
    print(environment.id)
    
    const client = new Anthropic();
    
    const environment = await client.beta.environments.create({
      name: "self-hosted",
      config: { type: "self_hosted" }
    });
    console.log(environment.id);
    
    using Anthropic.Models.Beta.Environments;
    
    var client = new AnthropicClient();
    
    var environment = await client.Beta.Environments.Create(
        new EnvironmentCreateParams
        {
            Name = "self-hosted",
            Config = new BetaSelfHostedConfigParams(),
        }
    );
    Console.WriteLine(environment.ID);
    
    package main
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/anthropics/anthropic-sdk-go"
    )
    
    func main() {
    	client := anthropic.NewClient()
    
    	environment, err := client.Beta.Environments.New(context.Background(), anthropic.BetaEnvironmentNewParams{
    		Name: "self-hosted",
    		Config: anthropic.BetaEnvironmentNewParamsConfigUnion{
    			OfSelfHosted: &anthropic.BetaSelfHostedConfigParams{},
    		},
    	})
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(environment.ID)
    }
    
    import com.anthropic.models.beta.environments.BetaSelfHostedConfigParams;
    import com.anthropic.models.beta.environments.EnvironmentCreateParams;
    
    var client = AnthropicOkHttpClient.fromEnv();
    
    var environment = client.beta().environments().create(
        EnvironmentCreateParams.builder()
            .name("self-hosted")
            .config(BetaSelfHostedConfigParams.builder().build())
            .build()
    );
    IO.println(environment.id());
    
    $client = new Anthropic\Client();
    
    $environment = $client->beta->environments->create(
        name: 'self-hosted',
        config: ['type' => 'self_hosted'],
    );
    echo $environment->id, PHP_EOL;
    
    client = Anthropic::Client.new
    
    environment = client.beta.environments.create(
      name: "self-hosted",
      config: {type: :self_hosted}
    )
    puts environment.id
    
  2. 设置环境密钥

    在控制台中,打开环境并单击生成环境密钥。然后在工作者主机上导出环境 ID 和密钥:

    export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..."
    export ANTHROPIC_ENVIRONMENT_ID="env_..."
    
  3. 安装 ant CLI

    在工作者将运行的计算机上运行此命令。

    VERSION=1.9.1
    OS=$(uname -s | tr '[:upper:]' '[:lower:]')
    ARCH=$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/')
    curl -fsSL "https://github.com/anthropics/anthropic-cli/releases/download/v${VERSION}/ant_${VERSION}_${OS}_${ARCH}.tar.gz" \
      | sudo tar -xz -C /usr/local/bin ant
    
  4. 运行工作者

    进程内

    ant beta:worker poll 获取分配给环境的会话,下载技能,在工作目录中执行工具调用,并将结果发回。

    Note

    技能可以包含智能体可能直接运行的可执行文件。CLI 会自动将下载的技能文件在沙箱中标记为可执行。如果您手动实现技能下载,您有责任设置可执行权限。

    ant beta:worker poll \
      --workdir "/workspace"
    

    工作者在 SIGTERM 或 SIGINT 时干净退出,在停止前排空进行中的工具调用。

    每个会话一个容器

    为了更强的隔离:每个会话的新文件系统、资源限制或网络控制。在各自的容器中运行每个会话。首先构建一个安装了 ant 并以 ant beta:worker run 作为入口点的镜像。当容器启动时,它从环境变量读取会话详细信息,处理该会话,然后退出:

    FROM your-base-image
    ARG ANT_VERSION=1.9.1
    ARG TARGETARCH
    RUN ARCH=$([ "$TARGETARCH" = "arm64" ] && echo arm64 || echo amd64) && \
        curl -fsSL "https://github.com/anthropics/anthropic-cli/releases/download/v${ANT_VERSION}/ant_${VERSION}_linux_${ARCH}.tar.gz" \
          | tar -xz -C /usr/local/bin ant
    WORKDIR /workspace
    VOLUME /mnt/session/outputs
    ENTRYPOINT ["ant", "beta:worker", "run"]
    

    然后编写一个生成脚本,将会话详细信息转发到新容器中,并启动指向它的轮询器:

    #!/bin/bash
    # spawn.sh: 每个会话调用一次
    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":/mnt/session/outputs \
      your-image
    
    ant beta:worker poll \
      --on-work ./spawn.sh
    
  1. 创建自托管环境

    控制台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"}
      }'
    
    ant beta:environments create \
      --name self-hosted \
      --config '{"type": "self_hosted"}'
    
    client = anthropic.Anthropic()
    
    environment = client.beta.environments.create(
        name="self-hosted", config={"type": "self_hosted"}
    )
    print(environment.id)
    
    const client = new Anthropic();
    
    const environment = await client.beta.environments.create({
      name: "self-hosted",
      config: { type: "self_hosted" }
    });
    console.log(environment.id);
    
    using Anthropic.Models.Beta.Environments;
    
    var client = new AnthropicClient();
    
    var environment = await client.Beta.Environments.Create(
        new EnvironmentCreateParams
        {
            Name = "self-hosted",
            Config = new BetaSelfHostedConfigParams(),
        }
    );
    Console.WriteLine(environment.ID);
    
    package main
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/anthropics/anthropic-sdk-go"
    )
    
    func main() {
    	client := anthropic.NewClient()
    
    	environment, err := client.Beta.Environments.New(context.Background(), anthropic.BetaEnvironmentNewParams{
    		Name: "self-hosted",
    		Config: anthropic.BetaEnvironmentNewParamsConfigUnion{
    			OfSelfHosted: &anthropic.BetaSelfHostedConfigParams{},
    		},
    	})
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(environment.ID)
    }
    
    import com.anthropic.models.beta.environments.BetaSelfHostedConfigParams;
    import com.anthropic.models.beta.environments.EnvironmentCreateParams;
    
    var client = AnthropicOkHttpClient.fromEnv();
    
    var environment = client.beta().environments().create(
        EnvironmentCreateParams.builder()
            .name("self-hosted")
            .config(BetaSelfHostedConfigParams.builder().build())
            .build()
    );
    IO.println(environment.id());
    
    $client = new Anthropic\Client();
    
    $environment = $client->beta->environments->create(
        name: 'self-hosted',
        config: ['type' => 'self_hosted'],
    );
    echo $environment->id, PHP_EOL;
    
    client = Anthropic::Client.new
    
    environment = client.beta.environments.create(
      name: "self-hosted",
      config: {type: :self_hosted}
    )
    puts environment.id
    
  2. 设置环境密钥

    在控制台中,打开环境并单击生成环境密钥。然后在工作者主机上导出环境 ID 和密钥:

    export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..."
    export ANTHROPIC_ENVIRONMENT_ID="env_..."
    
  3. 运行工作者

    EnvironmentWorker 获取分配给环境的会话,下载技能,在工作目录中执行工具调用,并将结果发回。使用您在步骤 2 中生成的环境密钥进行身份验证。

    Note

    技能可以包含智能体可能直接运行的可执行文件。SDK 会自动将下载的技能文件在沙箱中标记为可执行。如果您手动实现技能下载,您有责任设置可执行权限。

    import asyncio
    import os
    from anthropic import AsyncAnthropic
    from anthropic.lib.environments import EnvironmentWorker
    
    
    async def main() -> None:
        environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
        environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
        async with AsyncAnthropic(auth_token=environment_key) as client:
            await EnvironmentWorker(
                client,
                environment_id=environment_id,
                environment_key=environment_key,
                workdir="/workspace",
            ).run()
    
    
    asyncio.run(main())
    
    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 ctrl = new AbortController();
    process.once("SIGTERM", () => ctrl.abort());
    
    await new EnvironmentWorker({
      client,
      environmentId,
      environmentKey,
      workdir: "/workspace",
      signal: ctrl.signal
    }).run();
    
    // EnvironmentWorker 目前在 C# SDK 中不可用。请参阅上面的常驻(ant CLI)选项卡。
    
    package main
    
    import (
    	"context"
    	"log"
    	"os"
    	"os/signal"
    	"syscall"
    
    	"github.com/anthropics/anthropic-sdk-go"
    	"github.com/anthropics/anthropic-sdk-go/lib/environments"
    	"github.com/anthropics/anthropic-sdk-go/option"
    )
    
    func main() {
    	environmentKey := os.Getenv("ANTHROPIC_ENVIRONMENT_KEY")
    	environmentID := os.Getenv("ANTHROPIC_ENVIRONMENT_ID")
    
    	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
    	defer stop()
    
    	client := anthropic.NewClient(option.WithAuthToken(environmentKey))
    
    	worker := environments.NewEnvironmentWorker(client, environments.EnvironmentWorkerOptions{
    		EnvironmentID:  environmentID,
    		EnvironmentKey: environmentKey,
    		Workdir:        "/workspace",
    	})
    	if err := worker.Run(ctx); err != nil {
    		log.Fatalf("worker: %v", err)
    	}
    }
    
    
    // EnvironmentWorker 目前在 Java SDK 中不可用。请参阅上面的常驻(ant CLI)选项卡。
    
    // EnvironmentWorker 目前在 PHP SDK 中不可用。请参阅上面的常驻(ant CLI)选项卡。
    
    # EnvironmentWorker 目前在 Ruby SDK 中不可用。请参阅上面的常驻(ant CLI)选项卡。
    
  1. 订阅会话 webhook

    控制台中,定义一个侦听 session.status_run_started 事件的 webhook 端点。有关更多详细信息,请参阅 webhook 文档

  2. 创建自托管环境

    控制台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"}
      }'
    
    ant beta:environments create \
      --name self-hosted \
      --config '{"type": "self_hosted"}'
    
    client = anthropic.Anthropic()
    
    environment = client.beta.environments.create(
        name="self-hosted", config={"type": "self_hosted"}
    )
    print(environment.id)
    
    const client = new Anthropic();
    
    const environment = await client.beta.environments.create({
      name: "self-hosted",
      config: { type: "self_hosted" }
    });
    console.log(environment.id);
    
    using Anthropic.Models.Beta.Environments;
    
    var client = new AnthropicClient();
    
    var environment = await client.Beta.Environments.Create(
        new EnvironmentCreateParams
        {
            Name = "self-hosted",
            Config = new BetaSelfHostedConfigParams(),
        }
    );
    Console.WriteLine(environment.ID);
    
    package main
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/anthropics/anthropic-sdk-go"
    )
    
    func main() {
    	client := anthropic.NewClient()
    
    	environment, err := client.Beta.Environments.New(context.Background(), anthropic.BetaEnvironmentNewParams{
    		Name: "self-hosted",
    		Config: anthropic.BetaEnvironmentNewParamsConfigUnion{
    			OfSelfHosted: &anthropic.BetaSelfHostedConfigParams{},
    		},
    	})
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println(environment.ID)
    }
    
    import com.anthropic.models.beta.environments.BetaSelfHostedConfigParams;
    import com.anthropic.models.beta.environments.EnvironmentCreateParams;
    
    var client = AnthropicOkHttpClient.fromEnv();
    
    var environment = client.beta().environments().create(
        EnvironmentCreateParams.builder()
            .name("self-hosted")
            .config(BetaSelfHostedConfigParams.builder().build())
            .build()
    );
    IO.println(environment.id());
    
    $client = new Anthropic\Client();
    
    $environment = $client->beta->environments->create(
        name: 'self-hosted',
        config: ['type' => 'self_hosted'],
    );
    echo $environment->id, PHP_EOL;
    
    client = Anthropic::Client.new
    
    environment = client.beta.environments.create(
      name: "self-hosted",
      config: {type: :self_hosted}
    )
    puts environment.id
    
  3. 设置凭据

    在控制台中,打开环境并单击生成环境密钥。在您的处理器主机上导出所有三个:

    export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..."
    export ANTHROPIC_ENVIRONMENT_ID="env_..."
    export ANTHROPIC_WEBHOOK_SIGNING_KEY="whsec_..."
    
  4. 实现 webhook 处理器

    EnvironmentWorker 获取会话,下载技能,在工作目录中执行工具调用,发回结果,然后退出。在 session.status_run_started 触发时调用它。

    Note

    技能可以包含智能体可能直接运行的可执行文件。SDK 会自动将下载的技能文件在沙箱中标记为可执行。如果您手动实现技能下载,您有责任设置可执行权限。

    import os
    import anthropic
    
    environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
    environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
    client = anthropic.AsyncAnthropic(
        auth_token=environment_key,
    )
    
    
    async def handle(raw: bytes, headers: dict[str, str]) -> dict:
        event = client.beta.webhooks.unwrap(raw.decode(), headers=headers)
        if event.data.type != "session.status_run_started":
            return {"status": "ignored"}
        async for work in client.beta.environments.work.poller(
            environment_id=environment_id,
            environment_key=environment_key,
            block_ms=None,
            reclaim_older_than_ms=2000,
            drain=True,
            auto_stop=False,
        ):
            await client.beta.environments.work.worker(workdir="/workspace").handle_item(
                work_id=work.id,
                environment_id=environment_id,
                session_id=work.data.id,
                environment_key=environment_key,
            )
        return {"status": "ok"}
    
    import Anthropic from "@anthropic-ai/sdk";
    
    const environmentKey = process.env.ANTHROPIC_ENVIRONMENT_KEY!;
    const environmentId = process.env.ANTHROPIC_ENVIRONMENT_ID!;
    const client = new Anthropic({
      authToken: environmentKey
    });
    
    export async function handle(req: Request): Promise<Response> {
      const body = await req.text();
      let event;
      try {
        event = client.beta.webhooks.unwrap(body, { headers: Object.fromEntries(req.headers) });
      } catch {
        return new Response("signature verification failed", { status: 401 });
      }
      if (event.data.type !== "session.status_run_started") {
        return Response.json({ status: "ignored" });
      }
    
      for await (const work of client.beta.environments.work.poller({
        environmentId,
        environmentKey,
        blockMs: null,
        reclaimOlderThanMs: 2000,
        drain: true,
        autoStop: false
      })) {
        await client.beta.environments.work.worker({ workdir: "/workspace" }).handleItem({
          workId: work.id,
          environmentId,
          sessionId: work.data.id,
          environmentKey
        });
      }
      return Response.json({ status: "ok" });
    }
    
    // EnvironmentWorker 目前在 C# SDK 中不可用。
    // 要直接处理工作项,请参阅介绍中链接的环境工作端点。
    
    package main
    
    import (
    	"context"
    	"encoding/json"
    	"io"
    	"log/slog"
    	"net/http"
    	"os"
    
    	"github.com/anthropics/anthropic-sdk-go"
    	"github.com/anthropics/anthropic-sdk-go/lib/environments"
    	"github.com/anthropics/anthropic-sdk-go/option"
    )
    
    var (
    	environmentKey = os.Getenv("ANTHROPIC_ENVIRONMENT_KEY")
    	environmentID  = os.Getenv("ANTHROPIC_ENVIRONMENT_ID")
    	client         = anthropic.NewClient(
    		option.WithAuthToken(environmentKey),
    		option.WithWebhookKey(os.Getenv("ANTHROPIC_WEBHOOK_SIGNING_KEY")),
    	)
    	worker = environments.NewEnvironmentWorker(client, environments.EnvironmentWorkerOptions{
    		Workdir: "/workspace",
    	})
    )
    
    func handle(w http.ResponseWriter, r *http.Request) {
    	body, err := io.ReadAll(r.Body)
    	if err != nil {
    		http.Error(w, "bad request", http.StatusBadRequest)
    		return
    	}
    	event, err := client.Beta.Webhooks.Unwrap(body, r.Header)
    	if err != nil {
    		http.Error(w, "signature verification failed", http.StatusUnauthorized)
    		return
    	}
    	if event.Data.Type != "session.status_run_started" {
    		json.NewEncoder(w).Encode(map[string]string{"status": "ignored"})
    		return
    	}
    
    	// Go SDK 不提供 RunOne 便利方法:使用 WorkPoller 获取项,
    	// 然后使用 HandleItem 运行它。
    	// 从 r.Context() 分离:会话可能比 webhook 交付超时更长。
    	ctx := context.Background()
    	poller := environments.NewWorkPoller(ctx, client, environments.WorkPollerOptions{
    		EnvironmentID:  environmentID,
    		EnvironmentKey: environmentKey,
    		Drain:          true,
    	})
    	defer poller.Close()
    	if poller.Next() {
    		item := poller.Current()
    		if err := worker.HandleItem(ctx, environments.HandleItemOptions{
    			WorkID:         item.ID,
    			EnvironmentID:  item.EnvironmentID,
    			SessionID:      item.Data.ID,
    			EnvironmentKey: environmentKey,
    		}); err != nil {
    			slog.Error("handle work item", "work_id", item.ID, "err", err)
    			http.Error(w, "internal error", http.StatusInternalServerError)
    			return
    		}
    	}
    	if err := poller.Err(); err != nil {
    		slog.Error("poll work queue", "err", err)
    		http.Error(w, "internal error", http.StatusInternalServerError)
    		return
    	}
    	json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
    }
    
    func main() {
    	http.HandleFunc("POST /webhook", handle)
    	if err := http.ListenAndServe(":8080", nil); err != nil {
    		slog.Error("http server", "err", err)
    		os.Exit(1)
    	}
    }
    
    
    // EnvironmentWorker 目前在 Java SDK 中不可用。
    // 要直接处理工作项,请参阅介绍中链接的环境工作端点。
    
    // EnvironmentWorker 目前在 PHP SDK 中不可用。
    // 要直接处理工作项,请参阅介绍中链接的环境工作端点。
    
    # EnvironmentWorker 目前在 Ruby SDK 中不可用。
    # 要直接处理工作项,请参阅介绍中链接的环境工作端点。
    

启动会话

工作者运行后,创建目标为环境的会话。Anthropic 将其入队,您的工作者获取并执行它。

文件和 GitHub 资源挂载在您的容器镜像中处理,而不是由 Anthropic 处理。要加载具有会话特定文件的沙箱,您可以在创建会话时传递会话元数据。您的编排层可以读取该元数据,并在工作者开始执行之前挂载相关文件。

curl -sS --fail-with-body https://api.anthropic.com/v1/sessions \
  -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 @- <<EOF
{
  "agent": "$AGENT_ID",
  "environment_id": "$ENVIRONMENT_ID",
  "metadata": {"input_file": "s3://my-bucket/data.csv"}
}
EOF
ant beta:sessions create \
  --agent "$AGENT_ID" \
  --environment-id "$ENVIRONMENT_ID" \
  --metadata '{"input_file": "s3://my-bucket/data.csv"}'
session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    metadata={"input_file": "s3://my-bucket/data.csv"},
)
const session = await client.beta.sessions.create({
  agent: agent.id,
  environment_id: environment.id,
  metadata: { input_file: "s3://my-bucket/data.csv" }
});
var session = await client.Beta.Sessions.Create(new()
{
    Agent = agent.ID,
    EnvironmentID = environment.ID,
    Metadata = new Dictionary<string, string> { ["input_file"] = "s3://my-bucket/data.csv" },
});
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
	Agent:         anthropic.BetaSessionNewParamsAgentUnion{OfString: anthropic.String(agent.ID)},
	EnvironmentID: environment.ID,
	Metadata: map[string]string{
		"input_file": "s3://my-bucket/data.csv",
	},
})
if err != nil {
	panic(err)
}
var session = client.beta().sessions().create(SessionCreateParams.builder()
    .agent(agent.id())
    .environmentId(environment.id())
    .metadata(SessionCreateParams.Metadata.builder()
        .putAdditionalProperty("input_file", JsonValue.from("s3://my-bucket/data.csv"))
        .build())
    .build());
$session = $client->beta->sessions->create(
    agent: $agent->id,
    environmentID: $environment->id,
    metadata: ['input_file' => 's3://my-bucket/data.csv'],
);
session = client.beta.sessions.create(
  agent: agent.id,
  environment_id: environment.id,
  metadata: {input_file: "s3://my-bucket/data.csv"}
)
Note

记忆尚不支持自托管沙箱。

参考

标志描述
--environment-id轮询工作的环境。也从 ANTHROPIC_ENVIRONMENT_ID 读取。
--environment-key使用此环境对工作者进行身份验证。也从 ANTHROPIC_ENVIRONMENT_KEY 读取。
--workdir下载技能和工具读写文件的目录。默认为 /workspace
--on-work为每个获取的工作项调用的脚本,而不是在进程内运行工具。接收会话详细信息作为环境变量。
--unrestricted-paths允许工具调用访问 --workdir 之外的路径。
--max-idleend_turn 空闲后等待多长时间再关闭。默认为 60s
--log-format日志输出格式。使用 json 进行结构化日志摄取。默认为 text

SDK 在不同级别提供三个辅助程序:

  • EnvironmentWorker:开箱即用的工作者。端到端处理轮询、设置和执行。
    • .run():无限期运行,随会话到达而获取。在 SIGTERM 时干净退出。
    • .handle_item():获取一个待处理会话,处理它,然后退出。
  • work.poller():代表您轮询工作队列并向您提供每个获取的会话。当您想决定每个会话发生什么时使用此选项,例如启动容器而不是在进程内运行工具。
    • drain:队列为空时是否停止轮询,而不是等待新工作。
    • block_ms:在返回之前等待工作到达的时间(毫秒)。必须在 1 到 999 之间。设置为 None 进行非阻塞检查。
    • reclaim_older_than_ms:重新获取已租给停止响应的工作者的工作项。
    • auto_stop:迭代器退出后是否在工作项上发布停止信号。
  • tool_runner():为单个会话运行工具调用。当您已获取工作并且只需要执行层时使用。

EnvironmentWorker 覆盖大多数用例。当您想启动自己的每会话进程时,直接使用工作轮询器,例如为每个获取的会话启动容器:

# 轮询端点需要环境密钥(Bearer 身份验证),而不是您的
# API 密钥。使用下面的 SDK 选项之一。
# 轮询端点需要环境密钥(Bearer 身份验证),而不是您的
# API 密钥。使用下面的 SDK 选项之一。
import asyncio
import os

from anthropic import AsyncAnthropic
from anthropic.types.beta.environments import BetaSelfHostedWork


async def launch_container(work: BetaSelfHostedWork) -> None:
    # 替换为您自己的每会话沙箱启动器。将
    # ANTHROPIC_ENVIRONMENT_KEY 传入启动的容器,永远不要
    # 传入您的 API 密钥。
    print(f"claimed session {work.data.id}")


async def main() -> None:
    environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
    environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
    async with AsyncAnthropic(auth_token=environment_key) as client:
        async for work in client.beta.environments.work.poller(
            environment_id=environment_id,
            environment_key=environment_key,
            auto_stop=False,  # 启动的容器拥有停止调用
        ):
            await launch_container(work)


asyncio.run(main())
import Anthropic from "@anthropic-ai/sdk";
import { WorkPoller } from "@anthropic-ai/sdk/helpers/beta/environments";
import type { BetaSelfHostedWork } from "@anthropic-ai/sdk/resources/beta/environments";

const environmentKey = process.env.ANTHROPIC_ENVIRONMENT_KEY!;
const environmentId = process.env.ANTHROPIC_ENVIRONMENT_ID!;
const client = new Anthropic({ authToken: environmentKey });

async function launchContainer(work: BetaSelfHostedWork): Promise<void> {
  // 替换为您自己的每会话沙箱启动器。将
  // ANTHROPIC_ENVIRONMENT_KEY 传入启动的容器,永远不要
  // 传入您的 API 密钥。
  console.log(`claimed session ${work.data.id}`);
}

const poller = new WorkPoller({
  client,
  environmentId,
  environmentKey,
  autoStop: false // 启动的容器拥有停止调用
});

for await (const work of poller) {
  await launchContainer(work);
}
// 工作轮询目前在 C# SDK 中不可用。
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/anthropics/anthropic-sdk-go"
	"github.com/anthropics/anthropic-sdk-go/lib/environments"
	"github.com/anthropics/anthropic-sdk-go/option"
)

func launchContainer(work *anthropic.BetaSelfHostedWork) {
	// 替换为您自己的每会话沙箱启动器。Go 轮询器
	// 在此函数返回时调用 work.Stop(它没有自动停止
	// 选择退出),因此在此处阻塞直到会话完成,而不是
	// 像 Python 和 TypeScript 选项卡那样分离。
	fmt.Printf("claimed session %s\n", work.Data.ID)
}

func main() {
	environmentID := os.Getenv("ANTHROPIC_ENVIRONMENT_ID")
	environmentKey := os.Getenv("ANTHROPIC_ENVIRONMENT_KEY")

	client := anthropic.NewClient(option.WithAuthToken(environmentKey))

	ctx := context.Background()

	poller := environments.NewWorkPoller(ctx, client, environments.WorkPollerOptions{
		EnvironmentID:  environmentID,
		EnvironmentKey: environmentKey,
	})
	defer poller.Close()

	for work, err := range poller.All() {
		if err != nil {
			log.Fatal(err)
		}
		launchContainer(work)
	}
}
// 工作轮询目前在 Java SDK 中不可用。
// 工作轮询目前在 PHP SDK 中不可用。
# 工作轮询目前在 Ruby SDK 中不可用。

AgentToolContext 是工具调用的执行上下文,定义工作目录、路径策略,并在作为上下文管理器进入时可选地下载会话的技能。beta_agent_toolset_20260401(env) 接收 AgentToolContext 并返回标准工具实现(bashreadwriteeditglobgrep)。

使用 EnvironmentWorker 两者都自动管理。传递 tools 工厂来自定义工具列表:

EnvironmentWorker(client, ..., tools=lambda env: [beta_bash_tool(env), my_custom_tool])

使用 work.poller() / tool_runner() 在运行工具之前自己设置 AgentToolContextbeta_agent_toolset_20260401(env)

from anthropic.lib.tools.agent_toolset import (
    AgentToolContext,
    beta_agent_toolset_20260401,
)

async with AgentToolContext(
    workdir="/workspace", client=client, session_id=work.data.id
) as env:
    # 技能下载到 /workspace/skills/<name>/
    tools = beta_agent_toolset_20260401(env)
import {
  setupSkills,
  betaAgentToolset20260401
} from "@anthropic-ai/sdk/tools/agent-toolset/node";

const ctx = { workdir: "/workspace", client, sessionId: work.data.id };
await setupSkills(ctx);
const tools = betaAgentToolset20260401(ctx);
// AgentToolContext 目前在 C# SDK 中不可用。
env := &agenttoolset.AgentToolContext{Workdir: "/workspace"}
if err := env.SetupSkills(ctx, client, work.Data.ID); err != nil {
	panic(err)
}
// 技能下载到 /workspace/skills/<name>/
tools := agenttoolset.BetaAgentToolset20260401(env)
// AgentToolContext 目前在 Java SDK 中不可用。
// AgentToolContext 目前在 PHP SDK 中不可用。
# AgentToolContext 目前在 Ruby SDK 中不可用。

监控和运维

这些调用从您的监控或运维工具运行,使用您的 Claude API 密钥进行身份验证,以观察和管理工作者集群。获取和保活循环在工作者辅助程序内部处理,因此您不需要直接调用这些端点。

Warning

这些端点使用您的组织 API 密钥进行身份验证,而不是环境密钥。从工作者主机外部调用它们。在工作者主机上设置 ANTHROPIC_API_KEY 会将组织范围的凭据暴露给智能体工具调用。

读取队列深度

work.stats 返回环境的队列状态:

  • depth 是等待获取的项数。根据此值扩展您的工作者集群或对积压工作进行警报。
  • pending 是工作者已获取并当前正在处理的项数。
  • oldest_queued_at 是队列中最旧项的时间戳,如果队列为空则为 null
  • workers_polling 是在过去 30 秒内轮询的工作者数量。将此用于存活性警报。
curl -sS "https://api.anthropic.com/v1/environments/$ANTHROPIC_ENVIRONMENT_ID/work/stats" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  -H "anthropic-version: 2023-06-01"
ant beta:environments:work stats --environment-id "$ANTHROPIC_ENVIRONMENT_ID"
import os

import anthropic

client = anthropic.Anthropic()

stats = client.beta.environments.work.stats(os.environ["ANTHROPIC_ENVIRONMENT_ID"])
print(f"depth={stats.depth} pending={stats.pending}")
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const stats = await client.beta.environments.work.stats(process.env.ANTHROPIC_ENVIRONMENT_ID!);

console.log(`depth=${stats.depth} pending=${stats.pending}`);
using Anthropic;

var client = new AnthropicClient();

var environmentId = Environment.GetEnvironmentVariable("ANTHROPIC_ENVIRONMENT_ID")!;

var stats = await client.Beta.Environments.Work.Stats(environmentId);

Console.WriteLine({{CONTENT}}quot;depth={stats.Depth} pending={stats.Pending}");
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/anthropics/anthropic-sdk-go"
)

func main() {
	client := anthropic.NewClient()
	environmentID := os.Getenv("ANTHROPIC_ENVIRONMENT_ID")

	stats, err := client.Beta.Environments.Work.Stats(
		context.Background(),
		environmentID,
		anthropic.BetaEnvironmentWorkStatsParams{},
	)
	if err != nil {
		panic(err)
	}

	fmt.Printf("depth=%d pending=%d\n", stats.Depth, stats.Pending)
}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.environments.work.BetaSelfHostedWorkQueueStats;

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    BetaSelfHostedWorkQueueStats stats = client.beta()
        .environments()
        .work()
        .stats(System.getenv("ANTHROPIC_ENVIRONMENT_ID"));

    IO.println("depth=" + stats.depth() + " pending=" + stats.pending());
}
<?php

use Anthropic\Client;

$client = new Client();

$stats = $client->beta->environments->work->stats(getenv('ANTHROPIC_ENVIRONMENT_ID'));

printf("depth=%d pending=%d\n", $stats->depth, $stats->pending);
require "anthropic"

client = Anthropic::Client.new

stats = client.beta.environments.work.stats(ENV.fetch("ANTHROPIC_ENVIRONMENT_ID"))

puts "depth=#{stats.depth} pending=#{stats.pending}"
{
  "type": "work_queue_stats",
  "depth": 0,
  "pending": 0,
  "oldest_queued_at": null,
  "workers_polling": 0
}

优雅停止会话

使用 work.stop 请求处理特定会话的工作者干净地关闭它。工作者完成任何进行中的工具调用,发布最终状态,并释放会话。在请求体中传递 force: true 以立即中断,而不是等待当前工具调用完成。

因为这些调用从您的运维工具而不是工作者主机运行,ANTHROPIC_WORK_ID 不会自动设置。在运行以下示例之前,将其设置为目标工作项的 ID。

curl -sS "https://api.anthropic.com/v1/environments/$ANTHROPIC_ENVIRONMENT_ID/work/$ANTHROPIC_WORK_ID/stop" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{}'
ant beta:environments:work stop \
  --environment-id "$ANTHROPIC_ENVIRONMENT_ID" \
  --work-id "$ANTHROPIC_WORK_ID"
import os

import anthropic

client = anthropic.Anthropic()

work = client.beta.environments.work.stop(
    os.environ["ANTHROPIC_WORK_ID"],
    environment_id=os.environ["ANTHROPIC_ENVIRONMENT_ID"],
)
print(work.state)
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const work = await client.beta.environments.work.stop(process.env.ANTHROPIC_WORK_ID!, {
  environment_id: process.env.ANTHROPIC_ENVIRONMENT_ID!
});

console.log(work.state);
using Anthropic;

var client = new AnthropicClient();

var work = await client.Beta.Environments.Work.Stop(
    Environment.GetEnvironmentVariable("ANTHROPIC_WORK_ID")!,
    new()
    {
        EnvironmentID = Environment.GetEnvironmentVariable("ANTHROPIC_ENVIRONMENT_ID")!
    }
);

Console.WriteLine(work.State);
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/anthropics/anthropic-sdk-go"
)

func main() {
	client := anthropic.NewClient()

	work, err := client.Beta.Environments.Work.Stop(
		context.Background(),
		os.Getenv("ANTHROPIC_WORK_ID"),
		anthropic.BetaEnvironmentWorkStopParams{
			EnvironmentID: os.Getenv("ANTHROPIC_ENVIRONMENT_ID"),
		},
	)
	if err != nil {
		panic(err)
	}
	fmt.Println(work.State)
}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.environments.work.BetaSelfHostedWork;
import com.anthropic.models.beta.environments.work.BetaSelfHostedWorkStopRequest;
import com.anthropic.models.beta.environments.work.WorkStopParams;

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    BetaSelfHostedWork work = client.beta().environments().work().stop(
        WorkStopParams.builder()
            .environmentId(System.getenv("ANTHROPIC_ENVIRONMENT_ID"))
            .workId(System.getenv("ANTHROPIC_WORK_ID"))
            .betaSelfHostedWorkStopRequest(BetaSelfHostedWorkStopRequest.builder().build())
            .build()
    );

    IO.println(work.state());
}
<?php

use Anthropic\Client;

$client = new Client();

$work = $client->beta->environments->work->stop(
    getenv('ANTHROPIC_WORK_ID'),
    environmentID: getenv('ANTHROPIC_ENVIRONMENT_ID'),
);

echo $work->state . "\n";
require "anthropic"

client = Anthropic::Client.new

work = client.beta.environments.work.stop(
  ENV.fetch("ANTHROPIC_WORK_ID"),
  environment_id: ENV.fetch("ANTHROPIC_ENVIRONMENT_ID")
)

puts work.state

后续步骤