dsh-agent-dispatch · 帮手调度台
> 一块设置面板:决定主 agent 要不要别的 agent 帮忙、准哪几个帮手上场——而且真的生效。策略以 systemPrompt 段的形式注入,保存即生效。
- 看得见:一个设置分区,显示状态、通道与帮手名册(含实测首字延迟)。
- 点得动:总开关 / 三态模式 / 通道勾选 / 帮手勾选 / 并发上限 / 附加要求,改完点保存。
- 真的生效:策略文本是函数,每步重新求值;关闭时注入「本轮不派活」的明确指令。
- 会自检:每次动工前先估一句话——这活能不能拆成几块互不依赖的?帮手卡住就换人,换不动就交给兜底。
它解决什么 · 为什么省 token
主 agent 每推进一步都要重发完整上下文(系统提示 + 历史),按 cacheMiss 计费,是 token 账单的大头。独立的帮手各有独立上下文,且免费车道输入输出零成本。所以省钱的关键不是「少说话」,而是把自包含的活挪出去。这个插件管的就是「挪哪些、挪给谁、挪几个」。
分工原则(写死在注入文本里):简单、机械、自包含、不依赖主会话上下文的活(批量检索、逐项审计、抄改重排、列清单、翻译、格式化、初稿)派给免费车道的帮手;最难、最需要判断、后果在意的部分(架构与关键设计、跨模块推理、正确性与安全判断、分歧裁决、最终交付)留在主 agent。
安装
插件目录:D:\dsh-agent-dispatch(本仓库)。两条路:
A. 自动改 profile 清单(推荐)
node scripts/install-into-profile.mjs # 预演,打印将改的两行
node scripts/install-into-profile.mjs --apply # 真的改(自动备份 package.json)
node scripts/install-into-profile.mjs --revert # 撤回到备份
脚本只动一处文件:/profiles//package.json 的两处——dependencies 里的 link: 依赖,和 dsh.profile.bundles 里的包名。改完在该 profile 目录跑 pnpm install(让 link: 变成 node_modules 里的 junction)并重启 dsh(patchReload: "live" 管得了热更,管不了新增 bundle,重启会结束当前 GUI 会话)。
> 注意:pnpm install 会把 node_modules 对齐到 package.json 的声明版本——如果某个插件此前被插件管理器更新到更高版本,这一步会把它降回声明值。想保持新版:pnpm add <包>@<版本> 后再重启。
B. 手动
等价于手动改 C:\Users\qq167\.dsh\profiles\web\package.json 两处:
dependencies里加:
"dsh-agent-dispatch": "link:D:/dsh-agent-dispatch"
dsh.profile.bundles数组里加包名:"dsh-agent-dispatch"
然后 pnpm install + 重启 dsh。
装好后:设置 → 帮手调度。
面板速览
八个区块,从上到下:
| 区块 | 内容 |
|---|---|
| 总开关与三态模式 | 开启 / 关闭;模式三种:关闭 / 先问我 / 直接派 |
| 帮手通道 | workflow 扇出 / 子代理单派 / Agency 专家 / Agent Teams;没勾的通道写进禁止清单 |
| 帮手名册 | 三层来源合并(宿主已注册 / 车道实测 / 手填 + 内置参考),每行标来源;★ 是默认主力 |
| 派活规模 | 同时最多几个帮手(1–8)、任务超过多少步才值得派、短任务是否禁止派活 |
| 动工前预估 | 是否每次动工先估一句话;拆出几块才算值得建队;预估通过能不能当场建 Agent Teams 队 |
| 卡住改派 | 多少步没产出算卡住、同一个活最多改派几次、之后交给谁收尾(主 agent 或某个已勾选的帮手) |
| 附加要求 | 原样写进注入文本 |
| 查看注入文本 | 展开就是真正发给 agent 的策略,不是示意图 |
顶部另有首次引导(四步,看过一次不再出现)与宿主适配自检(解释每个功能为什么在或不在)。首次打开时,侧栏设置区还会出现一张引导小卡片(自带排版,不占用设置页布局):「打开设置去配」直达本页,「暂时不用」即只看这一次。
动工前预估与卡住改派
配置 v3 新增的两个行为。两者都写进注入文本(agent 每步都会读到),都能在面板上开关。
动工前预估(plan)。动工之前,主 agent 先用一句话回答:这份活能不能拆成几块互不依赖的?每一块能不能写成自包含 prompt?做错能不能一眼看出来?三问都是「是」→ 值得派;否则自己干,也不为了并行而硬拆。预估通过、并且拆出至少 plan.minBlocks 块时,插件允许当场建一个 Agent Teams 队、把每块写成一条任务——但必须同时满足 channels.teams 已勾选 且 plan.autoTeam 打开。询问模式下建队前仍先问用户一次;关掉 autoTeam 则永远要用户明确要求。
卡住改派(failover)。帮手连续 failover.waitSteps 步没有任何产出就算卡住:主 agent 先发一条消息问一句(长调研可能只是安静),确认没动静就把这块活先改派给另一个模型、另一条上游(同一个模型换个成员修不好它——实证卡住的根因是上游过载和模型被弃用),最多改派 failover.maxRetry 次,之后交给 failover.fallback。勾了 Agent Teams 时,观察与改派用 agent_teams_status / agent_teams_reassign_task;否则用 list_agents,子代理通道开着时用 interrupt_agent + subagent。改派时把已确认的约束和已有的部分产出一并转交;已经派出去的活绝不重复派,等待期间也不许整轮空转。
任一开关关掉都不是删掉文本,而是换成一句明确的「本轮不做」指令,agent 不会自己发挥一套预估或改派。
配置键表
落盘于 $DSH_HOME/agent-dispatch/config.json(默认 C:\Users\qq167\.dsh\agent-dispatch\config.json),原子写(tmp + rename)。面板每次打开重新读取,手改此文件与面板改等价。
| 键 | 类型 | 默认 | 作用 |
|---|---|---|---|
version |
number | 3 | 配置结构版本;v1 的裸模型键加载时自动补 provider 前缀,v2 文件会补上 plan / failover 两块 |
enabled |
boolean | true |
总开关;关 = 注入「不派活」明确指令 |
mode |
'off' \| 'ask' \| 'auto' |
'ask' |
派活模式 |
peer |
string | 'our-free-model' |
名册默认 provider 名(只决定参考名册挂在谁名下,不要求这台机器真有它) |
discover.llm |
boolean | true |
是否问宿主 llm 服务枚举 provider/model |
discover.siblings |
boolean | true |
是否扫 $DSH_HOME 下兄弟插件的状态文件 |
channels.workflow |
boolean | true |
允许 workflow 扇出 |
channels.subagent |
boolean | true |
允许子代理单派 |
channels.experts |
boolean | false |
允许 Agency 专家 |
channels.teams |
boolean | false |
允许 Agent Teams |
helpers.\{provider}:{model\} |
object | 见下 | 键为 provider:model,值 { enabled: boolean, label?: string } |
primary |
string | 'our-free-model:nemotron-3-ultra-free' |
默认主力帮手;必须是已勾选的模型,否则退回第一个已勾选 |
maxHelpers |
number | 4 | 同时最多派几个(夹回 1–8) |
minSteps |
number | 3 | 任务超过多少步才值得派(夹回 1–20) |
longTaskOnly |
boolean | true |
打开后一句话能答完的活不许派 |
plan.enabled |
boolean | true |
是否每次动工前先估一句话 |
plan.minBlocks |
number | 2 | 拆出几块互不依赖的活才算值得建队(夹回 2–8) |
plan.autoTeam |
boolean | true |
只在 channels.teams 已勾选时有意义:预估通过可以直接建队、不必等用户当场再提一次;关掉则仍要用户明确要求 |
failover.enabled |
boolean | true |
帮手卡住时是否改派,而不是继续干等 |
failover.waitSteps |
number | 6 | 多少步没产出算卡住(夹回 1–50) |
failover.maxRetry |
number | 1 | 同一个活最多改派几次,用尽即走兜底(夹回 0–3;0 = 一次不重试) |
failover.fallback |
string | 'main' |
兜底谁来做:'main'(主 agent 自己接手)或一个 provider:model 键;其它值一律读作 main |
onboarding.seen |
boolean | false |
看过首次引导后置 true |
notes |
string | '' |
附加要求,原样写进注入文本(截尾 2000 字符) |
出厂默认 helpers 勾选了六个 our-free-model 下的实测可用模型:nemotron-3-ultra-free、nemotron-3.5-lightning-free、space-bunny-free、longcat-2.5-preview-free、ling-3.0-flash-fin-free、mimo-v2.6-flash-free(v1 配置无 provider 前缀的键自动补 our-free-model:)。
通道与名册
通道(CHANNEL_IDS 顺序即面板顺序)
| id | 标签 | 说明 |
|---|---|---|
workflow |
workflow 扇出 | 一个脚本里并排跑多个独立子任务,可逐项指定 provider/model |
subagent |
子代理单派 | 把一整块独立任务丢给另一个上下文,只收回结果 |
experts |
Agency 专家 | 按领域召唤专家人格(需在设置里已启用) |
teams |
Agent Teams 团队 | 多成员共享任务板协作(需用户明确要求;动工前预估通过且 plan.autoTeam 打开时可直接建队) |
名册三层来源
每一行都有来源标签,面板显示为「车道实测 / 宿主已注册 / 手填 / 内置参考」:
- 宿主 llm 服务实枚举(来源
llm,标签「宿主已注册」)——唯一能证明「这个 provider/model 现在真的能调」的来源。 - 兄弟插件状态文件(来源
peer,标签「车道实测」)——扫$DSH_HOME/<任意插件目录>/catalog.json+availability.json,不写死名字,拿到实测首字延迟。 - 面板手填(来源
manual,标签「手填」)——用户可填任意provider:model,哪怕别的付费车道。 - 内置参考名册(来源
seed,标签「内置参考」)——11 行our-free-model模型快照,保证面板第一次打开不空。不是事实来源,只说明「长什么样」。
合并优先级:llm 枚举 → verified=true;兄弟插件 availability → state / ttftMs;内置参考 → 只补长什么样,不覆盖上面两者的判定。
HTTP API
全部走同源路由 /api/agent-dispatch/*,与宿主半身在同一进程。只服务本机浏览器,非 loopback 一律 403。
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | /api/agent-dispatch/summary |
返回当前配置、名册、预览策略、统计、宿主适配状态 |
| GET | /api/agent-dispatch/health |
返回服务连接状态、配置路径、名册来源计数 |
| POST | /api/agent-dispatch/config |
body { patch } 合并配置并落盘,返回完整 summary |
| POST | /api/agent-dispatch/rescan |
重新枚举 llm 并重建名册 |
| POST | /api/agent-dispatch/reset |
恢复出厂默认并落盘 |
兼容与降级
inject = [] + 嵌套 fiber 机会式获取 webServer / systemPrompt / llm 三个服务。任一缺席只少对应功能,插件照常可用:
- 无
webServer:面板 API 不可用(浏览器半身不能读写配置),但systemPrompt段照常注入。 - 无
systemPrompt:策略不注入(面板改完不生效),但 API 与名册正常。 - 无
llm:名册只剩车道实测 + 内置参考 + 手填;手填一条provider:model仍然可用。 - 无任何服务:面板照常可用(纯内存配置 + 内置参考名册)。
写盘失败不致命:配置先改内存并立刻生效,面板提示「写盘失败,重启后回旧值」。
卸载
node scripts/install-into-profile.mjs --revert(恢复备份),然后 pnpm install + 重启 dsh。
$DSH_HOME/agent-dispatch/ 可留可删。
开发与自测
node --check index.js # 语法检查宿主半身
node --check client.js # 语法检查浏览器半身
node scripts/check-i18n.mjs # zh/en 字典同键、且代码里 t() 引用的键都在字典里
node scripts/check-links.mjs # Markdown 相对链接与图片目标是否真实存在
node scripts/selftest.mjs # 纯函数自检(策略渲染 / 配置收敛 / 名册构建),131 项
node scripts/smoke-host.mjs # 假 cordis ctx 下跑真实 apply(),含 403、落盘、即时生效,39 项
node scripts/smoke-client.mjs # 桩 React + 真 /summary 数据,把 Panel 真渲染四遍,34 项
selftest.mjs 在临时目录造假伙伴状态,验证名册优先级、地区受限与暂不可用的降级、配置收敛(0 并发、非法 peer、非法键名都夹回安全值)。smoke-host.mjs 用假 webServer / systemPrompt 服务跑真实 apply(),确认路由挂上、配置落盘、段文本是函数(所以「保存后下一步生效」是结构事实),覆盖非 loopback 403、坏 JSON 500、disposer 可调用,以及 v3 全链路:预估与改派两块出现在注入文本里、预估通过后 teams 禁令解除、两块在 /reset 后回到出厂。smoke-client.mjs 用迷你 hooks 运行时当桩 React,喂给面板的是宿主半身真跑出来的 /summary,断言渲染树里有名册键、有真注入策略的原文、引导卡看过就消失,再勾上 Agent Teams 并调过预估与改派参数重渲染一遍,确认两个新区块(含禁用与锁定态)都真能渲染出来——node --check 抓不到「一开就是空白」这类崩法,这一层专门抓。
npm run check(语法)、npm run i18n(字典一致性)、npm run links(链接检查)、npm run test(自检)、npm run smoke(宿主半身)、npm run smoke:client(浏览器半身)、npm run verify(六项全跑)已配在 package.json 的 scripts 里。
相关文档
| 文件 | 用途 |
|---|---|
| README.md | 英文版(English primary) |
| AGENTS.md | 给 AI 编码 agent 的工作约定:结构、契约、自检 |
| INSTALL.md | 安装、验证与卸载步骤 |
| docs/GLOSSARY.md | 术语表:本仓库所有文档与界面文案的唯一口径 |
| CONTRIBUTING.md | 贡献指南:提交信息规范、分支流程、文档约定 |
| SECURITY.md | 安全策略与漏洞报告方式 |
| CODE_OF_CONDUCT.md | 社区行为准则 |
| CHANGELOG.md | 版本变更记录 |
| docs/market-entry.md | 上架 DSH 插件市场的条目与收录规则 |
| docs/publish.md | 发布流程(含需要人工授权的步骤) |
许可
MIT。仓库:github.com/Napstablooky233/dsh-agent-dispatch。
English version: README.md.