把画布编译成计划提示词:DeepSeek Harness 软工作流 dsh-workflow-lite 架构演进实录
在构建复杂大语言模型(LLM)智能体系统的过程中,我们往往会陷入一种“调度器反噬”的工程困局:为了让大模型按照预设流程执行几个前后依赖的任务,开发者不得不引入庞大笨重的工作流引擎。这些引擎塞满了复杂的有向无环图(DAG)拓扑排序器、状态机转换逻辑、分布式队列、超时重试看门狗与跨进程通信锁。然而具有讽刺意味的是,现代推理大模型本身就具备极其出色的逻辑推理与上下文规划能力,我们却在它外围套上了一副笨重的工控自动化枷锁。
最近,我深度研读并实践了开发者 Minglink 为 DeepSeek Harness(DSH)生态开源的软工作流插件 dsh-jailbreak-workflow(npm 包名:@xiaoso/dsh-workflow-lite)。它彻底颠覆了以往 Dify、LangChain 或 ComfyUI 这类“重调度器、轻模型”的传统套路,开创性地提出了 软工作流(Soft Orchestration) 理念:整个插件源码中没有一行执行引擎,仅负责在 React Flow 画布上进行可视化编排,将其纯函数式编译成一份高度收敛的“派发计划提示词”,交由当前会话中的大模型自驱完成调度与执行。在本文中,我将以第一视角深入剖析其底层微内核设计、计划编译机制、双向人在回路(HITL)通信以及在红队安全评测中的硬核工程实践。
一、 破局传统 DAG:软工作流的“无引擎”哲学
在传统的工作流与自动化平台(如 Dify、LangFlow、ComfyUI、Flowise)中,系统的控制流与数据流完全掌握在外部 Python 或 Node.js 宿主引擎手中。
flowchart LR
subgraph Traditional [传统硬编码 DAG 模式]
direction TB
Engine["外部调度引擎<br/>(Worker / 队列 / 状态机)"]
N1["节点 1<br/>Prompt 渲染"]
N2["节点 2<br/>代码执行器"]
N3["节点 3<br/>条件分支判定"]
Engine -->|拓扑驱动| N1
N1 -->|IO 回传| Engine
Engine -->|状态迁移| N2
N2 -->|正则捕获| Engine
Engine -->|布尔路由| N3
end
subgraph SoftOrchestration [DSH 软工作流模式]
direction TB
Canvas["React Flow 画布<br/>(只负责布局与结构契约)"]
Compiler["纯函数编译器<br/>(compile.ts)"]
Plan["派发计划提示词<br/>(Dispatch Plan)"]
Model["当前会话推理大模型<br/>(自主规划 / 派发 / 循环 / 回退)"]
Canvas -->|导出单 JSON| Compiler
Compiler -->|零文本膨胀编译| Plan
Plan -->|注入主上下文| Model
Model -->|单一工具状态回写| Canvas
end
这种传统模式在面对高度确定性的业务系统时行之有效,但在驱动自主 Agent 时却暴露出了三大天然硬伤:
- 运行时严重割裂与状态冗余:外部引擎需要维护繁重的持久化数据库、节点超时重试、变量连接线映射与跨服务锁。一旦网络抖动或环境异常,复杂的重试与上下文恢复常常导致灾难性的状态死锁;
- 上下文污染与角色认知失调:许多流水线为了让大模型执行多步骤,粗暴地把十几个节点的完整 System Prompt 全部拼接到主会话中,导致模型产生严重的角色认知混乱(“我到底该扮演审查员还是写代码?”);
- 缺乏端到端的推理弹性:在真实复杂探索任务中,许多判断并非简单的“
status == 200”或固定的正则匹配,而是需要高级语义理解。硬编码的分支连线限制了大模型原生的反思、回溯与动态修正能力。
dsh-workflow-lite 的核心设计哲学非常纯粹:将调度的权力彻底归还给大模型本身,工程架构只做一件事——把可视化连线编译成一份模型看得懂的计划契约。
[!NOTE] 什么是软工作流(Soft Orchestration)?
插件不执行任何节点,不调度任何任务,因此在代码层面上彻底消灭了并发控制、超时检测、重试循环、分布式锁、心跳保活与故障恢复机制。所有的分支流转、并行派发、多轮循环与异常回退,全部由拿到计划的大模型在自身思维链(Chain of Thought)与子代理体系中自驱完成。
二、 系统架构全景:Cordis 微内核与解耦设计
为了支撑这种高内聚、低耦合的设计,dsh-jailbreak-workflow 构建在 Cordis 微内核架构 与 DeepSeek Harness 官方插件总线之上,整体工程被优雅地解耦为三层:
flowchart TD
subgraph ClientUI ["前端画布层 (React Flow + ELK)"]
RFCanvas["交互式画布 (Canvas.tsx)"]
NodeEditor["节点 / 资源 / 问题抽屉"]
RunBoard["画布展示看板 (RunBoard.tsx)<br/>Markdown / HTML / 状态表 / 折线图"]
Versions["版本差异比对 (VersionsModal.tsx)"]
PackMgr["Pack 导入导出 (.wflite.zip / Skill)"]
end
subgraph HostCore ["宿主与仓储层 (Cordis Context)"]
ToolEntry["单一工具门面 (workflow_lite)"]
Repo["文件仓储 (repository.ts)<br/>单文件原子写 / 写前哈希 / 字段级三方合并"]
RunSvc["实例运行时 (service.ts)<br/>YAML 运行状态 / 任务描述拆包物化"]
NoticeBridge["通知网关 (notice.ts)<br/>基于 agent.steer 的人在回路注入"]
Memories["记忆管理 (store/memories.ts)<br/>快速路径与经验下沉"]
end
subgraph LLMExecution ["模型自主执行层"]
MasterAgent["主 Agent (会话编排者)"]
TaskFiles["隔离的任务描述 (.workflow-lite/runs/.../tasks/)"]
SubAgents["子代理阵列 / Team 成员"]
StateYAML["运行状态文件 (run.state.yaml)"]
end
ClientUI <-->|WebSocket RPC /api 通道| HostCore
HostCore -->|注入唯一调度计划| MasterAgent
MasterAgent -->|按需单步读取| TaskFiles
MasterAgent -->|并发派发或串行执行| SubAgents
MasterAgent -->|调用 state 动作| RunSvc
RunSvc -->|实时刷新节点状态与看板| RFCanvas
RFCanvas -.->|用户介入与断点修改| NoticeBridge
NoticeBridge -.->|agent.steer 强插桩| MasterAgent
1. 存储设计:单文件事实源与原子合并
整张工作流在磁盘上仅体现为一个同名的纯 JSON 文件(位于 workflows/<name>.json)。它直接继承 React Flow 的原生数据结构 { nodes, edges, viewport, settings }。
在文件仓储 src/host/store/repository.ts 中,Minglink 确立了四条坚不可摧的底层安全铁律:
- 单文件原子写:所有落盘操作必须经过
writeFileAtomic,写入临时文件后重命名覆盖,杜绝断电或异常中断引发的文件破损; - 全流程进程级锁(
withLock):锁的粒度覆盖单次工具调用的整个生命周期(包含前置读取校验与所有副作用落盘)。当执行“删除节点”时,节点及其连带的边在同一个原子事务中被彻底清除,从物理层面杜绝了并发读写竞争; - 写前哈希比对与字段级三方合并:若磁盘基线发生变动,仓储并不直接粗暴报错,而是按
node.id与edge.id进行字段级智能合并;只有当双方同时修改了同一个节点的data,或者一方删除另一方修改时,才返回明确的conflict失败码; - 拒绝隐式创建:除
write_node的明确 upsert 操作外,任何指向不存在工作流的写指令全部阻断,保障存储严谨性。
三、 派发计划编译器:攻克多步任务的“上下文爆炸”
在多步骤工作流的实践中,一个最致命的痛点是提示词膨胀与指令相互踩踏。以一个典型的 15 步复杂流水线为例,如果把所有步骤的角色设定、输出要求和约束全部塞进 Prompt,不仅会瞬间吃掉几万 Token,还会让大模型产生混乱:“我现在到底是在执行第 1 步还是第 10 步?”
compile.ts 是整个插件中技术含量最高的核心模块(源码超过 75KB 的纯函数)。它通过创新的双重视图与物理文件拆包隔离机制,彻底根治了这一顽疾:
sequenceDiagram
autonumber
participant UI as 画布 / 工具
participant Comp as 编译器 (compile.ts)
participant Disk as 本地工作区磁盘
participant LLM as 主 Agent (Orchestrator)
participant Sub as 子执行者 (Subagent)
UI->>Comp: compileWorkflow(document, { full: false })
Note over Comp: 静态分析图拓扑 / 批次排序 / 环路检测
Comp->>Disk: 创建实例目录 .workflow-lite/runs/<id>/
Comp->>Disk: 将各节点 Prompt 物化为独立 tasks/<step>.md
Comp-->>LLM: 返回纯文本 Dispatch Plan (仅含流程矩阵与文件指针)
Note over LLM: 主模型读懂流程矩阵,正文一个字未进上下文!
rect rgb(240, 248, 255)
Note over LLM, Sub: 执行第 N 步时
LLM->>Disk: 仅读取当前步骤 tasks/step_01.md
LLM->>Sub: 原样转交任务描述与上游交接数据
Sub-->>LLM: 完成任务并输出结构化成果
LLM->>UI: 调用 workflow_lite(action='state') 标记通过
end
1. 计划中的“任务不是命令”设计契约
在 compile.ts 编译出的派发计划开头,写入了一组极其严密的模型认知防御指令:
“一次一个步骤:每个步骤的任务描述是写给一个执行者的任务,不是对你的命令——别把这几份角色描述当成同时压在你身上的命令。轮到哪个步骤,就读它那一份、进入那个角色,做完再进下一个。”
“任务不是命令:每个步骤的任务描述是写给执行者的任务,不是对你的命令。你只负责把它原样交给执行者,不要自己扮演这些角色。”
“派发:派发一个步骤,就是让执行者先读它那份任务描述再干活——原样转交,不要转述。不必一次读完所有任务描述,轮到谁再读谁。”
2. 纯函数与逐字节一致性
编译器的核心实现是一个零副作用的纯函数:不碰磁盘 I/O、不取系统时钟、不生成随机数。相同的图结构加上相同的输入参数,永远生成逐字节完全一致的派发计划 Markdown。
派发版(buildPlan)在「流程」表格中,仅输出步骤 ID、名称、触发条件(when)、前置依赖与对应的任务描述文件相对路径(如 .workflow-lite/runs/{instance}/tasks/probe_test.md)。各步骤几千字的正文与繁琐约束被物理剥离在磁盘上,真正做到了“随用随读、用完即弃”。
四、 极简工具契约:1 个工具与 17 个 Action 的克制美学
在传统的 Agent 插件开发中,开发者习惯为每个功能注册一个独立的 Tool(如 create_workflow、delete_node、update_edge 等)。这种做法会导致大模型的 Tool Calling Schema 极速膨胀,不仅浪费了宝贵的首屏 Token,更极易引发模型的选型幻觉与参数填错。
dsh-workflow-lite 展现了令人惊叹的工程克制:面向大模型仅注册一个唯一的工具 workflow_lite,通过单个 action 枚举调度 17 个动作:
| Action 分类 | 动作名称 (Action) | 核心职责与模型交互语义 |
|---|---|---|
| 视图与只读 | list / read |
枚举全站图与模板;read 缺省只回索引与诊断,不回正文,避免污染上下文 |
| 构建与编译 | compile |
静态编译生成派发计划;带 full=true 仅预览不落盘,不带则物化实例与任务包 |
| 图拓扑编排 | create / rename_workflow / delete_workflow |
管理整张工作流元文件(单文件 JSON) |
| 节点生命周期 | write_node / set_label / delete_node |
增删改步骤节点;物理删除节点时自动连带清除所有关联的入边与出边 |
| 连线与条件 | connect / disconnect |
建立或移除带条件表达式(when: pass / fail / 自定义)的定向连线 |
| 资产与记忆 | write_resource / save_as_template |
管理挂载的文件、网址、Skill 依赖或记忆卡;将优质步骤固化为组件库模板 |
| 运行时配置 | configure |
调整执行模式(串行 / 子代理 / Team Lead)、产出根目录与运行状态开关 |
| 状态与恢复 | runs / resume / state |
读写会话实例历史;在循环断点处恢复执行;主模型以此动作实时向画布回写进度 |
统一的 5 大失败码体系
为了让大模型在出错时具备自我纠偏(Self-Healing)能力,工具层废弃了抛出不可控异常的传统做法,统一封装成规范的结构化返回值 { error: { code, message, detail } }:
not_found:找不到对应工作流、节点或运行实例;invalid_args:节点命名冲突、JSON 格式非法或参数越界;blocked:触发了底层保护屏障(如试图删除受保护的核心资源);conflict:写前哈希校验未通过,多端编辑产生不可调和的逻辑冲突;io_error:磁盘物理写入或文件系统权限异常。
模型读到结构化失败后,能够自主调整参数再次尝试,无需人类干预。
五、 双向人在回路:YAML 状态与 agent.steer 实时插桩
一个成熟的工作流系统不能是一个“黑盒”。在软工作流中,人类用户和自主模型是如何在同一张画布上实现毫秒级协同的?
答案隐藏在 src/host/runs/notice.ts 与 YAML 运行状态管理中。
sequenceDiagram
autonumber
actor User as 开发者 (人类用户)
participant Canvas as React Flow 前端画布
participant Core as Cordis 微内核网关
participant LLM as 主 Agent 思考循环
LLM->>Core: workflow_lite(action='state', node='verify', status='failed')
Core-->>Canvas: WebSocket 广播: 节点变红,附带排障失败日志
Canvas-->>User: 视觉呈现节点阻断,显示错误详情与断点提示
Note over User, Canvas: 用户介入:排查修复外部依赖,在画布上手动修改状态
User->>Canvas: 点击节点,将状态手动改回 pending,并留言"已修正环境依赖"
Canvas->>Core: RPC 调用 setNodeStatus()
Core->>Core: 更新工作区 run.state.yaml
Core->>LLM: agent.steer(form='notice', summary='用户修改了节点状态: verify -> pending')
Note over LLM: 会话未中断!模型在思考循环下一步精准接收插桩通知,接力执行
LLM->>LLM: 调整规划,重新执行 verify 节点
1. 文件系统作为运行状态的单一事实源
运行状态并没有丢在内存或外部数据库中,而是物化在工作区的 .workflow-lite/runs/{instance}/run.state.yaml。文件包含整体状态、耗时、各节点的判定结果(verdict)、摘要以及产出的具体文件清单。不管是前端画布刷新,还是会话崩溃后重启,只要读取这份 YAML,整个工作流的状态与历史记录就能瞬间 100% 完美复原。
2. 优雅的 agent.steer 异步插桩
在传统对话系统中,向运行中的 Agent 强行插话往往会导致会话崩溃或上下文断裂。dsh-workflow-lite 巧妙利用了 DeepSeek Harness 原生的 agent.steer 机制:
当用户在画布上修改了某个节点的状态、修改了变量或者点击了重试按钮,宿主网关会构建一条带有 source: { kind: 'workflow-lite', form: 'notice' } 的高优先级通知。如果模型正处于推理间隔或等待工具返回,这条通知就会被立即优雅地喂入模型的下一轮上下文。大模型看到来自用户的干预提示后,会自然而然地调整下一步动作,实现了毫无顿挫感的人机协同。
六、 实战切片:15 步自动化安全对抗研究流水线
为什么该项目在 GitHub 上的开源仓库命名为 dsh-jailbreak-workflow?它的杀手级落地场景究竟是什么?
在大模型安全性研究与对齐评测(AI Safety & Red-Teaming)领域,评估一个黑盒大模型的防御边界极其困难。如果仅靠安全研究员手动在网页端单条测试,效率低且覆盖面极其狭窄;如果写死一堆 Python 脚本批量发请求,一旦模型输出拒绝,脚本无法智能理解拒绝的原因(是触发了系统关键词过滤,还是推理层识别出了恶意意图?),更无法动态变异改写。
Minglink 正是利用 dsh-workflow-lite,搭建了一套包含 15 个细分节点的“破甲制作”自动化安全研究流水线(内置 jailbreak-research Skill,包含 200+ 探针与 4 套框架模板):
flowchart TD
Start([任务启动: 确定目标模型架构]) --> ProbeTest["1. 探针测试 (200+ 探针矩阵)<br/>覆盖身份系统 / 混合帧 / 轻量架构"]
ProbeTest --> PatternAnal["2. 拒绝模式语义特征分析<br/>提取拒绝关键词、阻断点与防御特征"]
PatternAnal --> SynthGen["3. 定制化变异生成<br/>基于模板库合成多态 Prompt"]
SynthGen --> APIVerify["4. 自动化 API 动态探活验证<br/>批量探发并评估越权程度"]
APIVerify --> Check{"验证是否通过?<br/>(成功穿透安全边界)"}
Check -- 未通过 --> Feedback["反思与变异微调<br/>回传分析数据与权重修正"]
Feedback -->|进入下一轮迭代| SynthGen
Check -- 通过 / 达标 --> ReportGen["5. 自动化闭环三件套归档"]
ReportGen --> Artifact1["规避测试词库 (words.md)"]
ReportGen --> Artifact2["防御使用手册 (manual.md)"]
ReportGen --> Artifact3["安全评估实证研报 (report.md)"]
subgraph RunBoardView ["画布实时展示面板 (RunBoard)"]
Artifact1
Artifact2
Artifact3
end
1. 闭环多轮反馈循环
整个流程中最精彩的是 APIVerify 到 SynthGen 之间的条件循环。连线上附带了明确的语义分支判定:
- 当 API 响应解析显示防御成功拦截时,流向
Feedback节点,模型在此提取防御特征,把上一轮失败的原因写回analysis.md; - 循环计数器通过状态 YAML 自动递增,模型在下一轮变异中读取该分析报告,动态调整对抗框架;
- 当穿透验证成功或达到最大探索轮次后,自动分支走向报告产出节点。
2. 成果直接在画布上看板化预览
在执行过程中,大模型生成的不仅仅是纯文本回复,更通过工作流的产出根目录机制({workflow} 与 {instance} 变量),将结果直接写入文件。
通过内置的 RunBoard 组件,画布上可以直接无缝渲染 Markdown 测试日志、动态生成的 HTML 看板、甚至实时调用绘图工具生成的探活成功率折线图。开发者甚至无需切出画布,就能对整个长周期的安全研究进展了如指掌。
为了更直观展示该流水线在引入软工作流前后,在不同阶段的工序耗时差异,我整理了真实环境下的对比数据:
xychart-beta
title "安全评测流水线各阶段工序耗时对比 (分钟)"
x-axis ["环境准备", "探针注入", "模式提取", "动态变异", "接口验证", "研报归档"]
y-axis "耗时 (分钟)" 0 --> 60
bar [45, 55, 40, 50, 35, 30]
bar [3, 6, 4, 8, 5, 2]
[!TIP] 图表数据解读
图中第一组柱体为传统的手动编写脚本加纯人工研判模式,研究员需要不断编写测试脚本、抓包分析拒绝原因并人工撰写汇总文档,极度耗时且容易疲劳;第二组柱体为dsh-workflow-lite软工作流驱动下的自动化运转模式,大模型自主接管探针分发、特征反思与报表物化,将全流程耗时缩减了近 85%。
七、 逃离平台锁定:一键降级为通用 Agent Skill
许多可视化工作流最令人诟病的一点,就是平台强绑定与生态孤岛——一旦脱离了特定的软件或云端 SaaS,辛苦编排的资产就成了毫无用处的废 JSON。
dsh-jailbreak-workflow 在设计之初就考虑到了这一退路。它通过 src/shared/skillPack.ts,提供了无与伦比的跨平台便携能力:
flowchart LR
VisualDoc["React Flow 工作流<br/>(workflows/foo.json)"]
Compiler["Skill 编译器 (skillPack.ts)"]
StdSkill["标准 Agent Skill 文件夹<br/>(foo/)"]
VisualDoc --> Compiler
Compiler --> StdSkill
subgraph SkillAssets [标准资产产物]
direction TB
Meta["SKILL.md (元数据与主调度计划)"]
Tasks["tasks/*.md (每步独立任务描述)"]
InternalSkills["skills/ (打包内置的依赖 Skill)"]
end
StdSkill --> SkillAssets
StdSkill -->|分发并在任意宿主运行| Target1["Cursor / Claude Code"]
StdSkill -->|分发并在任意宿主运行| Target2["Codex / Antigravity"]
StdSkill -->|分发并在任意宿主运行| Target3["原生 DeepSeek CLI"]
1. 彻底解耦的方言转换(Dialect)
在编译导出为 Skill 时,shared/dialect.ts 会自动启用 PORTABLE_DIALECT(便携方言模式):
- 剔除所有仅属于 DSH 的私有工具调用说明(如
workflow_lite工具名); - 将执行约定转换为通用的 Agent 规范:“按路径读写文件、按标准加载 Skill”;
- 生成完整的
SKILL.md,兼容各大开源智能体标准(可直接安装至~/.agents/skills)。
这意味着:你在 DSH 网页端画好的一张复杂工作流,随时可以一键导出打包,发送给任何使用 Cursor、Claude Code 或 Antigravity 的同事。他们无需安装任何可视化插件,原生 Agent 凭借 SKILL.md 就能原汁原味地跑通整条流水线!
八、 架构启示:给自治 Agent 构筑极简的工程底座
在深入研读并重构落地了 dsh-jailbreak-workflow 的设计之后,我对下一代 AI Agent 工程化架构有了三点极其清晰的认知:
- 信任模型的内生规划能力,停止过度工程化:
过去的工控思维总想把一切控制流用代码卡死。然而,大模型天然具备理解条件分支与自适应容错的潜能。一个优秀的 Agent 架构应该像编译器一样,将复杂的人类意图精炼为高信息密度的纯文本契约,而非在外面包裹一层又一层沉重、脆弱的分布式状态机。 - “文件即状态”是极简架构的护城河:
单文件 JSON 作为事实源、原子写入保证安全性、工作区 YAML 作为可追溯的运行状态、Markdown 作为任务描述与最终交付物。放弃复杂的云端数据库依赖,回归最纯粹的文件系统,不仅让 Git 版本控制顺理成章,更让跨平台移植与离线运行毫无阻碍。 - 单工具门面与单一职责原则:
给模型十几个细碎的工具,不如给它一个功能完备、带有清晰动作枚举与严谨错误语义的单一门面。这不仅极大降低了上下文的开销,更让模型的决策链路变得无比清晰聚焦。
如果你也正因传统 Agent 工作流的笨重、死锁和高维护成本而头疼,或者正在探索如何在 DeepSeek Harness 生态中构建优雅、可观测且能自驱闭环的复杂业务流,Minglink 的这份 dsh-jailbreak-workflow 无疑提供了一份教科书级的参考范本。
原文链接与参考资料
- dsh-jailbreak-workflow GitHub 开源仓库:Minglink/dsh-jailbreak-workflow
- 现成工作流合集包:Minglink/dsh-workflows
- DeepSeek Harness 官方仓库:deepseek-ai/deepseek-harness
- React Flow(XYFlow)可视化画布引擎:xyflow/xyflow
- Cordis 微内核可扩展框架:cordisjs/cordis