Skip to content

Agent SDK 生产部署

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/projectsCLAUDE_CONFIG_DIR 下的 projects/需要跨主机恢复时用 SessionStore 镜像
Memory files用户层 ~/.claude/CLAUDE.md,项目层工作目录内 CLAUDE.md不会被 SessionStore 替代,需要独立卷、对象存储或禁用策略
工作产物session 的 cwd用每租户/每任务目录、卷或对象存储同步

会话模式

模式适合场景关键设计
Ephemeral一次性修复、分析、转换、CI job每个任务一个容器或 sandbox,结束即销毁;只保留你显式导出的结果
Long-runningSlack 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 可能进入另一个租户的上下文。

隔离点TypeScriptPython目的
禁用文件系统 settingssettingSources: []setting_sources=[]不加载 user/project/local settings
禁用 auto memoryCLAUDE_CODE_DISABLE_AUTO_MEMORY=1CLAUDE_CODE_DISABLE_AUTO_MEMORY=1避免 ~/.claude/projects/<project>/memory/ 注入系统提示
每租户配置目录CLAUDE_CONFIG_DIR=/srv/claude-config/<tenant>同左隔离 ~/.claude.json、transcripts 和缓存状态
明确工作目录cwd: tenantDircwd=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_usdSDK 汇总的客户端成本估算计入每 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 都可能带 usagetotal_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_tokenscache_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,否则 PATHANTHROPIC_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

官方参考

面向编码工具与 Agent 工作流的稳定模型网关。