Agent SDK 生产部署
Claude Agent SDK 生产部署指南: 会话模式、隔离、观测、成本、结构化输出、tool search、缓存 TTL,以及 TypeScript/Python 配置片段。
Agent SDK 适合把 Claude Code 的 agent loop 嵌入服务、后台任务、CI 或多租户产品。生产部署时要先接受一个核心事实: query() 不是一次纯无状态 API 调用,而是启动并监管一个 claude Claude Code CLI 子进程,SDK 通过 stdio 和它通信。
每个运行中的 agent session 都有自己的进程树、工作目录和本地 transcript。你需要像部署有状态 worker 一样规划文件系统、隔离、观测和成本控制。
如果你要先理解 SDK 权限评估、Hooks、MCP、SessionStore 和成本字段,看 Agent SDK 能力矩阵。TypeScript/Python API、session API 和迁移差异见 Agent SDK API 参考速查。Agent loop、settingSources、skills、subagents、todo tracking 和 checkpointing 见 Agent SDK Agent 能力。Custom tools、system prompt、streaming、structured output、tool search、用户审批和 SDK slash commands 的运行时设计见 Agent SDK 运行时模式。本页聚焦部署拓扑、多租户隔离和运行时运维。
#运行模型
| 项目 | 生产含义 |
|---|---|
query() | 启动 Claude Code CLI 子进程,不是直接把 prompt 发给无状态 API wrapper |
| 子进程 | 拥有 shell、工具调用、当前工作目录和本地 session 文件 |
| 并发 | N 个并发 session 通常意味着 N 个子进程,需要按 CPU、内存、磁盘和 API 限额规划 |
cwd | 默认继承宿主应用工作目录;多 session 或多租户必须显式传入 |
| 本地状态 | 容器重启、扩缩容、迁移节点时会丢失,除非你单独持久化 |
本地默认状态主要有三类:
| 状态 | 默认位置 | 生产处理 |
|---|---|---|
| Session transcripts | ~/.claude/projects 或 CLAUDE_CONFIG_DIR 下的 projects/ | 需要跨主机恢复时用 SessionStore 镜像 |
| Memory files | 用户层 ~/.claude/CLAUDE.md,项目层工作目录内 CLAUDE.md | 不会被 SessionStore 替代,需要独立卷、对象存储或禁用策略 |
| 工作产物 | session 的 cwd | 用每租户/每任务目录、卷或对象存储同步 |
#会话模式
| 模式 | 适合场景 | 关键设计 |
|---|---|---|
| Ephemeral | 一次性修复、分析、转换、CI job | 每个任务一个容器或 sandbox,结束即销毁;只保留你显式导出的结果 |
| Long-running | Slack bot、邮件 agent、持续站点构建器 | 容器长期运行,HTTP/WebSocket 入口把同一 session 路由到同一 worker |
Hybrid + SessionStore | 用户间歇回来继续的研究、项目管理、客服工单 | 空闲时释放容器,下次用 session ID 加 SessionStore 恢复 transcript |
| Multi-agent container | 多 agent 协作或仿真 | 同容器内多个 SDK 子进程,每个 agent 单独 cwd、配置目录和权限边界 |
SessionStore 只镜像 transcript,不是本地状态的替代品。Claude Code 子进程仍然先写本地 transcript,SDK 再把批次转发到 store。CLAUDE.md、auto memory、文件 checkpoint blob 和工作目录产物都不由 SessionStore 接管。
如果 SessionStore.append() 失败,SDK 会重试有限次数,最终失败时继续运行并在消息流中发出 mirror_error。生产环境要监控 { type: "system", subtype: "mirror_error" },否则外部存储可能悄悄缺 transcript 批次。
#多租户隔离
默认 SDK 会读取本机的 user、project、local settings 和 memory。共享容器里如果不隔离,一个租户的 CLAUDE.md、MCP、命令或 auto memory 可能进入另一个租户的上下文。
| 隔离点 | TypeScript | Python | 目的 |
|---|---|---|---|
| 禁用文件系统 settings | settingSources: [] | setting_sources=[] | 不加载 user/project/local settings |
| 禁用 auto memory | CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 | CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 | 避免 ~/.claude/projects/<project>/memory/ 注入系统提示 |
| 每租户配置目录 | CLAUDE_CONFIG_DIR=/srv/claude-config/<tenant> | 同左 | 隔离 ~/.claude.json、transcripts 和缓存状态 |
| 明确工作目录 | cwd: tenantDir | cwd=tenant_dir | 隔离文件读写、命令执行和产物 |
| 租户 egress/proxy | 在网关或网络层配置 | 同左 | 独立 outbound IP、凭据注入、domain allowlist 和审计 |
Python SDK 旧版本曾把 setting_sources=[] 当作未设置处理。依赖空列表隔离时,先升级到当前版本再上线。
#观测与成本
Agent 是长生命周期进程,一次用户任务可能跨多轮 API、工具调用、MCP 请求和子代理。生产环境至少要导出 OpenTelemetry,并从 result message 记录 token 与成本字段。
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
OTEL_RESOURCE_ATTRIBUTES=service.name=agent-runtime,team=platform默认不要记录 prompt 原文、工具输入输出或 raw API body。只有在隔离排障环境短期开启这些高敏字段。
| 字段 | 含义 | 用途 |
|---|---|---|
message.total_cost_usd | SDK 汇总的客户端成本估算 | 计入每 session/租户账单和预算告警 |
message.usage.input_tokens | 本轮标准输入 token | 判断上下文膨胀 |
message.usage.output_tokens | 本轮输出 token | 判断回复和工具规划成本 |
message.usage.cache_creation_input_tokens | 写入 prompt cache 的 token | 缓存写入成本,通常高于标准输入 |
message.usage.cache_read_input_tokens | 从 prompt cache 读取的 token | 缓存命中收益,通常按较低输入价计费 |
成功和失败的 result message 都可能带 usage 与 total_cost_usd。不要只在 success 分支记录成本。
#结构化输出与工具规模
结构化输出适合把 agent 结果写入数据库、任务系统或 UI。配置 outputFormat / output_format 后,最终 result message 会带 structured_output;SDK 会按 JSON Schema 校验,不匹配时重试,最终失败则返回错误结果。
Custom tools 和 MCP 是生产 agent 的主要扩展方式:
| 能力 | 用法 |
|---|---|
| Custom tools | 用 SDK 的 in-process MCP server 包装应用内函数、数据库访问或内部 API |
| Remote MCP | 连接 HTTP/SSE/stdio MCP server,把 Slack、GitHub、DB、工单系统接入 agent |
allowedTools / allowed_tools | 预批准明确工具或 mcp__server__* 通配符 |
| Tool search | 工具很多时延迟加载定义,避免每轮都把全部工具 schema 放进上下文 |
ENABLE_TOOL_SEARCH 常用值:
| 值 | 行为 |
|---|---|
| 未设置 | 默认启用;在 Vertex AI 或非一方 ANTHROPIC_BASE_URL 下可能回退到 upfront 工具定义 |
true | 强制启用,代理或模型不支持 tool_reference 时请求可能失败 |
auto | 工具定义超过上下文窗口 10% 时启用 |
auto:5 | 工具定义超过 5% 时启用,更早进入搜索模式 |
false | 禁用 tool search,每轮加载全部工具定义 |
工具少于约 10 个时,全部 upfront 加载通常更简单。工具库达到几十、几百甚至上千个时,tool search 通常能显著降低上下文占用并改善工具选择。
#Prompt cache TTL
Agent SDK 会自动使用 prompt caching。你通常不需要手写 cache 控制,但要知道 5 分钟和 1 小时 TTL 的成本差异。
| 规则 | 说明 |
|---|---|
| 默认 API key / Bedrock / Vertex / Foundry | 缓存写入通常是 5 分钟 TTL |
ENABLE_PROMPT_CACHING_1H=1 | 可选请求 1 小时 TTL,适合短会话反复加载相同系统提示和上下文 |
| Claude subscription | 计划额度内通常自动使用 1 小时 TTL |
| 1 小时写入 | 写入价格更高,适合能换来更多 cache read 的工作负载 |
| Cache hit | 命中会刷新 TTL;TTL 是闲置过期时间,不是总寿命 |
| 前缀变化 | 换模型、换 effort、工具定义变化、MCP 连接变化、/compact 等都可能降低命中 |
把 cache_creation_input_tokens 和 cache_read_input_tokens 分开看。creation 每轮都很高,通常说明 prompt 前缀在变;read 持续升高,说明同一前缀被复用。
#TypeScript 配置片段
import path from "node:path";
import { query, type SessionStore } from "@anthropic-ai/claude-agent-sdk";
declare const prompt: string;
declare const sessionId: string | undefined;
declare const sessionStore: SessionStore;
const tenantId = "tenant_123";
const tenantDir = path.join("/srv/agent-work", tenantId);
const configDir = path.join("/srv/claude-config", tenantId);
for await (const message of query({
prompt,
options: {
cwd: tenantDir,
resume: sessionId,
sessionStore,
maxTurns: 30,
maxBudgetUsd: 5,
permissionMode: "dontAsk",
settingSources: [],
allowedTools: ["Read", "Grep", "Glob", "mcp__enterprise-tools__*"],
mcpServers: {
"enterprise-tools": {
type: "http",
url: "https://tools.example.com/mcp"
}
},
outputFormat: {
type: "json_schema",
schema: {
type: "object",
properties: {
summary: { type: "string" },
actions: {
type: "array",
items: { type: "string" }
}
},
required: ["summary"]
}
},
env: {
...process.env,
CLAUDE_CONFIG_DIR: configDir,
CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
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",
ENABLE_TOOL_SEARCH: "auto:5"
}
}
})) {
if (message.type === "system" && message.subtype === "mirror_error") {
console.error("SessionStore mirror failed", message);
}
if (message.type === "result") {
console.log({
subtype: message.subtype,
cost: message.total_cost_usd,
usage: message.usage,
structured: message.structured_output
});
}
}TypeScript 的 env 会替换子进程环境,不是 merge。生产代码里通常要展开 ...process.env,否则 PATH、ANTHROPIC_API_KEY 或 provider 变量可能丢失。
#Python 配置片段
import asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
session_store = ...
async def run_agent(prompt: str, session_id: str | None = None) -> None:
tenant_id = "tenant_123"
tenant_dir = Path("/srv/agent-work") / tenant_id
config_dir = Path("/srv/claude-config") / tenant_id
options = ClaudeAgentOptions(
cwd=tenant_dir,
resume=session_id,
session_store=session_store,
max_turns=30,
max_budget_usd=5,
permission_mode="dontAsk",
setting_sources=[],
allowed_tools=["Read", "Grep", "Glob", "mcp__enterprise-tools__*"],
mcp_servers={
"enterprise-tools": {
"type": "http",
"url": "https://tools.example.com/mcp",
}
},
output_format={
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"actions": {
"type": "array",
"items": {"type": "string"},
},
},
"required": ["summary"],
},
},
env={
"CLAUDE_CONFIG_DIR": str(config_dir),
"CLAUDE_CODE_DISABLE_AUTO_MEMORY": "1",
"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",
"ENABLE_TOOL_SEARCH": "auto:5",
},
)
async for message in query(prompt=prompt, options=options):
if getattr(message, "type", None) == "system" and getattr(message, "subtype", None) == "mirror_error":
print("SessionStore mirror failed", message)
if getattr(message, "type", None) == "result":
print(
{
"subtype": getattr(message, "subtype", None),
"cost": getattr(message, "total_cost_usd", None),
"usage": getattr(message, "usage", None),
"structured": getattr(message, "structured_output", None),
}
)
asyncio.run(run_agent("分析这个租户工作区并返回结构化摘要"))Python 的 env 会叠加到继承环境上,但仍建议把认证和代理变量交给容器 secret 或网关统一注入。
#上线检查
| 检查项 | 通过标准 |
|---|---|
| 子进程边界 | 每个 session 有明确 cwd、资源上限、最大轮数和预算 |
| Session 恢复 | 需要跨主机恢复的 session 已配置 SessionStore,并监控 mirror_error |
| 状态持久化 | memory files、工作目录产物和 transcript 分别有清楚的保留策略 |
| 租户隔离 | settingSources: [] / setting_sources=[]、CLAUDE_CODE_DISABLE_AUTO_MEMORY=1、每租户 CLAUDE_CONFIG_DIR 和 egress policy 都已落地 |
| 观测 | OTEL traces/metrics/logs 进 collector,敏感日志默认关闭 |
| 成本 | result message 的 total_cost_usd、usage 和 cache read/write tokens 都按租户归档 |
| 工具 | custom tools/MCP 有权限白名单,大工具集启用或评估 tool search |
| 缓存 | 已根据会话间隔选择默认 5m 或 ENABLE_PROMPT_CACHING_1H=1 |
#官方参考
Support / 支持
Need help? / 需要帮助?
接入、计费与模型异常可邮件联系;服务可用性以状态页为准。
For setup, billing, or model issues, email us. Check the status page for uptime.
也可使用右下角微信 / QQ 客服 · WeChat / QQ support is available at the bottom right

