Agent Platform · Detail Design

企业 Agent 端到端流程 · 8 类典型场景

用 时序图 把企业 Agent 平台最常见的 6 种交互模式画清楚:单 Agent 直答 / HITL 介入 / 多 Agent @ 派单 / Team 扇出 / Sequential Relay 串行接力 / 多轮对话 Session。每张图竖线是 T1 / T2 / T3 三条 lifeline,时间从上往下流,箭头是层与层之间的调用。

Concept · 概念参考 沙箱边界 / Sandbox Boundary Harness 跑在哪?什么 Skill 才进沙箱?为什么不是每个 Run 都开容器?
↓
Scenario · A

单 Agent 直答

最常见、最简单的情况:用户提问 → Agent 答完。没有 HITL、没有协作。一个 Run 在 T3 内核走完 8 步管道,T2 仅做路由和回收,T1 推回结果。

跨层调用 ↻层内自环 ● T1 ● T2 ● T3
T1 · 应用层 APP T2 · 协同运行时 RUNTIME T3 · 执行内核 KERNEL 1 任务输入 → 派发 2 Coordinator 起 Run 3 ↻ 8 步管道全包 装配 / 规划 / Policy / 工具调用 4 结果 + 写记忆 5 SSE 推回用户
关键观察:T3 是主战场,T2 只做"派单 + 回收"两笔,T1 只是"接 + 推"。这是企业 Agent 的基线流程——任何场景都至少跑这 5 笔。
Scenario · B

单 Agent + HITL 高风险审批

Agent 准备执行高风险动作(生产部署 / 数据破坏 / 金额操作),Policy 判定为 High → T3 暂停,绕去 T1 审批人界面,等批准后再恢复执行。

必走 HITL 路径 ↻层内自环
T1 · 应用层 APP T2 · 协同运行时 RUNTIME T3 · 执行内核 KERNEL ⏸ Run paused 1 任务输入 → 派发 2 Coordinator 起 Run 3 ↻ 准备 + Policy 判风险 8 步 02-05;命中 High → 暂停 4 ⤺ HITL 审批请求 action / risk_level / context 5 ↪ 批准 / 拒绝 通过则恢复 / 拒绝则 CLOSED_REJECTED 6 ↻ 工具 / LLM 调用 8 步 06:批准后才执行 7 结果 + 写记忆 8 SSE 推回 + 审计留痕
关键观察:HITL 把控制权暂时上浮到 T1。T3 的 Run 进入 AWAITING_HITL 状态不释放沙箱,等审批回来直接恢复,不重起 Run、不丢上下文。
Scenario · C

多 Agent @ 派单(Parent → Child)

协作场景:Parent Agent 在执行中需要专家协助,@ 一个子 Agent 派单。子 Agent 是独立 Run(独立沙箱、独立上下文),跑完返回结果,Parent 接着续跑。

主调用 ↻层内自环 ● Parent / Child 都在 T3
T1 · 应用层 APP T2 · 协同 COORDINATOR T3 · Parent Run KERNEL T3 · Child Run 独立 RUNSTATE ⏸ 等子 Agent 1 任务输入 → 派发 2 起 Parent Run 3 ↻ Parent 准备 + 规划 决定要 @ 子 agent 4 @ 子 agent 派单 5 起 Child Run(独立 RunState) 6 ↻ Child 8 步 7 Child 结果 → ResultSink 8 DelegationTrace → Parent 9 ↻ Parent 续跑 + 终态 10 Parent 结果回收 11 SSE 推回用户
关键观察:Parent 在 ④ 处不直接调用 Child——而是通过 T2 Coordinator 转交。Child Run 是独立 Run + 独立 Trace + 独立计费,不继承 Parent 的状态(沙箱按 Skill 实例化,与 Run 数量无 1:1 关系)。结果通过 DelegationTrace 回流。
Scenario · D

Team @team 扇出 + Round 2 汇总

真正的"协作群":用户 @team:soc,T2 把 Team 展开成 N 个成员,并行起 N 个 Child Run(Round 1)。所有 Child 终态后,T2 回到 Parent 触发 Round 2 汇总。

主调用 ┃ Parallel · 并行扇出 ↻层内自环
T1 · 应用层 APP T2 · 协同 COORDINATOR T3 · Parent Run ROUND 0+2 T3 · Team Children ROUND 1 · N PARALLEL ⏸ 等 Round 1 1 @team:soc + 任务 2 ↻ 展开 Team 成员列表 3 起 Parent Run · Round 0 4 ↻ Round 0 规划 + 分派 5 触发 fan-out 6 并行起 N 个 Child Run · Round 1 7 ↻ N×并行 8 步 8 N 个 ResultSink 收齐 9 ↻ Coordinator 聚合 10 触发 Round 2 汇总 11 ↻ Round 2 · 终态汇总 12 汇总结果回收 SSE 推回用户
关键观察:Round 0 (规划) → Round 1 (并行执行) → Round 2 (汇总) 是 Team 协作的核心三段。Parent 在 Round 1 全程暂停,由 T2 Coordinator 守住 N 个 Child 的 ResultSink;全部到齐才唤醒 Parent 跑 Round 2。这就是 GroupCollaborationCoordinator 的责任边界。
Scenario · E

Sequential Relay · 串行接力

日常工作流模式:Run A → Run B → Run C,每个 Run 终态后由 T2 直接起下一个 Run。和场景 C 不同——没有 Parent 等 Child,每个 Run 平级独立,通过 chain_remaining 字段串成任务链。典型场景:"研究 → 写稿 → 校对"。

主调用 ↻层内自环 ● 每个 Run 独立沙箱、独立计费
T1 · 应用层 APP T2 · 协同(链式调度) JOB DISPATCHER T3 · 执行内核 RUN A → B → C Run A Run #1 · 独立 Trace Run B Run #2 · 独立 Trace Run C Run #3 · 独立 Trace 1 输入 + chain_remaining=[A,B,C] 2 起 Run A 3 4 Run A 终态 · chain_remaining=[B,C] 5 起 Run B(A 输出 → B 输入) 6 7 Run B 终态 · chain_remaining=[C] 8 起 Run C(B 输出 → C 输入) 9 10 Run C 终态 · chain_remaining=[] 11 最终结果推回用户
关键观察:3 个 Run 是平级独立的——独立 RunState、独立 Trace、独立计费。靠 T2 的 JobDispatcher 看 chain_remaining 字段决定起下一个。Run A 失败时整链熔断;中间某 Run 超时不影响已完成 Run 的工件。沙箱按 Skill 实例化——纯文本任务不进沙箱,代码类任务才进。
Scenario · F

多轮对话 Session(同一 Agent 多 Run)

日常聊天模式:用户连发多条消息给同一个 Agent,每条消息是独立 Run,但共享 AgentSession 把对话历史串起来。T2 在每次起 Run 前从 Session 加载累积上下文,T3 永远在"全 history"基础上回答。

主调用 ↻层内自环 ━━ 用户思考间隔(时间不连续)
T1 · 应用层 CHAT UI T2 · 协同 + Session AGENT SESSION T3 · 执行内核 RUN 1 / RUN 2 / ... TURN 1 Run 1 history=[] 1 用户消息 1(首次对话) 2 ↻ 创建 AgentSession(session_id) 3 起 Run 1 · history=[ ] 4 5 Run 1 终态 · 写 Session 6 推回回答 1 ━━ 用户思考 N 秒 ━━ TURN 2 Run 2 history=[Q1, A1] 7 用户消息 2(续聊) 8 ↻ 加载 Session(含 Run 1 history) 9 起 Run 2 · history=[Q1, A1] 10 11 Run 2 终态 · 追加 Session 12 推回回答 2 · 第 N 轮同理 … Turn 3, 4, 5 同理(Session 持续累积,所有 Run 共享 agent 的同一沙箱)
关键观察:每一轮都是独立 Run,但 T2 通过 AgentSession 把跨 Run 的对话历史串起来。T3 不感知"轮次"——它每次拿到的就是累积 history,按"全部上下文"回答。所有轮次共享 agent 的同一沙箱(per-agent 模型);纯聊天 Run 根本不进沙箱,能扛更高并发。
Scenario · G

Lead-driven · Lead 主导分派

协作三大策略中第三种:Lead Agent 拆任务 → 派 executor → 收齐 → 整合。和 fan-out(D)的区别是 Lead 二次激活——先 plan、再 consolidate;和 sequential-relay(E)的区别是 executor 之间并行而非串行。

主调用 ↻层内自环 ┃ Parallel · 三线扇出 executor
T1 · 应用层 CHAT ROUTE T2 · Coordinator LEAD CHOREOGRAPHY T3 · Lead Run PLAN + CONSOLIDATE T3 · Executors N × PARALLEL ⏸ 等 Executors N×并行 1 @team / 任务请求 2 ↻ strategy=lead-driven 找出 lead 3 起 Lead Run 4 ↻ Lead 拆 sub-task plan 5 回传 sub-task 列表 6 并行起 N 个 executor Run 7 ↻ N×并行执行 8 N 个 ResultSink 收齐 9 把 N 个结果汇给 Lead 10 ↻ Lead 整合 + 终态 11 Lead 终态 · 团队最终输出 SSE 推回用户
关键观察:Lead 在 ⑤(plan)和 ⑪(consolidate)两次激活,中间挂起等 executor。Executors 之间无直接通信,全部通过 Coordinator → Lead 收口。和 fan-out(D)的本质区别:D 是"用户视角的扁平 N 路并发",G 是"Lead 主导的策划-执行-总结"——同样并行,但语义层次不同。
Scenario · H

Webhook 异步触发(非用户聊天入口)

入口不是用户消息——可能是邮件触发(用户发邮件 → 平台触发 Agent)、Cron 定时(每天 9:00 跑日报 Agent)、监控告警(系统异常 → 应急 Agent)、外部 Webhook(Github push → 代码审查 Agent)。结果回推事件源(callback URL / 邮件回执),不走 SSE。

主调用 ↻层内自环 ● 入口替换:事件源不是用户
事件源 · External 邮件 / Cron / Webhook T1 · Webhook Handler 鉴权 + 解析事件 T2 · Coordinator 起 Run + 路由回调 T3 · Run Kernel 8 步管道 1 POST /api/webhooks/... payload + 签名 + callback_url 2 ↻ 验签 + 解析 + 构造 Job 3 触发 Job · 含 callback_url 4 起 Run 5 ↻ 8 步管道全包 6 终态 + 写 Trace 7 HTTP POST → callback_url 不走 SSE,走 Webhook 回推 事件源消化结果
关键观察:Webhook 入口的核心差异是没有用户在等——T1 不维持 SSE 长连接,T2 收到终态后主动 HTTP POST 回推到 callback_url。事件源(邮件服务 / Cron 守护进程 / 监控系统)异步消化结果。这是"无人值守 Agent"模式:监控告警自动响应、Cron 定时报告、CI/CD 触发审查都走这条路径。

Harness 在哪?什么 Skill 才进沙箱?

前面 6 张时序图都把 T3 画成一根 lifeline,但"T3 内核运行在哪儿"需要单独说清楚。常见误解:每个 Run = 一个沙箱容器。实际不是——Harness 内核就在 Celery Worker 进程里,沙箱是按 Skill 实例化的执行环境。

CELERY WORKER PROCESS T2 Coordinator 协同 / 路由 / 派单 T3 Harness 内核 8 步管道 · RunState Tool 调用控制器 分流:本地 vs 沙箱 本地 Skill(同进程) no sandbox needed LLM API · DB · web_search memory · 外部 API · 邮件 Sandbox Provider 适配器 OpenClaw / K8s / Hermes 按 Skill 请求实例化容器 端口区段 / 资源配额 SANDBOX CONTAINER POOL Container · 8081 → 39137 claude_code_execute / Vite 服务 Container · 8082 → 39138 python_exec / Streamlit 服务 Container · 8083 → 39139 shell_exec · 文件构建 … 当前端口池容量:10 个 8081-8090 → 39137-39146 硬瓶颈,待扩容
Skill 类型典型例子是否进沙箱原因
代码执行claude_code_execute · shell_exec · python_exec必须进跑不可信代码 · 文件系统隔离
服务部署Vite / Streamlit / Flask / Gradio dev 服务必须进需要绑端口 · 端口池资源
构建 / 安装依赖npm install · pip install · 解压必须进大量文件 IO · 防止污染 worker
LLM 调用Claude / GPT API · 流式生成不进HTTPS 出站 · 无副作用
数据库查询SQL · 向量检索 · Memory 召回不进受应用权限控制即可
外部 API飞书 / 邮件 / 多维表 / Webhook不进纯 HTTP 调用 · 凭据托管
纯文本处理summarize / translate / classify不进无 IO 副作用
web_searchDDG / Bing API · 网页解析不进HTTP 请求 · 无代码执行(如改用 Headless 浏览器则需进)
⚠ 经验法则: Skill 只要不写文件、不起服务、不执行任意代码,就不需要沙箱。沙箱的容量(端口池 / 容器池)才是稀缺资源,必须按 Skill 类型按需申请。

沙箱使用边界 · 5 个维度

"哪些 Skill 进沙箱"只是维度 A。完整的沙箱使用边界还包括:沙箱内能用什么资源(B)、沙箱什么时候创建/销毁(C)、出问题怎么处理(D)、跨 agent 怎么不串扰(E)、宿主机文件系统怎么挂(F)。

★ 核心模型:沙箱跟 agent identity 走,不跟 Run 走。每个 agent(个人 Agent / 项目 Agent / Team Agent)拥有自己的沙箱——像员工各自有一台电脑。同 agent 的多个 Run 复用同一沙箱串行处理(agent 一次一件事),不同 agent 自然隔离。这套模型让协作群、父子 Run、并发请求全部自洽。

B · 资源约束(沙箱内能用什么)

每个沙箱容器有硬性资源上限,超过即终止——保护宿主机不被单个 Run 打死。

资源默认上限超出动作
CPU1 core / 沙箱(可上调)容器 throttle,不杀
内存2 GB / 沙箱OOM Kill → 沙箱内 Skill 调用失败 → Run 进入 Recovery
执行时长单次 Skill 调用 ≤ 30 分钟(默认 5 分钟,可在 Skill manifest 声明)超时强制终止 → Tool 调用控制器收到 TIMEOUT
磁盘容器内 /data/projects 共享卷 ≤ 1 GB写满 → IO 错误,Run 内自行处理
出站网络白名单:LLM API · OSS · 公开 Skill MCP · 公网 HTTPS非白名单访问被 nginx / iptables 拒绝
入站端口仅容器 8081-8090,映射到宿主 39137-39146(10 个)占满 → port_registry 拒绝新 Run 申请

C · 生命周期(沙箱跟 agent 走)

沙箱按 agent identity 实例化——一个 agent 一个沙箱(agent 的"电脑"),多个 Run 在沙箱内串行处理。沙箱跟着 agent 一起活过整个生命周期。

时机动作
创建agent 第一次调沙箱类 Skill 时,Sandbox Provider 为该 agent 起 1 个容器,分配端口,绑定 agent_id
占用该 agent 持有这个沙箱——不释放给其他 agent,包括同 user 拥有的其他 agent
多 Run 复用同 agent 后续 Run 都用这个沙箱串行处理(agent 一次只做一件事,像人一样)
预热常驻 agent(如团队 SOC agent)的沙箱保持热启动,避免每次冷启 5–15s
空闲 hibernateagent 长时间不活跃,沙箱挂起(容器 pause / 或 snapshot 销毁),保留状态、释放端口与内存
唤醒agent 下次激活时恢复沙箱(pause→resume,或从 snapshot 起新容器)
销毁agent 归档 / 删除 / 所有权转移时连同沙箱一起清理
强制销毁OOM / panic / cleanup 失败 → 立即销毁;下次唤醒走全新沙箱(agent 状态丢失)

D · 失败处理(沙箱出问题怎么办)

沙箱失败必须有显式兜底,不能让 Run 卡死或静默失败。

失败类型处理Run 状态影响
容器启动超时(> 30s)放弃当前申请 → 重试 1 次 → 仍失败则 Tool 调用 FAILEXECUTING → FAILED → CLOSED_FAILED
容器 OOM KillSkill 调用返回 OOMError;Run 自行降级或失败由 Skill 重试策略决定(默认不重试 OOM)
Skill 执行超时(≥ 5min 默认)SIGKILL 容器内进程 → 销毁容器Tool 调用 TIMEOUT → 由 Self-Healing 决定恢复
端口池耗尽(10 占满)port_registry 拒绝 → Tool 调用控制器排队等待EXECUTING 不变,但 Run 实际阻塞在 Tool 调用
容器进程 panic容器内 supervisor 重启 1 次;仍崩则销毁正在跑的 Skill 失败,未来 Tool 调用走新容器
沙箱网络白名单越界iptables / nginx 拒绝;Skill 调用收到网络错误不杀沙箱,由 Skill 决定是否重试

E · 跨 agent 隔离(不串扰的硬约束)

沙箱按 agent identity 隔离——不同 agent 永不共用沙箱容器,即使同 user 拥有这些 agent。这是一票否决底线。

隔离层级规则说明
Tenant 间不同租户的 agent 严禁共用沙箱一票否决错误,立即销毁容器 + 触发安全告警
Agent 间不同 agent identity 各自独立沙箱(即使同 user 拥有多个 agent,比如个人 + 项目 + Team agent)类比:员工各有自己的电脑
同 agent 多 Run复用同一沙箱,但串行处理,不在沙箱内并发跑多 Runagent 一次一件事,符合人类直觉
父子 Run(A 派单给 B)A 用 A 的沙箱,B 用 B 的沙箱——天然独立,因为是不同 agent identity不需要额外规则,identity 不同自动隔离
Team agent整个 Team 是一个 agent identity,共用一个沙箱(团队工作空间)类比:项目共享代码仓库 / 共享研发环境
Agent 转移 / 删除沙箱跟随 agent 一起归档 / 销毁,不留孤儿容器agent 删除 → 沙箱内文件 + 凭据全部清理

F · 挂载策略(沙箱 ↔ 宿主机文件系统)

"沙箱"两个字最容易给人安全错觉的地方:bind mount 共享 inode 时,沙箱里 rm -rf 直接打穿宿主机。沙箱与宿主机文件系统的边界由挂载策略决定,规则非常窄:默认零挂载,必须挂的走窄白名单。

层级规则说明
默认零 bind mount沙箱与宿主机 fs 默认完全隔离;需要传文件走 stage-in / stage-out 显式拷贝
用户 workspace仅挂 /data/users/<user_id>/workspace,rw这是沙箱里唯一能动到宿主机持久化数据的入口;按 user_id 隔离,不可越界
系统目录禁挂 / · /etc · /home · 其他用户目录一票否决;任何配置试图挂这些都视作高危,启动失败
Docker / 容器运行时禁挂 /var/run/docker.sock · /proc · /sys · cgroup fs挂任意一个 = 整台宿主机交出去(容器可起 privileged + 反挂宿主机根)
Skill manifest 声明需要额外挂载(如代码仓库子目录)→ manifest 显式声明 path + ro/rw + 子路径白名单不接受目录级 rw,必须细到子路径;rw 子路径触发 destructive 标记自动加 HITL
临时 rw 区容器内 /tmp/scratch(tmpfs,per-Run 隔离)Skill 自由读写;Run 结束销毁;零宿主机痕迹
destructive 兜底rm / rmtree / 覆写已有文件 / git reset --hard 即使在用户 workspace 内也走 HITL详见 治理与 HITL 设计 ↗ 风险矩阵;技术隔离 + 治理审批双层兜底

消费级 Agent 没有、企业必备的 5 条支线

8 个场景画的是主轴流程。下面 5 条才是"为什么需要平台"——每张卡说清它落在哪个场景的哪一笔。

DIFF · 01
Team 协作展开
@team 一次扇出 N 个 Agent;Round 0 → 1 → 2 接力;不是单 Agent 串行。
→ Scenario D · ②⑥
DIFF · 02
能力中心治理
Skill 用 manifest_hash 锁定版本;破坏性升级走 Major + 灰度。
→ A·③ B·③ C·③ D·④
DIFF · 03
共享记忆 scope
User / Workspace / Team 三层 scope,按粒度共享,不串、不漏。
→ A·④ D·⑨
DIFF · 04
HITL 嵌入
High 风险动作必须人在环路;T3 暂停不释放沙箱。
→ Scenario B · ④⑤
DIFF · 05
审计闭环
每个 Run 可 Replay;DelegationTrace 把父子关系穿起来。
→ C·⑧ D·⑧