# PLAN6 — 对标 Dify 无代码发布架构:差距分析与升级路线 ## 一、核心结论 **我们的流发布逻辑与 Dify 的底层思路高度一致(配置即数据 + 动态引擎),但在 7 个关键维度存在显著差距,需要补齐才能真正实现"无代码秒级发布,即刻可用"。** ### 已对齐的架构思路 | Dify 核心思路 | 我们的实现 | 对齐度 | |--------------|-----------|--------| | 配置即数据:前端生成 JSON,存入数据库 | ✅ FlowEditor 生成 nodes+edges JSON,存入 `FlowDefinition.definition_json` | 完全对齐 | | 零部署:发布 = 数据库状态变更,不启动新服务 | ✅ publish 仅修改 status 字段,执行时动态加载 JSON | 完全对齐 | | 动态编排引擎:解析 JSON → 执行 | ✅ `FlowEngine` 解析 JSON → 构建图 → traverse 执行 | 基本对齐 | | DAG 拓扑排序执行 | ✅ `_build_graph()` + `traverse()` 支持条件分支和循环 | 基本对齐 | | 多种节点类型 | ✅ 9 种节点:trigger/llm/tool/mcp/condition/rag/output/loop/code | 基本对齐 | | 双渠道发布 | ✅ 企微 + Web 双渠道发布状态管理 | 额外优势 | ### 存在差距的关键维度 | # | 维度 | 差距等级 | 影响 | |---|------|---------|------| | 1 | 版本快照 / 发布不可变 | 🔴 严重 | 发布后编辑直接影响线上服务 | | 2 | 流式输出 (SSE) | 🔴 严重 | 长流程用户体验极差,无法实时看到结果 | | 3 | 统一 API 网关 + App API Key | 🟠 高 | 无法被外部系统调用,无法做 API 市场 | | 4 | 工具 Schema 标准化 | 🟠 高 | 无法运行时扩展工具,无参数校验 | | 5 | Flow 节点 Memory | 🟠 高 | LLM 节点无上下文记忆,无法多轮对话 | | 6 | 变量类型系统 | 🟡 中 | 复杂业务逻辑难以表达 | | 7 | 执行监控与可观测性 | 🟡 中 | 无法追溯执行版本,缺少 token/延迟指标 | --- ## 二、逐维度详细对比 ### 1. 版本快照 / 发布不可变(🔴 严重) **Dify 的做法:** - 点击"发布"时,将当前草稿 JSON 创建一份**版本快照**(snapshot),存入独立的 `workflow_versions` 表 - `FlowDefinition` 有 `published_version` 字段,指向当前生效的版本 - 执行引擎加载的是 `published_version` 对应的 JSON,而非草稿 - 编辑草稿不影响已发布版本,回滚只需切换 `published_version` 指针 **我们的现状:** - `FlowDefinition` 只有 `version` 计数器(int),没有 `published_version` 字段 - 发布仅修改 `status="published"`,不创建快照 - **编辑草稿直接修改 `definition_json`,已发布的服务立即受影响** - `FlowExecution` 不记录执行时的版本号,无法追溯 **需要补齐:** ``` 新增模型:FlowVersion - id: UUID - flow_id: FK → FlowDefinition - version: int - definition_json: JSON(快照) - created_by: UUID - created_at: datetime 修改模型:FlowDefinition - 新增 published_version_id: FK → FlowVersion(nullable) - 新增 draft_version: int(草稿版本号) 发布逻辑改造: - publish → 创建 FlowVersion 快照 → 设置 published_version_id - execute → 加载 published_version.definition_json(而非草稿) - 编辑 → 只修改草稿,不影响 published_version - 回滚 → 切换 published_version_id 指针 ``` --- ### 2. 流式输出 SSE(🔴 严重) **Dify 的做法:** - 统一 API 支持 `response_mode: "streaming"`,返回 SSE 事件流 - 事件类型:`workflow_started` → `node_started` → `node_finished` → `workflow_finished` - LLM 节点支持 token-by-token 实时推送(`text_chunk` 事件) - 前端通过 EventSource 实时渲染 **我们的现状:** - `FlowEngine.execute()` 返回最终 `Msg`,无中间状态 - `LLMNodeAgent` 虽然配置了 `stream=True`,但 `model(prompt)` 等待完整响应 - WebSocket 端点仅 echo,未与 Flow 引擎集成 - 没有 SSE 端点 **需要补齐:** ``` 新增 SSE 端点:GET /api/chat/stream/{flow_id} - 接收 query 参数:message, session_id - 返回 text/event-stream - 事件格式: event: node_started data: {"node_id": "xxx", "node_type": "llm", "label": "生成摘要"} event: text_chunk data: {"node_id": "xxx", "content": "根据"} event: node_finished data: {"node_id": "xxx", "output": "..."} event: workflow_finished data: {"output": "最终结果"} FlowEngine 改造: - execute() 接受可选的 callback: Callable[[str, dict], None] - 每个节点执行前后调用 callback("node_started"/"node_finished", data) - LLMNodeAgent.reply() 改为 async generator,yield token ``` --- ### 3. 统一 API 网关 + App API Key(🟠 高) **Dify 的做法:** - 每个 App 有独立的 API Key(`app-xxxxxxxx`) - 统一入口:`POST /v1/chat-messages`(对话型)、`POST /v1/workflows/run`(工作流型) - 请求格式标准化:`{inputs: {}, query: "", response_mode: "blocking|streaming", user: "user-id"}` - 无需用户登录,API Key 即认证 **我们的现状:** - 所有 API 依赖 JWT 用户认证,无 App-level API Key - 执行分散在 `/api/flow/definitions/{id}/execute` 和 `/api/chat/message/{id}` - 无法被外部系统(如企微回调、第三方应用)直接调用 **需要补齐:** ``` 新增模型:FlowApiKey - id: UUID - flow_id: FK → FlowDefinition - key_hash: str(sha256) - key_prefix: str(前8位,用于展示) - name: str - created_by: UUID - created_at: datetime - last_used_at: datetime(nullable) 新增统一网关端点: POST /v1/chat-messages → 对话型 Flow(自动找 trigger → llm → output 路径) POST /v1/workflows/run → 工作流型 Flow(完整 DAG 执行) 认证方式: Header: Authorization: Bearer app-xxxxxxxx → 查 FlowApiKey 表 → 获取 flow_id → 加载 published_version → 执行 ``` --- ### 4. 工具 Schema 标准化(🟠 高) **Dify 的做法:** - 所有工具(内置/自定义 API/MCP)统一转换为 OpenAI Function Calling 的 JSON Schema - Schema 包含:name, description, parameters(JSON Schema 格式,含 type/enum/description) - 注入 LLM 时,工具 Schema 作为 `tools` 参数传入 - 用户可在前端自定义 API 工具(填 URL、Method、参数结构) **我们的现状:** - `ToolNodeAgent._TOOL_REGISTRY` 硬编码 12 个工具函数 - 工具函数只有 Python 签名,无结构化 Schema 描述 - `tool_params: dict = {}` 无校验 - 无法运行时扩展工具 **需要补齐:** ``` 工具 Schema 标准化格式: { "name": "send_notification", "description": "发送企业微信通知给指定用户", "parameters": { "type": "object", "properties": { "to_user": {"type": "string", "description": "接收人用户ID"}, "message": {"type": "string", "description": "消息内容"} }, "required": ["to_user", "message"] } } 改造 ToolNodeAgent: - _TOOL_REGISTRY 改为 _TOOL_SCHEMA_REGISTRY: dict[str, dict] - 每个工具注册时同时注册 Schema - 调用前基于 Schema 校验 tool_params - LLM 调用时将 Schema 作为 tools 参数传入 新增自定义 API 工具: - 用户可填入 OpenAPI/Swagger URL - 系统自动解析为标准 Schema 并注册 - 执行时通过 httpx 调用 ``` --- ### 5. Flow 节点 Memory(🟠 高) **Dify 的做法:** - 每个 App 有独立的对话记忆(窗口记忆/摘要记忆) - 记忆在多次调用间持久化(Redis/数据库) - LLM 节点自动注入历史对话上下文 **我们的现状:** - `UserIsolatedMemory` 存在但**未在 Flow 节点中使用** - Flow 中的 LLM 节点每次调用都是无状态的 - `ChatMessage` 表存储了历史消息,但 Flow 执行时不读取 **需要补齐:** ``` FlowEngine 改造: - execute() 接受 session_id 参数 - 创建 FlowSessionMemory(session_id, user_id) - LLM 节点执行前注入历史消息 新增 FlowSessionMemory: - 基于 ChatMessage 表持久化 - 按session_id + user_id 隔离 - 支持窗口大小配置(最近 N 条) - 支持摘要模式(超过窗口时调用 LLM 生成摘要) ``` --- ### 6. 变量类型系统(🟡 中) **Dify 的做法:** - 完整的变量面板:输入变量、环境变量、会话变量、上游节点变量 - 变量类型:string/number/array/object/file - 支持 Jinja2 模板、类型转换、默认值 - "变量聚合"节点:汇聚并行分支输出 - "迭代"节点:对列表逐项处理 **我们的现状:** - 仅有 `{{node_id.output}}` 和 `{{trigger.field}}` 模板 - 所有值都是 str,无类型系统 - 无并行汇聚、无迭代节点 **需要补齐:** ``` 变量系统升级: - 节点输出增加类型标注(string/number/array/object) - 模板解析支持类型转换和默认值 - 新增"变量聚合"节点(ParallelMergeNode) - Loop 节点支持迭代数组模式 - 输入变量面板(Flow 级别的入参定义) ``` --- ### 7. 执行监控与可观测性(🟡 中) **Dify 的做法:** - FlowExecution 记录执行时的版本号 - 统计 token 用量、延迟、费用 - 执行日志可按 App/时间/状态筛选 - 失败重试机制 **我们的现状:** - `FlowExecution` 不记录版本号 - 无 token/延迟统计 - 无失败重试 **需要补齐:** ``` FlowExecution 增加字段: - version: int(执行时的版本号) - token_usage: JSON(prompt_tokens, completion_tokens, total_tokens) - latency_ms: int - error_message: str(nullable) 执行引擎改造: - 记录每个节点的 token 用量和耗时 - 汇总到 FlowExecution - 失败节点支持重试配置 ``` --- ## 三、升级路线图 ### Phase 1 — 发布安全基础(P0,1-2周) | 任务 | 改动范围 | |------|---------| | 新增 FlowVersion 模型 + 迁移 | models, database | | FlowDefinition 增加 published_version_id | models, schemas | | 发布逻辑改造:创建快照 | flow_engine/router.py | | 执行逻辑改造:加载 published_version | flow_engine/engine.py, chat/router.py | | FlowExecution 记录版本号 | models, flow_engine/router.py | ### Phase 2 — 用户体验核心(P0,2-3周) | 任务 | 改动范围 | |------|---------| | SSE 流式输出端点 | chat/router.py(新增) | | FlowEngine callback 机制 | flow_engine/engine.py | | LLMNodeAgent async generator 改造 | flow_engine/engine.py | | 前端 EventSource 集成 | FlowChat.vue(新增) | | Flow 节点 Memory 集成 | flow_engine/engine.py, 新增 FlowSessionMemory | ### Phase 3 — 服务化能力(P1,2-3周) | 任务 | 改动范围 | |------|---------| | FlowApiKey 模型 + CRUD | models, schemas, 新增 router | | 统一 API 网关 `/v1/chat-messages`, `/v1/workflows/run` | 新增 gateway router | | API Key 认证中间件 | middleware | | 工具 Schema 标准化 | tools/*.py, ToolNodeAgent | | 自定义 API 工具(OpenAPI 导入) | 新增 custom_tool 模块 | ### Phase 4 — 高级能力(P2,2-3周) | 任务 | 改动范围 | |------|---------| | 变量类型系统 | schemas, engine.py | | 变量聚合节点 | 新增 ParallelMergeNodeAgent | | Loop 迭代数组模式 | LoopNodeAgent | | 执行监控指标 | FlowExecution, engine.py | | 工具认证改造(去掉硬编码) | tools/*.py | --- ## 四、架构哲学对齐度总结 | Dify 架构哲学 | 我们的现状 | 对齐度 | |--------------|-----------|--------| | **数据驱动**:复杂 AI 逻辑抽象为可配置参数 | ✅ 已实现。9 种节点类型,每种有独立 config | 90% | | **统一 Runner**:一套引擎解析千种 JSON 组合 | ⚠️ 部分实现。引擎存在但缺少流式/Memory/版本快照 | 60% | | **插件化架构**:Tool/Model 实现高度抽象接口 | ❌ 未实现。工具硬编码,无标准 Schema,无自动发现 | 20% | **核心差距一句话总结:我们的"配置即数据"和"零部署"思路与 Dify 完全一致,但缺少"发布不可变"(版本快照)、"实时反馈"(SSE 流式)、"开放接入"(统一网关+API Key)和"插件化工具"(标准 Schema)四大关键能力,导致无法真正实现"无代码秒级发布,即刻可用"的完整体验。** 补齐 Phase 1 + Phase 2 后,即可达到 Dify 约 80% 的核心能力。