Claude 自托管沙箱(Self-Hosted Worker)实战指南:从环境创建到 worker 部署与工具定制
Claude 自托管沙箱Self-Hosted Worker实战指南从环境创建到 worker 部署与工具定制【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks本指南以 managed_agents/self_hosted_sandboxes/docs/usage-guide.md 为主线骨架讲解如何在不使用 Anthropic 托管沙箱的前提下把 Claude Managed Agents 的会话执行放到你自己控制的计算资源本机、Docker 容器、Cloudflare、Modal、Daytona、Vercel 等上运行。读完本文你将掌握创建 Self-hosted 环境并签发环境密钥、用antCLI 以poll/run两种形态拉起 worker、通过--on-work脚本实现一个会话一个容器/沙箱的经典架构以及在 Python/TypeScript SDK 中以库函数方式嵌入 worker 并自定义工具集。文中的安装步骤与命令以已发布的 SDK 构建为准Pythonanthropic0.103.0、TypeScriptanthropic-ai/sdk0.97.0、antCLI v1.9.0并补充了本仓库各参考实现docker、cf、cf-worker、modal、daytona、vercel的源码级佐证帮你把命令背后轮询→领取→执行→回报→心跳→强制停止的完整闭环看清楚。一、先搞清楚整体架构Self-hosted Sandboxes 是怎么运转的在 self_hosted_sandboxes/README.md 中这一整套参考实现被归纳为同一契约、不同计算提供方的三种固定动作接收 webhook监听session.status_run_started并用client.beta.webhooks.unwrap()做标准 Webhook 签名校验本仓库的 Modal / Daytona / Vercel / Cloudflare 变体都是这种webhook 唤醒 排空队列模式排空环境工作队列client.beta.environments.work.poller(...)长轮询并 ack 每一项 work从而单次投递即可补回先前所有错过的工作项按 work 项拉起一次性沙箱每个会话启动一个独立的执行沙箱沙箱内部运行 SDK/CLI 的工具运行器内置bash/read/write/edit/glob/grep六把工具对租约心跳并把tool_result回报回会话。关键安全设计是组织级 API Key 永远到不了 runner沙箱只凭一把**环境密钥environment key**鉴权它同时服务于控制平面与所有会话级调用。六种参考实现的对应关系如下各有独立 README 与部署说明变体计算载体Runner 形态docker/你自控主机上的原生 Docker每会话一个容器entrypoint 为ant beta:worker runcf/Cloudflare Containers同一ant beta:worker run运行在 CF 容器cf-worker/Cloudflare Workers无容器Durable Object 内用 TStoolRunner 隔离内模拟文件系统modal/Modal SandboxPythonsandbox_runner.py 每会话 Volumedaytona/Daytona Sandbox复用同一份sandbox_runner.pyvercel/Vercel Functions SandboxNoderunner.mjs下面按 usage-guide 的三步流程逐步展开。执行环境无外网可访问时注意ant下载、SDK 安装等命令需要网络连通。二、第 0 步前置条件与工具链安装必需的 API 头所有下述公开 API 调用都必须携带以下两个头anthropic-version: 2023-06-01 anthropic-beta: managed-agents-2026-04-01SDK 的 helper 会自动附加这两个头因此使用 SDK 时通常无需手工设置若直接以裸 HTTP 调 REST API则必须带上。Self-hosted worker容器形态相关 API 与 Console 功能是按需放行的usage-guide 明确指出如果你读到这里却发现自己的账号没有这些 API/Console 的访问权限需要联系 Anthropic 开通。安装 SDK# Python SDK uv pip install anthropic # TypeScript SDK npm i anthropic-ai/sdk用antCLI 做 worker 管理ant内置 worker它负责轮询领取分配给本环境的会话任务并在本机执行。把它装到你希望 worker 运行的那台机器上。Linux / macOS 安装脚本VERSION1.9.0 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 ant --version注意两点uname -m会把x86_64映射成发布包命名的amd64、aarch64映射成arm64保证下载到匹配架构的压缩包主机上ant的构建版本必须与后续镜像/容器里固定的版本一致。本仓库 docker/README.md 专门强调anton the hosts PATH, thesame buildpinned inDockerfile(ARG ANT_VERSION)安装脚本同样通过VERSION1.10.0注释提示要与 docker/Dockerfile 的ARG ANT_VERSION1.10.0保持一致。三、第 1 步创建 Self-hosted 环境方式 AConsole 界面在 Console →Workspace → Environments → New → Self-hosted一路创建即可。方式 B代码内创建usage-guide 给出了 Python 与 TypeScript 两种等价写法client anthropic.Anthropic(api_keyAPI_KEY) environment client.beta.environments.create( nameself-hosted, config{type: self_hosted}, )const client new Anthropic({ apiKey: API_KEY }); const environment await client.beta.environments.create({ name: self-hosted, config: { type: self_hosted }, });config{type: self_hosted}即声明该环境使用自托管执行方式。这一签名在不同 SDK 版本间保持稳定——docs/upgrade-guide.md 在 Unchanged — leave these alone 一节里明确标注client.beta.environments.create(...)的签名无需改动。生成环境密钥environment key在 Console 中打开该环境点击Generate environment key生成环境密钥把它设置为 runner 主机上的环境变量ANTHROPIC_ENVIRONMENT_KEY。这把密钥鉴定了整个 worker 流程的全部环节poll轮询领取、ack确认、stop停止、heartbeat心跳、session 事件流、以及 skill技能下载。它是 worker 唯一需要的凭证——不再是service key 只管 poll 每个 work 再塞一个 secret 解码成 session token的旧模式。这一点是 usage-guide 配套的 upgrade-guide 中Change 1 — One credential的核心结论。四、第 2 步设置环境密钥在 Console 为该环境生成密钥后把它导出到 worker 主机export ANTHROPIC_ENVIRONMENT_KEYsk-ant-oat01-...五、第 3 步启动 worker5.1 长时间轮询形态ant beta:worker pollant beta:worker poll运行一个内置主循环领取分配给该环境的会话session在--workdir内执行工具调用bash、read、write、edit、glob、grep并把结果回报给会话。ant beta:worker poll \ --environment-id env_01... \ --workdir /workspace每个 flag 都可以用环境变量替代因此可以做到零 flag 调用非常适合 systemd 或 docker-composeANTHROPIC_ENVIRONMENT_IDenv_01... \ ANTHROPIC_ENVIRONMENT_KEYsk-ant-oat01-... \ ant beta:worker poll --workdir /workspaceworker 在收到 SIGTERM / SIGINT 时会优雅退出先排空drain进行中的工具调用再停止。5.2 每个工作项拉起独立进程--on-work script默认情况下工具在 worker 进程内直接执行。若你想每领取一个 work 项就运行一个外部脚本用--on-work传入该脚本路径。脚本能拿到ant beta:worker run同款的环境变量ANTHROPIC_WORK_IDANTHROPIC_ENVIRONMENT_IDANTHROPIC_SESSION_IDANTHROPIC_ENVIRONMENT_KEYANTHROPIC_BASE_URL从 poller 继承而来外加原始 work JSON从 stdin 流入。最简单的脚本每项工作起一个一次性容器#!/bin/bash exec docker run --rm \ -e ANTHROPIC_SESSION_ID -e ANTHROPIC_ENVIRONMENT_KEY \ -e ANTHROPIC_WORK_ID -e ANTHROPIC_ENVIRONMENT_ID -e ANTHROPIC_BASE_URL \ your-image ant beta:worker run --workdir /workspace行为约定poller 会等脚本退出后才继续下一次轮询脚本返回非零只会记日志、不会让 poller 停摆对 poller 发送 SIGTERM 会级联到正在运行的脚本。仓库实证docker 变体的运行细节。本仓库 docker/on-work.sh 就是这个模式的完整工业级实现有几个值得注意的坑脚本必须前台阻塞exec docker run --rm不能-d因为ant beta:worker poll在--on-work脚本一返回的瞬间就会对该 work 项发起 stop且CLI 没有开关可以关闭该行为。若用docker run -d立即返回poller 会在容器还没来得及领取会话前就把 work 停掉表现为 heartbeat reports shutdown, state stoppedbash 工具调用永远不会执行。幂等on-work.sh先检查docker ps -q --filter name^${NAME}$若该会话已有存活容器则直接跳过——重复投递的 work 项对正在服务的会话是无害的 no-op。每个会话使用独立命名空间与独立卷容器名cma-${ANTHROPIC_SESSION_ID}、卷cma-ws-${ANTHROPIC_SESSION_ID}挂载到/workspace保证技能下载与工作树跨容器存活供该会话的下一条消息复用docker volume rm即可丢弃。技能下载的隐蔽坑on-work.sh里额外设置了ANTHROPIC_AUTH_TOKEN${ANTHROPIC_ENVIRONMENT_KEY}。原因是 CLI 的技能下载客户端只认ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN并不认ANTHROPIC_ENVIRONMENT_KEY——不设置的话技能会静默下载失败。这正是 docker/README 文档知识 代码注释互相印证的典型例子。本仓库 docker/start.sh 展示了主机侧编排构建镜像后用--on-work $PWD/on-work.sh --workdir /tmp --log-format json拉起 poller。由于 poll 侧不执行任何工具它的--workdir只是摆设指向临时目录即可。5.3 每会话一个容器时用run作为 entrypoint如果你的控制平面不采用常驻 poller而是每个会话直接起一个新容器就用ant beta:worker run作为容器 entrypoint它不轮询而是直接附着到单个会话。FROM your-base-image ARG ANT_VERSION1.9.0 ARG TARGETOSlinux ARG TARGETARCHamd64 ADD https://github.com/anthropics/anthropic-cli/releases/download/v${ANT_VERSION}/ant_${ANT_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz /tmp/ant.tgz RUN tar -xzf /tmp/ant.tgz -C /usr/local/bin ant \ chmod x /usr/local/bin/ant rm /tmp/ant.tgz WORKDIR /workspace ENTRYPOINT [ant, beta:worker, run]启动容器时以环境变量方式传入ANTHROPIC_SESSION_ID、ANTHROPIC_ENVIRONMENT_KEY、ANTHROPIC_WORK_ID、ANTHROPIC_ENVIRONMENT_ID。仓库实证docker/Dockerfile 与 cf/container/Dockerfile 就是上述模式的落地版二者都在 entrypoint 里补上了三个实用开关可作最佳实践参考ENTRYPOINT [ant, beta:worker, run, --workdir, /workspace, --unrestricted-paths, --max-idle, 60s, --log-format, json]其中--unrestricted-paths放开文件工具的路径限制、--max-idle 60s让容器在会话空闲 60 秒后自动退出、--log-format json输出结构化日志便于采集。5.4 已有会话信息时的直连形态直接ant beta:worker run如果你自己的编排器已经领取了 work、并把 worker 作为子进程拉起就可以跳过 poll直接把会话细节喂给runant beta:worker run \ --session-id sesn_... \ --environment-key $ANTHROPIC_ENVIRONMENT_KEY \ --work-id work_... \ --environment-id env_... \ --workdir /workspace纯环境变量写法export ANTHROPIC_SESSION_IDsesn_... export ANTHROPIC_ENVIRONMENT_KEYsk-ant-oat01-... export ANTHROPIC_WORK_IDwork_... export ANTHROPIC_ENVIRONMENT_IDenv_... ant beta:worker run --workdir /workspacerun的退出语义它附着到会话事件流、执行工具调用并在以下两种情况下以 0 退出——会话终止或session.status_idle且stop_reason: end_turn出现后--max-idle超时。任何其他事件都会重置空闲计时器所以 agent 处于requires_action空闲正卡在等你执行工具时不会被误杀。cf-worker 的 runner.ts 头注释再次印证了这一默认策略toolRunner()exitsmaxIdleMsaftersession.status_idlewithstop_reason: end_turn; any other event … resets the clock。六、Flags 一览表usage-guide 给出的完整 flag 表如下表格即原文直接可用| Flag | 环境变量 | 默认值 | | :- | :- | :- | |--environment-id|ANTHROPIC_ENVIRONMENT_ID| 必填 | |--environment-key|ANTHROPIC_ENVIRONMENT_KEY| 必填 | |--on-work| | 进程内 runner | |--worker-id|ANTHROPIC_WORKER_ID| hostname | |--workdir| |.| |--unrestricted-paths| |false| |--max-idle| |1mend_turn 空闲后 | |--log-format| |text或json | |--base-url|ANTHROPIC_BASE_URL|api.anthropic.com|补充说明各 flag 的实际语义--max-idle默认值在 CLI 上写作1m即 60 秒。它只从session.status_idlestop_reason: end_turn那一刻开始计时事件流中任何其它事件包括工具请求requires_action都会重置时钟。这与各参考实现文档中的 SDK default … exitsDEFAULT_MAX_IDLE(60s) 完全一致docker 与 cf 变体都显式传--max-idle 60s为的是让声明不依赖默认值演进。--unrestricted-paths默认关闭即文件类工具read/write/edit/glob/grep默认被限制在--workdir内开启后放开绝对路径限制。升级指南提示它取代了旧版 CLI 的--allow-absolute-paths。--base-url/ANTHROPIC_BASE_URL默认为官方 API 地址api.anthropic.com可指向代理、内网网关或任何兼容端点各变体脚本均以ANTHROPIC_BASE_URL${ANTHROPIC_BASE_URL:-https://api.anthropic.com}的方式保持默认值一致。安全红线usage-guide 原文强调worker 直接在主机上执行 shell 与文件操作因此必须把它放进容器或你能掌控的其它隔离边界内运行。本仓库各变体正是该原则的实践——无论 docker、Modal 还是 Daytonarunner 永远跑在一次性沙箱/容器里主机上只有不做工具执行的 poller 或 webhook。七、Library 用法把 worker 嵌入你自己的进程同样的 poll/run worker 在各 SDK 里以库代码形式提供方便你把它嵌进自有进程或定制工具。核心入口是client.beta.environments.work.worker(...)它把完整闭环一次性组合好——poll轮询→ 准备 workdir 并下载该会话 agent 的 skills → 运行工具并同时给 work 项租约心跳 → 退出时 force-stop → 回到循环。它接受与client.beta.messages.tool_runner/.toolRunner相同形态的工具类型因此你用beta_async_toolPython或betaZodTool/BetaRunnableToolTypeScript定义的自定义工具都可以通过tools与默认工具并列传入。Python 示例import asyncio, os from anthropic import AsyncAnthropic environment_key os.environ[ANTHROPIC_ENVIRONMENT_KEY] async def main() - None: async with AsyncAnthropic(auth_tokenenvironment_key) as client: await client.beta.environments.work.worker( environment_idos.environ[ANTHROPIC_ENVIRONMENT_ID], environment_keyenvironment_key, workdir/workspace, ).run() asyncio.run(main()).handle_item()是单件形态适用于--on-work脚本或每会话一个沙箱的启动场景——它不轮询而是读取ANTHROPIC_*环境变量、服务那一个已经被领取的 work 项。仓库实证modal/sandbox_runner.py 正是.handle_item()的生产级用法——webhook 在创建 Modal Sandbox 时注入全套ANTHROPIC_*环境变量沙箱内仅执行async with AsyncAnthropic(auth_tokenenvironment_key) as client: await client.beta.environments.work.worker( environment_keyenvironment_key, workdirWORKDIR, unrestricted_pathsTrue, ).handle_item()其模块注释把handle_item()的内涵讲得很透它会构建该会话的AgentToolContext、把 agent 的 skills 下载到{workdir}/skills/name/随后运行一个SessionToolRunner心跳 reconcile 事件流 工具分发 结果回报退出时对 work 项做 force-stop。同时它用logging.basicConfig(levellogging.INFO, ...)把 worker 的生命周期日志start、idle-out、heartbeat shutdown、流重连、工具分发路由到 stdout——注释特别提醒若不加 handler这些 INFO 日志会被静默丢弃而退出原因正是该进程唯一的诊断线索。TypeScript 示例import Anthropic from anthropic-ai/sdk; const environmentKey process.env.ANTHROPIC_ENVIRONMENT_KEY!; const client new Anthropic({ authToken: environmentKey }); const ctrl new AbortController(); process.once(SIGTERM, () ctrl.abort()); await client.beta.environments.work .worker({ environmentId: process.env.ANTHROPIC_ENVIRONMENT_ID!, environmentKey, workdir: /workspace, signal: ctrl.signal, }) .run();与 Python 版一一对应.worker({...}).run()是长驻轮询循环.handleItem()是读取ANTHROPIC_*环境变量的单件形态。区别在于 TS 版通过AbortController把SIGTERM转成 signal 传给 worker实现优雅停机。Gousage-guide 中 Go 的进度为// TODO: pending a released Go SDK build for the self-hosted worker.即当前 Go SDK 尚未发布自托管 worker 的可用构建仓库中相关能力均以 Python / TypeScript 提供——这是原文档自带的能力边界说明请以最新官方发布为准勿将 Go 用法当作已实现事实。八、自定义工具列表默认六把工具bash、read、write、edit、glob、grep以agent_toolset_20260401这一实现集合形式提供。在 Python 中由beta_agent_toolset_20260401(env)返回TypeScript 中由betaAgentToolset20260401(ctx)返回。你可以过滤或扩展它再通过tools传给worker(...)——tools是一个工厂函数每个被领取的会话调用一次入参是该会话的工具上下文。Python去 grep、加自定义工具from anthropic.lib.tools import beta_async_tool from anthropic.lib.tools.agent_toolset import ( AgentToolContext, beta_agent_toolset_20260401, beta_bash_tool, beta_read_tool, ) # drop grep, add a custom tool beta_async_tool async def fetch_url(url: str) - str: ... def tools(env: AgentToolContext): return [t for t in beta_agent_toolset_20260401(env) if t.name ! grep] [fetch_url] # or build from individual factories def tools(env: AgentToolContext): return [beta_bash_tool(env), beta_read_tool(env), my_custom_tool] client.beta.environments.work.worker(..., toolstools)TypeScript同样支持过滤 扩展import { betaZodTool } from anthropic-ai/sdk/helpers/beta/zod; import { betaAgentToolset20260401, betaBashTool } from anthropic-ai/sdk/tools/agent-toolset/node; client.beta.environments.work.worker({ ..., tools: (ctx) [...betaAgentToolset20260401(ctx).filter(t t.name ! grep), myZodTool], });定制工具的典型价值结合 docker/README.md 的 MongoDB 场景自托管容器里 agent 的bash能直接读到MONGO_URI这在任务可信时很方便但若追求最小权限更优解是把内置工具集替换成你自己的 worker 工具对外只暴露一个收窄的mongo_query(...)而不是把裸连接串交给模型。也就是说——tools工厂 beta_async_tool就是你在自托管环境里实施凭证不落会话、功能窄口径暴露的安全通道。Go 侧同样处于待发布状态// TODO: pending a released Go SDK build for the self-hosted worker.仓库内的无容器特例cf-worker 变体当运行环境无法承载完整 Node 工具集时如 Cloudflare Workers 隔离区worker(...)组合器会引入仅 Node 可用的agent-toolset/node模块而不可用。此时 cf-worker/src/runner.ts 展示了一条手工组合的降级路径正好印证 worker 内部的真实分工它用client.beta.sessions.events.toolRunner(sessionId, { tools: fakeTools(...) })只做分发reconcile 事件流 工具执行 结果回报注意 TS 版sessionId是位置参数心跳与 force-stop 由调用方Durable Object自己负责heartbeatLoop维护work.heartbeat(workId, {environment_id, expected_last_heartbeat})并按返回的ttl_seconds动态调整心跳间隔收到state stopping / stopped或lease_extended false时中止退出时调用client.beta.environments.work.stop(workId, { environment_id, force: true })强制结束租约。这段代码恰好是 upgrade-guide 3c. 分发器拆分 结论的运行时形态SessionToolRunner只管分发work 项生命周期心跳、force-stop在 SDK 默认组合器worker(...)内部完成或由使用低层 API 的你自行承担。九、两个易踩的坑进程形态 × 空闲策略 × 技能下载综合 usage-guide 与仓库源码落地时最容易出问题的三点值得单独强调--on-work脚本必须前台阻塞。poll 侧没有脚本返回后不 stop的开关脚本一返回 poller 就对该 work 项发 stop。这也是 docker/on-work.sh 用exec docker run --rm而非-d的根本原因注释原文即提醒 a detacheddocker run -d… would make the poller stop the work before the just-spawned container could claim it。空闲退出只认end_turn。--max-idle默认 1m从session.status_idle且stop_reason: end_turn起算任何其它事件重置计时。反过来它也意味着只要会话在持续产生事件runner 就会一直存活--max-idle传 0 则运行到会话结束。docker 变体依赖该策略实现容器 idle 60s 后自删、--rm清理、而cma-ws-session卷保留给下一条消息的资源生命周期。技能下载需要额外鉴权变量。CLI 技能下载客户端只解析ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN不认ANTHROPIC_ENVIRONMENT_KEY。docker 变体因此在每个容器里额外注入ANTHROPIC_AUTH_TOKENenvironment key否则技能会静默失败。SDK 库用法无此问题——AsyncAnthropic(auth_tokenenvironment_key)构造的客户端天然携带该凭证如 sandbox_runner.py 所示。十、版本演进参考从旧版 API 迁移的对照usage-guide 是写给当前已发布 SDK 构建的文档。如果你的存量代码还停留在旧的预发布形态docs/upgrade-guide.md 提供了三个相互独立的迁移维度可作速查统一凭证ENVIRONMENT_SERVICE_KEY/ANTHROPIC_ENV_KEY→ANTHROPIC_ENVIRONMENT_KEYsecret/sessions_token/decodeWorkSecret从 worker 流程中整体移除CLI 更名ant worker poll→ant beta:worker pollant worker dispatch→ant beta:worker run--service-key→--environment-key--allow-absolute-paths→--unrestricted-paths库结构重塑anthropic.lib.runner不再存在组合器统一收敛到client.beta.environments.work.worker(...)Python 与 TS 两侧均有工具工厂迁至anthropic.lib.tools.agent_toolsetPython/anthropic-ai/sdk/tools/agent-toolset/nodeTS并统一beta_/beta前缀命名。对绝大多数使用者升级动作可以浓缩成一句话把手工pollertool_dispatcher循环替换为client.beta.environments.work.worker(...)即本文第七节给出的两个官方示例其余交给 SDK。十一、小结至此围绕 usage-guide.md 的一条完整自托管链路已经打通创建环境并签发唯一的环境密钥 → 用ant beta:worker poll常驻轮询、或借--on-work脚本把每个 work 项转交独立容器、或以ant beta:worker run作为每会话容器的 entrypoint → 需要嵌入自有编排时改用client.beta.environments.work.worker(...).run()/.handle_item()→ 需要自定义能力时用tools工厂过滤并扩展默认六件套。整个过程只有一把环境密钥流转组织级 API Key 始终不触碰 runner。仓库内 docker、cf、cf-worker、modal、daytona、vercel 六个变体含 modal_sandbox_webhook.py、daytona_webhook.py 这类 webhook 编排示例就是本文全部命令与库调用的可运行对照物可配合各自的 README 完成部署验证。需要迁移旧代码时upgrade-guide.md 中的 before/after 示例含 Python、TypeScript、webhook 三组是可直接对照的迁移模板。【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考