dsh-orchestrator 介绍与使用实践:让你的 AI 从「一个人硬扛」到「带团队干活

适合人群:已经在用 DeepSeek Harness(DSH)跑真实项目,却发现自己慢慢变成了「人肉调度员」的开发者------一边盯着 AI 写代码,一边在脑子里排优先级、记谁卡住了、催谁继续。

阅读收获:看懂「协调者 / 子会话 / 台账」这套结构;装好就能派出第一个任务;带走一份「目标怎么写、依赖怎么排、什么时候别用它」的避坑清单。


一、为什么你需要了解它?(WHY)

一个会话干所有活,会撞上三堵墙

你有没有遇到过这种场景:

早上九点,你打开一个 AI 编程会话,信心满满地说:「帮我做完这个功能------改接口、加页面、补测试、顺手把文档更新一下。」

AI 很配合,一路干到下午。你的会话窗口里,聊天记录已经长到需要压缩,中间夹着三段日志、两份报错、一次需求变更。然后它开始忘事:上午定好的字段名,下一轮又拼错了;原来的方案,下午悄悄变成了另一个样子。😭

这不是模型不够聪明,而是一个会话同时承担了太多角色:它既是产品经理,又是执行者,还是自己的验收人。三堵墙就摆在面前:

  1. 上下文墙:需求、日志、代码、报错全堆在一个上下文里,压缩之后最先丢的,往往是最重要的那条约束。
  2. 串行墙:一次只能推进一件事。你在等 A 的时候,B 和 C 只能排队------哪怕它们彼此毫不相干。
  3. 失忆墙:新开一个会话,前一天踩过的坑、定过的决策,全都归零。

真正的瓶颈不是「写」,是「调度」

我们早就过了「AI 写不出代码」这道坎。现在更常见的困境是:活太多,而你只有一个会话,而且你还得亲自当调度。

你在脑子里维护着一张表:哪个任务在跑、哪个卡住了、哪个要先等接口改完、哪个产物放在哪个目录。这张表一复杂,人就开始出错------忘了催、忘了看、忘了收产物。

而这张表本该是机器的事。

dsh-orchestrator 的解法

dsh-orchestrator 是 DeepSeek Harness 的一个插件(npm:@wjj865-code/dsh-orchestrator,当前 0.11.0,MIT)。它干的事很朴素:把你脑子里那张表,变成一份真的台账;把「拆任务、派任务、盯任务、收产物」这四件事,交给你自己的一个个子会话。

一句话总结:它不让你写得更快,它让你能同时开工的地方更多,而且不用你亲自记着。


二、它到底是什么?(WHAT)

一句话定义

一个「主会话 + 若干子会话」的编排层:主会话只负责拆任务和收回报,子会话各自在目标目录里干活,中间的一切状态(谁在跑、依赖谁、产物在哪、谁在等你拍板)都落在一份 SQLite 台账里。

三个角色,各管一摊

角色 是谁 能干什么 不能干什么
协调者 固定的那个「主会话」 拆任务、派子会话、看回报、用 present 声明交付产物 读写文件、跑命令------执行层有 guard 硬拦 read/write/bash 等工具
子任务 独立的 DSH 子会话,在指定目标目录运行 完整的文件 / shell / 搜索 / 网页 / todo / skill 工具;可只读、可续派、可停止、可重派 不能替别的任务说话(task_report 只对自己那一条任务生效)
台账 $DSH_HOME/state/orchestrator/tasks.sqlite 记状态、依赖、分组、写域、消息、产物、目标镜像;重启后账还在 不会自动恢复损坏的快照(只给路径提示)

这个分工的关键在于协调者被故意削弱了:它没有文件系统权限,所以「主会话手滑改了你的代码」这种事从根上不会发生;它只动嘴、只派活、只看文档。真正的手,在子会话那一边。

一次派发,走完这条流水线

text 复制代码
你说需求
  → 协调者 task_create():goal / acceptance / constraints 落库,生成任务编号 t-xxxxxxxx
      → 有 dependsOn 且前置没完成? 状态 waiting_dep,不派发、不占并发位
      → 前置全部 done? 自动晋升 pending,按 priority 排队
  → 派发:子会话拿到【任务编号】+「先调 task_detail 读详情」两行指引
  → 子会话 task_detail 自助读取:目标全文、验收、约束、背景、用户原话、
      工作目录与权限、前置任务摘要、经验文件、已有产物
  → 干活中:task_report progress / blocked / needs_auth(可带 question + options)
  → 收场:done → 结算为已完成;被中断 → 已取消;异常 → 失败;
      只报过进展就没了 → 会话丢失(等你在收件箱里拍板)
  → 收产物:done 时自动从工作区改动里收集文件,与显式声明的产物按路径去重合并
  → 沉淀:完成摘要进日志层只归档;被蒸馏成原子事实的经验/决策/偏好,下次派发时注入

注意其中一句:brief 只有两行指引 。这是 0.11.0 的一个刻意设计------目标正文不重复塞进提示词,子会话自己用 task_detail 去读创建时的快照。为什么这么改?后面「演进史」那节会讲,简单说:提示词塞得越满,子会话开局的注意力越贵。

12 个工具:10 个给协调者,2 个给子会话

归属 工具 作用
协调者 task_create 建任务并派发;支持 dependsOn / group / writeScopes / priority / readOnly
协调者 task_list 活跃 / 历史任务;按 group 看组进度
协调者 task_status 单任务详情:依赖、写域、分组、优先级、问题、最近消息;include: 'full' 看子会话最后一条回复全文
协调者 task_message 续派消息(改目标请走它)
协调者 task_stop 停止运行中 / 排队 / 等前置的任务
协调者 task_retry 重建 failed / orphaned 的任务,保留编排字段
协调者 task_inbox 收件箱:待你决定、失败/丢失、长时间没动静
协调者 history_search 查插件记忆、历史任务、平台历史会话
协调者 project_context 只读项目白名单文件、顶层结构、最近任务、相关记忆
协调者 memory_note 记一条跨任务有效的决策 / 偏好 / 经验(写入即蒸馏成原子事实)
子任务 task_report progress / done / blocked / needs_auth,可带 question / options / artifacts
子任务 task_detail 只读自己任务的创建详情;越权会被拒绝

12 个工具全局注册,但权限切得很干净:task_retry / task_inbox / history_search / project_context / memory_note 仅主会话可调,task_detail / task_report 只对任务自己的子会话开放。

和「多开几个窗口手动复制粘贴」有什么不同

维度 手动多开窗口 dsh-orchestrator
上下文 你自己在窗口间搬运背景,越搬越乱 每条子任务独立上下文,背景按需现读
状态 靠脑子记、靠标签页 台账落库,task_list / 面板随时看
依赖 靠你自己盯着 A 做完再开 B dependsOn 声明,完成自动晋升,失败沿链传播
失败 你发现它没动静,然后从头再来 结算自动收场 + 启动对账兜底,可直接 task_retry
产物 自己找、自己拷 done 自动收集,产物库按项目 / 日期分组
经验 下次重新踩一遍 蒸馏成原子事实,注入后续任务

三、怎么用?(HOW)

3.1 装它:一行包名 + 一次彻底重启

  1. DSH → 插件 → 添加插件,粘贴:

    text 复制代码
    @wjj865-code/dsh-orchestrator@0.11.0
  2. 彻底退出 DSH 再启动(只关窗口不生效)。

  3. 纯 dsh 表面左侧栏「插件」下方会出现「◈ 主会话」;Bot 模式用会话头「任务」按钮进面板。

两个入口的分工要记住:侧栏「主会话」始终可见 (没建主会话时靠它引导创建),会话头「☰ 任务」只在主会话显示------切到子会话或其他会话就自动隐藏。

装完发现是旧版? 大概率是镜像还没同步。npm publish 只写 registry.npmjs.org,而国内默认走的 registry.npmmirror.com 是另一套库,回源要几十分钟到几小时。维护者侧有 npm run mirror:check(只看不动)和 npm run mirror:sync(触发同步并轮询)两个脚本兜这件事。

还有一个隐蔽的坑:npm view --registry=https://registry.npmmirror.com 会命中本地 npm 缓存,显示旧 latest 甚至 404。怀疑时加 --prefer-online 再查,或直接看 HTTP 响应。

3.2 派第一个任务:把「验收标准」写进去

用自然语言对主会话说需求即可,协调者会把它翻译成 task_create:

js 复制代码
task_create({
  title: '修复空数据文案',
  goal: '空窗口时报表给出明确说明而不是空表',
  constraints: ['只改 src/report.js'],
  acceptance: ['空窗口输出「该窗口没有调用记录」', 'npm test 全绿'],
  user_verbatim: '你看这个空数据啥也没显示',
})

这四个字段值得逐个说清楚,因为任务质量九成取决于它们:

  • goal:要达成的结果,不是操作步骤。写「空窗口要有说明」而不是「打开文件、找到第 42 行、加一个 if」。
  • acceptance:怎么算完成,可判定的句子。「测试全绿」比「代码质量好」有用一百倍。
  • constraints:别碰什么。「只改 src/report.js」这类边界,比后面十次返工都便宜。
  • user_verbatim:用户原话。它是子会话理解语境的最后一道保险,而且这条不会写进日志。

3.3 依赖、分组、写域、优先级:把「排队」变成声明式

js 复制代码
// 先建 A,再建依赖 A 的 B
task_create({ title: '改接口', goal: '...', acceptance: ['...'], group: '登录改造' })
task_create({
  title: '联调登录',
  goal: '...',
  acceptance: ['...'],
  dependsOn: ['t-xxxxxxxx'],
  group: '登录改造',
  writeScopes: ['src/auth'],
  priority: 5,
})

四条规则记住就够了:

  • 前置没完成:B 是 waiting_dep,不派发、不占并发位------不会白白消耗一个子会话名额。
  • 前置全部 done:B 自动变 pending,用 task_detail 能看到前置任务的摘要和产物(按当前台账现读,不是创建时的快照)。
  • 前置失败:B 自动 failed,错误里写明原因。
  • writeScopes 只做冲突提示 ,不是文件锁;priority 高的先派。

3.4 你不盯着,它也不会装死

这是新用户最容易低估的一层:

机制 默认 作用
收件箱 task_inbox / 面板「待你决定」 开 集中看待确认、要授权、失败、长时间没动静的任务
blocked / needs_auth 带 question + options 按需 子会话卡住时,把问题和一个选择题一起递到你面前
结算收场(S1) 开 子会话被中断 → cancelled、异常 → failed、只报过进展就结束 → orphaned,不再让任务悄悄死在 running 里
启动对账(S2) 开 插件启动时扫台账,把「running 但子会话已不存在」的任务标 orphaned,兜住进程崩溃
空闲看门狗 开 每 5 分钟巡检,running 的任务 15 分钟无心跳/消息/状态变化就告警一次------只告警,不改状态、不 kill、不重派
日报 digestAt 关 到点汇总完成 / 在跑 / 待决定 / 失败 / 产物,比如 '09:00' + Asia/Shanghai

关于看门狗那句「只告警」,值得单独强调:它是观察者,不是执行者。你睡了一觉回来,它会告诉你「这三个任务没动静了」,但不会替你把它们杀掉------判断权始终在你手上。

3.5 任务面板:DeepSeek 风格的三段式

会话头点「☰ 任务」,会打开一个面板。它不是一张表格,而是三段式布局:

段 内容
顶部命令栏 标题 + 主会话状态点、任务搜索框、刷新、打开主会话;下面 4 张指标卡(进行中 / 待你决定 / 已完成 / 失败,点一下等于切筛选)
中部工作区 左侧筛选轨(带计数徽标)+ 任务卡列表;切到「产物」变成产物库(按项目 / 日期分组)
右侧详情抽屉 点任务卡或「详情」从右侧滑入,遮罩、Esc、关闭按钮都能收起

抽屉里有 7 个可折叠区块:概览、最新回复 (子会话最后一条 assistant 消息)、Brief (验收 / 约束 / 背景 / 用户原话)、目标状态 (只读)、消息 (待发 / 已投递 / 投递失败)、依赖 (上下游)、产物(复制路径);底部动作条是停止(内联二次确认)/ 重派 / 追加消息 / 打开子会话。

配色细节值得一提:颜色、圆角、阴影、动效全部取宿主的设计 token(--dsw-* 那套),所以深浅色主题自动跟随 ,面板自己不写死任何颜色。顺手修掉的一个老 bug 也很典型------旧代码用的 --dsw-alias-color-* 在宿主 token 表里根本不存在,一直走 fallback,深色模式下就是脏的。

3.6 几个值得专门开的高级开关

开关 默认 什么时候打开
readOnly: true 否 「先侦察、别动手」的任务:读代码、摸清结构、给结论。只认显式 true
keepAlive: true 关 希望子会话在回合边界接着跑 而不是先 idle 再结算;keepAliveMode 可选 steer(同回合多走一步)/ followup(新回合),keepAliveMaxRounds 默认 8 轮(1--50)
memoryInject / memoryLimit 开 / 3 条 新任务注入命中的事实层记忆(lesson/decision/preference/note);日志层不进注入
workerPreset: orch-debug dsh-orchestrator-worker 想让子任务跑在「创造模式」能力栈上(cordis_* 只读洞察工具 + dsh-doc 技能 + plugin_manager)调试插件时
staleMinutes / stallRounds 30 / 3 调整「多久算陈旧」和「连续几轮 progress 后提醒可能卡住」

keepAlive 的边界要写清楚,否则很容易被误解:它只 对「台账 running + 平台 goal 未完成」的任务生效,停止条件包括轮满、goal 已收口、任务离开 running、回合被中断、只读任务、收件箱里已排了下一轮。它是宿主替子会话开工(每轮一次完整模型调用),不是成本控制,也不能阻止显式 stop 或模型报错。


四、它是怎么长出来的?(0.0.0 → 0.11.0)

插件不是一次设计出来的,是被真实使用逼出来的。按 CHANGELOG 的版本主题看,主线非常清楚:

版本 主题 一句话
0.0.0-stage npm 占位(已 deprecate) 先占个名字
0.2.x 入口直达主会话、只读子任务 「能用」的最小事
0.3.x 发布流水线(provenance + 重复发布保护)、MIT LICENSE 敢往外发了
0.4.0 依赖编排 :dependsOn / group / writeScopes / priority 从「能派任务」变成「能编排」
0.5.0 收件箱 / 问题选项 / stallRounds / 日报 让卡点主动浮出来
0.6.x 产物库与面板操作台;拆出 ARCHITECTURE / LIMITATIONS 文档 面板成为主入口
0.7.0 面板动作错误码与幂等、详情抽屉、done 自动收集产物 操作要可解释、可重放
0.8.0 结算收场 S1 + 启动对账 S2 + 调试预设 不让任务悄悄死在 running
0.9.0 记忆分层 M1 + 空闲看门狗 W1 + 子会话目标 G1 上下文是最贵的资源
0.10.0 子会话读目标 G2、最新回复 B1、brief 瘦身 L1、协调者交付产物 P1 提示词瘦身,能力补齐
0.11.0 回合间 keep-alive K2 + task_detail T1 + 入口仅主会话 U1 + 面板 UI 重做 U2 从「塞提示词」转向「让子会话自己查」

几个转折点值得单独讲,因为它们解释了现在为什么长这样。

转折一:从「能派任务」到「能编排」

最早的版本只能派发独立任务,本质是「批量开工」。0.4.0 加上 dependsOn 之后,才有了依赖图 这件事:前置没完成的任务停在 waiting_dep、不占并发位,前置一完成自动晋升,前置失败则沿链传播。这也是「编排」这个词第一次名副其实。

转折二:最大的坑不是失败,是「沉默」

用久了会发现,真正让人抓狂的不是子会话报错------报错至少是个明确的信号。真正麻烦的是它什么都没说,就那么停在 running 里。你去睡觉,第二天回来一看:状态还在运行,会话早就没了。

0.8.0 的 S1(结算原因闭环)和 S2(启动对账)就是冲这个来的:消费子会话结束的 stopReason,把 running 推到终态(正常完成 → done、被中断 → cancelled、异常 → failed、只报过进展 → orphaned);插件重启时再扫一遍台账,把「running 但子会话已经不活跃」的任务标成 orphaned。

一句话:宁可给你一个「会话丢失」待你决定,也不要让它假装还在跑。

转折三:记忆不是越多越好

0.9.0 做了一件反直觉的事:把 done 的摘要从记忆里「降级」了 。完成摘要不再写进 task-done 这种会注入记忆的层,而是进日志层 task-log------只归档、不注入 brief。同时给写入加了蒸馏:整块丢弃代码围栏、shell/测试输出、引用原话、纯路径清单,事实层单条上限 160 字符。

只有真的带「结论 / 经验 / 注意 / 避免」这类标记的分句,才会被蒸馏成 lesson 原子事实,和 decision / preference / note 一起注入后续任务(默认 3 条)。

因为上下文是最贵的资源。 一条「这次改完了」的流水账对下一个任务毫无价值,但它会实打实占掉提示词预算。

转折四:brief 越写越短,子会话越来越自主

0.10.0 把目标挪给了平台 goal(子会话用 get_goal 读,brief 只留一句指引 + 160 字兜底),把经验挪进了 lessons/ 记忆文件(brief 只留条数、摘要和路径)。0.11.0 更进一步:brief 只剩两行------

text 复制代码
【任务编号】t-xxxxxxxx
【任务详情】先调 task_detail 读取完整任务详情......

目标全文、验收、约束、背景、用户原话、工作目录、前置任务、经验、产物,全部由子会话自己 task_detail 现读。好处很直接:提示词长度与任务复杂度、记忆条数彻底解耦,派发不再因为「塞了太多背景」而变贵;前置任务的进展也不再是创建那一刻的快照,而是派发时的实时台账。

代价也有:task_detail 必须是只读的、必须做越权校验(exec.agent.id 或 exec.agent.session.id 必须等于台账里那条任务的 session_id,否则返回「读取被拒绝」,主会话查任务请用 task_status)。

转折五:能力越强,越要默认关

0.11.0 的 keepAlive 是最能说明「设计克制」的一处:它能让子会话在回合边界接着跑,理论上是个很强的自动化能力。但作者给它的默认值是关,而且:

  • 只对「running + 平台 goal 未完成」的任务注入;
  • 轮数预算落台账(默认 8 轮),每次进入 running 归零,重复/并发事件用条件 UPDATE 原子加轮,不超发;
  • 与结算去重:不改状态、不写 progress_count、不写 followup 消息、不触发结算;
  • 监听器全量 try/catch,绝不把异常抛回派发链路。

同样克制的还有目标状态:任务派发时把 goal 同步成平台 goal,然后立即 disarmed------goal 只是持久目标状态,不会驱动子会话自动续跑。

顺带一个冷知识:0.8.0、0.9.0、0.10.0、0.11.0 都是 2026-11-15 发的 ,四个 minor 在同一天落地,0.11.0 的 npm test 跑完是 602 条全绿。


五、最佳实践

✅ 1. 目标写「验收」,不写「步骤」 goal 写结果,acceptance 写判定句。把步骤留给子会话------它比你更清楚自己这一轮要怎么走。

✅ 2. 一个任务一个写域 writeScopes 虽然只是提示,但它会帮你在派发前想清楚「这两个任务会不会打架」。真想避免冲突,靠的是拆分,不是靠声明。

✅ 3. 用 group 组织,别用编号 「登录改造」比「第 3 批」有用得多:组进度可以直接看,人的记忆也挂得住。

✅ 4. 别在目标里复述背景 经验、前置任务、已有产物都走 task_detail 现读。目标里复述一遍,既费提示词,又容易和实际状态不一致。

✅ 5. 把把关点做成选择题 卡住时用 blocked 带 question + options,让它出现在通知、详情和面板里------比在一条长消息里藏一句「请问这里要不要改」有效得多。

✅ 6. 侦察任务先上 readOnly 「先别动手,告诉我这块代码是怎么组织的」这类任务,用 readOnly: true,子会话进只读沙箱,物理上杜绝顺手改坏。

✅ 7. 产物写绝对路径 子会话的工作目录不是你的目录。present 交付时写绝对路径,才点得开。

✅ 8. 每天扫一眼收件箱 尤其是挂了日报的:完成 / 在跑 / 待决定 / 失败 / 产物一屏看完,比逐个点任务卡快得多。


六、常见误区与避坑指南

❌ 误区一:把它当「更快的 AI」

它不加速单次推理。它省的是你的调度成本 和单个会话的上下文压力 。每个子会话都是一次完整的模型调用,keepAlive 更是宿主替子会话连续开工------能力上很爽,账单上要自觉。

❌ 误区二:目标写成一篇需求文档

目标越长,子会话开局可用于「理解任务结构」的注意力越少。好的 goal 通常一两句:要什么、不要什么。背景放 constraints 和 refs,别全塞 goal。

❌ 误区三:以为 writeScopes 是文件锁

它只提示冲突 ,不是锁。两个任务同时盯上 src/auth 也不会互相阻塞,该串行请用 dependsOn。

❌ 误区四:依赖建完还想改

dependsOn 创建后不可编辑,也不允许指向未来的 id。所以拆任务时想清楚:不确定就先拆小、串成链,别指望事后调整。

❌ 误区五:装完没彻底重启,就说「插件坏了」

只关窗口不生效,必须彻底退出 DSH 再启动。另外「装到的是旧版」九成是镜像没同步------先 mirror:check,再怀疑插件。

❌ 误区六:在子会话里找任务面板

入口只在主会话显示,而且判定信号不可用时默认隐藏(fail-closed)。这不是 bug,是刻意的:宁可少一个入口,也不在子会话里露出一个会误导你的面板。

❌ 误区七:指望它替你控制成本

keepAlive 默认关,有轮数上限,注释里写得很直白:它是宿主替子会话开工,不是成本控制。想省钱,关掉它、缩短目标、减少并发,比调任何参数都直接。

❌ 误区八:以为日志会替你记住一切

goal / summary 在日志里只留 80 字,user_verbatim 不写日志。想让某个决定被记住,用 memory_note 显式记一条原子事实------写日志和写记忆是两件事。


七、它不适合什么场景(诚实说)

  • 一句话能改完的小改动:拆任务、建台账、写 brief 的处理开销大于收益,直接说就行。
  • 需要强实时协同的编辑 :两个人(或两个子会话)同时改同一批文件,writeScopes 帮不了你。
  • 想让它自动无限续跑 :keepAlive 默认关、有轮数预算,平台 goal 创建即 disarm。这是设计取向,不是待修 bug。
  • 需要跨机器或多人共享状态 :台账是本机 $DSH_HOME/state/orchestrator/ 下的一份 SQLite,插件不做多机同步------它是你一个人的记账本。
  • 完全无人工在场的流水线 :它明确把判断权留给人------blocked / needs_auth / orphaned 都在等你看一眼。想看「它自己跑完全程」,那它不是这个工具。

八、写在最后

这篇文章本身就是一次派发。

主会话建了「公众号推广」组里的一个子任务(编号 t-5a95ac12),只给了三样东西:目标(写一篇介绍和推广这个插件的文章)、五条验收标准、几份背景资料的路径。然后一个子会话在指定工作目录里读素材、提炼过往 17 篇文章的风格、成稿、跑自检、最后用 task_report done 把产物交代清楚。

中间没有人催,也没有人搬上下文------因为该记的都在台账里。

回到最开始那三堵墙:上下文墙靠「任务级隔离 + 按需现读」拆掉,串行墙靠「多条子会话并行」绕过,失忆墙靠「蒸馏后的原子事实注入」补上。这套东西没有任何魔法,它的价值就在于把一件本该由流程负责的事,从你的脑子里搬进了数据库。

装它不需要改你的工作方式:你照旧用自然语言说话,只是从今天起,你可以放心地一次说三件事,然后去喝杯咖啡。☕


参考链接

相关推荐
M--Y20 分钟前
LangGraph 工作流模式
开发语言·javascript·python·langchain·大模型·llm·langgraph
泯泷27 分钟前
AI Agent 工具设计:接了上百个工具,为什么反而更难干活?
llm·agent
小虎AI生活1 小时前
Vibe 知识视频火了!不剪辑不烧积分,我用 WorkBuddy 直出两条成片
aigc·ai编程
泯泷2 小时前
AI Agent 工具设计:读完一百页 PDF,不等于真的看懂了
人工智能·llm·agent
IT规划师4 小时前
热点速递 | Chrome、Edge、Firefox、MDN 把 Web 最佳实践做成 Skill
ai编程
泯泷5 小时前
AI 怎样"查资料"?——RAG 检索增强生成入门(三)
人工智能·llm
xyphf_和派孔明5 小时前
第五章: LangGraph的路由代理-Router Agent介绍
langchain·llm
泯泷6 小时前
让知识"长骨架"——RAPTOR、GraphRAG 与 OpenViking(四)
人工智能·llm