deerflow-code/offline-backend-20260512/backend/docs/WORKFLOW_STUDIO_COZE_FRONTEND_ADAPTATION_ZH.md
2026-09-07 18:24:55 +08:00

22 KiB
Raw Permalink Blame History

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 入口。