dsh-agent-bus
English | 中文
<p> <a href="https://github.com/MistyBridge/dsh-agent-bus/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT"></a> <a href="https://github.com/MistyBridge/dsh-agent-bus"><img src="https://img.shields.io/badge/platform-DeepSeek%20Harness-1a73e8" alt="DeepSeek Harness"></a> <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933" alt="Node.js"></a> </p>
DeepSeek Harness 上的多 Agent 编排。 别再当传话筒。
dsh-agent-bus 是 DeepSeek Harness 插件:把同一工作区里的活跃会话编成一套班子——互相派活、验收对方产出、按 DAG 跑多步骤流程。投递走的还是你已经在用的 Inbox。
专家还是那些专家。复制粘贴的人不再是你。
为什么要做这个
Harness 已经能在一个工作区里开多个 Agent。它并不能让它们协作。
没有这个插件时:
- 规划会话没法给编码会话派活。你得把 brief 贴过去。
- 编码会话没法等验收会话。你得把 patch 贴过去。
- 第 3 步失败了,你得从聊天记录里把第 1、2 步重新拼回来。
用总线来管理团队,而不是你自己在当传话筒。
你实际能得到什么
说话就是说话。 提问、确认、「看一下这个」用 send_note。不落台账、不验收、不做超时戏。对方离线,消息入队,上线再送。
干活就是干活。 create_task 是一件有正文、可选验收标准、有验收人的工作。执行方 report;验收方通过,或把同一条任务连同修改意见打回去。重做全程同一个 id。
计划可以在你不插手时往下跑。 create_flow 是一张命名 DAG。你(或规划 Agent)先写 plan,再按 flow_id 和 dependencies 拆任务。B 在 A 被验收之前根本不会投递。A 被取消或终态失败,B、C 跟着失败——不会留下还在空转的执行者。
下一棒读到的是链条,不是考古。 结算后,执行方可以给每个后向任务附交接(数值、决策、注意事项)。投递时拼进下游正文。第 3 棒不必靠 get_task 把第 1 棒翻出来。
你看得到。 Web 界面右侧胶囊打开工作台:任务列表,以及按流程的 DAG 画布。点节点看全文要求。已归档的祖先留在图上,淡显。
任务日志
会话聊天不适合当工作台账。问候和任务混在一起,压缩上下文时会丢,下一棒也无法查询「昨天验收通过的是哪一件」。
Agent-bus 在对话旁边另有一份 任务日志:只记真正的工作,不是每条消息的全文转储。
消息留在会话里。 send_note 是聊天。不落台账行、不上面板、不需要 report。那个会话的 jsonl 就是它的记录。
任务落台账行。 每次 create_task 写下谁派的、谁做、谁验收、任务要求、可选验收标准、依赖,以及后来的判定。状态在这一行上走(queued → submitted → working → completed → settle)。重做是同一个 id;get_task 能读出一件活的完整一生。
报告是文档,不是又一段聊天。 短报告内联在行上。长报告按 task id 外置到磁盘——引用是 id,不是模型可能泄漏的路径:
| 区 | 位置 | 内容 |
|---|---|---|
| 热 | ~/.dsh/agent-bus/cache/ | 活跃任务报告;7 天未访问清理 |
| 冷 | ~/.dsh/agent-bus/archive/ | 终态任务(completed / failed / canceled);30 天未访问清理 |
get_task 先热后冷。模型看不见分区。台账本身在 harness 存储域(agent_bus);每次打开还会在 ~/.dsh/agent-bus/backups/ 写一份 JSON 快照(保留最近 20 份),避免 schema 重建把表吃掉。
人看的和模型列的不一样。 面板是给人的日志:活跃工作、归档(结算超过 24 小时,或失败/取消)、token、某个流程的 DAG。list_tasks 故意不列归档行——执行方收件箱不是历史堆。历史在面板、get_task 和会话日志里。
目的就这一条:下一棒、验收方、以及你,读的是同一份记录,而不是从三个聊天窗口里把活重新拼出来。
快速开始
dsh plugin --profile web add dsh-agent-bus
dsh web本地开发:
dsh plugin --profile web add .
dsh --profile web --dump-config
dsh webweb-app bundle 已挂载存储和工作区注册表。自定义或 headless profile 须在自己的 cordis.patch.yml 里声明 storage、storage-json、storage-domain、workspace——否则加载即失败。记不住账的网关,不应以静默降级形态启动。
需要 Node.js ^22.19.0 或 >=24。
它怎么工作
投递是 harness 的 Inbox:一次 followup() 一轮 turn,空闲会话才取下一项。本插件不再造一条队列。
插件负责的是台账——谁派的、谁做、什么叫完成、谁依赖谁——以及读这份台账的面板。
没有接收侧工具。执行方看到的是普通一轮对话。做完调用 report_task。
消息 send_note → 对方用散文回复(也可以不回)
任务 create_task → queued → submitted → working → completed → settle
流程 create_flow + 任务 → DAG 在前置结算后自动投递下一节点选能覆盖需求的最轻通道。把聊天写成任务,工作会卡死在 working;把任务写成聊天,就丢掉验收。
Agent Bus 对比 Sub-agent
为什么放弃 sub-agent 架构
Harness 已经有 sub-agent:父会话调用 spawn_subagent,子会话拉起来干活,交回一份摘要。适合「隔离着查一下再回来」。
团队没有建在这套架构上。子代理通常继承主会话的权限组和配置——skill、MCP、插件组、模型、allowlist。你可以裁工具带(agent 类型、capability mode、persona),但很难做到:编码会话自己的仓库 MCP、调研会话自己的网页 MCP、验收会话更严的租户 allowlist——三套不同的配置。专家团队要的就是这种精细化配置,继承做不到。
所以总线上的每一个对象就是一个普通的 DeepSeek Harness 会话——和你在 dsh 里已经会配的那种。它保住自己的 skill、MCP、插件组、权限预设和模型。组团队就是这么组的;多租户要挂的也是这套会话模型——按租户 / 按角色的权限组与 dsh 插件组,而不是「父会话这次 spawn 了什么」。
| Sub-agent | Agent Bus | |
|---|---|---|
| 工作单元 | 为这一次活新开子会话,结束即丢 | followup() 投进已经存在的同伴会话 |
| 执行方是什么 | 一次性孩子:类型 + capability mode + 可选 persona | 你在 dsh 里配好的一等会话实例 |
| Skill / MCP / 插件 | 从父会话继承,spawn 时通常被裁 | 按会话:自己的 skill、MCP、插件组 |
| 权限 | 父会话的信封再收窄 | 按会话(多租户宿主上还可以按权限组) |
| 拓扑 | 星型:父会话是枢纽 | 同工作区 peer + 持久台账 |
| 谁验收 | 父会话读摘要 | 独立验收方,通过或把同一 task id 打回去重做 |
| 顺序 | 下一步必须由父会话再 spawn | DAG:A 结算之前 B 根本不会投递 |
| 失败 | 父会话得自己发现 | 终态失败 / 取消沿下游自动传播 |
| 进程重启 | 剧本在父会话上下文里 | 台账 + Inbox 检查点还在 |
| 并行 | 一个父会话可以同时挂很多子会话 | 多个 peer 同时干活;每个 peer 仍然一条一 turn |
成本实际花在哪
没有虚构的「快几倍」。差别是 token 和延迟花在谁身上。
| 成本 | Sub-agent | Agent Bus |
|---|---|---|
| Prompt cache | 每次 spawn 都付一遍冷前缀(系统提示、工具、指令)。 | 专家是长会话。下一件任务是同一前缀上的下一轮 user turn,缓存是热的。 |
| 编排者上下文 | 每个子会话的摘要都进父窗口。N 件活 → 父上下文按 N 份摘要涨。 | 发起方只收到一条短通知。全文在台账里(长报告落盘)。需要时再 get_task。 |
| 到首 token 的时间 | 拉起会话 + 冷缓存上的第一次解码。 | 同伴在线且空闲:就是下一轮,不新开进程。 |
| 专家记忆 | 子会话结束就没了。第 4 件活不记得第 3 件,除非父会话把摘要塞进下一次 spawn。 | 同一个编码会话窗口里还留着第 3 件活(工作区文件也还在)。其余靠交接文档。 |
| 一次性探索 | 用这个。 隔离窗口,父会话缓存不被污染。 | 别用。peer 是团队里的人,不是沙箱。 |
口诀: 要保护调用方上下文、干完就扔 → spawn sub-agent。被叫的那一方就是一个有自己 skill / MCP / 插件 / 权限、还要接下一件活的同事 → 用 bus。
工具
| 你想… | 用 |
|---|---|
| 问一句、不是派活 | send_note |
| 给一个同伴一件要验收的活 | create_task |
| 按顺序跑一个多步骤计划 | create_flow,再带 flow_id / dependencies 的 create_task |
| 交差 / 验收 / 重做 / 停掉 / 反问 / 换人 | report_task · settle_task · cancel_task · request_input · reassign_task |
| 把上下文交给下一棒 | submit_handoff |
| 改还没投递的节点,或查记录 | edit_task · list_flows · list_tasks · get_task |
| 看谁在线,声明自己能做什么 | list_peers · update_card |
文档
[docs/usage.md](docs/usage.md) | 操作手册:工具、状态机、模板 |
[docs/v1.5-resilience-spec.md](docs/v1.5-resilience-spec.md) | 离线消息、转派、离线宽限 |
[docs/v1.4-event-driven-scheduling-spec.md](docs/v1.4-event-driven-scheduling-spec.md) | 事件驱动排期、流程、交接 |
[docs/a2a-alignment.md](docs/a2a-alignment.md) | A2A 任务状态对齐 |
License
MIT