跳到正文
FunCoding

搜索

搜索文档、智能体、博客、Skill 和 MCP

托管 Agent SDK

在生产环境部署 Agent SDK:子进程架构、本地磁盘上的状态、四种会话模式(临时、长时间运行、混合、多智能体容器)、容器供给(沙盒、运行时依赖、资源、网络)、会话持久化、可观测性、认证与密钥、扩展与并发、成本、多租户隔离、已知限制与部署排障。

Agent SDK 派生并监督一个 claude CLI 子进程,该子进程拥有一个 shell、一个工作目录和磁盘上的会话文件。托管它不像托管无状态的 API 封装:每个运行中的智能体都是与本地状态绑定的长期进程,这决定了你如何分配资源、持久化会话和跨租户扩展。本页讲在你自己的基础设施上自托管;可部署的 Dockerfile 和 Kubernetes 清单见托管 cookbook。如果你不需要在自己的基础设施上运行智能体循环本身,可以考虑 Managed Agents:由 Anthropic 托管智能体循环,你的应用通过客户端 SDK 或 REST API 发送事件并接收流式结果;工具执行运行在 Anthropic 管理的云沙盒里,或你自己基础设施上的自托管沙盒里。

子进程模型

本页的每个托管决定都源自 SDK 运行智能体的方式。你的代码调用 query() 时,SDK 派生一个单独的 claude CLI 进程并通过 stdio 与它通信。该子进程拥有 shell、工作目录和本地磁盘上的 JSONL 会话记录。

请求流:客户端到你的应用,应用在容器内通过 stdio 派生一个 claude CLI 子进程;子进程写本地磁盘,并通过 HTTPS 调用 api.anthropic.com。

一个智能体会话对应一个子进程。运行 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);
}
import asyncio
from claude_agent_sdk import ClaudeAgentOptions, query


async def main():
    async for message in query(
        prompt="Summarize the files in this directory",
        options=ClaudeAgentOptions(cwd="/work/session-a"),
    ):
        print(message)


asyncio.run(main())

本页的 TypeScript 示例使用顶层 await,所以要保存为 .mts 文件,或在 package.json 里设 "type": "module"。

存在本地磁盘上的状态

默认有三类智能体状态存在容器的文件系统上,它们都无法在容器重启、缩容或迁移到不同节点后存活:

状态默认位置
会话记录~/.claude/projects/,或设了 CLAUDE_CONFIG_DIR 时它下面的 projects/ 目录
CLAUDE.md 记忆文件用户层是 ~/.claude/CLAUDE.md,项目层是会话的工作目录
工作目录产物会话的工作目录

要跨主机持久化记录,配置 SessionStore 适配器。记忆文件和其他工作目录产物需要它们自己的存储策略,如挂载卷或对象存储同步。会话、恢复和分叉在 API 层如何工作,见「会话」。

选择会话模式

这四种模式涵盖会话生命周期:容器相对于它服务的会话活多久。容器在哪里运行,托管 cookbook 有本地 Docker、Modal 和 Kubernetes 的可部署代码。在这里选会话模式,在 cookbook 里选部署目标。

临时会话

为每个用户任务创建一个容器,任务完成时销毁它。最适合一次性任务;用户在任务完成期间仍可以与 AI 交互,但一旦完成容器就被销毁。示例工作负载包括缺陷调查与修复、发票和收据提取、文档翻译以及媒体转换。容器运行一个一次性入口,从 TASK_PROMPT 环境变量读取任务、调用 SDK 然后退出。

import { query } from "@anthropic-ai/claude-agent-sdk";

const prompt = process.env.TASK_PROMPT!;

for await (const message of query({ prompt, options: { maxTurns: 20 } })) {
  console.log(message);
}
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 轮的限制,结果消息的 subtype 是 error_max_turns,query() 调用在产出它之后抛出错误,所以如果容器需要干净退出,要把循环包进 try 块(错误 subtype 见「处理结果」)。

长时间运行的会话

运行持久的容器实例(通常每个容器托管多个 SDK 进程)来服务持续的工作。最适合自主采取行动、提供内容或处理高流量消息流的智能体。示例工作负载包括分诊并回复来信的邮件智能体、通过容器端口托管每用户可编辑站点的站点构建器,以及处理来自 Slack 这类平台的持续流量的聊天机器人。容器暴露 HTTP 或 WebSocket 端点,并把每个活动会话映射到一个长期的查询及其背后的子进程。让会话保持打开和预热的调用在两个 SDK 之间不同:TypeScript——用 streamInput() 给活动会话添加轮次,调用 startup() 在流量到来之前预热子进程;如果在第一个请求到来之前不知道会话的工作目录,改用 prewarm() 预热。Python——用 ClaudeSDKClient 在多个轮次间保持会话打开。要给容器定大小,使它能在内存里容纳最大并发会话数。

混合会话

临时容器在启动时从 SessionStore 水合,并把更新持久化回去。最适合跨越许多交互、但交互之间空闲的会话:容器在空闲期间缩容,用户回来时再扩容。示例工作负载包括间歇性检查的个人项目经理、暂停并在数小时内恢复的深度研究,以及跨交互加载工单历史的客户支持智能体。要按你预期用户多久回来一次来调整提供商的空闲超时。没有配置 SessionStore 就关闭容器会连同记录一起丢失,所以对这种模式存储是必需的,不是可选的。这种模式的关键是在附带共享存储的情况下按 ID 恢复会话:

import { query, type SessionStore } from "@anthropic-ai/claude-agent-sdk";

declare const userInput: string;
declare const sessionId: string;          // 按用户从你的数据库查出
declare const sessionStore: SessionStore; // 对象存储、键值存储、数据库或你自己的适配器

for await (const message of query({
  prompt: userInput,
  options: { resume: sessionId, sessionStore },
})) {
  // ...
}
from claude_agent_sdk import query, ClaudeAgentOptions, SessionStore
import asyncio

user_input: str = ...
session_id: str = ...              # 按用户从你的数据库查出
session_store: SessionStore = ...  # 对象存储、键值存储、数据库或你自己的适配器


async def main():
    async for message in query(
        prompt=user_input,
        options=ClaudeAgentOptions(
            resume=session_id,
            session_store=session_store,
        ),
    ):
        ...


asyncio.run(main())

多智能体容器

在一个容器里运行多个 SDK 子进程。最适合必须紧密协作的智能体,例如智能体在共享环境里相互交互的多智能体模拟。给每个智能体自己的工作目录,使它们不会互相覆盖文件,并隔离设置加载,使每个智能体的 CLAUDE.md 文件不会跨智能体泄漏(具体选项见「多租户隔离」)。

供给容器

基于容器的沙盒

在沙盒容器里运行 SDK,以获得进程隔离、资源限制、网络控制和临时文件系统。选择提供商时要回答的问题:

  • 谁运行沙盒:沙盒即服务的提供商替你运营基础设施,而自托管选项给你软件在自己的基础设施上运行。
  • 冷启动延迟:从"创建沙盒"到"准备好接受第一个请求"多久。临时模式需要亚秒级启动,长时间运行的模式能容忍更久。
  • 持久存储:提供商是否提供持久卷,还是只有临时磁盘。混合模式需要在某处有持久存储,不论在沙盒里还是在它旁边。
  • 定价模型:按秒、按请求或固定按小时计费。按秒定价适合突发的临时工作负载,按小时适合长时间运行的会话。
  • 网络:对自定义出口规则、出站代理以及受监管环境的私有 VPC 对等的支持。

Docker、gVisor 和 Firecracker 这类自托管选项以及详细的隔离配置,见「隔离技术」。

运行时依赖

容器需要你的 SDK 所用语言的运行时:Python SDK 需要 Python 3.10+,TypeScript SDK 需要 Node.js 18+。TypeScript 和 Python SDK 在多数安装下都捆绑原生 Claude Code 二进制,派生的 CLI 不需要单独安装 Node.js(哪些安装需要单独的原生 Claude Code 安装,见快速开始的安装说明)。捆绑的二进制固定在 SDK 包版本上,所以更新 SDK 就是更新 CLI。SDK 遵循 semver:持续采用补丁发布,采用次版本前先审查 TypeScript 或 Python 的变更日志。

资源

每个智能体 1 GiB 内存、5 GiB 磁盘和 1 个 CPU 是新启动实例的合理起点。内存用量随会话长度和工具活动增长,所以要按你实际需要的会话长度和并发来定大小,而不是空闲基线(如何算出每台主机的智能体数,见「扩展与并发」)。

网络

SDK 需要到 api.anthropic.com 的出站 HTTPS,在 Amazon Bedrock 或 Google Cloud Agent Platform 上运行时则是到你提供商的区域端点。如果你的智能体使用 MCP 服务器或外部工具,它们也需要到这些端点的出站访问。生产里要经强制域名允许列表、注入凭据并记录请求的出口代理路由出站流量(完整模式见「安全部署」)。对入站流量,在容器上暴露 HTTP 或 WebSocket 端口:你的应用在该端口上处理客户端请求并在内部调用 SDK;子进程本身不监听网络。

处理生产问题

在发布自托管智能体之前,逐一处理这些决定。

会话和状态持久化

默认的本地磁盘在重启、缩容或迁移到不同节点时会丢失。对用户期望能恢复的任何会话,用 SessionStore 适配器把记录镜像到持久存储(对象存储、键值存储和数据库的示例适配器以及你自己适配器的一致性测试套件,见「参考实现」)。关于 SessionStore 的行为有三点要知道:

  • 只有记录:SessionStore 镜像记录,不镜像 CLAUDE.md 记忆文件或其他工作目录产物;要挂载共享卷或单独同步这些。
  • 镜像,不是替代:子进程先写本地磁盘,SDK 把每批的副本转发给存储。全新会话的本地记录比运行活得更久;从存储恢复的运行在结束时删除它的本地副本,所以存储持有唯一的持久副本(见「双写架构」)。
  • mirror_error 消息:SDK 无法把一批交付给存储时,它丢弃该批、发出 { type: "system", subtype: "mirror_error" } 消息并继续查询;如果存储持久性重要,要对这些告警(重试和超时行为见「镜像写入是尽力而为的」)。

可观测性

Agent SDK 智能体是长期运行的进程,在许多 API 往返中派生工具调用。没有遥测,你看不到运行了哪些工具、花了多久,或会话在哪里停滞。SDK 继承环境里的 OpenTelemetry 配置。在容器或编排器层设置 OTEL 环境变量,使每个 query() 调用都把 span、指标和日志事件导出到你的收集器。下面的例子为三种信号都启用 OTLP 导出;CLAUDE_CODE_ENHANCED_TELEMETRY_BETA 只对追踪必需,如果你只导出指标和日志就省略它。

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

默认导出里不含提示文本和工具输入(选择加入的标志见「控制导出里的敏感数据」,完整的信号目录见「可观测性」)。

认证与密钥

托管时有三个认证问题很重要:

  • Anthropic API:子进程从它的环境读取 ANTHROPIC_API_KEY。从你的密钥管理器提供它,或设 ANTHROPIC_BASE_URL 把模型调用经由在容器之外注入密钥的代理路由(代理模式见「凭据管理」,支持的认证方式见 SDK 快速开始的 Setup)。
  • 入站:在智能体容器前面的网关处放认证。智能体应该收到已预先认证的请求,不应该是验证用户令牌的组件。
  • 出站工具:让工具凭据留在智能体环境之外。把出站调用经由在请求离开容器之后注入 API key 的代理路由:智能体发出调用,代理添加凭据。

扩展与并发

每个会话运行在自己的子进程里,所以主机上的并发受它的内存能容纳多少子进程的限制。用这个公式给每台主机定大小:

agents per host = (host RAM - overhead) / (per-session RAM ceiling)

通过把有代表性的会话在你预期的工具负载下运行到目标长度并记录峰值 RSS 来测量每会话的上限;「资源」里的 1 GiB 起点是下限,不是上限。水平扩展的路由取决于你的模式。对长时间运行的会话(容器持有许多会话),在负载均衡器后面运行一个容器池,并对 sessionId 做一致性哈希把每个会话固定到一个容器:被固定的会话一直命中同一个容器,因而是同一个运行中的子进程,直到它被驱逐或容器重启。

成本

Anthropic 的 token 成本通常比容器基础设施成本高出一个数量级或更多。最小配置的容器每小时约 0.05 美元,而单个长的智能体会话就可能花掉几美元的 token(按会话的 token 核算见「成本跟踪」)。

多租户隔离

SDK 的默认行为从文件系统读取设置和 CLAUDE.md 记忆文件。在为多个租户服务的共享容器里,这些文件可能把一个租户的上下文泄漏进另一个租户的会话。要在共享容器里隔离租户:

  • 在 TypeScript 里传 settingSources: [],或在 Python 里传 setting_sources=[],跳过用户、项目和本地设置。
  • 在 env 里设 CLAUDE_CODE_DISABLE_AUTO_MEMORY=1:~/.claude/projects/<project>/memory/ 下的自动记忆不论 settingSources 如何都会加载进系统提示(无条件加载的其他输入见「settingSources 不控制什么」)。
  • 把 CLAUDE_CONFIG_DIR 指向每租户的目录,使租户不共享 ~/.claude.json 全局配置。当每个配置目录服务一个工作目录且你不传 SessionStore 时,还可以在 env 里设 CLAUDE_CODE_PROJECT_DIR_NAME,让它下面的记录路径保持简短(需要 TypeScript Agent SDK v0.3.234 及以上,或 Python Agent SDK v0.2.140 及以上)。
  • 使用每租户的工作目录:在每个 query() 调用上显式传 cwd。
  • 在你的代理上应用每租户的出口规则,如不同的出站 IP、凭据或域名允许列表,使被攻破的租户无法经另一个租户的出站策略外泄数据。

下面的例子把设置、自动记忆、配置目录和工作目录选项一起应用。要构造 tenantDir 和 configDir,使每个租户得到别的租户无法读取的路径。在 TypeScript 里,env 替换子进程环境,所以要展开 ...process.env 以保留 PATH 和 ANTHROPIC_API_KEY 这类继承的变量;在 Python 里,env 合并在继承的环境之上。

import { query } from "@anthropic-ai/claude-agent-sdk";

declare const prompt: string;
declare const tenantDir: string;
declare const configDir: string;

for await (const message of query({
  prompt,
  options: {
    cwd: tenantDir,
    settingSources: [],
    env: {
      ...process.env,
      CLAUDE_CONFIG_DIR: configDir,
      CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
    },
  },
})) {
  // ...
}
from claude_agent_sdk import query, ClaudeAgentOptions
import asyncio

prompt: str = ...
tenant_dir: str = ...
config_dir: str = ...


async def main():
    async for message in query(
        prompt=prompt,
        options=ClaudeAgentOptions(
            cwd=tenant_dir,
            setting_sources=[],
            env={
                "CLAUDE_CONFIG_DIR": config_dir,
                "CLAUDE_CODE_DISABLE_AUTO_MEMORY": "1",
            },
        ),
    ):
        ...


asyncio.run(main())

已知限制

在你的部署设计里要围绕这些做规划:

限制怎么做
没有顶层会话超时会话不会自己超时;在 TypeScript 里设 maxTurns、在 Python 里设 max_turns,限定智能体停止前进行多少次工具使用往返
长会话中内存增长限制会话长度,或定期回收子进程(见「扩展与并发」)
大规模并行子智能体扇出可能触发速率限制把工作分成较小的批次,而不是发出一次很宽的分派
没有按子智能体的挂钟截止时间在它的 AgentDefinition 里用 maxTurns 限制每个子智能体;CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS 设置一个子智能体停止产生输出时触发的停滞看门狗,它不是总运行时间的截止时间

排查部署失败

当在你机器上能工作的智能体在已部署的服务里失败时用这一节。下面每一项点名一种失败并链接涵盖它的条目:

  • 服务启动时找不到 CLI:在 Python 里,容器或服务管理器用与你的 shell 不同的 PATH 运行你的应用,所以在本地能用的安装对该进程不可见;在 TypeScript 里,镜像构建跳过了 SDK 的可选依赖,或 pathToClaudeCodeExecutable 指向镜像里不存在的文件(见「找不到 Claude Code」)。
  • 镜像里有 CLI 但无法启动:Claude Code 无法从与容器的架构或 libc 不匹配的二进制启动,也无法从在镜像构建中丢失执行权限的文件启动(见「启动 Claude Code 失败」)。
  • Claude Code 进程在运行中途退出:你的应用收到的错误取决于 SDK 语言,以及 CLI 是否先报告了错误结果;「CLI 进程退出」下的条目涵盖每条消息。

下一步

  • 托管 cookbook:带有 Docker、Modal 和 Kubernetes 可部署代码的 notebook 演练
  • 会话存储:用 SessionStore 适配器跨主机持久化记录
  • 可观测性:把 OTEL 追踪、指标和日志导出到你的收集器
  • 安全部署:网络控制、凭据管理和隔离加固
  • 成本跟踪:按会话的 token 和成本核算