# Open Canvas 后端实施清单 ## 1. 文档目标 本文档是 [openCanvas.md](./openCanvas.md) 的后端落地版清单。 目标是把 Canvas 模式在当前 DeerFlow 后端中的实施工作拆成可以直接排期和执行的任务,覆盖: - 线程状态扩展 - `canvas_agent` 设计 - LangGraph 节点拆分 - Gateway API 扩展 - 流式输出契约 - 模型配置与本地模型支持 - 结构化 artifact 的持久化 - 测试与验收顺序 本文档默认前端会新增独立的 Canvas 子系统,并通过现有 LangGraph 线程和 Gateway API 与后端通信。 --- ## 2. 实施原则 ### 2.1 总原则 - 不破坏现有 `lead_agent` - 不破坏现有 `artifacts: list[str]` 文件产物体系 - 优先增量扩展,不做推倒重构 - 先打通状态和协议,再实现复杂 AI 行为 - 先支持文本/代码双类型,再逐步扩展快捷动作 ### 2.2 推荐路线 推荐采用“新增独立 `canvas_agent`”方案,而不是把所有 Canvas 行为塞进现有 `lead_agent`。 推荐原因: - 风险更低 - 图逻辑更清晰 - 更容易对齐 open-canvas 的状态结构 - 不影响现有 DeerFlow 普通聊天与文件产物模式 ### 2.3 后端目标边界 本次后端实施重点是: - 新增 Canvas 专用线程状态字段 - 新增 Canvas Agent 图 - 新增前端需要的模型能力元数据 - 支持结构化 artifact 在线程状态中读写 本次不要求: - 完整复刻 open-canvas 的所有 TS 图结构 - 完整复刻 Supabase 相关逻辑 - 把所有模型 provider 完全抽象成统一能力中台 --- ## 3. 现有后端基础能力盘点 当前 DeerFlow 后端已经具备非常好的基础设施: - LangGraph 线程与流式交互 - FastAPI Gateway - 模型动态配置 - 本地线程状态管理 - Sandbox/工具体系 - 上传与文件 artifact 访问 - Memory 与标题生成 因此 Canvas 模式并不是“从 0 开始做 Agent 平台”,而是“在现有平台上新增一类结构化创作模式”。 --- ## 4. 目标代码落点 建议优先关注以下目录: ```text offline-backend-20260512/backend/ app/gateway/ routers/ deps.py app.py packages/harness/deerflow/ agents/ factory.py thread_state.py lead_agent/ middlewares/ runtime/ config/ tools/ ``` 建议为 Canvas 新增或扩展以下区域: ```text packages/harness/deerflow/ agents/ canvas_agent/ __init__.py agent.py prompt.py state.py nodes/ route_user_intent.py generate_artifact.py rewrite_artifact.py update_highlighted_text.py update_highlighted_code.py generate_followup.py quick_actions.py web_search.py ``` 如果不想把节点拆得太细,首版也可以把部分逻辑先收在 `agent.py` 里,但建议至少把 `state.py` 和 `prompt.py` 独立出来。 --- ## 5. 开发阶段总览 后端建议分为 11 个阶段: 1. 线程状态扩展 2. 结构化 artifact 协议定义 3. `canvas_agent` 接入到 Agent 工厂 4. Canvas 图的最小节点实现 5. Gateway/线程接口兼容 6. 流式状态输出适配 7. 模型能力元数据扩展 8. 本地模型兼容与配置统一 9. 快捷动作与局部改写 10. Web 搜索增强 11. 测试、回归、验收 --- ## 6. 阶段一:线程状态扩展 ### 6.1 任务目标 让 DeerFlow 的线程状态能原生保存 Canvas 所需的结构化信息。 ### 6.2 任务清单 - 扩展 [thread_state.py](../../offline-backend-20260512/backend/packages/harness/deerflow/agents/thread_state.py) - 定义 Canvas 相关 TypedDict 或 dataclass 结构 - 确保状态字段可序列化 - 不影响现有 `messages/title/artifacts/todos` 逻辑 ### 6.3 建议新增字段 在现有 `ThreadState` 上新增: - `artifact` - `highlighted_text` - `highlighted_code` - `canvas_mode` - `quick_action_id` - `web_search_enabled` - `web_search_results` ### 6.4 建议数据形态 可先采用 JSON-safe dict 结构,而不是在状态层引入过重的复杂对象。 建议: ```python class CanvasArtifactText(TypedDict): index: int type: Literal["text"] title: str fullMarkdown: str class CanvasArtifactCode(TypedDict): index: int type: Literal["code"] title: str language: str code: str class CanvasArtifact(TypedDict): currentIndex: int contents: list[CanvasArtifactText | CanvasArtifactCode] ``` ### 6.5 与现有 `artifacts` 的关系 必须保留现有: - `artifacts: list[str]` 新增: - `artifact: dict | None` 含义区分: - `artifacts` 表示文件型产物列表 - `artifact` 表示 Canvas 当前主文档 ### 6.6 验收标准 - 旧线程无 `artifact` 时不报错 - 新线程可写入 `artifact` - 线程状态能被 FastAPI 和 LangGraph 正确序列化 --- ## 7. 阶段二:结构化 Artifact 协议定义 ### 7.1 任务目标 统一前后端对 Canvas artifact 的字段约定。 ### 7.2 任务清单 - 在后端建立一份明确的 artifact 协议定义 - 明确字段命名、类型、兼容策略 - 明确版本管理规则 ### 7.3 协议要求 必须支持: - 文本 artifact - 代码 artifact - 标题 - 当前版本索引 - 历史版本列表 - 后续支持局部改写 ### 7.4 版本规则 - 每次 AI 整体生成,生成 `contents[0]` - 每次 AI 重写,追加一个新版本 - `currentIndex` 指向当前版本 - 历史版本默认只保留在线程状态中,不额外落库 ### 7.5 建议约束 - `index` 从 1 开始 - `currentIndex` 必须落在 `contents` 范围内 - `type` 只允许 `"text"` 或 `"code"` ### 7.6 验收标准 - 前后端字段完全一致 - 状态协议有明确默认值和边界规则 --- ## 8. 阶段三:接入 `canvas_agent` ### 8.1 任务目标 在现有 Agent 体系中注册一个独立的 Canvas Agent。 ### 8.2 任务清单 - 新建 `packages/harness/deerflow/agents/canvas_agent/` - 新建 `agent.py` - 新建 `prompt.py` - 新建 `state.py` - 修改 [factory.py](../../offline-backend-20260512/backend/packages/harness/deerflow/agents/factory.py) ### 8.3 注册方式建议 建议将 `canvas_agent` 作为一个显式 assistant id 暴露给 LangGraph 运行时。 前端调用时: - `assistant_id=canvas_agent` ### 8.4 Agent 设计原则 - Canvas Agent 不负责 DeerFlow 全部工具生态 - 首版专注于: - 生成文档 - 重写文档 - 局部改写 - 跟进回复 ### 8.5 工具策略 首版建议: - 不强依赖 shell/sandbox - 不强依赖 present_files - 先把生成与改写链路做好 后续可以根据需要接入文件导出能力。 ### 8.6 验收标准 - `canvas_agent` 可被识别 - 前端能以 assistant id 调用它 - 不影响 `lead_agent` --- ## 9. 阶段四:Canvas 状态与图定义 ### 9.1 任务目标 为 Canvas Agent 定义清晰、最小但可扩展的状态结构。 ### 9.2 任务清单 - 新建 `canvas_agent/state.py` - 定义图状态 - 约束与 `ThreadState` 的映射方式 ### 9.3 建议状态字段 - `messages` - `_messages` 或等价内部消息 - `artifact` - `highlighted_text` - `highlighted_code` - `quick_action_id` - `web_search_enabled` - `web_search_results` - `next` ### 9.4 状态设计建议 优先复用 LangGraph 原生消息 reducer。 对结构化字段,尽量采用: - 可整体替换 - 易序列化 - 无 Python 特殊对象依赖 ### 9.5 验收标准 - 状态能驱动 Canvas 节点运行 - 状态可被序列化回线程 --- ## 10. 阶段五:Canvas 图最小节点闭环 ### 10.1 任务目标 先实现最小闭环,不要一开始就追求 open-canvas 全量功能。 ### 10.2 首版最小节点 建议至少实现: - `route_user_intent` - `reply_general_input` - `generate_artifact` - `rewrite_artifact` - `generate_followup` ### 10.3 节点职责 #### `route_user_intent` 判断用户当前意图: - 普通聊天 - 新建 artifact - 重写当前 artifact #### `reply_general_input` 仅返回普通消息,不更新 artifact。 #### `generate_artifact` 根据用户需求生成第一版 artifact。 #### `rewrite_artifact` 根据当前 artifact 和用户请求生成新版本。 #### `generate_followup` 在 artifact 更新后补一条自然语言消息。 ### 10.4 输出要求 必须至少更新: - `messages` - `artifact` ### 10.5 验收标准 - 无 artifact 时能生成新 artifact - 有 artifact 时能整体重写 - 重写后版本数增加 --- ## 11. 阶段六:Prompt 设计与模型调用封装 ### 11.1 任务目标 把 Canvas 模式的 prompt 和模型调用逻辑集中管理。 ### 11.2 任务清单 - 在 `canvas_agent/prompt.py` 中集中定义 prompt 常量 - 在 `canvas_agent/agent.py` 或节点工具模块中封装模型调用 ### 11.3 最少需要的 prompt - 新建 artifact prompt - 整体重写 artifact prompt - 路由判断 prompt - followup prompt ### 11.4 设计建议 提示词必须显式约束: - 输出完整文档还是局部片段 - 是否允许修改类型 - 是否允许修改标题 - 对文本和代码的输出格式要求 ### 11.5 模型调用封装建议 不要在每个节点里重复拼模型配置。 建议提供统一函数: - `get_canvas_model(...)` - `invoke_canvas_model(...)` ### 11.6 验收标准 - prompt 集中管理 - 节点中模型调用逻辑可复用 --- ## 12. 阶段七:Gateway 线程接口兼容 ### 12.1 任务目标 让前端读取线程状态时能拿到结构化 `artifact`。 ### 12.2 任务清单 - 检查 [threads.py](../../offline-backend-20260512/backend/app/gateway/routers/threads.py) - 扩展线程 state 响应的 `values` - 确保 `artifact` 字段会被安全返回 ### 12.3 要求 - `GET /api/threads/{id}` - 线程历史 - 状态读取接口 都要兼容 `artifact` ### 12.4 注意事项 - 历史 checkpoint 可能没有 `artifact` - 旧线程必须兼容 - 结构化字段不能被错误裁剪 ### 12.5 验收标准 - 前端取线程时能拿到 `artifact` - 刷新页面后 Canvas 内容能恢复 --- ## 13. 阶段八:流式输出适配 ### 13.1 任务目标 让 Canvas 模式的 artifact 能通过 LangGraph 流实时更新。 ### 13.2 任务清单 - 检查运行时序列化逻辑 - 确保 `artifact` 在流事件中可见 - 明确前端消费 `values` 还是 `updates` ### 13.3 输出建议 首版允许采用较简单策略: - 每次节点完成后输出完整 `artifact` 不强制首版做精细的 token 级 artifact 局部流。 ### 13.4 原因 完整对象覆盖更新: - 容易实现 - 容易调试 - 前后端一致性更好 ### 13.5 二期方向 后续再考虑: - 局部 chunk 更新 - 文本与代码差异化流式拼接 ### 13.6 验收标准 - 前端发送请求时,artifact 能跟随 run 更新 - 流结束后状态与线程最终值一致 --- ## 14. 阶段九:模型接口扩展 ### 14.1 任务目标 让前端能拿到适合 Canvas 模式的模型能力信息。 ### 14.2 任务清单 - 扩展 [models.py](../../offline-backend-20260512/backend/app/gateway/routers/models.py) - 扩展模型响应结构 ### 14.3 现有字段之外建议新增 - `provider` - `supports_vision` - `supports_tool_calling` - `supports_canvas_generation` - `supports_canvas_rewrite` - `supports_canvas_code_edit` - `supports_streaming_text` - `supports_streaming_tool_calls` - `is_local` - `base_url` ### 14.4 注意事项 不是所有字段都必须从配置文件显式写出。 可以采用: - 配置直出 - provider 推断 - 默认值补齐 ### 14.5 验收标准 - 前端模型选择器能获取足够元数据 - 本地模型和云模型能区分 --- ## 15. 阶段十:本地模型支持与配置统一 ### 15.1 任务目标 支持像 open-canvas 一样自由配置本地模型,但配置真源在 DeerFlow。 ### 15.2 原则 - 以 `config.yaml` 为唯一真源 - 前端不依赖 open-canvas 的 TS 模型表 - 后端负责把配置转换成前端可消费的能力描述 ### 15.3 首批支持范围 推荐先支持: - Ollama - OpenAI-compatible 本地服务 例如: - LM Studio - vLLM - LocalAI - One API/OpenAI 兼容代理 ### 15.4 配置要求 建议模型配置支持以下字段: - `name` - `display_name` - `use` - `model` - `api_key` - `base_url` - `supports_vision` - `supports_thinking` - `supports_reasoning_effort` - `supports_tool_calling` - `is_local` ### 15.5 Canvas 额外能力字段 建议再增加: - `supports_canvas_generation` - `supports_canvas_rewrite` - `supports_canvas_code_edit` ### 15.6 验收标准 - 配置本地模型后 `/api/models` 可见 - `canvas_agent` 可调用本地模型 - 至少文本生成和整体改写可用 --- ## 16. 阶段十一:整体重写节点完善 ### 16.1 任务目标 让已有 artifact 能被稳定整体重写并产生新版本。 ### 16.2 任务清单 - 实现 `rewrite_artifact` - 明确文本与代码两种重写路径 - 维护标题和类型变更规则 ### 16.3 规则建议 - 默认保持当前类型 - 用户明确要求时才允许改类型 - 默认保持标题 - 用户明确换主题时可更新标题 ### 16.4 结果写入规则 - 新版本追加到 `contents` - `currentIndex` 指向新版本 ### 16.5 验收标准 - 文本 artifact 整体重写正常 - 代码 artifact 整体重写正常 - 历史版本保留 --- ## 17. 阶段十二:局部改写节点 ### 17.1 任务目标 支持文本和代码的局部选区修改。 ### 17.2 任务清单 - 新增 `update_highlighted_text.py` - 新增 `update_highlighted_code.py` - 在状态中消费选区信息 ### 17.3 文本局部改写要求 输入: - 当前 `fullMarkdown` - 选区起止 - 选中原文 - 用户修改指令 输出: - 只返回替换片段或返回完整合并后文本 ### 17.4 首版建议 首版优先采用“后端返回完整合并后文本”策略。 原因: - 前端逻辑更轻 - 状态更一致 ### 17.5 代码局部改写要求 输入: - 当前代码全文 - 选区代码片段 - 选区边界 - 用户修改指令 输出: - 新的完整代码文本 ### 17.6 验收标准 - 文本局部改写成功 - 代码局部改写成功 - 未选中区域不被误改 --- ## 18. 阶段十三:快捷动作节点 ### 18.1 任务目标 支持前端动作栏触发不同的重写行为。 ### 18.2 任务清单 - 新增 `quick_actions.py` - 定义动作 id 到 prompt 的映射 - 在路由节点中识别 `quick_action_id` ### 18.3 首版动作建议 文本: - `expand` - `shorten` - `formalize` - `casualize` 代码: - `add_comments` - `add_logs` - `fix_bugs` ### 18.4 实现建议 动作不要散落在各节点里硬编码。 建议集中成映射表: - `action_id -> prompt builder` ### 18.5 验收标准 - 指定动作可触发不同结果 - 动作执行后生成新版本 --- ## 19. 阶段十四:Web 搜索增强 ### 19.1 任务目标 支持 Canvas 模式在需要时引入搜索结果。 ### 19.2 任务清单 - 新增 `web_search.py` - 消费 `web_search_enabled` - 将搜索结果写入 `web_search_results` ### 19.3 首版策略 首版只做: - 打开开关后,生成/重写前先搜索 - 把结果摘要注入 prompt 先不做复杂页面抓取多轮链路。 ### 19.4 验收标准 - 开关打开时能引入搜索结果 - 结果可回传前端展示 --- ## 20. 阶段十五:线程创建与 metadata 策略 ### 20.1 任务目标 让 Canvas 线程在创建和恢复时保留必要上下文。 ### 20.2 任务清单 - 约定前端创建 Canvas thread 时写入 metadata - 后端在 thread search / get 中保留这些 metadata ### 20.3 推荐 metadata - `ui_mode: "canvas"` - `agent_id: "canvas_agent"` - `model_name` - `canvas_type` 可选 ### 20.4 验收标准 - Canvas thread 与普通 thread 可区分 - 恢复线程时前端可据 metadata 恢复 UI --- ## 21. 阶段十六:Gateway 扩展接口评估 ### 21.1 任务目标 判断是否需要新增 Canvas 专用 REST API。 ### 21.2 原则 能复用现有线程状态与 LangGraph 流,就不要新增太多新接口。 ### 21.3 可以先不新增的接口 - Canvas artifact 专用 CRUD - Canvas 快捷动作专用接口 这些都可以先通过: - 线程 state - runs.stream 完成。 ### 21.4 可能需要新增的接口 二期如果需要,可增加: - `GET /api/canvas/models` - `POST /api/canvas/export` - `POST /api/canvas/convert-to-file` ### 21.5 验收标准 - 首版尽量少增接口 - 协议尽量复用现有线程与 run 模型 --- ## 22. 阶段十七:持久化与导出联动 ### 22.1 任务目标 让 Canvas 文档能和现有文件 artifact 体系联动。 ### 22.2 任务清单 - 规划导出接口 - 规划 artifact 转文件逻辑 - 规划从 Canvas 生成 outputs 文件 ### 22.3 推荐能力 - 保存为 `.md` - 保存为代码文件 - 将导出文件加入现有 `artifacts: list[str]` ### 22.4 首版建议 这部分不是最早阶段必须做的。 建议等核心 Canvas 生成与重写稳定后再接。 ### 22.5 验收标准 - 导出文件能出现在现有 artifact 文件体系中 --- ## 23. 测试清单 ### 23.1 单元测试建议 优先覆盖: - artifact 协议校验 - 版本追加逻辑 - 标题/类型变更规则 - 文本局部改写拼接逻辑 - 代码局部改写拼接逻辑 ### 23.2 集成测试建议 应覆盖: - 创建 Canvas thread - 首次生成 artifact - 整体重写 - 局部改写 - 模型切换 - 本地模型调用 ### 23.3 回归测试重点 重点检查现有功能未被破坏: - 普通 `lead_agent` 对话 - 文件型 `artifacts` - uploads - memory - scheduled tasks ### 23.4 验收标准 - Canvas 新功能通过 - 现有 DeerFlow 核心路径无回归 --- ## 24. 推荐开发顺序 强烈建议按以下顺序推进: 1. 线程状态扩展 2. artifact 协议定稿 3. `canvas_agent` 注册 4. 最小图闭环 5. 线程读取与流式返回 6. 模型接口扩展 7. 整体生成与整体改写 8. 局部改写 9. 快捷动作 10. 本地模型增强 11. Web 搜索增强 12. 导出联动 不要一开始就做: - 快捷动作全集 - 局部 token 级流式 artifact 更新 - 复杂导出体系 --- ## 25. 多人协作拆分建议 ### A 组:状态与线程 - `thread_state.py` - 线程协议 - Gateway 线程兼容 ### B 组:Canvas Agent 图 - `canvas_agent` - prompt - route/generate/rewrite/followup ### C 组:模型与配置 - `/api/models` - 本地模型配置 - provider 兼容 ### D 组:增强能力 - 局部改写 - 快捷动作 - Web 搜索 --- ## 26. 首版最小可交付范围 后端首版建议只交付这些: - 扩展线程状态支持 `artifact` - 新增 `canvas_agent` - 支持文本/代码 artifact 首次生成 - 支持整体重写 - 支持版本历史 - 扩展 `/api/models` 返回 Canvas 能力元数据 - 支持至少一种本地模型接入 首版先不强制: - 局部高亮改写 - 全量快捷动作 - 搜索增强 - 导出文件联动 --- ## 27. 总验收标准 ### 27.1 功能层 - 无 artifact 时可生成新文档 - 有 artifact 时可整体重写 - 版本历史可保留 - 前端刷新后可恢复 ### 27.2 协议层 - `artifact` 字段稳定 - 流式与最终线程状态一致 - 模型能力字段可用于前端判断 ### 27.3 兼容层 - 不破坏 `lead_agent` - 不破坏现有 `artifacts: list[str]` - 不破坏现有 Gateway 核心接口 --- ## 28. 下一步建议 完成本文档后,建议继续补两份工程文档: ### 28.1 Canvas 状态协议文档 明确: - `artifact` - `highlighted_text` - `highlighted_code` - `quick_action_id` - `web_search_results` 的最终 JSON 格式。 ### 28.2 Canvas Agent 节点接口文档 逐节点定义: - 输入字段 - 输出字段 - prompt 责任 - 错误处理 这样进入实际编码时会更顺畅。