51 KiB
工作流编排与流式执行后端开发方案
文档状态:开发基线方案
适用项目:offline-backend-20260512/backend
对接前端:Coze Studio 独立工作流页面frontend/apps/coze-studio/src/workflow-standalone/
更新时间:2026-08-29
阶段 0 状态:已完成 — 协议代码packages/harness/deerflow/workflows/;文档见WORKFLOW_SCHEMA_V1_ZH.md/WORKFLOW_SSE_V1_ZH.md/WORKFLOW_OPENAPI_DRAFT_V1.yaml/adr/0001-workflow-studio-runtime.md;契约测试tests/test_workflow_schemas.py
阶段 1 状态:已完成 — 定义/版本持久化、草稿乐观锁、validate/publish、节点目录、Coze canvas/save/create/node_template 最小适配;路由/api/workflows*+/api/workflow_api*;测试tests/test_workflow_definitions.py(尚不执行运行)
阶段 2 状态:已完成 — run/node_run/event/artifact 表 + 迁移20260828_02;幂等启动、租约调度、重启恢复、持久事件日志、SSE 回放/追尾。编排内核改为自建 DAG 调度器,不再编译成 LangGraph(理由见adr/0001阶段 2 修订)
阶段 3 状态:已完成 — start/output/transform/condition/merge + 安全表达式引擎;http 节点 SSRF 默认拒内网;sql_read 只读策略 + 数据源登记(密钥加密、只写不读,迁移20260828_03);code 节点加固子进程执行器(默认关闭)
阶段 4 状态:已完成 — agent / skill 节点走 DeerFlow lead agent,模型增量与工具步骤映射为node.output.delta/node.tool.*
阶段 5 状态:已完成 —human_input暂停 + 一次性 resume token、子工作流(深度上限 3)、显式循环与max_loop_iterations/max_steps上限
阶段 6 状态:已完成 — iframe 票据(origin 白名单 + 60s 一次性 +frameAncestors)、按 owner/admin 的运行与数据源鉴权、每用户并发运行上限、结构化审计日志、事件/运行保留期配置与后台清理作业(WorkflowRetentionCleaner,每小时扫一次)、失败运行POST /retry(复制已完成节点从失败点续跑,新 run 标retry_of_run_id)、SSELast-Event-ID优先于after。制品落盘文件仍跟随现有 sandbox/artifact 生命周期,不做工作流专属文件回收。多实例租约竞争 + SSE 重连有单机并发测试(test_two_dispatchers_only_one_claims_a_run/test_sse_reconnect_storm_is_ordered_and_complete),不是外部分布式压测。
会话规划补充(2026-08-31) — 普通对话先由独立内置智能体workflow-planner(工作流总控)读取用户可见业务智能体目录、理解意图并选择角色,返回 2–3 条候选策略;它与roundtable-coordinator完全隔离,只做规划,不调用工具、不执行研究或正式运行。运行时强制disable_tools=True与thinking_enabled=False,因此只产生一次轻量规划模型调用。模型仅能挑选已验证的agentId与既定策略,服务端才负责构造和校验可编辑 DAG;控制器不可用时显式报错,绝不退回关键词/静态模板。POST /api/workflows/{workflow_id}/planning-sessions/stream复用 DeerFlow 控制器的真实流式回调,按目录读取、总控解析、角色选择、候选图组装、校验和完成依次发 SSE 里程碑,最后才发送候选会话;连接建立即写出一个真实的「已接收」传输里程碑,并以 SSE comment 保活/跨越代理缓冲阈值,comment 不会进入业务进度或持久化事件。前端默认收起该步骤条,只展示最新阶段,不能以本地定时器伪造进度。 审计整改(2026-08-29)状态:功能健壮性项已完成 — 按WORKFLOW_STUDIO_BACKEND_REMEDIATION_ZH.md「0. 整改决策记录」:①人工恢复原子化(resume_with_payload单条件更新 +set_awaiting_input一次写入 checkpoint);②草稿保存 CAS 条件更新(409 带currentRevision)+ 发布版本号计数器workflow_definitions.next_version_number;③事件 seq 计数器workflow_runs.next_event_seq(同事务取号,冲突自愈重试),事件落库失败丢弃不发布(删除负序号幽灵事件);④输入/输出完整 JSON Schema 校验(workflows/schema_validation.py,路由 400 + 节点错误均带字段路径);⑤测试纳入 Git(.gitignore白名单)+ 新增 SQL 并发回归tests/test_workflow_concurrency.py。迁移20260828_04_workflow_counter_columns.py增加两个计数器列(存量库由启动列同步自动补齐)。P0-01/02(内网可信环境,不实施)、P1-04/05(安全隔离,暂缓)见整改文档决策记录。
运行时测试:tests/test_workflow_engine.py(引擎、表达式、节点、安全策略)+tests/test_workflow_runs.py(HTTP 生命周期、SSE、暂停恢复、租约回收、数据源、票据)+tests/test_workflow_concurrency.py(整改并发回归:内存 + 真实 SQLite)
1. 目标与结论
本次建设的目标不是把 Coze Studio 或 ChatDev 2.0 整体并入 DeerFlow,而是在 DeerFlow 后端中增加一套可持久化、可恢复、可审计的工作流运行时,并让 Coze Studio 的工作流画布成为它的编辑器。
首期需要支持下面这类业务链:
开始/表单输入
→ SQL 只读查询 / HTTP 接口 / 数据采集智能体 / 技能
→ 数据清洗与整理智能体
→ 报告撰写智能体
→ 审核智能体
→ 条件分支(通过 / 退回修改)
→ 输出格式校验
→ Markdown、Word 或其他制品
最终技术选择:
- 画布与节点配置 UI 复用 Coze Studio,但不复用其完整后端协议和内置试运行系统。
- 工作流执行内核建设在 DeerFlow 中,编译为 LangGraph 图;智能体、技能、沙箱、模型、制品均复用 DeerFlow 的现有能力。
- ChatDev 2.0 仅作为 DAG、并行、循环、动态图和会话执行语义的参考,不整体搬入其运行时。
- 执行过程采用“数据库持久事件日志 + SSE 实时推送”。前端断线后使用
Last-Event-ID或after_seq补放,不依赖内存中的单次连接。 - SQL 节点只允许服务器端已登记的数据源,并强制只读账号、语句检查、超时、行数/字节上限、表白名单和审计。
- 代码节点必须运行在 DeerFlow 沙箱中,不能照搬 ChatDev 的宿主机
subprocess.run。
2. 范围与非目标
2.1 首期范围
- 工作流草稿、发布版本、复制、归档和权限校验。
- 画布 JSON 的保存、校验、发布和不可变版本快照。
- 节点:开始、输出、智能体、技能、HTTP、SQL 只读、代码、模板/转换、条件、合并、人工介入、子工作流、受限循环。
- 同一层无依赖节点并行执行。
- 运行创建、后台执行、取消、失败重试、人工介入后恢复。
- 流式返回节点状态、模型输出增量、工具步骤、制品和终态。
- 运行历史、事件重放、节点输入/输出审计和错误定位。
- Coze 画布所需的最小兼容接口。
- iframe 场景的短时票据、来源校验和权限隔离。
2.2 首期非目标
- 不复制 Coze Studio 全部 Bot、插件市场、数据库产品和发布渠道。
- 不实现 Coze 全量
workflow_api、试运行、Trace 和调试协议。 - 不把 ChatDev 的消息类、模型提供商、WebSocket 服务或 SQLite 存储直接接入 DeerFlow。
- 不允许用户把数据库密码、Bearer Token 等密钥写进画布 JSON。
- 不允许 SQL 节点执行写语句、DDL、多语句或存储过程。
- 不允许代码节点在 Gateway 宿主机直接启动任意进程。
- 首期不承诺任意环图;循环必须是显式循环节点,并带次数、时长和成本上限。
3. 总体架构
Coze workflow-standalone(iframe)
│
│ HTTPS + Bearer/短时 iframe ticket
▼
FastAPI Gateway /api/workflows/*
├─ 定义、版本、资源目录、数据源 API
├─ 运行、取消、恢复、历史 API
└─ SSE:持久事件补放 + 实时事件追尾
│
▼
WorkflowApplicationService(app 层组合)
├─ WorkflowStore / RunStore / EventStore
├─ Dispatcher(扫描、抢占、租约、恢复)
└─ Executor(后台任务)
│
▼
deerflow.workflows(harness 层)
├─ Schema + Validator
├─ Safe expression resolver
├─ Node registry
├─ LangGraph compiler
└─ Node executors
├─ DeerFlow agent
├─ DeerFlow skill/agent
├─ HTTP policy client
├─ read-only SQL
└─ DeerFlow sandbox
必须继续遵守当前仓库的分层约束:
packages/harness/deerflow/**可以定义通用工作流模型、编译器和节点执行协议。packages/harness/deerflow/**不得导入app.*。- FastAPI、鉴权、
app.state、数据库 Store 组合、后台 dispatcher/executor 启停放在app/**。 - 工作流智能体节点通过依赖注入使用现有 agent/runtime 能力,不新建另一套模型和工具体系。
4. 为什么不直接集成 ChatDev 后端
ChatDev 与本项目虽然同为 Python,但运行时边界不同。
| 对比项 | DeerFlow | ChatDev 2.0 | 处理方式 |
|---|---|---|---|
| 图运行时 | LangGraph + DeerFlow middleware | 自建 DAG executor | 保留 DeerFlow,参考 ChatDev 调度语义 |
| 智能体 | Lead Agent、定制 Agent、工具、MCP、技能 | ChatDev 自有 Agent runtime | 只调用 DeerFlow Agent |
| 流式协议 | 已有 run/event/stream 基础设施 | WebSocket 为主 | 建立 DeerFlow SSE 工作流协议 |
| 沙箱 | DeerFlow Sandbox 抽象 | Python executor 可直接 subprocess | 禁止搬入 subprocess,实现 SandboxNode |
| 持久化 | 项目统一 SQLAlchemy/数据库 | 自有 SQLite/存储服务 | 使用 DeerFlow persistence 层 |
| 技能 | SKILL.md 指令包,由智能体加载 |
skills manager 也偏指令加载 | 明确区分 agent_skill 与 callable_skill |
| 数据库节点 | 需要新增 | 未提供通用业务库查询节点 | 自己实现严格只读 SQL 节点 |
直接整包移植会产生两套图状态、两套消息协议、两套模型调用、两套会话和两套错误恢复。后续每增加一个 DeerFlow 能力都需要做两边适配,短期看似快,长期成本反而更高。
ChatDev 适合参考的源码路径见第 15 节。
5. 核心领域模型
5.1 定义、草稿与发布版本
建议采用“工作流元数据 + 可变草稿 + 不可变发布版本”的模型:
workflow_definitionsidnamedescriptionowner_idstatus:draft | active | archiveddraft_revision: 乐观锁版本号draft_graph_json: 当前画布草稿created_at / updated_at
workflow_versionsidworkflow_idversion_numbergraph_json: 发布时完整不可变快照graph_hash: 规范化 JSON 的 SHA-256input_schema / output_schemapublished_by / published_atchange_note
运行必须绑定 workflow_version_id。运行开始后,即使用户继续编辑草稿,已有运行也不能改变。
5.2 运行、节点运行和事件
workflow_runsidworkflow_id / workflow_version_idstatus:queued | running | awaiting_input | completed | failed | cancel_requested | cancelledowner_ididempotency_keyinput_jsonoutput_jsonerror_jsoncontext_json: 非敏感运行上下文lease_owner / lease_until / heartbeat_atattempt / max_attemptsversion: CAS 乐观锁字段started_at / finished_at / created_at / updated_at
workflow_node_runsidrun_idnode_id / node_typestatusattemptinput_snapshot / output_snapshoterror_jsonstarted_at / finished_atparent_node_run_id: 循环、子工作流或重试层级
workflow_run_eventsrun_idseq: 每个 run 内严格递增,唯一键(run_id, seq)event_typenode_id / node_run_idpayload_jsoncreated_at
事件日志是前端重建执行过程的事实来源;workflow_runs 和 workflow_node_runs 是便于列表查询的当前状态投影。
5.3 数据源与凭据
workflow_data_sourcesid / name / kindowner_scope: 用户、组织或全局connection_config: 主机、端口、库名、SSL 等非密钥字段credential_ref: 指向服务端密钥存储,不存明文allowed_schemas / allowed_tablesstatement_timeout_ms / max_rows / max_bytesenabledcreated_by / created_at / updated_at
画布只保存 data_source_id。前端永远拿不到数据库密码,运行事件也不能回传连接串或认证头。
HTTP 接口资源使用同一张表:kind=http 时,base_url 和 allowed_methods 是可展示的非密钥元数据;请求头仍加密放在 credential blob,列表、详情、资源目录和运行事件均不得回显。创建时至少提供一个允许方法,地址只接受不含账户密码、查询参数或片段的 http(s) URL。迁移:20260829_01_workflow_http_source_metadata.py。
5.4 iframe 短时票据
如 Coze 页面与 DeerFlow 不同域,新增短时、一次性的嵌入票据:
workflow_embed_ticketsticket_hashuser_idworkflow_idallowed_actionsallowed_originexpires_atconsumed_at
只保存票据哈希。父页面请求票据后通过 postMessage 交给 iframe,iframe 再兑换短期访问令牌。不得把正式 JWT、数据库密钥或 API 密钥放在 iframe URL 查询参数中。
6. 画布 JSON 与运行数据契约
6.1 推荐的标准图结构
Coze 画布 DTO 进入 Gateway 后要转换成内部标准结构,运行时不得直接依赖 Coze 私有字段:
{
"schemaVersion": "1.0",
"id": "wf_report_001",
"name": "专题报告生成",
"inputSchema": {
"type": "object",
"required": ["topic"],
"properties": {
"topic": { "type": "string" },
"taskId": { "type": "string" }
}
},
"outputSchema": {
"type": "object",
"required": ["reportMarkdown"],
"properties": {
"reportMarkdown": { "type": "string" },
"artifacts": { "type": "array" }
}
},
"nodes": [
{
"id": "start_1",
"type": "start",
"name": "开始",
"config": {}
},
{
"id": "sql_1",
"type": "sql_read",
"name": "查询业务数据",
"config": {
"dataSourceId": "ds_001",
"statement": "SELECT id, title FROM report_source WHERE task_id = :task_id",
"parameters": {
"task_id": "{{ inputs.taskId }}"
},
"maxRows": 1000
}
}
],
"edges": [
{ "id": "e1", "source": "start_1", "target": "sql_1" }
],
"settings": {
"runTimeoutSeconds": 1800,
"nodeTimeoutSeconds": 300,
"maxSteps": 200,
"maxLoopIterations": 5,
"maxParallelism": 4
}
}
6.2 统一节点结果
所有节点统一返回 NodeResult,避免节点之间直接传递某个 LLM SDK 的对象:
{
"data": {},
"messages": [],
"artifacts": [
{
"artifactId": "artifact_01",
"name": "专题报告.md",
"mimeType": "text/markdown",
"path": "/mnt/data/专题报告.md"
}
],
"metadata": {
"rowCount": 25,
"model": "configured-model-name"
},
"warnings": []
}
大文本、二进制文件和超大查询结果不直接塞进事件或 output_json,而是保存为 artifact,再传引用和有上限的预览。
6.3 安全表达式
支持以下只读引用:
{{ inputs.topic }}{{ nodes.sql_1.data.rows }}{{ nodes.agent_1.data.answer }}{{ run.id }}
实现要求:
- 使用受限 JSON Path/JMESPath/自研字段访问器;禁止 Python
eval、JavaScript 执行和任意属性反射。 - 发布时解析全部表达式,检查引用节点是否在当前节点上游。
- 条件节点只支持白名单运算符:相等、包含、数值比较、空值、布尔组合。
- 模板渲染默认 HTML/文本转义;HTTP 和 SQL 参数分别走参数绑定,不能字符串拼接。
- 运行时记录“引用不存在”和“类型不匹配”的结构化错误。
7. 节点类型与实现边界
7.1 开始节点 start
- 按工作流
inputSchema校验调用参数。 - 注入用户、运行 ID、时间等安全上下文。
- 不注入访问令牌、数据库密码等密钥。
7.2 输出节点 output
- 从上游结果中映射最终字段。
- 按
outputSchema做 JSON Schema 校验。 - 校验失败时工作流失败,错误中返回字段路径和期望类型。
- 可选“LLM 修复格式”必须是明确配置,并保留原始结果与修复记录;首期建议默认关闭。
7.3 智能体节点 agent
配置建议:
{
"agentId": "agent_xxx",
"promptTemplate": "根据以下数据撰写报告:{{ nodes.merge_1.data }}",
"responseMode": "text|json",
"responseSchema": {},
"threadMode": "isolated_per_node|shared_run_thread",
"timeoutSeconds": 600
}
实现原则:
- 通过现有
make_lead_agent/ 定制 Agent 配置加载机制执行。 - 不复制模型 provider、MCP、工具、memory 或 middleware。
- 每个 node run 使用稳定的 child thread 或命名空间,保证重试不把半次输出混入下一次。
- 将模型文字增量转成
node.output.delta;工具调用转成node.tool.started/finished。 - 不向工作流事件写入模型隐藏思维链;只记录可展示的回答、状态和工具摘要。
- 节点取消要传播到 agent run 和沙箱任务。
7.4 技能节点 skill
现有 DeerFlow 技能主要是 SKILL.md 指令包,并不是每个技能都具有可直接调用的 Python 函数。因此首期要区分两种模式:
agent_skill:选择一个 DeerFlow 智能体和技能列表,由智能体加载指定技能完成任务。可立即落地。callable_skill:技能必须显式声明入口、输入 JSON Schema、输出 JSON Schema、权限和超时。此模式需要新增技能 manifest 后再开放。
不能把“技能文件存在”直接等同于“可以当函数节点执行”。
7.5 HTTP 节点 http
- 方法白名单:
GET/POST/PUT/PATCH/DELETE,生产环境可按角色收紧。 - URL 模板和请求体可引用上游数据。
- 认证使用服务端 credential 引用,画布不保存密钥。
- 支持 JSON、文本和有限大小的文件响应。
- 默认禁止重定向;如允许,每一跳都重新执行安全检查。
- 阻止 loopback、link-local、内网保留地址、云元数据地址和 DNS rebinding。
- 配置域名白名单、连接/读取/总超时、最大响应字节数。
- 日志和事件对
Authorization、Cookie、API Key 和配置的敏感字段脱敏。 - 返回非 2xx 时按节点策略决定失败、重试或进入错误分支。
7.6 SQL 只读节点 sql_read
这是新增能力,不能把 DeerFlow 自身业务数据库连接直接暴露给节点。
强制策略:
- 数据库必须由管理员在服务端登记。
- 使用数据库原生只读账号;账号不拥有 INSERT/UPDATE/DELETE/DDL/EXECUTE 权限。
- 应用层再做 SQL AST 检查,只允许一个只读查询语句。
- 参数全部使用驱动绑定变量,不做字符串替换。
- 禁止多语句、注释逃逸、锁表/锁行、写入型 CTE、
SELECT ... INTO、文件读写、危险函数和存储过程。 - 设置 statement timeout、连接超时、最大行数、最大字段长度和最大总字节数。
- 数据源配置可限制 schema、表和列。
- 结果中按策略遮蔽敏感列;审计记录操作者、数据源、规范化 SQL hash、参数名、行数、耗时和状态,不记录密钥。
- 发布校验时可做
EXPLAIN或语法预检,但不能在保存画布时自动执行用户 SQL。 - 生产环境建议从只读副本查询,避免报表工作流影响主业务库。
首期驱动建议只做项目实际使用的 MySQL 和/或 PostgreSQL,不要一开始抽象所有数据库。
7.7 代码节点 code
- 使用
deerflow.sandbox抽象创建隔离执行环境。 - 输入通过 JSON 文件或 stdin 传入,输出必须符合约定 JSON。
- 设置 CPU、内存、磁盘、进程数、网络和执行时长上限。
- 默认禁网;如业务需要访问网络,按域名能力授权。
- 代码、依赖声明和运行镜像版本进入发布版本快照或可追溯引用。
- 输出文件登记为 artifact。
- 禁止调用 ChatDev
python_executor.py中的宿主机subprocess.run方案。
7.8 模板/转换节点 transform
- 负责字段映射、数组过滤、模板渲染、Markdown 拼装。
- 优先提供声明式操作,减少为了简单数据整理而启动智能体或代码沙箱。
- 操作符与函数采用白名单。
7.9 条件和合并节点
condition:根据安全表达式选择一个或多个具名出口。merge:支持all、first_success、any_success等明确策略。- 并行分支写入独立命名空间,合并节点使用确定性 reducer,不能依赖任务完成先后顺序。
7.10 人工介入节点 human_input
- 发出
run.awaiting_input,持久化表单 schema、提示和可执行动作。 - 将 run 状态原子更新为
awaiting_input,释放 worker 租约。 resumeAPI 必须校验 run 当前状态、用户权限和输入 schema。- 同一个
resume_token只消费一次,重复提交幂等返回当前状态。
7.11 子工作流与循环
- 子工作流绑定一个已发布的不可变版本。
- 记录 parent run/node run,事件中保留层级路径。
- 防止直接或间接递归依赖。
- 循环只能通过显式 loop 节点,必须配置最大次数;同时受全局
maxSteps、超时和预算限制。
8. 编译与执行流程
8.1 发布时校验
发布必须完成:
- JSON Schema 和内部 DTO 校验。
- 节点 ID、边 ID 唯一性。
- 开始/输出节点数量和可达性检查。
- 普通边拓扑检查;环只能存在于受控 loop 结构。
- 节点输入引用必须来自上游。
- 条件出口与边名称一致。
- Agent、Skill、数据源、子工作流版本存在且调用者有权限。
- 资源限制在系统上限内。
- 输出映射能够生成声明的 output schema。
- 规范化后生成
graph_hash,创建不可变版本。
8.2 LangGraph 编译
内部编译器将标准节点转换成 StateGraph:
WorkflowState保存 inputs、各节点NodeResult、运行控制字段和 artifact 引用。NodeRegistry根据node.type返回 executor factory。- 条件节点编译为 conditional edges。
- 无依赖分支由 LangGraph 并行调度。
- 合并字段必须注册确定性 reducer。
- interrupt 用于
human_input,恢复时从持久 checkpoint 继续。
编译结果可按 graph_hash 做有界缓存;缓存对象不能包含用户级密钥或运行态数据。
8.3 后台运行状态机
queued
→ running
→ awaiting_input → queued/running
→ completed
→ failed
→ cancel_requested → cancelled
实现参考 roundtable job 的成熟模式:
- 创建运行时用
idempotency_key去重。 - dispatcher 只抢占租约过期或 queued 的任务。
- worker 定期 heartbeat 延长租约。
- 所有状态变更使用版本字段/CAS,防止多个实例重复完成。
cancel先设置cancel_requested,worker 在安全点确认后写cancelled。- Gateway 重启后扫描
queued和租约过期的running任务。 - 非幂等 HTTP 节点默认不自动重试;节点必须声明重试策略和幂等性。
9. SSE 流式协议
9.1 原则
- 先持久化,后发布:用户可见事件先写
workflow_run_events,提交成功后再推送到 live bridge。 - 事件严格有序:同一 run 的
seq单调递增。 - 可重放:断线后从最后一个 seq 继续。
- 增量事件有上限:高频 token 可在 30–100ms 窗口内合并,避免一 token 一条数据库记录。
- 终态明确:
completed/failed/cancelled后停止重连。 - 不泄密:事件中不得出现凭据、完整认证头、数据库连接串和隐藏思维链。
9.2 端点
GET /api/workflows/runs/{run_id}/stream?after_seq=128
Accept: text/event-stream
Authorization: Bearer ...
Last-Event-ID: 128
服务端游标优先级建议:
- 合法的
Last-Event-ID after_seq0
连接流程:
- 校验 run 访问权限。
- 从数据库按
seq > cursor分页补放。 - 订阅 live bridge。
- 再查一次数据库关闭“补放到订阅”之间的竞态窗口。
- 实时追尾;发现 seq 跳号时回数据库补齐。
- 无事件时每 15–25 秒发送 heartbeat/comment。
- 收到终态且数据库已补齐后关闭连接。
9.3 帧格式
id: 42
event: node.output.delta
data: {"schemaVersion":"1.0","runId":"run_01","workflowId":"wf_01","versionId":"wv_03","seq":42,"nodeId":"agent_writer","nodeRunId":"nr_09","timestamp":"2026-08-26T10:00:00+08:00","data":{"channel":"answer","delta":"第一段内容"}}
通用 envelope:
{
"schemaVersion": "1.0",
"runId": "run_01",
"workflowId": "wf_01",
"versionId": "wv_03",
"seq": 42,
"nodeId": "agent_writer",
"nodeRunId": "nr_09",
"timestamp": "2026-08-26T10:00:00+08:00",
"data": {}
}
9.4 事件清单
| 事件 | 关键数据 | 前端用途 |
|---|---|---|
run.created |
input 摘要、version | 创建历史记录 |
run.queued |
queue 信息 | 显示排队 |
run.started |
startedAt | 运行计时 |
node.queued |
node 信息 | 画布等待态 |
node.started |
attempt、输入摘要 | 高亮节点、创建步骤卡 |
node.progress |
phase、message、percent | 进度条/说明 |
node.output.delta |
channel、delta | 流式文本 |
node.tool.started |
toolCallId、name、input 摘要 | 工具步骤进行中 |
node.tool.finished |
toolCallId、status、output 摘要 | 完成工具步骤 |
artifact.created |
artifact 元数据 | 文件卡和预览 |
node.completed |
output 摘要、duration | 节点完成 |
node.failed |
error code、message、retryable | 节点失败 |
run.awaiting_input |
formSchema、actions、resumeToken | 人工介入卡片 |
run.resumed |
action | 恢复时间线 |
run.cancel_requested |
requestedBy | 取消中 |
run.cancelled |
finishedAt | 终态 |
run.completed |
output、artifacts | 终态与结果 |
run.failed |
error、failedNodeId | 终态与定位 |
heartbeat 建议使用 SSE comment : heartbeat,不写入数据库,也不进入前端业务 reducer。
9.5 事件粒度与存储控制
- 模型输出增量按时间窗口或字符数批量落库,例如 50ms/1KB 合并一次。
- 工具输入输出只存有界摘要;完整文件写 artifact。
- SQL 结果默认只在事件里放列名、行数和前 N 行预览;完整结果按节点设置进入运行快照或 artifact。
- 单事件 payload 设置字节上限;超限时写
{truncated: true, artifactId: ...}。 - 事件保留周期应配置化;终态 run 可异步压缩快照,但在保留期内不能破坏 seq 重放。
10. HTTP API 设计
10.1 工作流定义
GET /api/workflows
POST /api/workflows
GET /api/workflows/{workflow_id}
PATCH /api/workflows/{workflow_id}
DELETE /api/workflows/{workflow_id} # 软删除/归档
PUT /api/workflows/{workflow_id}/draft # draft_revision 乐观锁
POST /api/workflows/{workflow_id}/validate
POST /api/workflows/{workflow_id}/publish
POST /api/workflows/{workflow_id}/copy
GET /api/workflows/{workflow_id}/versions
GET /api/workflows/{workflow_id}/versions/{version_id}
PUT draft 请求必须包含 expectedRevision,冲突返回 409 和服务端当前 revision,前端提示刷新或另存副本。
10.2 节点资源目录
GET /api/workflows/node-types
GET /api/workflows/resources/agents
GET /api/workflows/resources/skills
GET /api/workflows/resources/data-sources
GET /api/workflows/resources/subworkflows
资源目录返回工作流节点需要的精简 DTO,不直接暴露后端内部对象。
10.3 数据源
GET /api/workflows/data-sources
POST /api/workflows/data-sources
GET /api/workflows/data-sources/{id}
PUT /api/workflows/data-sources/{id}
DELETE /api/workflows/data-sources/{id}
POST /api/workflows/data-sources/{id}/introspect
POST /api/workflows/data-sources/{id}/validate-query
introspect 只返回白名单内 schema/table/column 元数据,不返回样例敏感数据。
HTTP 接口资源的创建体使用 kind=http、baseUrl、allowedMethods 和可选 headers;headers 是只写字段,服务端加密后不会出现在响应。资源目录 GET /api/workflows/resources/data-sources 返回前端所需的 dataSourceId、baseUrl、allowedMethods、description 等精简字段。
10.4 运行
POST /api/workflows/{workflow_id}/runs
GET /api/workflows/{workflow_id}/runs
GET /api/workflows/runs/{run_id}
GET /api/workflows/runs/{run_id}/events?after_seq=0&limit=500
GET /api/workflows/runs/{run_id}/stream
POST /api/workflows/runs/{run_id}/cancel
POST /api/workflows/runs/{run_id}/resume
POST /api/workflows/runs/{run_id}/retry
GET /api/workflows/runs/{run_id}/artifacts
启动示例:
{
"versionId": "wv_03",
"inputs": { "topic": "市场分析", "taskId": "123" },
"idempotencyKey": "client-generated-uuid",
"executionMode": "normal"
}
10.5 Coze 最小兼容层
独立页面当前画布仍可能调用 Coze 生成 API。建议建立薄适配路由,而不是让内部 service 使用 Coze DTO:
POST /api/workflow_api/canvas
POST /api/workflow_api/save
POST /api/workflow_api/create
POST /api/workflow_api/node_template_list
兼容层职责只有:
- Coze request/response DTO 与内部 draft DTO 互转。
- 返回节点模板和图标元数据。
- 调用内部 workflow service。
Coze 自带的 test-run、trace、workflow debug 接口首期不实现;前端关闭内置测试运行入口,改用第 10.4 节的运行 API。
10.6 iframe 会话
POST /api/workflow-embed/tickets
POST /api/workflow-embed/tickets/exchange
- 创建票据需 DeerFlow 正常登录。
- ticket 绑定
workflow_id + user_id + origin + permissions,有效期建议 60 秒。 - exchange 一次后即失效,返回短期访问令牌和用户/工作流精简信息。
- 响应带
Cache-Control: no-store。
11. 安全、权限与审计
11.1 权限
工作流至少区分:
view:查看定义和运行。edit:修改草稿。publish:发布不可变版本。run:启动运行。manage_data_source:登记或修改数据源。view_sensitive_output:查看未脱敏的节点输出。
所有列表、详情、SSE、artifact 下载和 resume/cancel API 都必须重新校验权限,不能因为知道 run ID 就可访问。
11.2 凭据处理
- 数据源、HTTP 认证和第三方 Token 使用服务端 credential reference。
- 画布和版本快照仅保存引用 ID。
- 进入日志、异常、SSE 和 node snapshot 前统一 redaction。
- 发布者必须有权引用相应 credential;运行者是否可使用由策略决定。
- credential 轮换不修改已发布图,但要记录实际使用的 credential version/audit id。
11.3 审计
记录:
- 草稿保存、发布、归档、权限变更。
- 运行创建、取消、恢复、重试。
- 数据源测试与 SQL 执行摘要。
- HTTP 目标域名、状态码和耗时。
- 人工介入提交者和动作。
- artifact 创建、下载和删除。
审计中保存 hash/摘要而不是密钥和完整敏感数据。
11.4 资源上限
系统级上限必须覆盖画布配置:
- 每用户并发 run 数。
- 单 run 最大时长、步骤数、循环次数、并行节点数。
- 单节点重试次数。
- LLM token/费用预算。
- HTTP/SQL 响应大小。
- 单 run artifact 数量与总大小。
12. 失败、重试、取消和恢复
12.1 错误结构
统一错误:
{
"code": "WORKFLOW_SQL_POLICY_DENIED",
"message": "SQL 节点只允许单条只读查询",
"retryable": false,
"nodeId": "sql_1",
"details": {
"rule": "single_read_statement"
}
}
用户可见 message 不包含堆栈、SQL 密码、完整响应体或内部路径;完整异常进入受控服务端日志。
12.2 重试
- 智能体节点:仅对明确的临时网络/限流错误重试,沿用稳定 node run lineage。
- SQL 只读:连接临时失败可有限重试;语法/权限错误不重试。
- HTTP:GET/HEAD 可按策略重试;POST 默认不重试,除非配置幂等键。
- 代码:资源不足或代码错误默认不重试。
- 工作流级 retry:
POST /api/workflows/runs/{run_id}/retry仅接受failed运行。新 run 复制已完成节点输出并从失败节点续跑;没有任何可回放 checkpoint 时则整图重跑,并标记retry_of_run_id。
12.3 取消
- API 原子设置
cancel_requested并发事件。 - worker 在节点边界、模型流、HTTP、SQL、沙箱等待点检查取消令牌。
- 尽力取消外部操作;无法中止的请求完成后也不能再提交后续节点结果。
- 终态只允许一次,不能出现 completed 后又 cancelled。
12.4 服务重启
- 事件已持久化,因此 SSE 可恢复。
- dispatcher 根据租约扫描未完成任务。
- awaiting_input 不占 worker。
- 对可能产生外部副作用的节点,恢复前检查幂等记录;状态不确定时标记人工处理,不能盲目重放。
13. 可观测性
日志统一带:
workflow_idworkflow_version_idrun_idnode_idnode_run_iduser_idtrace_id
建议指标:
- queued/running/awaiting_input 数量。
- run 和节点成功率、P50/P95/P99 耗时。
- SSE 活跃连接、重连次数、补放事件数、游标落后量。
- dispatcher 抢占、租约过期、重复执行阻止次数。
- 各类节点错误率。
- SQL 行数/超时、HTTP 状态码、sandbox 超时。
- LLM token 与费用。
14. DeerFlow 现有代码参考路径
以下路径相对于 offline-backend-20260512/backend/。
14.1 运行与流式事件
| 路径 | 参考内容 | 复用方式 |
|---|---|---|
packages/harness/deerflow/runtime/runs/manager.py |
run 生命周期、去重、取消、持久化组合 | 复用模式,不把 thread run 数据模型硬套到 workflow run |
packages/harness/deerflow/runtime/runs/worker.py |
agent.astream、流模式、结束/异常处理 |
智能体节点适配时参考 |
packages/harness/deerflow/runtime/runs/schemas.py |
run DTO | 参考命名和序列化 |
packages/harness/deerflow/runtime/runs/store/base.py |
store 接口边界 | 为 workflow store 建立同风格接口 |
packages/harness/deerflow/runtime/stream_bridge/base.py |
发布/订阅协议 | 可复用接口思想 |
packages/harness/deerflow/runtime/stream_bridge/memory.py |
内存 replay、heartbeat/end sentinel | 作为 live bridge;不能代替 DB 事件日志 |
packages/harness/deerflow/runtime/events/store/base.py |
事件 store 抽象 | 工作流事件仓库参考 |
packages/harness/deerflow/runtime/events/store/db.py |
DB 批量事件写入、按 seq 读取 | 优先抽取可复用机制或照此实现 workflow event store |
packages/harness/deerflow/runtime/journal.py |
事件 journal | 参考批量与刷新策略 |
app/gateway/routers/thread_runs.py |
run/SSE Gateway 写法 | 参考鉴权、响应头、断连处理 |
docs/STREAMING.md |
当前流协议约定 | 新协议必须兼容仓库通用规则 |
14.2 后台任务与租约
| 路径 | 参考内容 |
|---|---|
app/gateway/routers/roundtable_jobs.py |
start/get/stream/resume/cancel API |
app/gateway/roundtable_job_executor.py |
后台执行、状态投影、恢复 |
app/gateway/roundtable_job_dispatcher.py |
扫描、唤醒、租约 |
packages/harness/deerflow/persistence/roundtable_jobs/model.py |
job 表和状态字段 |
packages/harness/deerflow/persistence/roundtable_jobs/sql.py |
claim、heartbeat、CAS、取消 |
应复用租约和状态机经验,但不要继续采用“前端轮询完整 job snapshot”作为工作流主流协议;工作流使用增量事件 SSE。
14.3 智能体、技能、沙箱与制品
| 路径 | 参考内容 |
|---|---|
packages/harness/deerflow/agents/lead_agent/agent.py |
make_lead_agent 与 middleware 组装 |
packages/harness/deerflow/config/agents_config.py |
定制 Agent 配置加载 |
app/gateway/routers/agents.py |
智能体资源列表与权限 |
app/gateway/routers/skills.py |
技能列表/读取 API |
packages/harness/deerflow/skills/storage/local_skill_storage.py |
技能存储模型 |
packages/harness/deerflow/tools/builtins/skill_tools.py |
Agent 如何查看/加载技能 |
packages/harness/deerflow/sandbox/sandbox.py |
沙箱能力接口 |
packages/harness/deerflow/sandbox/tools.py |
沙箱工具调用 |
packages/harness/deerflow/sandbox/middleware.py |
Agent 与沙箱生命周期 |
packages/harness/deerflow/sandbox/local/local_sandbox.py |
本地沙箱实现及风险边界 |
app/gateway/routers/artifacts.py |
artifact 查询/读取 API |
14.4 Gateway 和持久化接线
| 路径 | 参考内容 |
|---|---|
app/gateway/deps.py |
app.state 初始化 store/service/runtime |
app/gateway/app.py |
router 注册、生命周期、CORS |
packages/harness/deerflow/persistence/models.py |
ORM model 汇总导入 |
packages/harness/deerflow/persistence/engine.py |
数据库 session/engine |
packages/harness/deerflow/persistence/types.py |
MySQL/SQLite 可移植 JSON、LongText、时间类型 |
tests/test_harness_boundary.py |
harness → app 导入防火墙 |
14.5 iframe 参考但不要直接复用
| 路径 | 可参考 | 不可照搬原因 |
|---|---|---|
app/gateway/routers/public_embed.py |
session 映射、嵌入接口形态 | public embed 偏匿名/公开读取,不满足工作流编辑与执行权限 |
packages/harness/deerflow/persistence/embed_sessions/model.py |
嵌入 session 表结构 | 工作流需要一次性票据、origin 和 actions 绑定 |
packages/harness/deerflow/persistence/embed_sessions/sql.py |
session store 写法 | 需新增独立安全模型 |
15. ChatDev 2.0 参考与明确不复用的路径
以下路径相对于 F:/react01/chatDev/。
15.1 可参考
| 路径 | 参考点 |
|---|---|
workflow/topology_builder.py |
从配置构建拓扑 |
workflow/graph.py |
图对象职责 |
workflow/graph_context.py |
图运行上下文 |
workflow/graph_manager.py |
图生命周期管理 |
workflow/cycle_manager.py |
显式循环控制 |
workflow/subgraph_loader.py |
子图加载 |
workflow/executor/dag_executor.py |
DAG 调度语义 |
workflow/executor/parallel_executor.py |
并行分支 |
workflow/executor/cycle_executor.py |
循环执行 |
workflow/executor/dynamic_edge_executor.py |
动态边选择 |
workflow/executor/resource_manager.py |
并发资源控制 |
workflow/runtime/execution_strategy.py |
执行策略抽象 |
workflow/runtime/runtime_builder.py |
runtime 组装 |
workflow/runtime/runtime_context.py |
运行上下文 |
entity/configs/graph.py |
图配置 schema |
entity/configs/node/ |
节点配置拆分方式 |
entity/configs/edge/ |
边配置拆分方式 |
server/services/workflow_run_service.py |
run service 边界 |
server/services/session_execution.py |
会话执行编排 |
server/routes/execute.py |
异步执行 API 形态 |
server/routes/execute_sync.py |
同步执行边界;本项目不建议面向长任务开放 |
server/routes/websocket.py |
实时消息类型参考;传输改为 SSE |
15.2 不复用
| 路径 | 原因 |
|---|---|
runtime/node/executor/python_executor.py |
直接宿主机 subprocess,无法满足本项目沙箱边界 |
server/services/websocket_executor.py |
本项目统一 SSE,避免并存两套重连协议 |
server/services/websocket_manager.py |
同上 |
server/services/workflow_storage.py |
使用 ChatDev 自身存储模型,会与 DeerFlow persistence 重叠 |
runtime/node/agent/skills/manager.py |
只说明技能加载,不等于通用可调用技能节点 |
| ChatDev 模型 provider、消息对象、Agent runtime | 与 DeerFlow middleware/MCP/skill/memory 重复 |
16. 建议新增和修改的后端代码路径
以下均相对于 offline-backend-20260512/backend/。路径是开发拆分基线;实现时可在不破坏分层的前提下微调文件名。
16.1 harness:通用工作流内核
packages/harness/deerflow/workflows/
├── __init__.py
├── schemas.py # 图、节点、边、NodeResult、运行参数 DTO
├── state.py # WorkflowState 与确定性 reducer
├── errors.py # 稳定错误码和异常类型
├── expressions.py # 安全变量引用与模板求值
├── validator.py # 发布前结构/引用/资源限制校验
├── registry.py # NodeRegistry 与 executor factory
├── compiler.py # 标准图 → LangGraph StateGraph
├── events.py # 事件类型、envelope、EventSink 协议
├── context.py # RunContext、取消令牌、依赖接口
├── nodes/
│ ├── __init__.py
│ ├── base.py # NodeExecutor 协议
│ ├── start.py
│ ├── output.py
│ ├── agent.py
│ ├── skill.py
│ ├── http.py
│ ├── sql_read.py
│ ├── code.py
│ ├── transform.py
│ ├── condition.py
│ ├── merge.py
│ ├── human_input.py
│ └── subworkflow.py
├── security/
│ ├── __init__.py
│ ├── http_policy.py # SSRF、域名、重定向、响应大小
│ ├── sql_policy.py # AST、只读规则、表白名单
│ └── redaction.py # 事件/日志脱敏
└── execution/
├── __init__.py
├── service.py # 单 run 执行用例
├── checkpoint.py # interrupt/resume checkpoint 适配
└── limits.py # 超时、步骤、循环、预算
16.2 persistence:定义、运行、事件和数据源
packages/harness/deerflow/persistence/workflows/
├── __init__.py
├── model.py # definition/version ORM
├── base.py # repository 协议
└── sql.py # SQLAlchemy 实现
packages/harness/deerflow/persistence/workflow_runs/
├── __init__.py
├── model.py # run/node_run ORM
├── base.py
└── sql.py # claim/lease/CAS/idempotency
packages/harness/deerflow/persistence/workflow_events/
├── __init__.py
├── model.py
├── base.py
└── sql.py # append/list/max_seq/批量写入
packages/harness/deerflow/persistence/workflow_data_sources/
├── __init__.py
├── model.py
├── base.py
└── sql.py
packages/harness/deerflow/persistence/workflow_embed/
├── __init__.py
├── model.py
├── base.py
└── sql.py
需要修改:
packages/harness/deerflow/persistence/models.py:汇总新 ORM model,确保create_all和迁移发现。packages/harness/deerflow/persistence/types.py:仅在现有 portable 类型不足时扩展,禁止模型直接使用仅某一数据库支持的类型。alembic/versions/<revision>_add_workflow_studio_tables.py:正式迁移。- 仓库现有 MySQL 增量脚本目录:按当前约定补齐等价 DDL,不能只依赖
create_all。
16.3 app:FastAPI、后台任务和资源适配
app/gateway/routers/workflows.py # 定义/草稿/发布/版本
app/gateway/routers/workflow_runs.py # start/get/events/SSE/cancel/resume
app/gateway/routers/workflow_resources.py # agents/skills/subworkflows/node-types
app/gateway/routers/workflow_data_sources.py # 数据源管理/测试/元数据
app/gateway/routers/workflow_embed.py # iframe ticket/exchange
app/gateway/routers/workflows_coze_compat.py # 最小 Coze DTO 兼容层
app/gateway/workflow_executor.py # app 依赖注入后调用 harness runtime
app/gateway/workflow_dispatcher.py # scan/claim/lease/wakeup/shutdown
app/gateway/workflow_event_hub.py # 单进程实时 fan-out;DB 仍为事实来源
app/gateway/workflow_dependencies.py # Agent/Sandbox/HTTP/SQL adapter 组合
需要修改:
app/gateway/deps.py:创建 repositories、event hub、executor、dispatcher,放入app.state。app/gateway/app.py:注册 routers,生命周期中启动/停止 dispatcher。config.yaml:增加工作流运行上限、SSE、数据源和 iframe origin 配置;密钥继续使用环境变量/凭据引用。packages/harness/deerflow/config/:如需强类型配置,在此增加workflow_config.py,不要让 harness 读取app.state。
16.4 测试文件
tests/test_workflow_schemas.py
tests/test_workflow_validator.py
tests/test_workflow_expressions.py
tests/test_workflow_compiler.py
tests/test_workflow_runtime.py
tests/test_workflow_parallel_merge.py
tests/test_workflow_loop_limits.py
tests/test_workflow_agent_node.py
tests/test_workflow_skill_node.py
tests/test_workflow_http_policy.py
tests/test_workflow_sql_policy.py
tests/test_workflow_code_sandbox.py
tests/test_workflow_run_repository.py
tests/test_workflow_event_repository.py
tests/test_workflow_stream.py
tests/test_workflow_stream_replay.py
tests/test_workflow_cancel_resume.py
tests/test_workflow_dispatcher.py
tests/test_workflow_embed_auth.py
tests/test_workflow_coze_compat.py
tests/test_workflow_migrations.py
tests/test_harness_boundary.py 必须继续通过。
17. 分阶段开发计划
阶段 0:协议冻结与样例图(2–3 人日)
- 冻结内部 graph schema、NodeResult、错误码和 SSE envelope v1。
- 建立“专题报告生成”标准样例图及每类节点最小样例。
- 确认 MySQL/PostgreSQL 首期支持范围、凭据存储方式和 iframe 部署域名。
- 用 ADR 记录“Coze 只负责画布、ChatDev 只参考、LangGraph 为执行内核”。
交付:schema 文档、事件表、样例 JSON、接口 OpenAPI 草案。
阶段 1:定义、版本与 Coze 保存兼容(5–8 人日)
- 新建 workflow definition/version repository 和迁移。
- 实现草稿 revision 乐观锁、validate、publish。
- 实现节点目录和 Coze canvas/save/node_template 最小适配。
- 前端可以真实加载、编辑、保存、发布工作流,但尚不执行。
阶段 2:运行骨架与持久 SSE(7–10 人日)
- 新建 run/node_run/event 表。
- 实现 run start/get/list/cancel 和 idempotency。
- 建立 PersistingEventSink、event hub 和 SSE replay/tail。
- 实现 dispatcher 租约、重启恢复和空图/start→output 运行。
- 先完成事件顺序、断线重连、终态一致性测试。
阶段 3:基础数据处理节点(7–10 人日)
- start/output/transform/condition/merge。
- HTTP 节点及 SSRF/大小/超时策略。
- SQL 数据源管理、元数据和只读查询节点。
- code 节点接 DeerFlow sandbox。
- 完成并行、错误分支和 artifact 引用。
阶段 4:智能体、技能和流式工具步骤(7–12 人日)
- AgentNode 接定制 DeerFlow Agent。
agent_skill模式。- 模型 delta、工具 started/finished、artifact 事件映射。
- JSON 输出 schema、超时、取消和有限重试。
- 以“采集→整理→撰写→审核”跑通端到端。
阶段 5:人工介入、子工作流和受控循环(5–8 人日)
- awaiting_input/resume 与一次性 resume token。
- 子工作流版本绑定和层级事件。
- 显式循环、max iteration/steps/time/budget。
- 审核不通过退回撰写节点的业务用例。
阶段 6:iframe 安全与生产加固(5–8 人日)
- ticket/exchange、strict origin、CSP
frame-ancestors。 - 权限、审计、限流、配额、凭据脱敏。
- 多 Gateway 实例、租约竞争、数据库短暂失败、SSE 大量重连压测。
- 事件保留、清理和 artifact 生命周期。
阶段 6 已落地:WorkflowRetentionCleaner 消费 workflows.retention.*;POST /api/workflows/runs/{run_id}/retry;SSE Last-Event-ID;单机租约竞争与 SSE 重连风暴测试。制品文件仍走 sandbox/artifact,不另做工作流专属文件回收。
阶段 7:试点与扩展(持续)
- 选择 1–2 个报告业务真实试点。
- 根据执行数据增加 callable skill manifest、更多数据库驱动或专用业务节点。
- 暂不满足稳定性前,不开放任意用户自建外网 HTTP 和数据库数据源。
18. 端到端报告工作流验收样例
建议以如下图作为首个验收基线:
开始(topic, taskId, dateRange)
├─ SQL:查询内部结构化数据
├─ HTTP:调用只读业务接口
└─ Agent:使用检索技能搜集外部材料
↓
Merge:合并材料并保留来源
↓
Agent:数据整理与事实去重(JSON Schema 输出)
↓
Agent:撰写 Markdown 报告
↓
Agent:审核,输出 {passed, issues, revisedAdvice}
↓
Condition
├─ passed=true → Output + Word/Markdown artifact
└─ passed=false → Writer(最多回退 2 次)
验收必须覆盖:
- 用户点击运行后 1 秒内看到
run.created/queued或明确排队状态。 - 节点开始、模型文字、工具步骤和 artifact 实时可见。
- 刷新 iframe 后能从数据库恢复完整时间线并继续追尾。
- 网络中断后无重复文字、无丢步骤、无跨 run 串流。
- SQL 写语句、多语句和越权表被发布校验或运行策略拒绝。
- HTTP 内网探测和云元数据地址被拒绝。
- 代码超时后沙箱任务终止,Gateway 不受影响。
- 审核退回次数达到上限时明确失败,不形成无限循环。
- 取消后不再启动新节点。
- 最终输出符合 JSON Schema,报告 artifact 可下载。
19. Definition of Done
一个阶段只有同时满足以下条件才算完成:
- API、数据库迁移、配置和权限实现齐全。
- 单元、repository、router、流重放和端到端测试通过。
- 多实例/重启场景不会重复提交终态或丢事件。
- 所有用户可见事件均可重放且不包含密钥和隐藏思维链。
- 新代码通过 Ruff、类型检查和
test_harness_boundary.py。 - 更新后端
README.md、CLAUDE.md/相关架构说明和 OpenAPI 示例。 - 前后端基于同一个事件 schema fixture 做契约测试。
20. 实施时优先顺序
建议严格按下面顺序推进:
- 先冻结标准图和事件协议。
- 再做持久化、运行状态机和断线重放。
- 先接 start/output/transform,确认基础链路可靠。
- 再加 SQL、HTTP、Sandbox 等高风险节点。
- 最后接 Agent/Skill、人工介入、循环和复杂分支。
这样 Coze 画布、DeerFlow 后端和 iframe 时间线不会各自形成一套难以兼容的临时协议。