Skip to content

dsh-agent-graph

Verified

dsh-agent-graph · v0.2.1 · MIT · Web UI

Lightweight graph orchestration for DeepSeek Harness: scoped agent nodes, handoff contracts, bounded rework, and a layered global ledger.

Install

dsh plugin add dsh-agent-graph

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-agent-graph

DeepSeek Harness 的图编排插件:职责单一的 agent 节点、结构化交接、有界返工,以及分层的全局账本。

npm version npm downloads Node.js >=22 MIT license

English · 简体中文

dsh-agent-graph 的 DAG 工作流:职责清晰的 agent 结构化交接、向直接上游的有界返工,以及共享账本记录

一个「交付这个需求」的任务,可以拆成各管一段的节点:调研、spec、实现、测试计划、写测试。dsh-agent-graph 负责运行这张图。每个节点都是一次性的 DSH 子 agent;上游把交付以结构化契约的形式交给下游;下游发现上游交付不足时,带着证据把问题退回直接上游;所有节点的进度、todo、关键决策与踩坑都记录在同一个分层账本里,人和 agent 都能从 README 分层下钻查看。

dsh plugin --profile web add dsh-agent-graph

目录

它是什么

关注点 dsh-agent-graph 的做法
任务拆解 needs 边声明依赖的 YAML 图;运行前校验环、未知引用、重复 id
节点执行 每次激活 = 一次 ctx.subagents.start('spawn', …);注入 scope prompt、上游交接、账本指针与结构化输出 schema
注意力聚焦 节点之间不共享会话;一次激活只看到自己的 prompt、上游交付与账本
可视化编排 在 DSH Web 的 编排 会话页签中,以拖拽画布、节点属性、依赖勾选和即时校验创建 DAG
节点间交付 结构化交接版本(summary / artifacts / openIssues)注入每一个直接下游激活
上游交付不足 有界返工请求退回直接上游,附证据与验收标准;可 provide / decline / 逐跳 forward
共享记忆 每次运行一个账本(READMEindex → 节点分区 → 明细),节点读写,引擎确定性再生
失败行为 停图(fail-fast);/graph resume 重试失败与中断的节点
可观察 /graph status + 账本;每次激活、交接、返工轨迹与索引都落盘

图以人类斜杠命令(/graph)的形式在 DSH 会话里执行,不是暴露给模型的工具。但节点本身是普通子 agent,可以使用宿主授予它们的工具。

为什么需要它

DSH 已经很擅长一次性的委派。长链路多步任务会碰到三个反复出现的问题:

  • 上下文串味:一个会话把所有关注点堆在一起;被要求写测试的节点开始操心架构。历史越长,职责边界越模糊。
  • 沉默补偿:上游结果不够好时,下游倾向于猜、绕过去、或悄悄扩大自己的范围,而不是把精确的请求退回去。
  • 推理蒸发:节点被重新拉起(重试、resume、追加)时,对话没了,当时的关键决策和踩坑也一并消失。

dsh-agent-graph 用机制而不是提示词来对应这三件事:用声明的图管边界,用带证据与上限的返工协议管升级,用持久账本管记忆。agent 保持一次性;图和账本持久。

与其他方式对比

这些能力解决的是相邻而非相同的问题,可以组合使用;按任务形态选表面即可。

需求 dsh-agent-graph 内置 subagent 内置 workflow 内置 ralph
工作单元 依赖图中的一个声明节点 一次委派调用 模型写的 JS 编排脚本 一个固定目标的迭代
依赖关系 显式 needs 边 + 拓扑调度 在对话里人工决定 脚本里命令式安排 不适用
agent 间数据 结构化交接契约 + 产物 最终回答文本 脚本变量 有界结构化报告
上游交付不足时 有界返工请求退回直接上游,附证据与验收标准 调用方重试或重新提示 脚本自行决定(无协议) 工作区 + 下一轮
重启后保留什么 图、交接版本、账本、运行状态(/graph resume 运行快照 / effect cache(随插件) 工作区
人类审查面 /graph status + 分层账本 聊天 运行 UI 聊天

任务有真实依赖结构、参与者超过两三个、且链路长到「谁在什么时候决定了什么」必须活过重新拉起时,用 dsh-agent-graph。只有一两次委派时,内置 subagent 是更短的路。

使用示例

仓库自带五节点示例(examples/feature-delivery.yaml)与无模型干跑脚本(examples/fake-script.json):

npm install
npm run demo

真实运行用同一张图,节点换成真实子 agent:

/graph run examples/feature-delivery.yaml
Started run feature-delivery-20260918-024342 (graph "feature-delivery", 5 nodes, concurrency 2).
Ledger: /path/to/workspace/.agent-graph/runs/feature-delivery-20260918-024342
Track with /graph status; nodes are DSH subagents, so this may take a while.

追踪与查看都在会话里完成:

/graph status

Run feature-delivery-20260918-024342 — feature-delivery · status completed
activations 7/24 · started 2026-09-17T18:43:42.782Z · finished 2026-09-17T18:43:42.807Z

node               state            try  handoff
implement          ok               2    nodes/implement/handoff/v1.md
research           ok               1    nodes/research/handoff/v1.md
spec               ok               1    nodes/spec/handoff/v2.md
test-plan          self_handled     2    nodes/test-plan/handoff/v1.md
tests              ok               1    nodes/tests/handoff/v1.md

/graph show test-plan     # 打印该节点的账本分区

干跑脚本演示了两种返工结局:implement 把缺失细节退回 specspec 提供补充件(provided);test-planimplement 索要接口边界,implement 以「不属于我的职责」拒绝 —— test-plan 随后自行明确缺口(declinedself_handled)。两次交换都记录在 requests/ 下。

可视化 DAG 编排器(V2)

安装到 DSH 的 Web profile 后,会话顶部与内置视图并列的位置会出现新的 编排 页签。它编辑的仍是同一份图定义:拖动卡片调整画布布局;在右侧属性面板修改节点 id、职责范围、提示词与产物;勾选直接上游即可绘制依赖边。草稿和布局按浏览器会话保存;点击保存会把规范化后的 YAML 写入 .agent-graph/graphs/<name>.yaml。这个路径相对于 DSH 主进程的工作目录(即启动 dsh web 时所在的目录),可用插件配置里的 workspace 覆盖;因此图定义与运行账本都落在该工作区,而不是各个会话自己的 cwd

节点会话的创建边界是刻意严格的:

  1. 编辑或保存图:只校验并写 YAML,绝不调用 ctx.subagents.start(),因此不会创建任何 agent 会话。
  2. 保存并运行:先保存同一份 YAML,再启动一次图运行。
  3. 调度器激活就绪节点:只有根节点会立即启动;下游必须等所有直接上游结算。真正轮到某个节点时,调度器才为这一次激活调用 DSH 的 ctx.subagents.start()
  4. DSH 侧边栏展示子会话:这些激活是普通 DSH 子 agent 会话(标签为 agent-graph:<graph>:<node>),会出现在父会话的侧边栏中。仍在 pending 或被依赖阻塞的节点没有子会话,自然也不会占用侧边栏。

因此画布可以先作为低成本的规划面:先搭建、移动并校验复杂 DAG,不产生任何模型工作;真正运行后,再通过 DSH 的子会话和持久账本观察执行过程。

安装

装进 DSH

从 npm 安装(推荐):

dsh plugin --profile web add dsh-agent-graph

从 GitHub 安装(可加 #<sha> 固定版本):

dsh plugin --profile web add github:wrc093/dsh-agent-graph

从本地目录(开发):

cd /path/to/dsh-agent-graph
npm install && npm run build
dsh plugin --profile web add "$(pwd)"

安装或修改后重启 dsh web。确认插件行已进入合成树:

dsh --profile web --dump-config | grep -i agent-graph

要求:Node.js >=22;DSH profile 具备 commandssubagents 服务;subagent provider 必须支持结构化输出(spawn 支持;acpclaude-codecodex 不支持)。

更新 / 卸载

dsh plugin --profile web add dsh-agent-graph                       # 从 npm 更新
dsh plugin --profile web add github:wrc093/dsh-agent-graph#<sha>  # 或固定 commit
dsh plugin --profile web remove dsh-agent-graph                    # 卸载

图定义

name: feature-delivery
description: 调研 → spec → 实现 → 测试计划 → 测试
concurrency: 2
budget:
  maxNodeRuns: 24     # 整个 run 的最大激活次数(含返工重跑)
  reworkPerEdge: 2    # 每条依赖边允许的返工次数
nodes:
  - id: research
    scope: 调研事实、约束与风险;不产出方案、不写代码
    prompt: |
      围绕本次需求完成调研:相关模块、约束、风险……
    outputs: [facts, constraints, risks]
  - id: spec
    needs: [research]        # 直接上游;同时也是唯一允许返工的对象
    scope: 把调研固化为可执行的 spec;不写代码
    prompt: |
      基于上游交接产出 spec……
字段 含义
namedescription 图的身份,记录进账本
concurrency 最大并行激活数,默认 2
budget.maxNodeRuns 整个 run 的激活上限,默认 40
budget.reworkPerEdge 每条依赖边的返工上限,默认 2
nodes[].id ^[a-z][a-z0-9_-]*$,图内唯一
nodes[].scope 职责边界;是节点(和上游)判断返工请求是否成立的依据
nodes[].prompt 每次激活注入的任务说明
nodes[].needs 直接上游 id;决定调度顺序,也是唯一可返工对象
nodes[].outputs 可选,仅文档用途(展示在节点账本分区)

编译器会拒绝环、未知/重复引用、自环、非正数预算与非法字段。拓扑序按 id 排序的 frontier 计算,同一份图永远得到同一顺序。

命令

命令 行为
/graph run <file.yaml> 校验图、建立账本、后台启动运行并立即返回
/graph status [runId] 展示运行:状态表、未决返工请求、失败、最近动态(默认最近一次)
/graph resume [runId] run.json 重建引擎继续跑;失败与中断节点重试
/graph show [runId] <node> 打印某个节点的账本分区(nodes/<id>/index.md
/graph runs 列出本工作区的运行,最新在前
/graph editor save <base64url> 编排 页签使用的内部桥接命令:校验并保存规范 YAML,绝不启动 run 或子 agent

节点是完整子 agent,所以运行在后台执行;账本是权威进度面,/graph status 在运行活跃时读内存状态,否则读 run.json

运行机制

  1. 校验与编译:解析 YAML、检查图、计算上下游映射与规范拓扑序。
  2. 调度:所有 needs 已结算(ok / self_handled)的节点进入就绪队列,最多并行 concurrency;未决返工请求的 holder 激活占用同一并发额度。
  3. 激活:以一次性子 agent 启动。prompt 由引擎确定性拼装:scope + prompt、上游最新交接、账本路径(运行 README、本节点分区、decisions/pitfalls 索引)、记录义务,以及适用时的返工请求或重试结论。
  4. 交接:成功后写出 nodes/<id>/handoff/vN.md;其 summary / artifacts / openIssues 注入每一个直接下游激活。
  5. 记录:每次激活写入 nodes/<id>/attempts/NNN.md;账本的 README、索引、时间线与节点分区在每次状态变化后确定性再生。
  6. 结算或失败:所有节点 ok/self_handled 即完成;第一个节点失败即停图(在途激活被中止),resume 把失败与中断节点放回 pending

所有持久化写入经过单一 persist 链串行化,并发节点完成不会在同一批文件上竞争。

返工协议

无法完成职责的节点以 status: "needs_rework" 结束,并给出:

字段 含义
target 直接上游节点 id(非直接上游会降级为自理)
problem 缺什么 / 哪里不对
evidence 账本/交接引用,例如 nodes/spec/handoff/v1.md#interface
acceptance 补到什么程度算够

上游被重新拉起作为 holder,必须给出恰好一种结局:

结局 效果
provided 补充件写为该 holder 的新交接版本并交回发起方;发起方带着新材料与出处(providedBy)重新激活
declined 发起方重新激活并被要求自己补齐;最终状态为 self_handled
forward holder 以 needs_rework 把请求交给自己的直接上游;请求逐跳传递并保留完整轨迹

有界规则保证循环必收敛:

  • 每个请求对每个节点最多 forward 一次;非法/缺失 target 降级为 declined
  • 同一问题(problem 归一化后按 origin 哈希)只允许请求一次。重复请求给发起方一次自理机会;再犯则节点显式失败(unbounded rework)。
  • budget.reworkPerEdge 限制每条边的请求数;超限的请求降级为自理。
  • holder 激活失败与普通节点失败一致:停图。

每个请求都写入 requests/R-XXXX.md 并带完整轨迹,升级路径事后可审。

全局账本

.agent-graph/runs/<runId>/
├── README.md              # 运行总览、节点表、最近动态
├── index.md               # 分区索引
├── progress.md            # 完整活动时间线
├── run.json               # 机器可读的持久化状态
├── nodes/<id>/
│   ├── index.md           # 职责、状态、上下游、交接、尝试
│   ├── handoff/vN.md      # 交付版本(含返工补充件)
│   └── attempts/NNN.md    # 引擎写的每次激活记录
├── decisions/index.md     # 节点写的 `D-*.md` 汇总
├── pitfalls/index.md      # 节点写的 `P-*.md` 汇总
└── requests/R-XXXX.md     # 返工请求与完整轨迹

节点 agent 被要求用普通文件工具维护自己的工作记录(attempts/decisions/D-<HHMMSS>-<slug>.mdpitfalls/P-<HHMMSS>-<slug>.mdtodo.md)。布局与索引归引擎所有,节点从不编辑共享文件。阅读是分层的:READMEindex → 节点分区 → 按需下钻。

.agent-graph/ 默认被仓库 .gitignore 忽略;是否提交或导出某个账本,按工作区自行决定。

节点输出契约

每次激活必须以结构化输出结束,通过 DSH outputSchema 强制(子 agent 调用 structured-output 工具):

{
  "status": "ok | needs_rework | failed",
  "summary": "给下游的交接摘要",
  "artifacts": ["产物路径"],
  "openIssues": ["下游需要知道的事"],
  "rework": {
    "target": "直接上游 id",
    "problem": "缺什么",
    "evidence": ["nodes/spec/handoff/v1.md#section"],
    "acceptance": "补到什么程度算够"
  },
  "reworkOutcome": "provided | declined",
  "addendum": "交回发起方的补充内容",
  "declineReason": "拒绝理由"
}

statusneeds_rework 时必须给出 rework;节点作为返工 holder 应答时必须给出 reworkOutcomeaddendum/declineReason。违反契约的结果会让节点带明确原因失败;引擎对真实与脚本结果一视同仁地校验。

CLI

CLI 负责校验、干跑与检查。真实执行发生在 DSH 内,因为节点是宿主子 agent。

dsh-agent-graph validate <file.yaml>                        # 解析 + 校验,打印拓扑序
dsh-agent-graph run <file.yaml> --fake <script.json>        # 用脚本化响应干跑(无模型、无网络)
dsh-agent-graph status [--run <runId>] [--workspace <dir>]  # 查看运行(状态表、请求、失败)
dsh-agent-graph resume [--run <runId>] --fake <script.json> # 继续已停止/失败运行

--fake 脚本格式:{ "nodes": { "<nodeId>": [ {result}, … ] } },示例见 examples/fake-script.json。退出码:0 成功、1 用法/校验错误、2 运行失败结束。

数据处理与限制

  • 账本是 <workspace>/.agent-graph/ 下的本地纯文本,包含节点摘要、产物路径、决策与踩坑;不包含模型凭据、其他会话的 prompt 或工具调用参数。节点写的摘要可能引用项目内容,分享账本前请自行审查。
  • 节点以 DSH 子 agent 身份运行,权限跟随宿主授予;本插件不会放宽沙箱或审批设置。
  • 每次激活都是一次真实子 agent 运行并消耗 token。预算用于约束花费:budget.maxNodeRuns 限制总激活数,budget.reworkPerEdge 限制返工,nodeTimeoutMs(插件配置,默认 30 分钟)限制单次激活。
限制 默认 含义
concurrency 2 最大并行激活数
budget.maxNodeRuns 40 整个 run 的激活总数(含重试与返工)
budget.reworkPerEdge 2 每条依赖边的返工请求数
nodeTimeoutMs 1800000 单次激活硬超时;0 表示关闭

超出 maxNodeRunsbudget exhausted 结束运行;超时使该节点失败(停图),原因写入账本。

排查

现象 检查
/graph 变成普通聊天 插件是否装在当前 profile、commands 服务是否存在、安装后是否重启
subagent provider … does not support … capability 换支持 outputSchema 的 provider(spawn/fork);acpclaude-codecodex 不支持
finished without structured output 子 agent 未调用 structured-output 工具;检查 provider 支持情况并重试该节点
运行立刻 failedbudget exhausted 调大 budget.maxNodeRuns 或减少返工;返工循环可在 requests/ 里看到
unbounded rework 失败 节点重复请求了一个已被自理的问题;收紧节点 prompt 或上游交付
no runnable nodes: the graph is stuck 某个依赖从未结算;在 /graph status 看节点状态与未决请求
时间线里出现 invalid rework target 节点指向了非上游节点,已降级自理;修节点 prompt 或 needs
崩溃后状态里有 pending 节点 执行 /graph resume <runId>;已完成节点不会重跑
启动时报图校验错误 未知/重复 id、环、自环或非正数预算;错误信息会列出全部问题

开发

npm install
npm run typecheck
npm test
npm run demo        # 完整干跑自带示例(不需要模型)
npm run build       # dist/(插件入口 + CLI)
文件 职责
src/core/graph.ts YAML 解析、校验、规范拓扑编译
src/core/engine.ts 调度、交接、返工状态机、持久化
src/core/store.ts 账本布局与索引/时间线的确定性再生
src/core/runner.ts 节点结果契约、JSON schema、脚本化测试替身
src/core/prompt.ts 确定性激活 prompt 组装(scope、交接、账本、规则)
src/core/types.ts 共享词汇(图、运行状态、请求、结果)
src/dsh/contracts.ts ctx.commandsctx.subagents 的结构契约
src/dsh/subagent-runner.ts 一次性 spawn runner(结构化输出)
src/plugin.tssrc/cli.ts 斜杠命令面与 CLI
test/examples/ 单元/集成测试与自带示例

测试是确定性的:脚本化 runner 替代子 agent,调度、返工与账本语义无需模型或网络即可覆盖;插件命令面针对 fake host 验证。V1 设计记录见 mydocs/specs/

本地 DSH 开发:构建 checkout、把路径装进开发 profile,改完重启 DSH。不要把 .agent-graph/ 与凭据提交进仓库。

贡献

带最小图、脚本化 trace(或账本片段)与期望的调度/返工行为,开一个 issue。语义改动请补一个聚焦的回归测试并跑上面的检查。文档需要区分「协议保证的行为」与「依赖节点 prompt 与模型行为的部分」。

许可

本项目基于 MIT License 开源。