托管智能体的环境和数据:沙箱网络、文件和仓库、凭据、记忆、成果、预算和定时运行
托管智能体跑起来以后要解决的事:沙箱能访问哪些网站、文件和代码怎么放进去、第三方服务的密钥怎么不让它看到、怎么跨会话记东西、怎么定义做完的标准、怎么给花费封顶、怎么定时跑。按官方文档整理,全文未实测。
本文根据公开分享整理,本站尚未实测。操作前请对照文末的原始来源。
本页目录(12 节)
上一篇《托管智能体入门》讲了怎么建智能体、开会话、收发事件。这一篇讲它真正干活时绕不开的十件事,可以当手册按需查。
全文按官方文档整理,没有实测,原因同上一篇:这组接口只在官方平台上有,中转站返回 404。示例代码来自官方文档,本站没有运行过。
| 你想做的事 | 看哪一节 |
|---|---|
| 控制沙箱能访问哪些网站、预装哪些包 | 一 |
| 把文件、代码仓库放进沙箱,把产出取回来 | 二 |
| 让它调第三方服务,但不让它看到密钥 | 三 |
| 接 MCP 服务器、挂 skill | 四、五 |
| 让它记住上一次会话的东西 | 六 |
| 告诉它“做到什么程度算完” | 七 |
| 给一个会话的花费封顶 | 八 |
| 事后查它干了什么、花了多少 | 九 |
| 不轮询也能知道会话状态变了 | 十 |
| 定时自动跑 | 十一 |
一、环境:沙箱的网络和预装的包
环境是一份沙箱的配置,建一次,多个会话共用。每个会话拿到的是自己独立的沙箱,一个全新的 Linux 容器,会话之间互不相通。
网络
| 模式 | 含义 |
|---|---|
limited |
只能访问 allowed_hosts 里列出的主机 |
unrestricted |
可以访问任何主机,只有一份通用的安全黑名单拦着 |
一个容易踩的默认值:用接口建环境时不写 networking,得到的是 unrestricted。 控制台的表单则默认选 limited。文档的建议是始终显式写上,并且用 limited。
environment = client.beta.environments.create(
name="api-access",
config={"type": "cloud",
"networking": {"type": "limited",
"allowed_hosts": ["api.example.com"],
"allow_mcp_servers": True,
"allow_package_managers": True}},
)
limited 下的规则:
allowed_hosts写主机名,不带协议、端口和路径。example.com只匹配它自己,不匹配www.example.com;要匹配所有子域名写*.example.com。- 什么都不列时,一个主机都访问不了。挂载的文件、记忆库、代码仓库不受影响。
allow_package_managers打开后,可以访问一批公开的包仓库和代码托管站(npm、PyPI、crates.io、GitHub 等,名单由 Anthropic 维护)。环境里配了预装包就必须打开它。allow_mcp_servers打开后,可以访问智能体上声明的 MCP 服务器。不打开的话,智能体声明了不在allowed_hosts里的 MCP 服务器,建会话会直接失败。
文档对 unrestricted 的风险讲得很具体,值得原样记下:
bash能发任何请求,智能体可以在外部网站上改东西,不只是读。- 内置工具默认不经确认就执行,所以这些请求默认没有人拦。
- 沙箱里的任何东西都可能被带出去,包括你放进去的密钥。
- 它读到的网页和接口返回里可能有诱导它的指令。
- 模型的行为不是安全措施。 一个网站拦了它,它可能换个办法再试。
- 安全黑名单不是白名单。
还有一条对 limited 也成立:放行是按主机的,不是按操作的。 放行了 GitHub,沙箱就能往那里推代码、发包。
预装包和沙箱的规格
环境的 packages 字段可以按包管理器列出要预装的包(apt、cargo、gem、go、npm、pip),装好的结果在共用这个环境的会话之间缓存。
云端沙箱本身已经带了不少东西:
| 项目 | 内容 |
|---|---|
| 系统 | Ubuntu 24.04,x86_64,最多 8 GB 内存、10 GB 磁盘 |
| 语言 | Python 3.10 到 3.13、Node.js 20 到 22、Go、Rust、Java 21、Ruby、PHP 8.3、C/C++ |
| 数据库 | PostgreSQL 16 和 Redis 7 装了但默认没启动;SQLite 可用 |
| 工具 | git、curl、jq、tmux、ripgrep、ffmpeg、ImageMagick、pandoc、LibreOffice、tesseract、TeX Live |
| 浏览器自动化 | Playwright 和 Chromium |
二、把文件和代码放进去,把产出取回来
文件:先用 Files API 上传,再在建会话时列进 resources:
session = client.beta.sessions.create(
agent=agent.id, environment_id=environment.id,
resources=[{"type": "file", "file_id": file.id, "mount_path": "/data.csv"}],
)
- 上面这个文件在沙箱里的位置是
/mnt/session/uploads/data.csv。 - 挂进去的是只读的副本,智能体改不了你上传的原件。
- 一个会话最多 500 个文件。会话运行中也可以增删。
- 产出要让它写到
/mnt/session/outputs/。 写在这里的文件可以通过 Files API 按会话列出来、下载。会话刚空闲时可能要等几秒才列得到。
代码仓库:同样放在 resources 里,类型是 github_repository,要给仓库的 HTTPS 地址和一个用来克隆的令牌,可以指定分支或提交。默认克隆到 /workspace/<仓库名>。仓库有缓存,之后用同一个仓库的会话启动更快。
- 令牌用细粒度的个人访问令牌,只给必需的权限。它不会在接口的响应里回显。
- 要让它开 PR,再给智能体接上 GitHub 的 MCP 服务器。
- 挂载仓库时,仓库根目录
.claude/skills下的 skill 会被自动加载,见第五节。
三、凭据保险库:让它用密钥,但看不到密钥
保险库(vault)解决的问题是:智能体要代表某个最终用户去调第三方服务,凭据不应该写在可复用的智能体定义里,也不该每次请求都由你传一遍。
做法是:给每个最终用户建一个保险库,把凭据登记进去,建会话时用 vault_ids 引用。凭据的值只能写入,接口永远不会读回来。
凭据有三种:
| 类型 | 用在哪 | 说明 |
|---|---|---|
mcp_oauth |
用 OAuth 的 MCP 服务器 | 给了刷新用的信息,Anthropic 会在令牌过期时替你刷新 |
static_bearer |
接受固定令牌的 MCP 服务器 | 不用刷新 |
environment_variable |
沙箱里的命令行工具、SDK、直接调接口 | 见下 |
前两种按 MCP 服务器的地址匹配:智能体连到那个地址时,令牌自动带上。
第三种的设计值得细看。沙箱里的环境变量放的不是真密钥,而是一个不透明的占位符;请求从沙箱出去的那一刻,占位符才被换成真值。 智能体自始至终看不到密钥。
client.beta.vaults.credentials.create(
vault_id=vault.id,
display_name="Notion API key for sandbox",
auth={"type": "environment_variable",
"secret_name": "NOTION_API_KEY",
"secret_value": "ntn_your-secret-here",
"networking": {"type": "limited", "allowed_hosts": ["api.notion.com"]}, # 只对这些主机替换
"injection_location": {"header": True}}, # 只在请求头里替换
)
由此带来几个必须知道的后果:
- 在本地拿密钥做计算的客户端用不了。 比如要用密钥算请求签名的(文档举的例子是 AWS 的签名方式),算出来的是占位符的签名。启动时校验密钥格式的客户端也可能直接拒绝。
- 替换只发生在出去的方向。 用存的密钥去换一个会话令牌,换回来的令牌会原样进到沙箱里。这类流程自己在外面换好,把结果存进保险库。
- 凭据里的
allowed_hosts管的是“对哪些主机替换”,不是“允许访问哪些主机”。要真的访问得到,环境那一层也得放行。 - 占位符出现在没开替换的位置(比如只开了请求头,密钥却在请求体里),会被原样发给对方,对方报认证错误。控制台里建的凭据默认只开请求头。
- 凭据在登记时不校验,到会话运行时才知道对不对。
- 给密钥本身最小的权限,智能体能做这把密钥允许的一切。
限制:一个保险库最多 20 条凭据;保险库和凭据属于工作区,同一个工作区的任何 key 都能在建会话时引用。
四、接 MCP 服务器
MCP 的配置分成两步,目的是让密钥不进智能体的定义:
- 智能体上声明服务器的名字和地址,并在
tools里放一个对应的mcp_toolset。两边必须一一对应,否则请求被拒。一个智能体最多 20 个服务器。 - 建会话时通过保险库提供认证。
- MCP 工具集的默认权限策略是
always_ask,每次调用都要你批准。信得过的服务器可以改成always_allow。 - 服务器暴露的工具很多、只用得上几个时,把默认设成全关,再逐个打开。这样服务器的运营方新加的工具不会自动生效。
- 支持的是提供 HTTP 端点的远程服务器。只在你内网里的服务器要通过 MCP 隧道(研究预览,要申请)或者第三篇讲的自托管办法。
五、挂 skill
两种办法:
- 列在智能体的
skills里。 可以是 Anthropic 现成的四个文档 skill,也可以是你上传到工作区的。不写版本默认用最新的。 - 放在挂载的仓库里。 仓库根目录下
.claude/skills/<名字>/SKILL.md这个位置的 skill,会在会话开始时自动加载,不用上传,也不用列在智能体上。
一个会话最多 500 个 skill,但挂得越多,沙箱启动越慢,只挂用得上的。
第二种办法有一个安全上的含义,文档单独强调了:仓库里的 skill 是给智能体的指令,所以挂载的仓库是信任边界的一部分。 任何能往仓库提交的人(一个被合并的外部 PR、一个被攻破的依赖)都能加或改 skill,平台在会话开始时不经审查就加载。
skill 怎么上传、怎么写,见《在自己的程序里用 Agent Skills》。
六、记忆库:跨会话记东西
默认每个会话都从零开始。记忆库(memory store)是工作区里的一组文本文档,挂到会话上以后在沙箱里是一个目录(/mnt/memory/<名字>/),智能体用平常的读写文件工具去用它。
store = client.beta.memory_stores.create(
name="User Preferences",
description="Per-user preferences and project context.", # 这句会给智能体看
)
session = client.beta.sessions.create(
agent=agent.id, environment_id=environment.id,
resources=[{"type": "memory_store", "memory_store_id": store.id,
"access": "read_write",
"instructions": "User preferences and project context. Check before starting any task."}],
)
| 项目 | 规则 |
|---|---|
| 什么时候挂 | 只能在建会话时挂,运行中不能增删 |
| 一个会话挂几个 | 最多 8 个 |
| 访问方式 | 默认 read_write,可以设 read_only |
| 单条记忆 | 最大 100 kB(约 2.5 万 token) |
| 一个库 | 最多 1 万条。文档建议拆成很多小文件,不要几个大文件 |
每次改动都会留下一个不可变的版本,可以查是哪个会话改的、改之前是什么。没有专门的回滚接口,要回滚就把旧版本的内容写回去。旧版本保留 30 天,还在的记忆最近的几个版本会一直留着;要留得更久,自己用接口导出。
你也可以直接用接口或在控制台里读写记忆:预先灌入参考资料、纠正写错的记忆、导出。
安全:读写的记忆库是一个注入的落脚点。智能体处理了不可信的内容(用户的输入、抓来的网页),被诱导着往记忆里写了东西,之后的会话会把它当成可信的记忆读出来。共享的参考资料用 read_only 挂,和每个会话自己可写的库分开。
多个库的常见分法:一份只读的公共规范挂给所有会话;每个最终用户、团队或项目各一个库,共用同一个智能体配置。
Dreams:让 Claude 整理记忆
记忆是一次次增量写进去的,时间长了会有重复、矛盾和过时的条目。Dream 是一个异步任务:读一个已有的记忆库,加上 1 到 100 个过去的会话记录,产出一个新的、整理过的记忆库:重复的合并,过时的换成最新的,从会话里提炼出新的认识。
- 输入的库不会被改动,产出不满意可以直接丢掉。
- 可以用
instructions指方向,比如“重点整理代码风格方面的偏好”。 - 一次要几分钟到几小时,按所选模型的 token 价格计费,费用大致和输入会话的数量、长度成正比。先用少量会话试。
- 这是研究预览,要单独申请,还要另一个 beta 头。
七、成果:告诉它做到什么程度算完
普通的会话里,智能体自己觉得做完了就停。定义一个成果(outcome)以后,平台会另起一个评分者,在独立的上下文里按你给的评分标准检查产出;不达标就把意见交还给智能体,让它再改,如此循环。
client.beta.sessions.events.send(session_id=session.id, events=[{
"type": "user.define_outcome",
"description": "Build a DCF model for Costco in .xlsx",
"rubric": {"type": "text", "content": RUBRIC}, # 也可以引用一个上传好的文件
"max_iterations": 5, # 可选,默认 3,最多 20
}])
- 评分标准必须给,是一份 Markdown 文档。写成一条条能判定的标准,比如“CSV 里有一列价格,值都是数字”,不要写“数据看起来不错”。评分者逐条独立打分,含糊的标准只会得到摇摆的结果。
- 手头没有标准,可以给 Claude 一份已知的好样例,让它分析好在哪,再把分析改写成标准。
- 发出这个事件,智能体就开始干活,不用再发用户消息。一次只能有一个成果,可以一个接一个。
每轮评分结束时的结果:
| 结果 | 接下来 |
|---|---|
satisfied |
达标,会话空闲 |
needs_revision |
没达标,开始下一轮修改 |
max_iterations_reached |
轮数用完,再说一句收尾的话后空闲 |
failed |
标准不适用于产出,比如描述和标准自相矛盾 |
interrupted |
你中途打断了 |
评分者内部怎么想的你看不到,只能看到它在工作,以及每轮的结论和意见。
八、预算:给一个会话的花费封顶
建会话时带一个 budget,花费到了就停:
session = client.beta.sessions.create(
agent=agent.id, environment_id=environment.id,
budget={"type": "limit", "max_list_cost": {"amount": "125", "currency": "USD"}}, # 1.25 美元
)
amount 是一个字符串,单位是美分,必须是整数。
它衡量的是“标价花费”,由三部分组成:
| 组成 | 价格 |
|---|---|
| 模型的 token | 各模型的公开标价 |
| 联网搜索 | 每 1,000 次 10 美元 |
| 会话的运行时长 | 每小时 0.08 美元 |
要点:
- 按标价算,不按你的合同价算。 有折扣的组织,实际账单可能低于预算。
- 预算只能在建会话时设。 没设预算的会话事后加不上。设了的可以改(新值必须高于已经花掉的)或去掉;去掉是单向的,去掉以后不能再加。
- 检查发生在两次模型请求之间。 越线的那一次请求会跑完,所以实际花费可能略超。
- 到了预算,会话是空闲(原因是
budget_reached),不是终止,历史和沙箱都在。这时只接受“把已经在进行的事了结”的事件(工具结果、确认、打断),新派活的消息会被拒。把预算调高或去掉,被暂停的工作自动继续。 - 多个智能体协作的会话共用一份预算。
- 用了没有公开标价的模型,带预算的会话建不出来。
九、事后查:它干了什么,花了多少
控制台里有一个会话查看器(开发者和管理员角色可见),不用写代码就能看:
- 一条可缩放的时间线,多智能体的会话每个线程一条;
- 按模型请求分组的对话记录,包括思考、工具调用的输入和结果;
- 一个检查面板:会话的花费曲线和预算使用情况、每个原始事件的 JSON、每个工具的调用次数和失败次数、挂载的资源和这个会话对记忆做的改动、每个线程的上下文大小和花费。
用程序查的话,会话对象上有一个 usage 字段:没走缓存的输入、输出、缓存读、缓存写(按 5 分钟和 1 小时分开)、标价花费。等会话空闲后再取,拿到的是最新的累计值。事件流里也有 session.usage 事件给出同样的快照。
十、webhook:不用轮询
会话可能跑很久,一直连着事件流不现实。在控制台里登记一个 webhook 地址,状态有大的变化时平台来通知你。
- 通知里只有事件的类型和对象的 ID,没有完整的对象。 收到以后自己去取。这样重试时不会送来过期的数据。
- 事件的名字和事件流里的不完全一样。 事件流里是
session.status_idle,webhook 里是session.status_idled。 - 覆盖的范围:会话的状态变化、到达预算、成果评分结束,以及智能体、环境、保险库、记忆库、部署这些资源的创建、更新、归档、删除。
接收端要做对的几件事:
| 事情 | 规则 |
|---|---|
| 地址 | HTTPS、443 端口、公网可解析 |
| 验签 | 用 SDK 的 unwrap(),签名不对或超过 5 分钟就抛异常。签名密钥只在创建时显示一次 |
| 应答 | 返回任意 2xx 表示收到 |
| 去重 | 同一个事件可能送来多次,按事件的 id 去重 |
| 顺序 | 不保证按发生的先后送达。以取回来的对象的当前状态为准 |
| 重试 | 最多三次,间隔 5 到 120 秒 |
| 重定向 | 返回 3xx 会让这个地址立刻被停用,平台从不跟随重定向 |
| 补发 | 没有。订阅之前、停用期间发生的事件不会补 |
十一、定时部署
部署(deployment)让一个智能体按计划自己开会话。它就是“建会话需要的全部配置”加一个时间表:
deployment = client.beta.deployments.create(
name="Weekly compliance scan",
agent=agent.id,
environment_id=environment.id,
initial_events=[{"type": "user.message",
"content": [{"type": "text", "text": "Run the weekly compliance scan."}]}],
schedule={"type": "cron", "expression": "0 20 * * 5", "timezone": "America/New_York"},
)
- 时间表是标准的五段 cron 表达式加一个时区,最细到分钟。
- 实际触发时间有抖动,最多是两次运行间隔的 15%,最少 5 秒,最多 9 分钟。不要指望分秒不差。
- 按当地的钟面时间匹配。夏令时切换那天,不存在的时刻不触发,重复的时刻触发两次。要避免就别排在当地凌晨 1 点到 3 点,或者用 UTC。
- 可以给部署设预算,它会被复制到每一次运行的会话上,限制的是每次运行,不是累计。
- 每次触发都留一条运行记录,成功的带着会话 ID,失败的带着原因(环境被归档、智能体被归档、建会话被限流等)。被限流的那次不重试,等下一个时间点。
- 暂停以后不再按计划触发,已经在跑的会话不受影响,错过的不补。归档是终止性的。
- 可以手动触发一次,用来在上计划之前先试。
- 每个组织最多 1,000 个定时部署。
和 Claude Code 的例程是两回事:例程跑在 Claude Code 网页版上、算订阅用量,见《团队用 Claude API 之前要管好的事》第十节;这里的部署跑的是托管智能体,算平台用量。
没有实测的部分
- 全文没有实测。 托管智能体只在官方平台上有,本站用的中转站对这组接口返回 404。
- 示例代码来自官方文档,本站没有运行过。
- 价格和各项上限(预算的三项价格、文件数、记忆条数、部署数等)是文档当前的数字,这是 beta 功能,可能调整。
- Dreams 和 MCP 隧道是研究预览,需要单独申请,本文只做了简介。
- 控制台的会话查看器没有看过,是按文档的描述写的。
来源
- Cloud environment setup(Claude 开发平台文档)
- Cloud sandbox reference(Claude 开发平台文档)
- Adding files(Claude 开发平台文档)
- Accessing GitHub(Claude 开发平台文档)
- Authenticate with vaults(Claude 开发平台文档)
- MCP connector(Claude 开发平台文档)
- Skills(Claude 开发平台文档)
- Using agent memory(Claude 开发平台文档)
- Dreams(Claude 开发平台文档)
- Define outcomes(Claude 开发平台文档)
- Session budgets(Claude 开发平台文档)
- Inspect sessions and track usage(Claude 开发平台文档)
- Subscribe to webhooks(Claude 开发平台文档)
- Scheduled deployments(Claude 开发平台文档)