用 Docker 本地托管 Claude Agent SDK 研究型 Agent:临时单次运行与持久会话的两种落地模式
发布时间:2026/9/8 17:36:43 锦皓数字建站

用 Docker 本地托管 Claude Agent SDK 研究型 Agent临时单次运行与持久会话的两种落地模式【免费下载链接】claude-cookbooksA collection of notebooks/recipes showcasing some fun and effective ways of using Claude.项目地址: https://gitcode.com/GitHub_Trending/an/claude-cookbooks本文介绍 claude-cookbooks 仓库中 hosting/docker 的Tier 1 本地 Docker 托管方案它复用研究 Agent 的共享镜像通过docker run实现「一个提示、一个进程、跑完即退」的临时Ephemeral模式或通过docker compose拉起 FastAPI SSE 服务把会话目录挂载到宿主机让多轮对话在容器重建后依然可续。读完你将掌握基于 Claude Agent SDK 打包 Agent 镜像、用环境变量注入 API Key 与提示词、以及利用CLAUDE_CONFIG_DIRresume机制做会话持久化的完整实操方法。背景这个 Docker 方案在整个托管体系中的位置这份 README 是 hosting 目录 中「三层托管」的第一层。整体设计围绕 00_The_one_liner_research_agent.ipynb 里构建的研究 Agent 展开WebSearch 搜集信息、按要求输出带来源引用的研究结论。三层托管本地 Docker / Modal / Kubernetes共享同一份 Agent 代码、同一个容器镜像、同一个 HTTP 接口契约只是容器外围的运维机制不同Docker 正是其中最轻、最适合本机验证与批处理的一层。镜像的构建上下文是claude_agent_sdk/hosting 的父目录而非hosting/因为 Agent 会 import 与hosting/平级的research_agent/与utils/模块。所有部署命令都假定你先进入该目录cd claude_agent_sdk/准备工作构建共享镜像与准备密钥在尝试两种运行模式之前先用 hosting/Dockerfile 构建一次镜像cd claude_agent_sdk/ docker build -f hosting/Dockerfile -t research-agent .该镜像值得拆开看几个关键点它们是后面两种模式成立的前提运行时缺一不可的依赖基于python:3.11-slim由于 Agent SDK 底层会把任务交给 Claude Code CLI 执行因此镜像内先安装 Node 20 再npm install -g anthropic-ai/claude-code2.1.140版本被硬锁定在 requirements.txt 注释所说的 pin 集合内升级需连同claude-agent-sdk0.1.50一起刻意为之。Python 依赖固定版本claude-agent-sdk、fastapi、uvicorn[standard]、sse-starlette、python-dotenv全部硬 pin避免未来版本悄然破坏 notebook 的端到端流程。工作目录被钉死WORKDIR /app。原因正如 Dockerfile 注释所述SDK 生成的会话记录存放在$CLAUDE_CONFIG_DIR/projects/编码后的cwd/下只有 cwd 稳定resume才能在容器重启后找回旧会话。会话存储重定向ENV CLAUDE_CONFIG_DIR/data。镜像内不声明VOLUME而是由每一层托管显式地往/data挂载持久化存储compose 的 bind mount、Modal Volume、k8s PVC避免docker run时泄漏匿名卷。镜像的构建还依赖同目录下的 Dockerfile.dockerignore它排除了.env、notebook、会话目录以及hosting/docker|modal|kubernetes各层专属文件让镜像只包含 Agent、utils 与共享服务代码。该忽略文件采用了带前缀的名字需要 BuildKitDocker 23.0 与 Docker Desktop 的默认 builder老引擎需DOCKER_BUILDKIT1。镜像入口统一为 entrypoint.sh它按第一个参数分发到两种模式正好对应 README 的两条路径# 默认临时模式——用 $PROMPT 跑一次 Agent 后退出 exec python -m hosting.run_once # 传入 serve启动 FastAPI 服务 exec uvicorn hosting.server:app --host 0.0.0.0 --port 8000临时模式一个 Prompt、一个进程、跑完即退README 把临时模式定义为 One prompt, one process, then exit适合没有对话需要恢复的任务型job-shaped工作例如批处理、一次性分析。cd claude_agent_sdk/ docker build -f hosting/Dockerfile -t research-agent . docker run --rm \ -e ANTHROPIC_API_KEY$ANTHROPIC_API_KEY \ -e PROMPTWhat is the Claude Agent SDK? \ research-agent命令要点逐条说明--rm容器在进程退出后自动清理契合跑完即退的无状态语义-e ANTHROPIC_API_KEY接口契约要求的最小环境变量集合之一-e PROMPT本次运行要交给 Agent 的提示词与下面的entrypoint.sh分发逻辑联动。执行链路为entrypoint 未收到serve→python -m hosting.run_once→ hosting/run_once.py 读取$PROMPT缺失时向 stderr 打印错误并返回退出码 2随后调用research_agent.agent.send_query(prompt, model..., display_resultFalse)并把最终结果打印到 stdout、返回 0。与常驻服务相比run_once.py 有两处值得注意的取舍默认模型DEFAULT_MODEL claude-sonnet-4-6与 server.py 一致让测试跑得更便宜可用-e MODELclaude-opus-4-6覆盖以完全对齐 research_agent/agent.py 中 notebook 00 的默认配置其默认是claude-opus-4-6。保留完整工具集run_once.py 沿用了 agent.py 中allowed_tools[WebSearch, Read]的默认工具。这是安全的——调用方掌握$PROMPT容器里也没有其他会话的状态恶意网页结果没有可让Read偷读的敏感目标而常驻服务恰恰因此主动移除了 Read见下文服务模式的说明。混合模式docker compose 拉起 FastAPI 服务并持久化会话临时模式跑完即退无法多轮对话。README 的第二条路径是混合模式Hybrid启动 FastAPI 服务并把宿主机的./sessions挂载到容器/data让对话在容器重启后依然存活。cd claude_agent_sdk/hosting/docker/ docker compose up --build这条命令背后是 docker-compose.yml其设计比表面看起来更讲究逐项拆解services: research-agent: build: # 构建上下文是 claude_agent_sdk/这样 research_agent/ 与 utils/ 都在范围内 context: ../.. dockerfile: hosting/Dockerfile image: research-agent command: [serve] # 覆盖 entrypoint 参数进入服务模式 env_file: - ../.env # 从 hosting/.env 读取 ANTHROPIC_API_KEY 等 ports: # 仅回环地址。服务默认无鉴权默认不暴露到局域网—— # curl localhost:8000 仍然可用。生产部署必须在前方加带鉴权的反向代理。 - 127.0.0.1:8000:8000 volumes: # 让会话记录跨容器重启持久化 - ./sessions:/data配置里隐藏着三个关键工程决策端口只绑回环127.0.0.1:8000:8000意味着服务只在本机可达。原因是 hosting/server.py 明文警告这个服务默认没有任何鉴权它信任一切能摸到 8000 端口的人。因此 README 明确建议生产环境必须在前面加一个能做「调用者鉴权 只转发属于该调用者的 session_id」的网关/反向代理绝不要把 8000 直接暴露到公网。.env注入密钥compose 从../.env即hosting/.env读取环境变量。仓库在 hosting/.env.example 中给出了模板——把ANTHROPIC_API_KEY填成你的真实密钥并另存为.env.env已被 gitignore绝不能把真实密钥提交进仓库可选MODEL覆盖默认模型。会话持久化挂载./sessions:/data把宿主机上hosting/docker/sessions/目录映射到容器内CLAUDE_CONFIG_DIR指向的/data。会话记录包括 SDK 内部会话 ID 映射都落在这里容器删了、重建了记录还在。服务模式下的 Agent 配置与 notebook 00 略有不同体现为 server.py 中_build_options()的几点收紧系统提示复用直接 importresearch_agent.agent的RESEARCH_SYSTEM_PROMPT保证「部署的就是 notebook 00 那个 Agent」工具裁剪为仅WebSearch常驻服务没有上传入口Read唯一能读到的是容器内部——其他会话在/data的对话记录、/proc/self/environ里的 API Key。被注入的恶意网页结果可能诱导 Agent 用Read把这些敏感内容外带所以托管形态下研究 Agent 只有 WebSearch缓冲与请求体上限MAX_BUFFER_SIZE 10 * 1024 * 1024与 notebook 一致MAX_BODY_BYTES 256 * 1024在请求体到达 JSON 解析器之前拦截超大请求仅对带Content-Length的请求生效生产应在网关处再加真上限会话 ID 白名单校验session_id必须匹配^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$。这并非装饰性约束——该 ID 最终会进入持久化映射并参与文件系统查找非法字符会导致路径穿越因此非法 ID 直接返回 400。从另一个 shell 发起多轮对话服务起来后README 建议从另一个终端验证。发送第一轮curl -N -X POST http://localhost:8000/sessions/demo-1/messages \ -H Content-Type: application/json \ -d {prompt:What are the latest AI agent trends?}紧跟一轮追问——Agent 记得第一轮的内容# Follow-up — the agent remembers the first turn: curl -N -X POST http://localhost:8000/sessions/demo-1/messages \ -H Content-Type: application/json \ -d {prompt:Tell me more about the second one.}健康检查刻意不设鉴权供编排器做存活探测curl http://localhost:8000/health接口行为符合 hosting/README.md 定义的接口契约GET /health→200 {status: ok}POST /sessions/{session_id}/messages请求体为{prompt: ...}响应是text/event-streamevent: message携带序列化后的 SDK 消息SystemMessage | AssistantMessage | ResultMessagetype字段标注了消息类名event: done表示本轮结束event: error携带{message: ...}会话已存在则续接不存在则新建流中只含本轮新产生的消息不含历史必填环境变量ANTHROPIC_API_KEY可选MODEL默认claude-sonnet-4-6、CLAUDE_CONFIG_DIR默认/data、AGENT_AUTH_TOKEN。重启后上下文为何还在resume 机制的底层原理README 用一句话描述了验收标准Stop the container,docker compose upagain, send another follow-up — the agent still has context because./sessionspersisted/data。 这背后是 server.py 点明的一个 SDK 设计事实SDK 会自行生成会话 ID调用方无法指定。所以服务端维护了一张小的持久化映射落在CLAUDE_CONFIG_DIR/hosting_session_map.json即挂载卷内的/data/hosting_session_map.json把调用方 URL 里的session_id如demo-1映射到 SDK 内部生成的 ID首轮外部 ID 尚无映射_build_options(resumeNone)开启新会话当消息流中出现带session_id的ResultMessage时_remember()学习这个内部 ID 并原子写入映射先写.tmp再replace后续轮从映射取出 SDK 内部 ID传给ClaudeAgentOptions(resumesdk_session_id)配合钉死的cwd/app与CLAUDE_CONFIG_DIR/dataSDK 便能定位到同一次会话的历史记录。映射文件与会话记录放在同一目录因此它与会话一起跨重启持久化。README 与源码也坦诚指出了两个边界其一同一外部 ID 若并发发起两个首次请求会各自开启全新 SDK 会话并发生「后写覆盖」对 cookbook 这种每会话单调用方的形态没问题生产服务应把「读-建-写」整段加锁Kubernetes 那一层就是用 RedisSET NX规避的其二服务端不做生命周期管理空闲容器由编排器负责回收。安全提示无鉴权服务的正确打开方式混合模式虽方便但必须牢记 hosting/README.md 的红色警告服务默认无鉴权。本地验证时 compose 已把端口绑到127.0.0.1回环地址这本身是一道防线若要跨机器暴露二选一前面架设能鉴权调用者且只转发属于该调用者的 session_id的网关/反向代理三层托管的 Kubernetes 网关即按此契约路由在没有网关的场景下例如 Modal 分发出公网隧道时设置AGENT_AUTH_TOKEN服务端随即要求/sessions/*携带Authorization: Bearer token且用secrets.compare_digest做常数时间比较避免时序侧信道/health保持开放。源码注释强调这只是网关的最小替身而非替代品因为它并不把 session_id 隔离到调用者。小结两种模式如何选维度临时模式Ephemeral混合模式Hybrid启动命令docker run --rm -e PROMPT... research-agentdocker compose up --build进程形态一个进程跑完即退FastAPI SSE 常驻服务会话能力无天然无状态多轮续接重启后上下文仍在典型场景批处理、一次性分析需要与人多轮交互的服务形态数据持久化不适用./sessions:/databind mount安全面调用方自己掌控$PROMPT默认无鉴权端口绑回环 / 需网关或AGENT_AUTH_TOKEN选型逻辑一句话即可概括没有需要恢复的对话就用docker run的临时模式把容器当一次性的函数调用需要对话在容器重启间存活、或要对外提供接口就切到docker compose的混合模式并把/data的持久化挂在显眼的位置。想继续往规模化演进可以接着阅读同一托管体系中基于 Modal 的 Tier 2 与基于 Kubernetes 的 Tier 3两者复用本层的同一镜像与同一接口契约只是把会话持久化从本机 bind mount 换成了 Volume 与 PVC。【免费下载链接】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),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。