学 AI 产品 · 专业 AI 产品经理播客第 2 章 · T2 解剖 Grok Build:Rust 写的生产级 Coding Agent · EP 05
第 2 章 · EP 05

子 Agent 的四个隔离维度 · 五种沙箱 Profile

时长 12:59音色 云健 · 男声

同步字幕

章节导航(点击跳转)

0:00开场 · 隔离不是等级,是一组正交维度1:12
1:12上下文来源 · New 与 Resumed 的真实语义2:14
3:27文件空间 · 隔离模式只有两种0:52
4:19多 Agent 组织 · 定义层和协调层1:42
6:02组织策略 · 先解决任务图再选机制1:17
7:19五种沙箱 Profile · 名字只提供方向1:46
9:06自定义与平台 · 防降级设计和诚实的边界2:06
11:12收尾 · 七条可以带走的结论1:46
解读全文

grok05 · 子 Agent 的四个隔离维度 · 五种沙箱 Profile

来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)
本内容改编自小山学堂《学 AI 产品,从入门到精通》,为二次演绎配音版

本集定位

本集属于技术侧(S2)模块 T2「解剖 Grok Build:Rust 写的生产级 Coding Agent」,取材自 3 节课:

  1. 子 Agent 的四个隔离维度
  2. 多 Agent 的组织方式
  3. 五种沙箱 Profile

这三节课有一条共同的主线:凡是听起来像一个「等级」的东西,拆开看往往是一组正交维度。隔离如此,沙箱 Profile 如此,多 Agent 的组织方式也是如此。


一、能力地图

读完本集,你应该能够:

能力具体表现
拆解隔离维度不再用「隔离强弱」一把尺子,而是分别判断上下文来源、恢复定位、文件空间、沙箱能力
选择上下文来源知道何时用 New、何时用 Resumed,以及 Resumed 会复制什么、不复制什么
预判恢复失败能提前判断哪些情况会触发「失败关闭」而不是「尽力而为」
选择隔离模式区分 None 与 Worktree,知道 worktree 可被快照重新水化
设计多 Agent 组织先看任务图(依赖 / 上下文 / 文件冲突 / 汇总成本),再选产品机制
读懂 Profile能按 capability set 而非名称判断 Workspace / Devbox / ReadOnly / Strict / Off 的真实边界
配置 custom会写 extends 与 deny,并知道项目无法覆盖全局同名定义
诚实评估沙箱知道哪些环节会退化,以及为什么「无法绕过」不能承诺

二、隔离是一组正交维度

2.1 核心命题

隔离不是一个等级,而是一组正交的维度。把它们合成一个「隔离等级」,容易误读恢复和 worktree 的真实行为。

同一个 Resumed 子 Agent,可以复用 worktree,也可以继承普通 cwd。上下文连续 ≠ 文件空间隔离,两者必须分别判断。

2.2 四个维度速查

维度控制什么源码载体取值
上下文来源信息是否从父会话/同级带过来ContextSourceNew / Resumed
恢复定位这次恢复靠什么找回来ResumeSourceData身份 / 目录 / 会话 / 快照
文件空间改动落在哪个目录树SubagentIsolationModeNone / Worktree
能力边界能读什么、写什么、能否联网ProfileName + capability set五种 + Custom

三、维度一:上下文来源

3.1 ContextSource::New

新会话,不继承历史。spawn 流程建立新的 system prompt 与 prompt context,再接收当前任务输入。

关键澄清:New 不代表独立文件空间。文件空间仍取决于 isolation 与 cwd。这是第一个高频误读。

3.2 ContextSource::Resumed

从已完成的 peer subagent 继续。源码复制原始 transcript 与 tool state,模型沿用 source model;system prompt 与 prompt context 根据当前 AgentDefinition 重新渲染。

组合起来是:历史是旧的,包装是新的。你复活的是一个有记忆的角色,但它当前的身份与行为约束由这次的定义决定。

3.3 实现补充(不要当成公开语义)

公开枚举只有 New 与 Resumed。shell 内部还有 InitialContextSource::Forked,用于从父会话镜像或摘要上下文,属于另一条 bootstrap 分支,不应被伪装成公开枚举成员。

提示1 · 文档与实现分层,别把内部类型写成对外承诺

写技术文档或做架构描述时,严格区分「公开枚举」与「内部实现分支」。内部类型会变,公开枚举是契约。把 Forked 塞进 ContextSource 去描述,等于给了一个随时会被打破的承诺。


四、维度二:恢复定位与身份校验

4.1 ResumeSourceData 携带四类信息

类别字段用途
身份subagent_id、type、persona、model_id校验与沿用
目录child_cwd、worktree_path定位工作空间
会话数据child_session_id定位 transcript 与 tool state
快照snapshot_ref重建已删除的 worktree

4.2 身份校验三条

  1. subagent_type 必须与 source 一致;
  2. 显式给出的 Persona 必须与 source 一致;未显式给出则沿用 source;
  3. model 不参与身份 gate——请求中的 model override 会被软忽略,并 pin 到 source model。

第三条最反直觉:你没法在恢复一个子 Agent 时顺手给它换脑子。这是有意设计。

4.3 恢复的三条硬限制

  • 复制 transcript 或读取失败时,显式 resume 失败关闭(不降级为半吊子状态);
  • source transcript 超过目标模型上下文窗口 80% 时拒绝恢复;
  • 恢复只复制 tool state,不复制 plan state、plan mode state 与 signals。

提示2 · 恢复是严格事务,别为它设计「尽力而为」的兜底

这三条限制背后是一致的设计态度:要么成,要么败。80% 那条尤其值得抄进自己的系统——恢复完马上就要溢出,不如早点拒绝。给你的恢复流程加上同样的阈值检查与显式的失败返回,比事后排查「为什么 Agent 忽然失忆」要便宜得多。


五、维度三:文件改动空间

SubagentIsolationMode 只有两个成员:

成员语义常见误读
None使用解析后的 cwd,通常与父工作区相同✗ 误以为等于共享对话历史。实际上上下文窗口依然是独立子会话
Worktree使用独立 worktree 路径✗ 误以为枚举里还有 sandbox 成员

Worktree 的恢复:优先复用 source worktree;路径已移除且存在 snapshot_ref 时,可从持久 git ref 重新水化。

沙箱不在这个枚举里——它由 Profile 控制,是另一层机制。


六、多 Agent 的组织方式

6.1 定义层:AgentDefinition + Persona

  • AgentDefinition:prompt、工具、权限、模型、MCP 继承、可 spawn 类型——一份合同
  • Persona:行为指令、I/O 契约、部分运行时默认值

解析顺序:type → definition → role/persona runtime config

两者合起来解决一个问题:让每个子 Agent 有可观察的身份与能力边界。不是一句「你是 reviewer」就完事,而是工具、权限、外部连接全部可枚举、可审查。

6.2 协调层:SubagentEvent


start_subagent_coordinator  →  只启动一次 drain task
每个 Spawn 事件             →  启动本地异步任务 → handle_subagent_request

六类事件:Spawn · Query · Cancel · ListActive · Completions · Outstanding

关注点机制
并行Spawn 独立进入 spawn_local,协调器登记 pending / active / completed。并行度来自异步任务,不受 Persona 数量限制
等待Query 可立即返回快照,也可注册 block wait slot 并轮询
完成Completions drain 待通知项,按 suppress_ids 过滤
取消Cancel 支持按 subagent ID 或 parent prompt ID;协调器淘汰过期记录并记录显式 kill

6.3 跨产品对照的边界

可对照的是产品表面能力:Claude Code 公开支持自定义 subagents,每个可拥有独立上下文、system prompt、工具权限与模型,主会话可自动委派或由用户显式调用;公开的 Agent Teams 描述包含共享任务、成员间消息与独立上下文。

不把 Coordinator 或 Swarm 当作其内部类型,也不推断调度器实现。

6.4 三种策略与四个判据

策略适用
单主会话委派一个 owner 统一拆解、串联依赖并汇总
主会话 + subagents子任务互不依赖(如检索 8 个模块后汇总风险清单)
多成员共享任务成员需要彼此通信、认领共享任务(如三服务并行迁移需同步接口变更)

判据:任务依赖 · 上下文需求 · 文件冲突 · 结果汇总成本。

提示3 · 先画任务图,再选产品机制

顺序不能反。先选了机制再硬套任务,是多数多 Agent 项目跑偏的起点。实操上,把任务画成一张有向图:有边就有依赖,就串行或等待;无边就并行派生;只有在「成员之间需要互相同步」时才引入共享任务与成员通信——这一步的成本最高,不该默认开启。


七、五种沙箱 Profile

7.1 最重要的一句话

名称只提供方向,真实边界要看解析后的 capability set。

7.2 五种内置 Profile

Profile默认读可写子进程网络
Workspace全文件系统可读workspace、GROK_HOME、临时目录不限制
Devbox全文件系统可读枚举根目录广授(/data 与 VFS 除外)不限制
ReadOnly全文件系统可读GROK_HOME、临时目录、必要设备(workspace 不可写)限制
Strict关闭全局默认读,只开放系统运行目录与 workspaceworkspace、GROK_HOME、临时目录限制
Off跳过 capability set 应用,仅记录「Sandbox disabled」;接受别名 none;不可作为 custom 基类——

7.3 两个几乎人人都有的误读

  1. workspace 仍允许读取工作区之外的文件——它不是「只读工作区」的笼子;
  2. strict 仍允许写 workspace——strict 严格的是读,不是写。

read-only 也保留了运行所必需的最小写目录。

提示4 · 名称服从源码,不要按字面补全规则

看到 read-only 就以为哪儿都写不了,看到 strict 就以为哪儿都不能动,这两种推断都是错的。凡是靠字面补全的规则,都是错的规则。评估一个沙箱时,一定去读它的 resolve() 结果、可写路径清单与 deny 列表,而不是读它的名字。


八、Custom Profile 与配置边界

8.1 extends 四条规则

  1. custom 默认从 workspace 开始;
  2. 可 extends:workspace / devbox / read-only / strict;
  3. 不能 extends off / none;
  4. 不能 extends 另一个 custom。

read_only、read_write、deny 追加到基类之上。需要限制子进程网络时,必须在 custom 中显式设置 restrict_network=true——继承不会替你做。

8.2 全局优先保护(防降级设计)


读取顺序:~/.grok/sandbox.toml  →  .grok/sandbox.toml
项目配置:只能新增 profile 名称
同名冲突:merge 使用 entry.or_insert,全局定义保持生效

项目无法悄悄削弱用户全局已经定下的同名策略。 这堵住了一个很现实的攻击面:恶意仓库往项目配置里塞一个同名但更宽松的沙箱定义。

8.3 平台机制

层面实现
文件系统启用 enforce 且运行在 Unix 时,capability set 应用到 Landlock 或 Seatbelt;macOS deny 用 Seatbelt 规则;Linux 子路径 read-deny 还需 bwrap bind-over
网络主进程网络保持开放以访问模型 API;restrict_network 通过子进程过滤表达;源码中的 seccomp 实现在 Linux 生效,非 Linux 为空操作

8.4 诚实卡:不能承诺「无法绕过」

若平台不支持、构建未启用 enforce,或内核层 Sandbox::apply 失败,源码会记录警告并继续运行。

但要注意区分:能力集与 profile 解析这一步是用 ? 上抛的,失败时 apply 直接返回 Err,不是静默降级。因此只有 is_active() 才能反映是否实际应用。

结论:不能承诺所有环境都「无法绕过」。这句话不性感,但它是诚实的。


九、审查清单

  • 谈「隔离」时,是否明确了是哪一维(上下文 / 恢复 / 文件空间 / 能力)?
  • 是否有人把 New 当成「独立文件空间」?把 None 当成「共享对话历史」?
  • 恢复流程是否设置了 transcript 占比阈值(如 80%)并显式失败?
  • 恢复是否只复制 tool state,未错误带上 plan state 与 signals?
  • 身份校验是否覆盖了 type 与 Persona,且不对 model 做放行?
  • 多 Agent 选型是否先画了任务图,再选机制?
  • 并行度预期是否建立在实际异步能力上,而非 Persona 数量上?
  • 沙箱边界是按 capability set 判断的,还是按名字脑补的?
  • custom profile 是否显式设置了 restrict_network(如需要)?
  • 配置合并方向是否从宽到严会被拒绝(全局优先、项目只新增)?
  • 是否在文档中区分了公开枚举与内部实现分支?
  • 是否用 is_active() 之类的运行时状态来判定沙箱真的生效,而不是看配置写了什么?

十、约束说明

本集内容受以下约束限制:

  1. 取材边界:仅取自小山学堂对应 3 节课正文,不引入外部案例或未经素材支持的数据。
  2. 源码语义:文中枚举成员、字段与行为均来自素材引用的源码快照(xai-grok-subagent-resolution、xai-grok-shell、xai-grok-sandbox、xai-tool-types),实现可能随版本演进,落地前请以当前版本源码为准。
  3. 跨产品对照:Claude Code 一侧仅描述公开功能行为,不涉及内部类型与调度器实现推断。
  4. 平台差异:Landlock / Seatbelt / bwrap / seccomp 的生效条件随操作系统与构建开关变化,非 Linux 平台上部分能力为空操作。
  5. 不要绝对化:沙箱在部分环境会退化为「记录警告并继续运行」,不得表述为「任何环境都无法绕过」。
  6. 数值口径:80% 等阈值为素材中源码既定的行为,不代表通用推荐值。
  7. 二次演绎:音频为改写后的口播版本,段落顺序与措辞相对原课程有调整,以音频与本文为准。

十一、一句话总结

工程上真正可靠的安全,不来自某个听起来很厉害的名字,而来自一组被明确定义、并且可以被逐条验证的边界。

把压缩成一个等级的东西拆开,往往会发现之前想不明白的问题突然就清楚了。


*来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)*

*本内容改编自小山学堂《学 AI 产品,从入门到精通》,为二次演绎配音版*