19 KiB
Open Canvas 后端实施清单
1. 文档目标
本文档是 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. 目标代码落点
建议优先关注以下目录:
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 新增或扩展以下区域:
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 个阶段:
- 线程状态扩展
- 结构化 artifact 协议定义
canvas_agent接入到 Agent 工厂- Canvas 图的最小节点实现
- Gateway/线程接口兼容
- 流式状态输出适配
- 模型能力元数据扩展
- 本地模型兼容与配置统一
- 快捷动作与局部改写
- Web 搜索增强
- 测试、回归、验收
6. 阶段一:线程状态扩展
6.1 任务目标
让 DeerFlow 的线程状态能原生保存 Canvas 所需的结构化信息。
6.2 任务清单
- 扩展 thread_state.py
- 定义 Canvas 相关 TypedDict 或 dataclass 结构
- 确保状态字段可序列化
- 不影响现有
messages/title/artifacts/todos逻辑
6.3 建议新增字段
在现有 ThreadState 上新增:
artifacthighlighted_texthighlighted_codecanvas_modequick_action_idweb_search_enabledweb_search_results
6.4 建议数据形态
可先采用 JSON-safe dict 结构,而不是在状态层引入过重的复杂对象。
建议:
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
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或等价内部消息artifacthighlighted_texthighlighted_codequick_action_idweb_search_enabledweb_search_resultsnext
9.4 状态设计建议
优先复用 LangGraph 原生消息 reducer。
对结构化字段,尽量采用:
- 可整体替换
- 易序列化
- 无 Python 特殊对象依赖
9.5 验收标准
- 状态能驱动 Canvas 节点运行
- 状态可被序列化回线程
10. 阶段五:Canvas 图最小节点闭环
10.1 任务目标
先实现最小闭环,不要一开始就追求 open-canvas 全量功能。
10.2 首版最小节点
建议至少实现:
route_user_intentreply_general_inputgenerate_artifactrewrite_artifactgenerate_followup
10.3 节点职责
route_user_intent
判断用户当前意图:
- 普通聊天
- 新建 artifact
- 重写当前 artifact
reply_general_input
仅返回普通消息,不更新 artifact。
generate_artifact
根据用户需求生成第一版 artifact。
rewrite_artifact
根据当前 artifact 和用户请求生成新版本。
generate_followup
在 artifact 更新后补一条自然语言消息。
10.4 输出要求
必须至少更新:
messagesartifact
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
- 扩展线程 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
- 扩展模型响应结构
14.3 现有字段之外建议新增
providersupports_visionsupports_tool_callingsupports_canvas_generationsupports_canvas_rewritesupports_canvas_code_editsupports_streaming_textsupports_streaming_tool_callsis_localbase_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 配置要求
建议模型配置支持以下字段:
namedisplay_nameusemodelapi_keybase_urlsupports_visionsupports_thinkingsupports_reasoning_effortsupports_tool_callingis_local
15.5 Canvas 额外能力字段
建议再增加:
supports_canvas_generationsupports_canvas_rewritesupports_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 首版动作建议
文本:
expandshortenformalizecasualize
代码:
add_commentsadd_logsfix_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_namecanvas_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/modelsPOST /api/canvas/exportPOST /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. 推荐开发顺序
强烈建议按以下顺序推进:
- 线程状态扩展
- artifact 协议定稿
canvas_agent注册- 最小图闭环
- 线程读取与流式返回
- 模型接口扩展
- 整体生成与整体改写
- 局部改写
- 快捷动作
- 本地模型增强
- Web 搜索增强
- 导出联动
不要一开始就做:
- 快捷动作全集
- 局部 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 状态协议文档
明确:
artifacthighlighted_texthighlighted_codequick_action_idweb_search_results
的最终 JSON 格式。
28.2 Canvas Agent 节点接口文档
逐节点定义:
- 输入字段
- 输出字段
- prompt 责任
- 错误处理
这样进入实际编码时会更顺畅。