Claude智能体系列第 20 / 22 篇

托管智能体的环境和数据:沙箱网络、文件和仓库、凭据、记忆、成果、预算和定时运行

托管智能体跑起来以后要解决的事:沙箱能访问哪些网站、文件和代码怎么放进去、第三方服务的密钥怎么不让它看到、怎么跨会话记东西、怎么定义做完的标准、怎么给花费封顶、怎么定时跑。按官方文档整理,全文未实测。

发布 · 约 14 分钟读完 · 经人工审核()· 本站的内容怎么来的

本文根据公开分享整理,本站尚未实测。操作前请对照文末的原始来源。

本页目录(12 节)
  1. 一、环境:沙箱的网络和预装的包
  2. 二、把文件和代码放进去,把产出取回来
  3. 三、凭据保险库:让它用密钥,但看不到密钥
  4. 四、接 MCP 服务器
  5. 五、挂 skill
  6. 六、记忆库:跨会话记东西
  7. 七、成果:告诉它做到什么程度算完
  8. 八、预算:给一个会话的花费封顶
  9. 九、事后查:它干了什么,花了多少
  10. 十、webhook:不用轮询
  11. 十一、定时部署
  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 的配置分成两步,目的是让密钥不进智能体的定义:

  1. 智能体上声明服务器的名字和地址,并在 tools 里放一个对应的 mcp_toolset。两边必须一一对应,否则请求被拒。一个智能体最多 20 个服务器。
  2. 建会话时通过保险库提供认证。
  • 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 隧道是研究预览,需要单独申请,本文只做了简介。
  • 控制台的会话查看器没有看过,是按文档的描述写的。

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章