deerflow-code/offline-backend-20260512/backend/docs/adr/0001-workflow-studio-runtime.md
2026-09-07 18:24:55 +08:00

55 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR 0001:工作流编排运行时边界
- **状态**:Accepted(阶段 0 冻结;决策 3 已按阶段 2 实现修订,见文末修订记录)
- **日期**:2026-08-28
- **关联**:`docs/WORKFLOW_STUDIO_BACKEND_DEV_ZH.md`
## 背景
需要在 DeerFlow 中支持可持久化、可恢复、可审计的工作流运行,并用 Coze Studio 独立画布作为编辑器。可选路径包括:整包接入 Coze 后端、整包接入 ChatDev 2.0、或在 DeerFlow 内自建运行时。
## 决策
1. **Coze Studio 只负责画布与节点配置 UI**
不复用其完整后端协议、内置试运行、Trace/Debug。Gateway 仅提供薄兼容层(canvas/save/create/node_template)。
2. **ChatDev 2.0 只作语义参考**
可参考 DAG、并行、循环、动态边、会话执行边界;**不**引入其 WebSocket、SQLite 存储、宿主机 `subprocess` 代码执行、自有 Agent/模型 runtime。
3. **执行内核 = DeerFlow 自建 DAG 调度器;LangGraph 只作智能体内核**
见下方「阶段 2 修订」。智能体 / 技能 / 沙箱 / 模型 / 制品全部复用现有 harness 能力。`deerflow.workflows` 不得导入 `app.*`。
4. **流式协议 = 持久事件日志 + SSE**
先写 `workflow_run_events`,再推 live;断线用 `Last-Event-ID` / `after_seq` 补放。不与 ChatDev WebSocket 并存。
## 阶段 0 配套确认
| 议题 | 结论 |
|------|------|
| SQL 节点驱动 | 首期仅 **MySQL 和/或 PostgreSQL**(与项目实际库一致);不抽象全数据库 |
| 凭据存储 | 画布只存 `credentialRef` / `dataSourceId`;明文密钥进服务端凭据存储,不进 JSON / SSE / 日志 |
| iframe 域名 | 由 `config.yaml` → `workflows.embed.allowed_origins`(阶段 6 落地配置项);票据绑定 origin + workflow + user + actions,约 60s 一次性兑换 |
| 应用库 vs 业务查询库 | DeerFlow 自身业务库 **不得**直接暴露给 SQL 节点;必须管理员登记独立只读数据源 |
## 阶段 2 修订:工作流级编排不编译成 LangGraph
阶段 0 原定「标准图编译为 `StateGraph`」。实现阶段 2 时改为**自建 DAG 调度器**(`deerflow/workflows/runtime/engine.py`),LangGraph 退到**单个 `agent` / `skill` 节点内部**继续做智能体内核。
**原因**:运行状态必须只有一个权威来源。工作流的可恢复性建立在 `workflow_node_runs`(已完成节点的输出)+ `workflow_run_events`(事件日志)之上——重新租约或人工恢复时,执行器从这两张表回放,只跑缺失的节点。若同时用 LangGraph checkpoint 保存工作流级状态,就会出现两份状态需要保持一致(checkpoint 与 node_runs 谁说了算?租约被抢走后 checkpoint 归谁?),恢复语义无法自证。此外 `human_input` 的暂停需要把 `resume_token` 与 `pending_input` 暴露到 HTTP/SSE 层,条件分支需要「未选中的端口整条子图跳过」的显式边状态,循环需要按 `bodyEntry` 精确失效 body 内的 node_runs——这些都要求调度器直接读写运行存储。
**代价**:调度、并行度、重试、超时、循环上限这些机制要自己实现和测试(`tests/test_workflow_engine.py`、`tests/test_workflow_runs.py`)。**收益**:崩溃恢复、取消、暂停/恢复都只有一条真相路径,且不需要为工作流额外引入一套 checkpoint 生命周期。
调度规则本身很小:每条边有 `pending` / `active` / `pruned` 三态,节点在「非回边入边都已定型且至少一条 `active`」时就绪,全部 `pruned` 则跳过并继续下推剪枝。`loop` 节点的 `continue` 回边是唯一允许的环,不参与就绪判定。
## 后果
- 短期需自建 schema / store / dispatcher / SSE,工作量高于“整包移植”。
- 长期只维护一套模型调用、鉴权、沙箱与审计,避免双运行时分叉。
- 前端时间线必须与本仓库冻结的事件 envelope(`deerflow.workflows.events`)契约一致。
## 否决方案
- 把 Coze `workflow_api` 全量试运行当作执行引擎。
- 把 ChatDev `python_executor.py`(宿主机 subprocess)当作代码节点。
- 在画布 JSON 或 URL query 中传递 JWT / DB 密码 / API Key。