渡雨鸣泽
dsh-aifisher-canvas
把 DeepSeek Harness 变成一间会动手的创作工作室。
一块专业级 AI 创作无限画布 —— 你说一句话,DSH 原生 Agent 在画布上替你搭好整条流水线;
而按下「生成」的那一下(费用授权),永远由你自己按。
Version: v1.89 | Node: ≥22.19 | React: 19 | TypeScript: 5.8 | License: AIFISHER Noncommercial
同一块画布 · 78 秒宣传片
🎬 观看 78 秒宣传片(1920×1080 · 带音轨 · 13 MB)
点海报或链接播放。下面的 GIF 是片中第 02 场「它的手」,自动循环。
它的手:Agent 在画布上连续调用 canvas_control
一分钟看懂:它和前一种 AI 画布差在哪
| 没有 agent 的画布 | 这块画布 |
|---|
| 谁在动手 | 你手动摆节点、点按钮、一张一张生成 | 你说「做一段 15 秒、三段的分镜」,它把剧本、分镜、关键帧、视频节点摊在画布上 |
| 提示词在哪 | 写在对话框里,图和画布是两回事 | 提示词、参考图、结果、调色都是画布上的节点,看得见、连得上、改得动 |
| 跨镜头一致 | 每个镜头重新交代一遍背景 | 项目记忆 + 视觉一致性「锚点」,跨镜头锁脸锁服装 |
| 缺一个功能 | 插件做好什么你用什么 | 技能给方法、画布给手:缺什么让它当场写一个节点挂上去 |
| 花钱的那一下 | —— | 它只准备批次,确认卡片递到你面前,扳机在你自己手里 |
一句话:你定规矩,它动手。
🎬 宣传片 · 同一块画布(78 秒)
片中只有两个光标:白色箭头是你,青色圆环是它。片头擦身而过,结尾汇成一个 ∞。
| 时间 | 场次 | 画面 |
|---|
| 0:00 | 片头 | 点阵暗场,「同一块画布」逐字升起,两枚光标擦身 |
| 0:05 | 01 定制 | 一笔青色笔锋扫过,v1.70 的旧皮肤变成玻璃面板;版本胶囊 v1.70 → v1.86 逐个点亮 |
| 0:18 | 02 它的手 | 右侧 DSH 会话里,read → recipe → configure → connect → preview → progress 六条工具调用驱动画布,最后停在「6 项生成已准备 · 在画布内批准」 |
| 0:31 | 03 差别 | 同一句提示词、同一段十秒:左边 1 个节点、0 条连线;右边 9 个节点、6 个镜头 |
| 0:41 | 04 技能 | storyboard-director 输出结构化的 canvas-shots/v1,Agent 一次 recipe 摊成 6 个镜头 |
| 0:50 | 05 平台 | 画布是 DSH 侧边栏里的一个工作台,不是独立的 App |
| 1:01 | 06 能力 | 本地 ComfyUI、云端通道、自定义端点逐条「已就绪」;调色合成器、整页扩图、分镜、素材库逐张加进工具条 |
| 1:10 | 收尾 | ∞ 徽标,两条轨迹汇合:「你定规矩,它动手」 |
05 · 画布不在浏览器里,它在 DeepSeek 里
05 平台
宣传片用这套插件自己的视觉语言做的(点阵、玻璃面板、双光标),源工程与出片脚本在片尾署名;成片 78 秒、无摄像机运动,只靠元素的进场与生长。
📸 界面
无限画布主界面:左边是 DSH,中间是画布(节点 + 调色结果),右边是能动手改画布的 DSH 原生 Agent
v1.81 起的「玻璃面板」皮肤:冷灰蓝底、半透明面板、青色连线;右下角是界面缩放(80–150%)与「退出画布」,左下角是小地图,底部是副驾 / 分镜。
| 调色合成器:四轮调色 + 达芬奇同款示波器 | 生成中:噪波点阵 + 不定进度环 |
|---|
| 调色合成器 | 生成中 |
更多取证图见 docs/shots/。
✨ 核心亮点:DSH Agent 原生接管画布
这不是在插件里套了一个写死提示词的"生图对话框"——你右侧的聊天栏就是完整的 DSH 原生 Agent(模型走 DSH 配置,可接入任意兼容的大模型 API:Base URL + Key):
- 🎯 直接操作画布对象:Agent 能看到画布上所有节点、连线、素材,通过
canvas_control 工具直接放节点、连参数、调位置、批量整理素材,不需要你手动拖拽
- 🗣️ 自然语言搭工作流:你只需要说"帮我在画布中间放一个文生图节点,接上我常用的 SDXL 大模型,提示词写一只在雨夜路灯下的三花猫,生成4张",Agent 会自动完成节点创建、参数填充、连线,并把生成确认卡片递到你面前——最后那一键(费用授权)始终由你自己按
- 🧩 完整技能生态打通:Agent 不只能调用画布内置能力——它能读取你项目级、用户级安装的所有 DSH 技能,查文档、写脚本、处理素材后直接把结果放回画布
- 🛠️ 对话式功能开发:不只是操作已有节点——你可以直接让它写自定义 ComfyUI 工作流、写素材处理脚本、配置并暴露新的画布节点、调整通道参数,写完即挂到画布上跑,不需要你手写 JSON 或改代码
- 🖼️ 素材自动回流:Agent 跑脚本处理完图片、下载完素材、生成完结果,会自动放到画布你指定的位置,连参数引用、连线都帮你接好
- 🔍 上下文全感知:Agent 知道你当前画布上有什么项目、你上次用的是哪个模型、你之前生成过什么图,不需要反复跟它交代背景
- 📎 @ 画布节点当上下文:在输入框敲
@ 直接选画布上的文本 / 图像 / 视频 / 音频节点,发送时自动把该节点的只读快照(标题、正文、结果地址)带在消息后面——指着某个节点说「把这张图改成…」就行,不用先让 Agent 自己 read 一轮
- 🔔 生成事件推回面板:Agent 准备的批次跑完后,宿主会把结果(成功/失败、错误码、节点 id)推回这条对话——Agent 接着就能看图自评、修失败、重新准备,你不用再问一句「生成完了吗」;费用确认仍然只在画布上按
- 🧠 项目记忆:画风、角色设定、常用模型/参数记进项目记忆后,新开一个对话会自动带上(同一份内容只注入一次),不用每次重新交代
- 🎭 视觉一致性:把角色定妆 / 画风参考节点设为「锚点」,一次
lock 就接到所有镜头上(锚点即上游参考图),跨镜头锁脸锁服装;副驾会提示还剩几个镜头没锁
- 🎬 长任务编排:一句话提交「做完这组镜头」——host 端按既定流水线建节点、锁参考、准备批次,然后等你在确认卡上按一次;某个镜头失败会自动再准备一批重试(仍然要你确认,不自动花钱)
- 🗂️ 分镜板:画布底部一条缩略图带,按镜头号排好、点一张聚焦过去、按播放自动翻页——只看顺序,不塞完整导演台
- 🧭 主动副驾:发现失败节点、空提示词、没锁的镜头时,在左下角安静冒一个小胶囊,点开才看建议;没有建议时不出现,点「不再提示」彻底关掉
- 🔁 在画布里换会话,不被打断:右侧栏标题本身就是会话列表(可搜、标出当前、滤掉归档与子代理会话),点一条即切;头部「+」在当前工作区新建(或复用空白)会话。两个动作都在画布内完成——不会再被 DSH 侧边栏那条「先切回对话区」的导航把你带出画布
传统 AI 画布是"你手动摆节点 + 点按钮生成单张图",这个插件是"你跟 AI 说你要做什么,AI 自己在无限画布上帮你搭整个创作流水线"。
🗺️ 其他核心能力
本地 ComfyUI 深度集成
- 自动扫描本机 ComfyUI 节点、模型、LoRA 列表,下拉选模型不用手动敲文件名
- 支持导入你自己在 ComfyUI 里
Export(API) 的自定义工作流,勾选要暴露的参数后一键变成画布节点
- 导入后在「ComfyUI 工作流配置」里看可视化节点图:逐节点挑模型 / LoRA / 参数,改完写回工作流
- 提交前自动校验节点和模型是否存在,缺哪个节点/模型直接告诉你本机相近候选是什么
- 节点上模型名和你本机文件名差量化后缀时自动对账,不用手动改
多云端生成通道
| 通道 | 支持能力 |
|---|
| 火山方舟直连 | 生图/生视频,模型自动同步 |
| OpenAI 兼容接口 | 任意符合 OpenAI schema 的生图端点都能接 |
| 魔搭 ModelScope | 一键预设,支持参考图数组传入 |
| FisherAI | 异步任务轮询,自动上传参考图公网直链 |
- 新建节点自动沿用上一次你选过的模型,不用每次重新下拉
- 视频节点的参考图按模式给:多图参考最多 9 张参考图(与 MiniMax H3 参考模式同口径),首帧 1 张、首尾帧 2 张
- 多通道多模型并行管理,任务队列里统一看进度、取消、清空
- 生成历史不覆盖:同一个图片 / 视频节点重复生成时,旧结果留在头部的「历史 N」里,点开可预览、下载、设为主图 / 主视频(图片留 20 条、视频留 12 条)
调色合成器(画布里的整套调色台)
- 主画布
Color Grade 节点双击进入全屏合成器:上方一块预览,下方一张节点画布(输入 / 输出 / 预览 + 15 种效果)
- 求值在浏览器端(WebGL2、linear 空间、RGBA16F 中间纹理、按节点缓存),拖滑块只重算下游
- Blender Color Balance 同款四轮调色(暗部 / 中灰 / 亮部 / 偏移),参数就是界面上的数字(ASC CDL:
slope = gain − lift、offset = lift)
- 达芬奇同款示波器:波形图 / 分量图 / 矢量图,10-bit 0–1023 刻度、75% 彩条靶位按 BT.601 反算
- 手势按主画布对齐:左键框选、中键平移、双击断线、
Ctrl+右键 划线断开、Shift+右键 同源线收进转接点、拖节点压线即串进链路、Ctrl+Z / Ctrl+Shift+Z 撤销重做
- 「保存并退出」把结果落成节点右侧的一张「调色结果」图,下游生成节点直接拿来当参考
无限画布原生体验
- 节点连线、编组、框选复制粘贴、历史回退全套基础能力完整保留
- Blender 式切刀:按住
Ctrl + 鼠标右键拖拽,划过的连线当场变红、松手即断开(Esc 取消,只断划过的那条)
- 划选合并 / 转接口:按住
Shift + 鼠标右键拖拽,划过的连线当场变绿;同一个上游分出去的几条线会并成一条主干,并在划过处插入一个 34×34 的「转接」小圆点(圆里直接显示上游缩略图;一路进、多路出,只整理走向、不改参考图,下游照旧拿到上游那张图)。不同上游各得一个转接点。Esc 取消
- 多选图片自动折叠成叠图,不展开也能在卡片上翻页查看
- 两步式智能扩图:整页工作台先按目标比例画出「扩图画面」(原图落位、待补区斜纹标注),再生成你自己的扩图节点,提示词参考图自动填好,生成由你自己触发
- 内置图片裁剪、宫格切分、标注蒙版、尺寸调整工具,不用跳出去用其他软件
- 文本节点直接吃 Markdown:把
.md / .txt 从系统里拖到画布上,指针处就落一个排好版的文本节点(标题 / 嵌套清单 / 表格 / 代码块 / 加粗),节点里的「导入文本」也按 Markdown 解析;正文给模型读的是去掉记号的干净纯文本,屏幕上看到的是排版(见 docs/markdown-text-nodes.md)
- 文本节点全屏预览:选中文本节点 → 头部「全屏预览」把整篇摊开只读(标题/清单/表格原样),可调字号、复制全文,
Esc 关闭;一个拖拽手势只落一个节点(同一事件重复触发 / 文件重复 / 手势重复三种情况都挡掉)
- 换肤与界面缩放:玻璃面板皮肤(与调色合成器同一套视觉语言)、80 / 90 / 100 / 110 / 125 / 150% 六档缩放、生成中的噪波点阵——都不改画布内容本身
项目库与设置页
- 打开「无限画布」直接进项目库:全部 / 最近编辑 / 收藏 / 已归档,图片·视频·文本筛选,真实本地占用,卡片墙与「5 列清单 + 右侧详情」两种视图
- 打开项目走
fisherai:open-project,不刷新页面;点一次就进画布(v1.86 修掉的「要点两次」)
- 设置页:外观与主题 / 生成 / 服务与通道 / 项目与数据 / 高级与关于;界面缩放、光标辉光、画布背景图、本地 ComfyUI、新建节点默认模型、数据目录、导出目录、自动保存都真接上
- 没做完的选项置灰并标「设计稿」——页面不撒谎
项目迁移与数据自主
- 一键导出
.fisherai 项目归档包,换机导入直接恢复整个画布
- 项目数据目录可以自定义到任意磁盘,C 盘不够用直接搬走,不污染系统盘
- 所有素材、工作流、配置本地存储,不上传到第三方服务器
🚀 安装
0. 环境要求
- Node.js ≥ 22.19 + pnpm
- DeepSeek Harness ≥ 0.1.0-rc.7(桌面版 / Web 版都行)
- 要跑本地生成才需要 ComfyUI(默认地址
http://127.0.0.1:8188);只用云端通道不需要
1. 准备上游 AIFISHER 源码
构建链的第一步 pnpm vendor 每次都会从一份完整的上游 AIFISHER 源码树重新复制到 vendor/aifisher/,再逐条打补丁(每条补丁都带「命中 N 次」断言)——仓库里 vendor/aifisher/ 现存的内容是上一次构建的产物,不是这一步的输入。上游源码按其原版权方的许可证分发,请自行获取(版权方 Work-Fisher)。
默认按候选顺序自动探测,最省事是把它放在与本仓库同级的 AIFISHER-main/;也可以显式指定:
# PowerShell
$env:AIFISHER_SRC = 'D:\path\to\AIFISHER-main'
# bash
export AIFISHER_SRC=/path/to/AIFISHER-main
报 [vendor] 找不到 AIFISHER 源码 就是这一步目录没找对。
2. 构建
git clone <本仓库> dsh-aifisher-canvas
cd dsh-aifisher-canvas
pnpm install
pnpm build
pnpm build = clean → vendor-aifisher → build-canvas-css → tsdown,顺序不可变。产物三件,DSH 加载的就是它们:
lib/index.js — host 半(路由、ComfyUI、生成、媒体库、诊断)
lib/client/apply.js — 浏览器半(panel / slot 注册)
lib/canvas/canvas-app.js — 画布应用本体
没有上游源码树、只想编译现有 vendor/:用 pnpm build:offline(CI 走的就是这条)。
3. 装进 DSH
- 打开 DSH → 设置 → 插件 →「添加插件」。
- 填插件目录的绝对路径(就是第 2 步构建出
lib/ 的那个目录),点安装。
DSH 会把这个目录 link 进当前 profile,并把 dsh-aifisher-canvas 追加进 profile 的 dsh.profile.bundles,默认启用。
- 重启 DSH。host 半是进程内加载的,只刷新页面不会更新它。
不想用界面时,手工等价做法是改 $DSH_HOME/profiles/<profile>/package.json,然后在 profile 目录跑 pnpm install、重启 DSH:
{
"dependencies": { "dsh-aifisher-canvas": "link:D:/path/to/dsh-aifisher-canvas" },
"dsh": { "profile": { "bundles": [ /* …原有组合包… */, "dsh-aifisher-canvas" ] } }
}
4. 验证
侧边栏出现 「无限画布」 → 打开 → 项目库 → 新建项目。要用本地生成的话,画布左上角 ComfyUI 胶囊应变绿;云端通道在 设置 → 服务与通道 里配。具体上手见 使用说明.md。
5. 更新与卸载
- 更新:改完源码重新
pnpm build → 重启 DSH。DSH 不自动更新插件,换版本要在插件页重装。
- 卸载:设置 → 插件里移除(DSH 会先把组合包摘出
dsh.profile.bundles,再执行 pnpm remove)。画布数据留在 $DSH_HOME/storages/dsh-aifisher-canvas/,不会被删。
安装常见报错
| 现象 | 原因 / 处理 |
|---|
[vendor] 找不到 AIFISHER 源码 | 上游源码没准备或路径不对 → 见第 1 步 |
| 构建报「找不到模块 tsdown / jsdom」 | 盘符变过,node_modules 里 junction 存的是绝对路径已悬空 → node scripts/fix-node-modules-junctions.mjs --dry 先看计数,去掉 --dry 真改 |
| 装完侧边栏没有「无限画布」 | 组合包没启用,或没重启 DSH → 设置 → 插件 里确认 dsh-aifisher-canvas 已开启,再重启 |
| 画布能打开但接口 404 | host 半没重新加载(只刷新页面不够)→ 重启 DSH |
基础操作速查
| 操作 | 方式 |
|---|
| 唤起节点搜索 | Shift + A / 双击空白处 |
| 自定义工作流 | 左侧栏导入 ComfyUI 导出的 API JSON |
| 配置云端通道 | 设置 → 服务与通道,填 Base URL + API Key |
| 切刀 / 合线 | Ctrl + 右键 划断 / Shift + 右键 划合 |
| 让 AI 帮你操作画布 | 直接在右侧聊天栏说你的需求即可 |
🧱 技术实现
vendor/aifisher/ # 上游 AIFISHER 源码树,零修改
├─ 锚点补丁脚本 # 每条补丁带命中次数断言,上游改写法立刻构建报错
├─ 整文件覆盖 # 仅修改需要适配的核心文件
└─ 装配层 # 多安装器注入画布槽位
| 层 | 内容 |
|---|
| 前端 | React 19、Tailwind CSS 4、shadcn/ui、TipTap、Wavesurfer |
| 后端 | Node.js 原生 HTTP 服务,所有接口统一 /api/aifisher/* 命名空间 |
| 架构 | 双 bundle:host 半进程内加载,client 半浏览器独立运行,跨 bundle 通过全局桥通信 |
| 与 DSH 的边界 | 画布会把「从系统拖进来的文件」事件止步在自己的容器上(containFileDrags)。宿主把「拖入即可添加附件」的全屏提示层挂在 document 的 dragenter/dragleave 计数上,而画布的 onDrop 会 stopPropagation —— 不拦住的话宿主永远收不到那次 drop,计数停在 1,整屏遮罩再也退不掉(真机踩过)。同理,任何一次鼠标点击都会补一次 dragleave,把已经卡住的遮罩解开 |
| Agent 契约 | docs/canvas-agent-contract.md —— canvas_control 每类 operation 的字段、configure 的硬约束、生成授权边界与错误码速查。要让 AI 正确操作画布,先喂它这份文档 |
| 测试 | 类型门禁(pnpm typecheck)+ 55 套 jsdom 契约测试(3000+ 项)+ 真机浏览器视觉校验(browser-check / verify-live-*) |
实现细节与版本日志(v1.58 – v1.89) —— 每个版本改了什么、踩了什么坑
数据与保存
- 保存链路(v1.58,见
scripts/patches/aifisher/src/stable/persistence/canvasWorkflow.ts):上游的乐观并发是「撞 409 → 重读服务端 → 三向合并 → 重发」,但三向合并遇到同一字段两边都改会抛冲突,并把这份文档的保存永久封锁(只有重新打开项目才解),画布照常能编辑、实际一个字都存不进去。本插件改成「救援合并」:节点/分组按 id 取并集、同 id 以当前窗口为准、一定落盘,并提示用户核对;保存/读取请求另加 20s 超时,避免一次挂住的请求把保存队列永久占死。agent 的每次改动还会由 host 立刻追加一次保存,回执里用 saved 说明有没有真的写进 canvas.json。
- 云端结果落盘(2026-10-20):云端通道回的是服务商临时链接(如
https://file*.aitohumanize.com/file/<hash>.png),host 侧会先把字节下载进素材库,节点的 resultUrl 才是同源 /api/aifisher/canvas/media/...。图床在任务刚回报 SUCCESS 的头几秒常常还是 404(文件没落地),所以下载带 4 次退避重试;真要失败,节点会显示「图片链接已失效」并留痕到 /api/aifisher/canvas/diagnostics(installImageLoadTelemetry 收 img 的 error 事件),而不是静默留一块黑。
- 火山方舟直连(v1.60):一张卡片管两种媒体,只认
ARK_API_KEY。图片走 POST /api/v3/images/generations(arkImageProvider.ts:档位+比例 → size、参考图内联成 data URL、组图/图层拆分按模型能力分流);视频(Seedance 2.0,arkVideoProvider.ts)走异步任务 —— POST /api/v3/contents/generations/tasks 提交 content 数组(text / first_frame / last_frame / reference_image / reference_video / reference_audio)再轮询 GET …/tasks/{id},出片后立刻把 mp4 下载进素材库。参考图的字节内联(方舟访问不到 127.0.0.1),参考视频/音频只收公网直链;模型名逐字取自画布目录(豆包视频2.0),界面的「自定义模型 ID」可换成账号下的推理接入点(ep-…)。
视频节点(v1.64 – v1.68)
- 生成历史(v1.64):图片节点本来就有的「历史 N」现在视频节点也有 —— 合并/换位/文案的纯函数在
src/canvas-ui/canvas/CanvasVideoHistory.ts(去重、视频上限 12 条),经装配层挂成 __FISHERAI_VIDEO_RESULT_HISTORY__ 桥;canvasGeneration.ts(生成成功)、generationRecovery.ts(断线/刷新回收)两处都走它,播放器(整文件覆盖 canvasVideoPlayer.tsx)负责头部药丸、扇形历史叠与「设为主视频」。换主视频会清掉 lastFrame 与旧比例,首尾帧/比例按新片重算;历史是节点数据,随 .fisherai 一起导出。
- 视频截帧(v1.66):视频底栏那枚按钮变成三项菜单 —— 截取首帧 / 截取尾帧 / 首尾帧各一张,单截一帧不会多掉一个要删的节点。取帧在
src/canvas-ui/canvas/CanvasVideoFrames.ts(输出最长边 2048、尾帧从 duration - 0.05 起试、探到空白画面自动往前退),落位在 CanvasFrameLayout.ts(贴视频右侧 60px、首上尾下、避开已有节点,截完把「视频 + 新帧」一起聚焦进视口),vendor 侧只做接线(补丁 3.8x);截帧节点的提示词留空,不会被当成生成提示词。
- 截帧加固(v1.67,2026-10-23 报障「截取视频首尾帧失败了」):等待判定从「单次
seeked 快照」改成(事件来早了、干脆不来都不会再白等 15s 报「视频定位超时」);落点容差 0.5s(Chromium 会吸到最近可解码帧);按 钳制目标(远端素材只缓冲了一截时不硬拖片尾);空白帧先等最多 800ms 再判;超时;错误文案带现场( / 是否仍在寻址 / 落点 / 时长)。同时 host 的 (206 + )——以前远端视频经代理后浏览器当它是「不可寻址」,拖进度条和截尾帧都会卡死。
📚 文档地图
📂 数据存储
所有数据默认存放在 $DSH_HOME/storages/dsh-aifisher-canvas/,支持在设置中一键迁移到其他磁盘:
canvas.json:画布项目、节点、视口自动存档
media/:生成与上传的图片/视频/音频素材
workflows/:自定义 ComfyUI 工作流配置
cloud-channels.json:云端通道与密钥配置(密钥值不回传前端)
📄 许可证
⚠️ 本项目基于 AIFISHER 源码构建,采用 AIFISHER Noncommercial Source License 1.0:
- ✅ 个人学习、非商业创作、技术研究可自由使用与修改
- ❌ 禁止任何商业用途:包括接私单、付费出图、售卖插件、商业 SaaS、付费托管
- 再分发必须保持免费,并保留 LICENSE / NOTICE 文件与修改说明
- 商用授权请联系原版权方 Work-Fisher 获取书面许可
🤝 贡献
欢迎提交 Issue 和 PR。修改代码前请阅读 README.dev.md 中的开发约束章节,了解 vendor 补丁机制、双 React bundle 隔离规则和画布几何的 dpr 约束,避免踩历史踩过的坑。
AI 参与的提交怎么署名(Co-authored-by: trailer 的写法与边界)见 CONTRIBUTING.md §7 AI 协作与署名。