DSH EXPERIMENTAL · v0.2.1-rc.2

智能体团队

不是一个多智能体调度进程,而是 DSH 内核上的一层组合插件:把 Subagent 的 continuable child 机制,包装成以 Lead 会话日志为唯一事实源的具名团队与共享任务 DAG。

4 个包 · 1 个 patch 0 额外进程 / 0 额外存储 单进程 · 共享 checkout
ARCHITECTURE BLUEPRINT
SCROLL
01

装配层

PROFILE BUNDLE · CORDIS PATCH · OPT-IN

「智能体团队」在插件页里只是一个开关,落到磁盘上是 profile 里多了一层 bundle;落到运行时上,是四行 patch:禁掉全局 subagent 工具,插入 Team 服务、Team 工具与 Web 面板。没有运行时代码开关,也没有独立入口。

L1 · profile 的有序层

dsh.profile.bundles = dsh-base dsh-web-app … dsh-experimental-agent-team-profile
APPLY PATCH AFTER dsh-base
# cordis.patch.yml — 运行时内容就是这个 patch - id: tool-subagent-control disabled: true - id: tool-subagent-list-agents disabled: true - id: tool-subagent disabled: true - id: tool-subagent-fork disabled: true - insert: - id: agent-team name: '@deepseek-ai/dsh-experimental-agent-team' config: { maxMembers: 8, maxTasks: 256, maxMessageBytes: 65536 } - id: tool-agent-team name: '@deepseek-ai/dsh-experimental-tool-agent-team' config: { freshProvider: spawn, forkProvider: fork } - id: ui-agent-team name: '@deepseek-ai/dsh-experimental-client-ui-agent-team'

为什么禁用全局工具? 默认工具目录保持简单;“委派”这条语义只在这层组合里由 Team 工具接管,避免同名工具彼此遮蔽。

为什么拆成三个插件? 领域状态(服务)、模型可见面(工具 + prompt)、浏览器呈现(UI)各自独立,headless 可以只装前两个。

DISABLED IN THIS LAYER

tool-subagent tool-subagent-fork tool-subagent-control tool-subagent-list-agents
PKG / 01

@deepseek-ai/dsh-experimental-agent-team

领域服务:注册 ctx.agentTeams,持有 roster、任务板与持久事务。状态只写 Lead 的 Session 日志。

INJECT
agents · sessions · sessionPersistence
sessionProjections · subagents
PKG / 02

@deepseek-ai/dsh-experimental-tool-agent-team

模型可见面:8 个 Agent 作用域工具 + 2 段 system prompt(team:policy / team:identity)。

INJECT
agents · agentTeams · tools · systemPrompt
PKG / 03

@deepseek-ai/dsh-experimental-client-ui-agent-team

Web 只读面板:挂在会话头部 action 槽位,读 Lead 会话的 agentTeam 投影帧。零 RPC。

INJECT (CLIENT)
sessions · uiWorkspace · slots · locale
02

运行时全景

ONE LEAD LOG · FIVE OWNERS · ONE PROJECTION

中间那个方框才是 Team 领域本体:ctx.agentTeams 把状态、事务、权限、任务板收进一个服务。左边是模型,右边是复用的 Subagent 执行层,下边是持久化与浏览器。点任意模块查看它的职责与代码位置。

模型表面 · TOOL-AGENT-TEAM CTX.AGENTTEAMS · AGENT-TEAM SUBAGENT 运行时 · 复用 工具调用 append → flush 成员快照 spawn · steer · interrupt 纯 fold wire view 事件落盘 冷恢复读取 Lead Agent · root Session 隐式 Team · TeamId = SessionId 模型工具 ×8 spawn_teammatesend_message list_agentswait_agent interrupt_agentteam_task_* 仅 Team 成员作用域可见 system prompt team:policy · team:identity TeamJournal 串行事务 · 先落盘再发布 TeamRoster 身份 · spawn · 恢复 · interrupt TeamTaskBoard CAS 命令 · DAG · 视图 TeamActivity wait_agent 一次性等待者 TeamRuntimeLifecycle 准入截止 · track / settle · 有界 dispose Lead Session 日志 · 唯一事实源 team/member · 全量快照 v2 team/task · revision 连续 永不进入模型历史 · flush 后才发布 sessionProjections · agentTeam stateVersion 4 · 每条 committed event 做纯 fold wire view 按 members/tasks 引用缓存 apply 只校验并替换 whole-value 快照 Subagent 运行时 startActivation(fresh | fork) steerHostSubagentPrompt interrupt(kind: "ancestor") drainChildren / stopTeammates 运行时生命周期仍归 Subagent 子系统 reviewer · continuable child Session · 可冷恢复 · inbox 归目标 tester · continuable child Session · 与 Lead 共享 cwd Web 面板 · client-ui-agent-team 读 projectionsBySession[lead].values.agentTeam 成员列表 + 任务板 · 点成员打开 child 会话 · 零 RPC 每次 roster / task 变化 → 广播完整视图 sessionPersistence · 每个 Session 独立落盘(JSONL / SQLite) Lead 日志 · 子会话独立持久化 · projection checkpoint(带 stateVersion)· Team 不引入新存储
调用 / 数据 生命周期 / 冷路径 实时流 点击模块查看职责与代码位置 →
03

日志即状态

APPEND-ONLY EVENTS → PURE FOLD → WIRE VIEW

Team 没有数据库。每次变更把完整后态快照追加进 Lead 日志并 flush;读取方从事件流折叠出 roster 与任务板。下面这个沙盒按真实规则运行:命令级别会拒绝非法操作,投影级别会拒绝非法事件(failure 之后的事件被忽略)。

Event source · 模拟命令 规则照抄 projection.apply
成员
任务
注入
Projection · agentTeam wire view(只读)

roster

task board

04

创建 Saga

PROVISIONING → ACTIVATION → ACTIVE · 名字永不复用

teammate 不是“凭空多出来一个 agent”。先持久化意图,再启动预留身份的 child,最后确认初始 prompt 已落盘才宣告 active。任何一步失败都留下 failed 而不是回收名字;冷启动用对账而不是重放 prompt。

STEP 0 / 5

等待开始

按「播放」或「单步」推进。每一步都会标注它在哪一侧留下痕迹:Lead 日志、子 Session,还是纯运行时。

_
idle
05

消息投递

INBOX STEER · NOT A MAIL SERVER

Team 自己不存消息。发送 = 校验成员身份 + 加发送者前缀与 source,然后借用 Subagent 的 steer 路径写进目标 inbox;排序、持久化、去重都归目标 Agent。切换场景看三种结果。

reviewer 发送者 · Team member content 加前缀与 source: kind: "agent-message" senderSessionId = 真实发送者 TeamService.sendMessage ① 校验成员身份 / 拒绝自发 ② 大小 ≤ maxMessageBytes ③ 目标解析:lead 或 active mate ④ 前缀 Team message from … ⑤ steer / steerHostSubagentPrompt Lead Agent 活会话 · root.steer() 消息落在目标 inbox 返回 messageId ≠ 模型已处理
Outcome

Lead 是始终活的会话:直接 root.steer(createUserMessage(...)),消息留在 Lead 的 inbox,在最近 step boundary 被看到。

模型看到:Team message from reviewer: …
What Team guarantees

成功 = 已进入目标 inbox(inbox 接收)。Team 不保留未接收意图、不在重启后重试、不对显式重发去重;冷恢复时先复用持久会话,再追加消息。

06

代码地图

FILES · LINE ANCHORS · KNOWN LIMITS

四个包、约 2700 行打包后代码。核心逻辑集中在 agent-team/lib/index.js:一个服务门面 + 五个 owner。右侧的边界清单不是缺陷列表,而是这套设计明确选择不解决的问题。

dsh-experimental-agent-team-profile

组合

cordis.patch.yml — 运行时内容:禁用全局工具 + 插入三插件
lib/index.js — 空模块(export {})
dsh.profile.bundles — 开关就是这一行的增删

dsh-experimental-agent-team

服务 / 状态机

index.js:1430 TeamService 门面 · :156 appendAndFlush
:943 roster.spawn · :1004 spawnAdmitted saga
:1112 reconcileProvisioning · :1587 sendAdmitted
:1200 TeamTaskBoard · :727 projection 定义

dsh-experimental-tool-agent-team

模型表面

index.js:22 teamPolicy(协作策略)
:227 install:8 个 scoped 工具 + 两段 prompt
:531 apply:agent/created 安装,disposed 卸载
:211 紧凑 JSON 输出 + schema 校验

dsh-experimental-client-ui-agent-team

浏览器

client.js:222 TeamAction:头部按钮 + 弹层
:465 registerAgentTeamUi:slots.inject
读 projectionsBySession[lead].values.agentTeam
无 额外 RPC / 后端接口

Design boundaries明确不做的事
  • 01单进程、共享 checkout。没有 worktree、没有文件锁;Bash / codegen 可绕过版本检查,最终 diff 仍由 Lead 负责。
  • 02owner 不自动释放。idle、interrupt、进程退出都不会把任务放回板;崩溃 owner 会永久留住任务。
  • 03发送无重试、无去重。成功只代表进入 inbox;未到达目标的历史消息不会被补投。
  • 04扁平 roster、实验性。无嵌套 Team、无重命名、名字永不复用;约定在孵化期可自由变更。