# Workflow Studio:Coze 独立前端对接 DeerFlow 后端适配开发文档 > 状态:待实施 > 日期:2026-08-29 > 前端范围:F:/react01/code-stutsiu/frontend/apps/coze-studio/src/workflow-standalone/ > 后端范围:offline-backend-20260512/backend/ > 关联文档: > > - docs/WORKFLOW_STUDIO_BACKEND_DEV_ZH.md:工作流运行时总体设计与已有能力。 > - docs/WORKFLOW_STUDIO_BACKEND_REMEDIATION_ZH.md:安全、并发、租约与事件持久化整改。 > > 本文只处理“当前独立 Coze 页面与真实 DeerFlow 后端之间”的适配,不重新设计工作流运行内核。 ## 1. 目标与结论 当前独立页的交互、三栏布局、资源卡、运行历史、SSE reducer 与 mock 演示已可用,但其保存、校验、发布和部分运行事件仍使用 mock 协议。真实 DeerFlow 后端采用 WorkflowGraph v1.0 和持久化运行时,两者不能直接互换。 本次适配目标: 1. Coze 画布能够保存、重新加载、校验、发布,并由 DeerFlow 运行。 2. 画布上的 Agent、Skill、SQL、HTTP、Code、Input、Output 节点都有真实、可审计的执行配置。 3. 左侧对话运行和右侧执行过程能从同一条 DeerFlow SSE 事件流稳定恢复工具步骤、人工介入与文件制品。 4. 不把 Coze 的内部数值节点类型、任意画布 JSON、凭据明文或完整工具参数泄漏到运行时协议中。 结论:不能仅在现有 POST /api/workflows/{id}/runs 中增加几个兼容字段。必须先建立“画布表示”和“执行图表示”的明确边界;否则保存成功的流程仍无法发布或运行。 ## 2. 当前差异清单 | 编号 | 当前独立页行为 | DeerFlow 当前行为 | 影响 | 所属 | | --- | --- | --- | --- | --- | | A01 | 保存 body 为 schemaJson,读取 workflowId / draftRevision / schemaJson | 草稿接口要求 graph,读取返回 id / draft_graph / draft_revision | 自动保存、冲突恢复、发布均无法真实联调 | 前端主改,后端提供工作室 DTO | | A02 | 校验期待 valid 字段 | 后端返回 ok 字段 | 前端始终判定校验不通过 | 前后端契约统一 | | A03 | Coze JSON 被原样持久化 | 后端运行时只接受 WorkflowGraph v1.0 | Coze 数值节点类型不可执行 | 前端编译,后端持久化与验证 | | A04 | 拖入资源只预填节点标题 | Agent / Skill 执行器要求 agentId、skillNames、promptTemplate 等 | 资源卡看起来可用,真实运行必失败 | 前端表单 + 后端发布校验 | | A05 | 工具事件读取 name、status | 后端发送 toolName、ok | 工具步骤丢失,失败状态错误 | 后端规范化,前端兼容旧字段 | | A06 | 人工介入读取顶层 prompt / formSchema | 后端放在 pendingInput 内 | 介入卡没有问题文本和表单 | 后端事件扁平化,前端兼容 | | A07 | 文件预览请求单个 artifact 内容接口 | 后端只提供制品列表 | 文件卡可见但无法打开或下载 | 后端新增制品内容接口 | | A08 | 资源 hook 请求 credentials 目录 | 后端没有该路由 | 整个资源加载 Promise.all 可能显示失败 | 后端补只读引用目录,或前端暂停请求 | | A09 | UI 可选“当前草稿试运行”和 mock 场景 | 后端忽略 target / scenario,只运行发布版本 | 用户以为执行草稿,实际执行旧版本 | 前端先隐藏;后端后续可实现草稿快照 | | A10 | 连续输入会创建新的 run | 后端无 conversation / turn 模型 | 不是 ChatDev 式多轮会话 | 二期可选能力 | ## 3. 必须冻结的总体设计 ### 3.1 两种表示,两个职责 同一工作流必须保留两份有明确用途的数据,不能让任何一份冒充另一份。 | 数据 | 责任方 | 用途 | 是否可直接执行 | | --- | --- | --- | --- | | canvasSchemaJson | Coze 前端产生;后端仅作为受限 JSON 文档保存 | 画布恢复、节点位置、Coze 表单内部字段、编辑体验 | 否 | | executionGraph | 前端适配器编译;后端以 WorkflowGraph 校验并版本化 | 发布校验、权限检查、调度、恢复、SSE、审计 | 是 | 约束: 1. DeerFlow harness 只接收 executionGraph,绝不解析 Coze StandardNodeType 或 Coze 内部服务字段。 2. canvasSchemaJson 不进入 SSE,不进入运行快照,不作为表达式或节点配置来源。 3. 发布版本至少冻结 executionGraph;建议同时保存其来源 canvasSchemaJson 的哈希,便于审计与排错。 4. Coze 到 WorkflowGraph 的逐节点转换放在前端 adapters/coze-canvas-adapter.ts 或其同级编译器中。后端不耦合 Coze 的数值节点类型。 5. 后端应拒绝“只有 canvasSchemaJson、没有合法 executionGraph”的发布请求,不能静默将其当作空图。 ### 3.2 推荐的工作室适配层 保持现有 /api/workflows 的标准运行时 API 语义,不在其核心请求中混入 Coze 专属字段。新增一层仅面向独立页的 studio facade: GET /api/workflows/{workflow_id}/studio-document PUT /api/workflows/{workflow_id}/studio-draft POST /api/workflows/{workflow_id}/studio-validate POST /api/workflows/{workflow_id}/studio-publish 现有以下端点继续作为运行时和其他客户端的标准接口: POST /api/workflows/{workflow_id}/runs GET /api/workflows/{workflow_id}/runs GET /api/workflows/runs/{run_id}/events GET /api/workflows/runs/{run_id}/stream 这样既避免污染通用 API,也让前端能获得它需要的 camelCase DTO。 ## 4. Studio facade API 契约 ### 4.1 获取文档 请求: GET /api/workflows/{workflow_id}/studio-document 响应: { "workflowId": "wf_001", "name": "周报生成", "draftRevision": 12, "publishedVersionId": "ver_009", "canvasSchemaJson": "{...}", "executionGraph": { "schemaVersion": "1.0", "id": "wf_001", "nodes": [], "edges": [] }, "updatedAt": "2026-08-29T10:00:00+08:00" } 要求: - 从现有 definition 的 id、draft_revision、draft_graph、published_version_id 等字段适配生成。 - canvasSchemaJson 为空时返回空画布 JSON,而不是 null。 - 权限沿用现有 owner/admin 规则。 - 不返回 DSN、HTTP 请求头、resume token、完整 agent 配置或任何私密字段。 ### 4.2 保存草稿 请求: PUT /api/workflows/{workflow_id}/studio-draft { "expectedRevision": 12, "canvasSchemaJson": "{...}", "executionGraph": { "schemaVersion": "1.0", "id": "wf_001", "nodes": [], "edges": [] } } 响应: { "revision": 13, "updatedAt": "2026-08-29T10:01:00+08:00" } 规则: 1. 先做 WorkflowGraph 结构解析;无法解析返回 400,错误码 WORKFLOW_SCHEMA_INVALID。 2. 画布 JSON 只做大小、JSON 格式、深度和危险字段控制,不做执行解释。 3. canvasSchemaJson 与 executionGraph 必须在同一次 CAS 更新中落库,不能出现画布和执行图不同 revision。 4. revision 冲突返回 409: { "code": "WORKFLOW_DRAFT_CONFLICT", "message": "草稿版本冲突,请刷新后重试或另存副本", "currentRevision": 13 } 5. 不建议让旧 PUT /draft 同时接受 schemaJson 与 graph。若为灰度必须暂时兼容,兼容逻辑只能位于 facade 路由,且必须在一个发布周期后删除。 ### 4.3 校验与发布 校验请求: POST /api/workflows/{workflow_id}/studio-validate { "executionGraph": { ... } } 校验响应: { "valid": false, "issues": [ { "code": "WORKFLOW_RESOURCE_MISSING", "nodeId": "agent_writer", "field": "config.agentId", "message": "未选择可访问的智能体" } ], "graphHash": null } 发布请求: POST /api/workflows/{workflow_id}/studio-publish { "expectedRevision": 13, "changeNote": "配置报告智能体" } 发布响应: { "versionId": "ver_010", "publishedAt": "2026-08-29T10:02:00+08:00" } 发布必须复用标准发布版本表和不可变 graph 快照。facade 只负责 DTO 命名、画布关联和调用标准发布服务,不能复制一套版本逻辑。 ## 5. 后端实施项 ### 5.1 持久化 canvas schema 与草稿原子更新 现有 workflow_definitions 只有 draft_graph_json。新增独立字段,例如 draft_canvas_schema_json;不要把 Coze 画布塞进 draft_graph_json。 需要修改: | 路径 | 改动 | | --- | --- | | packages/harness/deerflow/persistence/workflows/model.py | WorkflowDefinitionRow 增加 draft_canvas_schema_json;如需审计可增加 published_canvas_schema_hash | | packages/harness/deerflow/persistence/migrations/versions/ | 新增 Alembic migration,存量行默认 {} | | packages/harness/deerflow/persistence/workflows/base.py | save_studio_draft 或扩展 save_draft 的原子接口,输入两份文档和 expected_revision | | packages/harness/deerflow/persistence/workflows/sql.py | 单条 CAS UPDATE 同时更新两份文档、revision、updated_at | | packages/harness/deerflow/persistence/workflows/memory.py | 同步实现,保证测试环境语义一致 | 禁止: - 先保存 canvas、后保存 graph 的两次写入。 - 在 SQL store 之外用“先读 revision、再无条件写”的方式实现 CAS。 - 把 canvasSchemaJson 输出到运行事件或 run context。 ### 5.2 Studio facade 路由与 DTO 新增 app/gateway/routers/workflow_studio.py,负责: 1. studio-document、studio-draft、studio-validate、studio-publish 四个端点。 2. camelCase DTO 与现有 persistence snake_case 字段之间的映射。 3. 统一错误结构和 owner/admin 鉴权。 4. 调用通用 WorkflowGraph 结构校验与 app 层资源校验。 在 app/gateway/app.py 或当前工作流 router 注册位置挂载该 router。不要让 packages/harness/deerflow 导入 app.gateway。 前端移交项: - api/workflows.ts 改为调用 studio facade。 - coze-canvas-adapter.ts 产出 executionGraph,不再只做 JSON.stringify。 - 画布新增或修改节点时,同时更新可编译的节点配置。 ### 5.3 发布前资源和权限校验 现有 validator 支持 resource_checker 钩子,但通用 harness 不应直接导入应用层的 agent、skill、data-source store。应在 app 层构造 checker 后传入。 在 studio-validate、studio-publish 和标准 publish 入口都执行: | 节点 | 必检字段 | 后端校验 | | --- | --- | --- | | agent | agentId、promptTemplate、responseSchema | 智能体存在、调用者可见、提示词非空、输出 schema 合法 | | skill | mode、agentId、skillNames、promptTemplate | 技能存在且启用;agent_skill 必须属于智能体白名单;callable_skill 未开放时明确拒绝 | | sql_read | dataSourceId、queryTemplate、parameters | 数据源可见、只读、表白名单、SQL 解析与 maxRows 上限 | | http | dataSourceId 或 credentialRef、method、path、headers 映射 | 资源可见、方法白名单、URL/内网策略、凭据引用可用 | | code | language、source、timeoutSeconds、output mapping | 沙箱已启用、语言白名单、超时和输出目录限制 | | output | schema、field mapping | 输出字段可解析,输出 schema 可校验 | | subworkflow | workflowId、versionId | 子工作流已发布、调用者有权限、深度限制 | 校验必须返回 nodeId 与字段路径,便于前端定位 Coze 节点表单。运行时仍保留二次校验,防止资源被禁用或权限在发布后撤销。 ### 5.4 资源目录与凭据引用 保留现有: - app/gateway/routers/workflow_resources.py:Agent、Skill、数据源目录。 - app/gateway/routers/workflow_data_sources.py:SQL/HTTP 数据源创建、加密与只读策略。 补充: GET /api/workflows/resources/credentials 响应只允许: { "credentials": [ { "credentialId": "http_source_001", "name": "CRM 接口密钥", "type": "http_headers", "sourceKind": "http" } ] } 绝不返回 headers、token、DSN、加密密文或可逆提示。若一期 UI 没有凭据选择器,前端必须停止请求该端点,不能让一个 404 使整个资源面板显示“加载失败”。 ### 5.5 运行目标、输入 schema 与草稿试运行 一期决策: 1. POST /runs 只接受发布版本或明确的 versionId。 2. 前端隐藏“当前草稿(试运行快照)”和 mock scenario 选择器。 3. 后端若收到 target=draft,返回 422 WORKFLOW_DRAFT_RUN_UNSUPPORTED,禁止静默回退到发布版本。 二期若需要草稿试运行,新增明确的 test-run API,而不是篡改正式 runs: POST /api/workflows/{workflow_id}/test-runs 流程为:CAS 读取草稿 → 生成不可变 test snapshot → 记录 snapshot id → 运行 → 保留期清理。运行记录必须注明 test=true,不能被误认为发布版本运行。 运行输入: - studio-document 响应必须带 inputSchema。 - 前端根据 inputSchema 渲染输入,而非永远发送 input.query。 - POST /runs 继续使用后端 JSON Schema 验证;错误返回 field/path/expected,前端在表单附近展示。 ### 5.6 SSE 事件契约收敛 后端事件是前端执行过程唯一事实来源。所有事件须先持久化再广播,seq 单调递增且可 REST 重放。 规范化以下 payload: node.tool.started { "name": "web_search", "toolCallId": "call_001", "title": "联网检索", "args": { "path": "/mnt/user-data/outputs/report.md" } } node.tool.finished { "name": "web_search", "toolCallId": "call_001", "status": "succeeded", "summary": "获得 12 条结果" } run.awaiting_input { "resumeToken": "one-time-token", "nodeId": "review", "prompt": "请确认是否发布", "formSchema": { "type": "object", "properties": {} }, "actions": ["approve", "reject"] } artifact.created { "artifactId": "artifact_001", "name": "weekly-report.docx", "kind": "file", "mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "sizeBytes": 1234 } 规则: - 工具字段统一为 name;不要只发 toolName。 - 失败工具必须明确 status=failed 与脱敏 error。 - pendingInput 可以继续存储在数据库,但对外事件须扁平化为 prompt、formSchema、actions。 - agent 节点继续发送安全展示元数据 agent、parallelGroupId、roundIndex;不发送系统提示词、技能白名单、凭据和完整配置。 - 不将每个模型 token 当作 aria/live 或持久化事件;继续使用节流后的 node.output.delta。 - 事件类型表若仍不含 node.tool.input_delta,应从前端 mock 中删除该依赖,或后端正式实现该事件;不能只有 mock 有。 涉及路径: | 路径 | 改动 | | --- | --- | | packages/harness/deerflow/workflows/nodes/agents.py | on_tool 输出 name、status、最小安全 args | | app/gateway/workflow_executor.py | run.awaiting_input 对外 payload 扁平化 | | packages/harness/deerflow/workflows/events.py | 冻结事件类型与 payload schema 样例 | | app/gateway/routers/workflow_runs.py | REST 重放与 SSE 输出保持同一 envelope | | packages/harness/deerflow/workflows/runtime/engine.py | 保持 node.started 的 agent / parallelGroupId / roundIndex 安全投影 | ### 5.7 制品内容、预览和下载 新增: GET /api/workflows/runs/{run_id}/artifacts/{artifact_id} GET /api/workflows/runs/{run_id}/artifacts/{artifact_id}/download 内容接口返回受限预览: { "artifactId": "artifact_001", "name": "weekly-report.md", "kind": "markdown", "mimeType": "text/markdown", "text": "# 周报", "sizeBytes": 2048 } 策略: 1. 每次读取先校验 run owner/admin,再校验 artifact 属于该 run。 2. 文本、JSON、表格只返回大小受限预览;大文件返回 truncated=true。 3. 二进制文件不塞入 JSON;download 接口以附件或受控 inline 响应流返回。 4. 路径只能从已登记的 artifact id 解析,禁止接收任意 sandbox 路径。 5. 制品存储生命周期沿用现有 sandbox/artifact 机制;工作流表只存引用与元数据。 涉及路径: - app/gateway/routers/workflow_runs.py:新增两个路由。 - packages/harness/deerflow/persistence/workflow_runs/*:按 run_id + artifact_id 查询的 store 方法。 - app/gateway/routers/artifacts.py:仅可参考现有 thread artifact 的鉴权和文件响应,不可直接跨 thread 复用。 ### 5.8 取消状态和事件游标 统一运行状态: | 后端状态 | 前端显示 | 说明 | | --- | --- | --- | | queued | 排队中 | 已入队未领取 | | running | 运行中 | 正在执行 | | awaiting_input | 等待人工介入 | 暂停并持有一次性 token | | cancel_requested | 取消中 | 等待运行器确认,非终态 | | completed / failed / cancelled | 终态 | SSE 停止重连 | 后端工作: 1. _public_run 输出 lastSeq 时映射 persistence 的 next_event_seq,不能读取不存在的 last_seq。 2. 前端 RunStatus 类型和 RunStatusBadge 增加 cancel_requested;后端不应把它伪装为 cancelled。 3. list events 空页必须返回请求 cursor,防止前端游标回退。 ### 5.9 连续对话运行(二期,可选) 当前“对话运行”是单个 run 的视图投影:每次发送创建一个新 run。若业务要 ChatDev 式连续问答,需要独立设计,不能复用 run_id 作为会话 id。 建议新增: workflow_conversations workflow_conversation_turns workflow_runs.conversation_id (nullable) 接口: POST /api/workflows/{workflow_id}/conversations GET /api/workflows/{workflow_id}/conversations POST /api/workflows/conversations/{conversation_id}/turns 每个 turn 创建一个 run,并记录输入、run id、可展示摘要和制品引用。默认不把完整模型上下文拼接给每个节点;由 graph 明确配置哪些字段、哪些历史摘要可见。 ## 6. 推荐实施顺序 ### 阶段 A:真实保存与发布闭环(阻断项) 1. 增加 canvas schema 持久化迁移和 store 原子接口。 2. 实现 studio facade 四个端点。 3. 前端实现 Coze JSON 到 WorkflowGraph 编译,并改接 facade。 4. 完成输入/输出、代码、Agent、Skill、SQL、HTTP 的最小节点配置表单。 5. 加入发布前 app 层资源检查。 验收:编辑一个“Input → SQL → Agent → Output”流程,刷新后画布与配置不丢失,发布后执行的是同一版本的标准图。 ### 阶段 B:真实执行过程闭环 1. 统一工具、人工介入、制品的 SSE payload。 2. 实现制品内容与下载接口。 3. 修复 lastSeq、cancel_requested。 4. 增加跨 REST 重放、SSE 断线重连、人工介入恢复的契约测试。 验收:报告工作流在 iframe 刷新后能恢复步骤、工具摘要、文件卡和人工介入状态。 ### 阶段 C:草稿试运行与连续会话 1. 先由产品确认是否真的需要草稿试运行;需要时实现独立 test-run snapshot。 2. 再由业务确认是否需要跨 run 的上下文记忆;需要时实现 conversation/turn。 在阶段 C 前,前端必须隐藏对应入口,禁止用“看似可点、实际忽略”的体验替代能力。 ## 7. 测试与验收 ### 7.1 后端新增测试 建议新增: | 文件 | 重点场景 | | --- | --- | | tests/test_workflow_studio_facade.py | studio DTO、camelCase、CAS、错误结构、owner/admin 鉴权 | | tests/test_workflow_studio_graph_contract.py | Coze 编译产物可被 WorkflowGraph 接受;非法 graph 被拒绝 | | tests/test_workflow_resource_validation.py | Agent/Skill/SQL/HTTP/Code/Output 配置与权限校验 | | tests/test_workflow_event_contract.py | tool、awaiting_input、artifact、parallel agent payload 与 REST/SSE 一致 | | tests/test_workflow_artifact_content.py | 文本预览、二进制下载、越权、路径穿越、超大文件 | | tests/test_workflow_runs.py | target=draft 显式拒绝、cancel_requested、lastSeq | 保留并扩展: - tests/test_workflow_engine.py:并行、循环、节点 started 元数据。 - tests/test_workflow_runs.py:运行、取消、恢复、SSE、重放。 - tests/test_workflow_data_sources.py:SQL 只读、HTTP allowlist、秘密不回显。 ### 7.2 前后端契约 fixture 在 backend/tests/fixtures/workflow_studio/ 固化 JSON fixture,并让前端测试复制或通过生成脚本消费: 1. studio-document。 2. 保存成功与 409 冲突。 3. 资源缺失校验。 4. Agent 工具 started/finished。 5. human_input 暂停与恢复。 6. markdown/json/table/docx 制品。 7. 并行三智能体与取消中状态。 禁止前后端各自手写近似 mock 事件。fixture 变更必须同时更新双方测试。 ### 7.3 发布准入 - [ ] Coze 画布和 executionGraph 采用两份数据、一次 CAS 保存。 - [ ] 真实后端不再依赖 schemaJson、valid 等 mock 专用字段。 - [ ] 所有可拖入资源都能在 graph 中找到真实绑定字段。 - [ ] 发布前可定位到节点和字段的资源/权限错误。 - [ ] 工具、人工介入、制品在 SSE 与 REST replay 中字段一致。 - [ ] 制品内容和下载经过 run 级鉴权,不能按任意路径读取。 - [ ] target=draft 不会被静默执行为发布版本。 - [ ] typecheck、后端 pytest、ruff 与至少一条 iframe E2E 全部通过。 ## 8. 明确不做的事项 1. 不把整个 Coze Go 后端或 ChatDev 后端迁入 DeerFlow。 2. 不在 DeerFlow harness 中导入 app.gateway 或 Coze 前端类型。 3. 不为了兼容 mock 而让标准 WorkflowGraph API 永久接受任意 schemaJson。 4. 不向浏览器返回数据库 DSN、HTTP headers、token、resume token、沙箱真实文件路径或模型隐藏思维链。 5. 不在未实现草稿快照、连续会话前保留误导性的 UI 入口。