托管智能体进阶:多个智能体协作、动态工作流,以及把沙箱放在自己的机器上
一个智能体忙不过来时,可以让它把活派给子智能体,或者自己写一段工作流让几百个智能体在后台跑;数据不能出内网时,可以把工具的执行搬到自己的机器上。本文按官方文档整理这两部分的用法、上限和安全分工,全文未实测。
本文根据公开分享整理,本站尚未实测。操作前请对照文末的原始来源。
本页目录(8 节)
这是托管智能体三篇里的最后一篇,讲两件进阶的事:
- 一个智能体干不过来:要审三百份合同、要同时查很多个来源。怎么让多个智能体一起干?
- 数据不能离开自己的网络:智能体要碰内网的服务和数据。怎么把执行放在自己的机器上?
全文按官方文档整理,没有实测:这组接口只在官方平台上有,中转站返回 404。示例代码来自官方文档,本站没有运行过。
一、把活交出去的三种办法
会话运行的那个智能体,可以用三种方式借助别的智能体或模型。都在智能体定义的 multiagent 字段里设置:
| 办法 | 发生了什么 | 什么时候用 |
|---|---|---|
| 子智能体 | 它自己把任务派给子智能体,读每个子智能体的汇报,再决定下一步 | 需要和子智能体来回沟通;需要你指定的、各有自己提示词和工具的专门角色 |
| 动态工作流 | 它写一段程序,由服务端在后台运行。程序分阶段启动很多智能体,把结果汇总 | 大多数需要不止一个智能体的活:量大、可以拆、可以并行 |
| 顾问 | 它自己干活,在关键时刻向一个更强的模型请教 | 一个智能体就够,只是在定方案、收尾检查时要更好的判断 |
把 multiagent 的类型设成 multiagent_20261001 后,三样可以同时用,各自能开能关。默认是子智能体开、动态工作流开、顾问关。
三种模式都好在哪:并行(互不相干的子任务同时做)、分工(安全审查、写文档各有专门的提示词和工具,而不是把所有能力塞给一个智能体)、升级(难的那一小部分交给更强的模型)。但每多一个智能体就多一份 token,官方另有一篇文章专门讲什么时候不该用多智能体。
二、子智能体
预定义的和临时的
每个被派活的智能体是两种之一:
- 预定义的:你事先建好、列在
multiagent里的智能体。它用自己的配置:自己的模型、提示词、工具、MCP 服务器、skill。 - 临时的:没有保存的智能体,由派活的那一方当场定义。它沿用派活方的模型、工具、MCP 服务器和 skill。
lead_agent = client.beta.agents.create(
name="Engineering Lead",
model="claude-opus-5-5",
system="You coordinate engineering work. Delegate code review to the reviewer agent "
"and test writing to the test writer agent.",
tools=[{"type": "agent_toolset_20260401"}],
multiagent={
"type": "multiagent_20261001",
"subagents": {
"type": "enabled",
"predefined_agents": [
{"type": "agent", "id": reviewer_agent.id},
{"type": "agent", "id": test_writer_agent.id},
],
},
},
)
规则:
- 只有一层。 被列出来的智能体自己不能再有
multiagent设置,否则请求返回 400。 - 版本是钉住的。 列表里的智能体在保存的那一刻就定在当时的版本,之后它们更新了也不会自动跟上。
- 每个列表最多 20 个不同的智能体。同一个可以被叫起多份。
- 列表里可以写
{"type": "self"},表示它自己的副本。 - 临时智能体默认是允许的。只想让它用你列出来的那些,把
inline_agents设成关闭。 - 所有相关的智能体要么都不指定推理地区,要么指定同一个。
线程:每个智能体一条
所有智能体共用同一个沙箱、同一套文件、同一批保险库凭据,但每个智能体在自己的线程里运行,各有各的对话历史,上下文互不相通。
- 主线程就是会话一级的事件流。上面是所有活动的浓缩版:看得到子智能体开始和结束、它们之间传的消息,看不到子智能体内部的每一步。
- 要看某个子智能体具体做了什么,去读或者订阅它自己线程的事件流。
- 子智能体的线程是持续的。主智能体可以回头给之前叫过的子智能体追加消息,它还记得之前的事。
- 只要有一条线程在运行,整个会话的状态就是运行中。
- 一个会话同时最多 25 条子线程,空闲的也算,归档了才腾出位置。顾问和工作流的线程不占这个名额。
两件和安全、权限有关的事:
- 子智能体等你回应的调用,会转发到主线程上。 需要确认的工具调用、自定义工具调用,都在主线程的事件流里出现,带着来源线程的 ID。你照常回应,服务端自己会送到对的线程。不用等会话空闲,事件一到就可以回。
- MCP 服务器按智能体配,凭据按会话配。 只想让某个角色碰 GitHub,就只在它的定义里声明那个服务器。但临时智能体沿用派活方的全部配置,限制不了;要严格限制,就关掉临时智能体。
三、动态工作流
子智能体的模式里,每一步都要主智能体自己读汇报、做决定。活的规模一大(文档的例子是审三百份合同),这样太慢,上下文也装不下。
动态工作流换了一个办法:主智能体写一段程序,程序自己去启动智能体、收结果、决定下一步,主智能体不再逐步参与。服务端在后台把这段程序跑成一次“工作流运行”。
程序能做的事:
- 同时跑很多智能体(扇出)。
- 把一个智能体的结果传给另一个。 结果回到程序手里,不经过主智能体。
- 重复和分支。 比如一份草稿反复改,直到复查通过或者用完规定的轮数。
- 处理失败。 某个智能体失败了,程序可以兜住,也可以让整次运行结束。
运行可以分阶段,比如“读合同”“核对结论”,你靠阶段事件跟进度。运行结束后,主智能体得到一轮机会去读结果,然后回答你,或者再开一次运行。
怎么让它开一次运行
没有专门的接口。 你在用户消息里描述活,由智能体自己决定要不要、什么时候开一次运行。你能做的是引导:在系统提示词里写清楚哪类任务该用运行、哪类小事自己做。文档给的几个方向:
- 怎么拆:“一份合同一个智能体。再让第二个智能体把每一份都复查一遍,不只是复查有发现的那些。”
- 失败了怎么办:“一个智能体失败不能让整次运行失败。读不了的合同列为未覆盖。”
- 时间上限:“给这次运行一小时的时限。”
运行只用在真正需要的任务上,因为运行里的每个智能体都在花 token。
跟着一次运行走
运行的事件出现在会话的事件流里:
| 事件 | 含义 |
|---|---|
workflow_run.created |
开了一次运行。带着运行的 ID、名字、说明,和它声明的各个阶段 |
workflow_run.status_running |
开始执行,或者暂停后恢复 |
workflow_run.status_idle |
暂停了,比如到了会话的预算 |
workflow_run.phase_started、phase_ended |
进入、离开一个阶段 |
workflow_run.status_ended |
结束了,带着结果 |
workflow_run.error |
运行出错,或者启动被服务端拒绝 |
结束时的结果:
| 结果 | 含义 |
|---|---|
completed |
程序跑完了 |
stopped |
智能体把它停了,或者会话被归档 |
出错:timeout_error |
到了运行的时限 |
出错:program_error |
程序本身失败,或者某条线程失败而程序没兜住 |
出错:thread_limit_error |
启动的智能体超过了上限 |
completed 只说明程序跑完了,不说明活干成了。 运行可以在线程上的工作失败、甚至线程没建起来的情况下以 completed 结束。要知道有没有失败的部分,得去读各条线程的事件。
怎么算“做完了”
这是用工作流时最容易判断错的地方。运行期间,会话可能一直是运行中,也可能因为运行被暂停而空闲。会话空闲不等于活做完了。 文档给的判断是两条同时成立:
- 你见过的每一次运行,都已经收到了它的
workflow_run.status_ended。 - 在那之后,收到一个原因是
end_turn的会话空闲事件,并且不是你自己打断造成的。
还要留意:智能体读完结果可能再开一次运行,所以要重新检查。
打断、限制和上限
- 打断不会结束任何运行。 发
user.interrupt只停下主智能体当前这一轮。要停掉运行,发一条用户消息请它停;要继续,发一条消息请它继续。 - 运行开着的时候,不能改会话的智能体配置(返回 400,错误码是
workflow_run_open);归档和删除会话也可能被拒。 - 运行里的线程也会来找你要工具结果或确认,照常在主线程上回应。
- 只有主线程上的智能体能开运行,运行里的智能体不能再开,运行不嵌套。
- 你看不到程序的代码。运行结束后可以发消息让智能体把它写的工作流原样打出来。
| 上限 | 数值 |
|---|---|
| 一次运行里同时工作的线程 | 64 |
| 一次运行从头到尾启动的智能体 | 1,000 |
| 一次运行的时限 | 默认 24 小时,智能体可以设得更短 |
| 一个会话同时开着的运行 | 默认 10 |
费用和限流:
- 运行没有单独的价格。里面的 token 和会话别的 token 一样计费,算进会话的预算。到了预算,所有开着的运行暂停;每条正在工作的线程会把手头那次请求跑完,所以可能超出一点。
- 运行里的模型请求占用你组织的消息接口限流额度,和你别的流量算在一起。一次扇出几十个智能体,要给它留余量。
- 服务端可能为同一件活建不止一条线程,所以文档提醒:让智能体调的工具做到调用两次也安全。
四、顾问
给会话配一个顾问模型,主线程可以在一轮当中向它请教:定方案、卡住了、收尾前检查。
multiagent={"type": "multiagent_20261001",
"advisor": {"type": "enabled", "model": "claude-opus-5-5"}}
- 只有主线程能请教,子智能体不能。它不是子智能体,不占线程名额。
- 顾问模型不能比智能体自己的模型弱,同档可以。不合规的搭配在保存智能体时就会被拒。
- 建议通过线程事件送达。你的程序看不看得到建议的原文,取决于顾问模型:有的返回明文,有的返回一个“已隐去”的占位。
- 请教失败或被打断不会让智能体这一轮失败,它会在收到一个“请教失败”的提示后接着干。
- 上面的例子只开了顾问,子智能体和工作流仍然是默认的开启状态。要一个“只请教、活全自己干”的智能体,把那两样显式关掉。
消息接口上也有顾问,是一个服务器端工具,见《服务器端工具》第六节。两处的原理相同,配置的字段和结果送达的方式不同。
五、自托管沙箱:执行放在你的机器上
默认情况下,工具在 Anthropic 托管的云端沙箱里执行。自托管沙箱把这一部分搬到你控制的机器上:循环和模型仍然在 Anthropic 那边,工具的执行、文件、进程、出网流量留在你这边。
适合的场合:数据不能出你的网络边界;要访问公网到不了的内部服务;要在你们自己的合规和审计之下运行。
它是怎么工作的
一个 self_hosted 类型的环境就是一个工作队列。你在自己的机器上跑一个 worker 进程来消费它:
- 你建一个指向这个环境的会话,Anthropic 把它放进队列。
- 你的 worker 领走这个任务,下载智能体的 skill 和会话挂的记忆库。
- Claude 在 Anthropic 那边运行,发出工具调用;worker 在本地执行每一个调用,把结果送回去。
要想清楚的一点:工具的输入和输出仍然会经过 Anthropic,模型得看到结果才能决定下一步。留在你这边的是执行本身、文件和网络访问,不是全部数据。
| 云端环境 | 自托管沙箱 | |
|---|---|---|
| 工具在哪执行 | Anthropic 的沙箱 | 你的机器 |
| 能访问什么网络 | Anthropic 的出网控制 | 你的网络策略 |
| 挂文件和代码仓库 | Anthropic 负责 | 不支持,你自己放 |
| 记忆库 | 直接挂载 | worker 下载到本地再同步回去 |
| 生命周期 | Anthropic 管 | 你管 |
自托管的会话只能挂记忆库,带文件或仓库资源的会话会被直接拒绝。要给会话准备文件,文档的办法是:把文件的位置(比如对象存储的路径、提交的哈希)写进会话的 metadata,由你的启动脚本读出来、提前放进工作目录。
跑起第一个 worker
- 建一个
self_hosted环境(接口或控制台都行)。 - 在控制台里给它生成一把环境密钥。这把密钥只能在控制台生成,它只管这一个环境的队列。
- 在 worker 的机器上设好环境 ID 和环境密钥,启动 worker:
sudo mkdir -p /workspace && sudo chown "$USER" /workspace
ant beta:worker poll --workdir /workspace
- 在你自己的机器上建会话、发任务。没有 worker 在线时,会话会在队列里等着,不会失败。
worker 的机器只需要有 /bin/bash 和出站的 HTTPS。
一条反复出现的提醒:不要把你的 API key 放在 worker 的机器上。 智能体的工具调用就在那台机器上执行,能读到环境变量。worker 用环境密钥就够了;建会话、看队列这些操作在你自己的机器上用 API key 做。
两个要做的选择
怎么领任务:
- 常驻:一个长期运行的进程不停轮询。最简单,只要出站 HTTPS。
- 由 webhook 触发:收到会话开始运行的通知才起来干活。省掉一个空转的轮询进程,但要有一个 Anthropic 访问得到的 webhook 地址。
每个会话在哪跑:
- 同一个进程里:领任务的 worker 自己执行工具调用,所有会话共用一个工作目录。
- 每个会话一个沙箱:轮询的进程为每个领到的会话起一个全新的沙箱。隔离更强:独立的文件系统、资源限制、各自的网络控制。
官方提供了现成的 worker:命令行工具 ant 里一个,Python、TypeScript、Go 的 SDK 里各一个。能力有差别:
| 能力 | ant 命令行 |
SDK |
|---|---|---|
| 常驻轮询 | 有 | 有 |
| webhook 触发 | 没有 | 有 |
| 每个会话一个沙箱 | 有 | 有 |
| 记忆库 | 有,同步设置用默认值 | 有,可以调 |
| 自定义工具 | 没有 | 有 |
也可以直接调队列的接口自己写一个。文档另外列了十来个沙箱平台各自的接入指南。
在 worker 里提供自定义工具
自定义工具本来是“你的程序执行”的工具。在自托管的场景里,worker 可以就是那个程序:它在你的沙箱里运行,够得着内网的服务和凭据。这要用 SDK 的 worker,命令行的那个不支持。
由此有一个很实用的做法:把只在内网里的 MCP 服务器包成自定义工具。 平台的 MCP 连接是从 Anthropic 那边连出去的,服务器必须公网可达(或者走隧道)。换一个方向:让 worker 去当 MCP 的客户端,把服务器上的工具逐个声明成智能体的自定义工具。模型调用时,worker 把请求转给内网的服务器,再把结果送回去。服务器不需要任何入站的连接。
如果智能体上声明了一个自定义工具,却没有任何 worker 或客户端来执行它,会话会一直停在等待你回应的状态。
记忆库在自托管下
用法和云端一样,区别是谁来落地:worker 把每个记忆库下载到 /mnt/memory/ 下的一个目录,把智能体的改动同步回去。默认最多每 15 秒同步一次,会话结束时再同步一次。
要求:ant 1.33.0 以上或 SDK 的 worker;POSIX 文件系统(不支持 Windows);/mnt/memory 目录存在并且 worker 的用户可写。
只读挂载的记忆库,保护的是“不会被传回去”,不是“本地改不了”:worker 的写和编辑工具会拒绝,但沙箱里的别的进程(比如一条 bash 命令)仍然可能改到本地那份副本,只是不会同步。
看队列,停任务
| 字段 | 含义 | 用来 |
|---|---|---|
depth |
等着被领的任务数 | 决定要不要加 worker,或者对积压告警 |
pending |
被领走但还没确认的任务数,正常时接近零 | 发现卡在半路的 worker |
oldest_queued_at |
队列里最老的任务的时间 | 看最久等了多长 |
workers_polling |
最近 30 秒里轮询过的 worker 数 | 存活告警 |
停一个任务有两种:默认是优雅地停,worker 在下一次心跳时发现,取消正在跑的工具调用再确认;加 force 是立刻标记为已停。停 worker 要让它收到终止信号后正常退出;直接杀掉的进程什么收尾都不做。
六、自托管时,安全上谁负责什么
Anthropic 负责控制面:会话和队列的完整性、多租户隔离。沙箱以内都是你的事:
- 镜像和运行时的加固。 Anthropic 不检查你的镜像。去掉不需要的权限、用非 root 用户、根文件系统只读。
- 出网控制。 由你的防火墙和网络规则决定。不限制的话,一次被攻破的工具执行可以连到任意外部主机。
- 环境密钥的存放和轮换。 放进密钥管理服务,不要写进环境文件或镜像。怀疑泄露立刻吊销重发,吊销在 worker 下一次请求时就生效。
- 隔离不可信的工作负载。 环境密钥管的是一整个环境的队列。跑不可信的代码时,考虑按信任边界分工作区和环境。
- 每个会话的密钥。 每个任务可以带一个只属于这个会话的密钥,访问记忆库必须用它。只传给服务这个会话的沙箱,不要写进日志。
- 工具执行的影响范围。 工具以你的进程的权限运行。进程用户给最小权限,只挂工具需要的目录。
- 日志和会话内容的留存。 对话内容和工具输出经过你的 worker、留在你的环境里,保留多久、要不要脱敏,按你们自己的规定办。
Anthropic 做不到的:知道你的密钥泄露了;验证你的 worker 是不是你以为的那个版本;替你在沙箱内部把工具彼此隔开;替你在你的环境里执行数据留存策略。
和 Claude Code、Agent SDK 的对应
这三篇里的很多东西在 Claude Code 里有对应:子智能体、skill、MCP、权限确认、定时任务、云端会话。区别是一贯的那一条:Claude Code 和 Agent SDK 跑在你这边,托管智能体跑在 Anthropic 那边,再由你的程序通过事件去驱动。
- 想自己掌握运行环境、已经在用 Claude Code 的工作方式:Agent SDK,部署见《把智能体部署上线》。
- 不想管任何基础设施、任务又长又异步:托管智能体。
- 执行要留在内网,其余不想管:托管智能体加自托管沙箱。
没有实测的部分
- 全文没有实测。 托管智能体只在官方平台上有,本站用的中转站对这组接口返回 404。
- 示例代码来自官方文档,本站没有运行过。
- 各项上限(25 条子线程、64 条并发线程、1,000 个智能体、24 小时、10 次运行)是文档当前的数字,文档自己也说其中有的不作保证、可能调整。
- 动态工作流的实际效果(智能体写出来的程序质量如何、什么情况下会选择开一次运行)没有观察过,文中只转述了文档的说明。
- 自托管 worker 没有搭过,包括各沙箱平台的接入指南。
来源
- Multiagent orchestration(Claude 开发平台文档)
- Session threads(Claude 开发平台文档)
- Workflow runs(Claude 开发平台文档)
- Self-hosted sandboxes(Claude 开发平台文档)
- Deploy self-hosted workers(Claude 开发平台文档)
- Custom tools in self-hosted sandboxes(Claude 开发平台文档)
- Memory stores in self-hosted sandboxes(Claude 开发平台文档)
- Monitor and troubleshoot self-hosted workers(Claude 开发平台文档)
- Security model(Claude 开发平台文档)
- Self-hosted worker reference(Claude 开发平台文档)