Claude Code SDK #42:Hosting 全解——subprocess × SessionStore × tenant isolation,把 Agent 跑进生产环境

Claude Code SDK #42:Hosting 全解——subprocess × SessionStore × tenant isolation,把 Agent 跑进生产环境

Agent SDK 自托管不是普通 API wrapper 部署,而是要管理 claude CLI 子进程、本地 transcript、工作目录、路由粘性和多租户边界。本文拆解 Hosting 文档里的 session 模式、SessionStore、资源估算、网络代理、OTEL 与成本上限,帮助开发者把 Agent 服务跑得可恢复、可观测、可控。

如果你把 Claude Agent SDK 当成一个普通 API wrapper 来部署,第一天可能能跑,第二天就会开始丢 session、串租户上下文、容器内存飙升,最后变成一个很难排查的生产事故。官方 Hosting 文档开头讲得很直接:SDK 调 query() 时会拉起一个独立的 claude CLI 子进程,这个子进程拥有 shell、工作目录和本地 JSONL 会话文件;所以它不是无状态 HTTP 客户端,而是一套带本地状态的长生命周期运行时。1

1. 先把部署心智模型换掉:不是 API 调用,是子进程

传统后端函数的心智模型通常是:请求进来,调用模型 API,结果返回,请求结束。Agent SDK 不一样。
官方文档说明,SDK 会在你的应用进程里启动一个 claude CLI subprocess,并通过 stdio 和它通信;这个 subprocess 负责 shell、工作目录、会话 transcript,也会向 Anthropic API 发出 HTTPS 请求。一个 agent session 对应一个 subprocess,N 个并发 session 就是 N 个 subprocess。1
这句话决定了后面所有工程选择:
你以为在部署实际在部署
一组无状态 API handler一组会长期占用内存的本地进程
一段 prompt 处理逻辑prompt + shell + 工作目录 + session 文件
横向扩容后自动无感session 需要路由回原容器或从持久层恢复
只要限 API key 就够了还要限文件系统、网络、进程和租户上下文
所以第一条建议很简单:不要先问怎么上 Kubernetes,先问一个用户任务到底要不要保留本地状态。

2. cwd 是隔离的第一道门

默认情况下,所有 subprocess 都会继承应用的工作目录。官方建议如果不同 session 需要独立文件系统,就在每次 query() 里显式传 cwd1
TypeScript 写法长这样:
query({
  prompt,
  options: { cwd: "/work/session-a" }
})
Python 写法是:
query(
  prompt=prompt,
  options=ClaudeAgentOptions(cwd="/work/session-a")
)
这不是洁癖。Agent 会读文件、改文件、跑命令。如果两个用户共用一个工作目录,轻则文件名冲突,重则 A 用户的 CLAUDE.md、中间产物、临时凭据被 B 用户的 session 读到。生产环境里,cwd 应该按 user、tenant、task 或 session 切开,而不是让所有人挤在应用根目录。

3. 三类状态默认都在本地盘,重启就可能没了

Hosting 文档列出三类默认落在容器文件系统里的状态:session transcripts、CLAUDE.md memory files、工作目录产物。文档也明确说,这些状态默认不会在容器重启、缩容或迁移到另一台节点后保留下来。1
这里最容易误判的是 transcript。你以为 session ID 在数据库里就能恢复,实际 transcript 还在本地盘。官方建议用 SessionStore adapter 把 transcript 持久化到共享存储;但 SessionStore 只镜像 transcript,不负责 CLAUDE.md memory 文件和工作目录里的其它产物。1
换成工程决策就是:
  • transcript 要恢复:接 SessionStore
  • 用户文件要恢复:挂载 volume,或同步到对象存储。
  • CLAUDE.md/memory 要恢复:单独设计存储策略。
  • 只跑一次就结束:不要为它强行做长期状态。
不要把 SessionStore 当成万能存档。它解决的是会话文本,不是整个工作区快照。

4. 四种 session 模式,别混着用

官方把 Hosting 模式分成四类:Ephemeral sessions、Long-running sessions、Hybrid sessions、Multi-agent container。1
我会把它翻译成一张选型表:
模式容器怎么活适合什么任务最容易踩的坑
Ephemeral一个任务一个容器,结束即销毁修 bug、抽取票据、翻译文档、媒体处理用户回头想继续,状态已经没了
Long-running容器长期在线,里面挂多个 sessionSlack bot、邮件 agent、站点 builder并发 session 把内存吃满
Hybrid闲时缩容,回来后从 SessionStore 恢复项目经理、深度研究、客服工单没配 store 就缩容,transcript 丢失
Multi-agent container一个容器里跑多个 SDK subprocess多 Agent 仿真、共享环境协作工作目录和 settings 互相污染
这张表比「我用什么云」更先发生。云平台只决定容器跑在哪里,session 模式决定容器和用户任务怎么绑定。

5. 容器资源不能按 idle baseline 算

官方给了一个起点:新启动的 agent instance 可以先按 1 GiB RAM、5 GiB disk、1 CPU 估算;但内存会随 session 长度和工具活动增长,所以要按真实 session 长度和并发来测,而不是按空闲状态拍脑袋。1
Scaling 部分也给了一个很朴素的公式:
agents per host = (host RAM - overhead) / (per-session RAM ceiling)
这个 per-session RAM ceiling 不是文档替你填的数字。你得拿一条有代表性的任务跑到目标长度,打开预期工具负载,记录 peak RSS,再决定一台机器能塞几个 session。1
对国内团队来说,这里还有一个现实问题:很多人会先在单机 Demo 里看到「能跑」,然后直接把它套到多租户服务里。Agent 的内存不是请求结束就归零。长 session、工具结果、subagent 扇出都会把峰值推高。

6. 长连接 session 要做路由粘性

如果你用 long-running 模式,一个用户的 session 不是每次请求都能随便打到任意容器。官方建议对 long-running sessions 使用容器池和负载均衡,并按 sessionId 做 consistent hashing,让同一个 session 持续命中同一个容器和同一个运行中的 subprocess。1
这点和普通 Web API 很不一样。普通 API 没状态,负载均衡随便打;Agent SDK session 背后有一个活着的 subprocess,换容器就等于换运行时。
如果你不能保证粘性路由,就要接受另一个成本:每次都从持久化 transcript 恢复,并重新建立工作区状态。对低频 hybrid 任务可以;对实时 chat bot 或协作型 agent,会明显影响体验。

7. 网络边界别直接交给 agent

Hosting 文档说,SDK 至少需要出站 HTTPS 到 api.anthropic.com;如果跑在 Bedrock 或 Google Cloud Agent Platform,就要到对应 provider endpoint;如果 agent 用 MCP server 或外部工具,也需要访问这些 endpoint。官方建议生产环境把出站流量走 egress proxy,用它做域名 allowlist、凭据注入和请求日志。1
Secure Deployment 文档把这个思路说得更硬:不要把敏感凭据直接放进 agent 环境里,可以把 proxy 放在 agent 边界之外,由 proxy 给外部请求注入 API key;agent 能发请求,但看不到凭据本身。2
这比「把 API key 放到环境变量」麻烦,但多租户场景里很值。prompt injection 一旦让 agent 尝试把数据发到陌生域名,egress proxy 至少能在网络层挡一次。

8. 多租户隔离,settingSources 还不够

共享容器是成本友好方案,但也是上下文泄漏高发区。Hosting 文档提醒,SDK 默认会从文件系统读取 settings 和 CLAUDE.md memory;在共享容器里,这些文件可能把一个租户的上下文带进另一个租户的 session。1
官方给的隔离组合很具体:
query({
  prompt,
  options: {
    cwd: tenantDir,
    settingSources: [],
    env: {
      ...process.env,
      CLAUDE_CONFIG_DIR: configDir,
      CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
    },
  },
})
Python 侧对应 setting_sources=[]cwd=tenant_dirCLAUDE_CONFIG_DIRCLAUDE_CODE_DISABLE_AUTO_MEMORY=1。文档还特别提醒:TypeScript 的 env 会替换 subprocess 环境,所以要展开 ...process.env,否则 PATHANTHROPIC_API_KEY 这类继承变量可能没了。1
这里有个细节容易漏:settingSources: [] 不等于关掉所有本地上下文。官方明确说 auto memory 会从 ~/.claude/projects//memory/ 进入 system prompt,所以多租户时还要设 CLAUDE_CODE_DISABLE_AUTO_MEMORY=11

9. 可观测性要放在容器层,而不是业务代码里临时打 log

Agent SDK 的 session 会跨很多 API round-trip,期间还会触发工具调用。Hosting 文档说,如果没有 telemetry,你看不到哪些工具跑了、耗时多久、session 卡在了哪里。SDK 会继承环境里的 OpenTelemetry 配置,所以可以在容器或编排层设置 OTEL 环境变量,让每次 query() 都导出 spans、metrics 和 log events。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
还有一个隐私边界:prompt text 和 tool inputs 默认不会进入导出数据;如果要导出敏感内容,需要看 Observability 文档里的 opt-in 开关。1

10. 成本控制:先控轮数,再算账

Hosting 文档的 Known limitations 里有一条很关键:没有顶层 session timeout。一个 session 不会自己超时,建议用 maxTurns 限制 agent 在停止前最多走多少轮 tool-use round trip。1
Cost Tracking 文档也提醒,total_cost_usd / costUSD 是客户端估算,不是权威账单。它适合开发调试和预算预估,但不能拿来给终端用户计费或触发财务决策;权威数据要看 Usage and Cost API 或 Claude Console。3
实战里我会把成本控制拆成三层:
  1. 执行上限maxTurns、任务级 timeout、subagent 批量大小。
  2. 资源上限:容器 CPU、内存、磁盘、进程数。
  3. 费用观测:读取 result message 的 cost estimate,再和平台账单对齐。
只做第 3 层没有用。账单出来时,钱已经花了。

最小落地清单

如果你今天要把 Agent SDK 服务接到生产环境,不建议从完整平台化开始。先把下面 8 件事做完:
  1. 每个 session 显式传 cwd,不要共用应用根目录。
  2. 判断 session 模式:ephemeral、long-running、hybrid、multi-agent container 只能先选一种主路径。
  3. 需要恢复的 session 接 SessionStore,工作目录产物另做 volume 或对象存储同步。
  4. 按真实任务测 peak RSS,再决定单机并发,不按空闲内存估。
  5. long-running 模式按 sessionId 做粘性路由。
  6. 出站网络走 proxy,凭据尽量在 proxy 注入,不直接暴露给 agent。
  7. 多租户共享容器时,同时设置 cwdsettingSources: []、独立 CLAUDE_CONFIG_DIRCLAUDE_CODE_DISABLE_AUTO_MEMORY=1
  8. 上线前打开 OTEL,并用 maxTurns 给每类任务设硬上限。
Agent SDK 的价值是把 Claude Code 的工具执行、上下文管理和 agent loop 放进你的应用进程。代价也在这里:你接手了 subprocess、文件系统、网络和多租户边界。把这层运行时当成生产系统来管,它才会像生产系统一样可恢复、可观测、可控。

Contenido relacionado

  • Inicia sesión para comentar.
More from this channel