Skill - 把无限画布装进 Codex:Cowart 的架构拆解与实践指南

发布时间:2026/7/29 1:23:20
Skill - 把无限画布装进 Codex:Cowart 的架构拆解与实践指南 文章目录一、从「许愿池」到「工作台」二、核心洞察Agent 缺的不是能力是指称手段三、整体架构四层结构依赖清单里的信息量四、关键演进从本地网页服务到原生 widget五、数据模型为什么画布必须存在用户项目里六、四条核心工作流6.1 打开画布6.2 生成图片让框的尺寸成为 prompt 的一部分6.3 按标注改图这是整个项目的灵魂6.4 AI HTML 与 AI Slides把画布变成产出容器七、Skill 机制Agent 侧的行为契约八、上手实践安装本地开发几条实用建议九、局限与思考十、可迁移的五条设计经验结语当 Coding Agent 开始处理图像与视觉设计纯文字对话就成了瓶颈。Cowart 用一块 tldraw 画布把「指哪打哪」还给了人类。一、从「许愿池」到「工作台」用 Codex、Claude Code 这类 Coding Agent 干活有一种熟悉的别扭感你把需求写成一段话扔进对话框然后祈祷它理解得八九不离十。文字处理代码时问题不大——函数名、文件路径、行号都是精确的坐标。但一旦任务进入视觉领域坐标就消失了。试着用纯文字描述这样一个修改「把右上角那个价格标签往左移一点字号调小颜色换成和下面按钮一致的那种蓝另外中间那块留白太大了把插图放大填一填。」每一个「那个」「一点」「那种」都是一次信息损耗。人和人沟通时靠的是手指——指着屏幕说「这里」。而在对话框里手指被剥夺了。Cowart 解决的正是这件事。它是一个面向 Codex 的原生无限画布 widget 插件基于 tldraw 构建让 Codex 不只读文字提示词还能读到你画在图上的箭头、圈选和批注文字。项目 2026 年 6 月开源MIT 许可短时间内在 GitHub 收获五千余星作者是钟二信ZHONG XIN一位有 Sketch 插件与设计工具背景的产品经理。本文面向的读者是想理解 Agent 与图形界面如何耦合的开发者正在做 AI 工具前端的工程师以及想把 Codex 用作日常视觉工作台的重度用户。我们会从「为什么需要画布」讲到代码结构、数据模型、MCP widget 机制再到实际使用中的取舍与局限。二、核心洞察Agent 缺的不是能力是指称手段先把问题定义清楚。当前的图像生成模型GPT Image 系列及同类在生成质量上已经足够真正卡住普通用户的是两个环节第一prompt 的冷启动。看到一张好图你不知道该怎么描述它才能复现。第二迭代时的指称reference。生成之后要改改的往往是局部——某个位置、某个元素、某种比例关系。语言在描述空间关系时天生笨拙而这恰恰是设计工作的主战场。这两点合起来导致一个荒谬的现象模型能力足够强但人类的表达带宽不够于是产出停留在「差不多能看」很难推进到「就是我要的」。Cowart 的思路不是加强 prompt 工程而是换一种输入模态。画布上的一个箭头等价于一段冗长的方位描述一个圈等价于「我说的是这块区域」框的尺寸本身等价于「按这个比例生成」。空间信息用空间方式传递几乎零损耗。更进一步这套交互还带来了「留痕」的副作用原图、标注、修订图并排放在画布上一次迭代的完整推理链条可见可回溯。这是对话式界面很难提供的——聊天记录是线性的而设计过程是并行发散的。三、整体架构四层结构Cowart 的仓库结构相当克制顶层只有几个目录cowart/ ├── .codex-plugin/ # Codex 插件声明 ├── .mcp.json # MCP server 配置 ├── mcp/ │ ├── server.mjs # MCP 服务主入口 │ └── lib/ │ ├── plugin-root.mjs │ ├── widget-resource.mjs │ ├── cowart-static-widget.mjs │ └── canvas-storage.mjs ├── skills/ # 三个 Agent skill 定义 ├── src/ # tldraw 画布前端React ├── scripts/ # start-mcp / probe-mcp / vite-build-once / start-canvas.sh ├── public/ ├── index.html ├── vite.config.js └── package.json映射成运行时的四层层职责关键实现插件声明层向 Codex 注册 skill 与 MCP server.codex-plugin/、.mcp.jsonMCP 服务层暴露工具、托管 widget 资源、读写画布数据mcp/server.mjs及mcp/lib/*画布前端层无限画布、AI 框、标注、演示src/tldraw React 19项目数据层画布 JSON 与图片/HTML 资源用户项目下的canvas/目录这个分层里最值得注意的一点是前三层住在插件仓库第四层住在用户项目。数据与代码彻底分离后面会看到这个决定的分量。依赖清单里的信息量package.json是最诚实的架构文档{name:cowart-canvas,version:0.1.20,type:module,dependencies:{modelcontextprotocol/ext-apps:^1.7.4,modelcontextprotocol/sdk:^1.29.0,tldraw:^5.1.1,tldraw/assets:^5.1.1,react:^19.0.0,react-dom:^19.0.0,vite:^7.0.0,html2canvas:^1.4.1,fractional-indexing:^3.2.0,lucide-react:^1.24.0,zod:^4.4.3}}逐条读下来实现路径几乎不用猜modelcontextprotocol/sdkmodelcontextprotocol/ext-apps不只是普通 MCP server而是用了 MCP 的应用扩展能力把 UI 作为资源交付给宿主渲染。这是「原生 widget」而非「本地网页」的技术前提。tldraw5.x画布引擎。选它意味着形状系统、选择状态、相机变换、撤销栈这些重活全部复用Cowart 只需扩展自定义 shapeAI 图片框、AI HTML 框、Slides。html2canvas把标注后的画布区域导出为位图。这是「按标注改图」链路的关键一环——模型看到的是一张包含箭头和文字的合成图而不是一串坐标。fractional-indexing分数索引排序常用于维护形状层级z-order或 Slides 页序插入元素时无需重排整个序列。zodMCP 工具入参校验。工具边界上的类型安全能显著降低 Agent 传错参数导致的静默失败。vite7 React 19现代构建链npm run build走的是自写的scripts/vite-build-once.mjs把产物打成可被 MCP 托管的静态 widget。仓库还提供了一条聚合质量校验命令npmrun quality# npm run check npm run build npm run probe:mcpcheck用node --check对每个.mjs做语法校验probe:mcp主动探活 MCP server。对于一个插件项目这套自检比接一堆测试框架更务实——插件失效的主要形态就是「MCP 起不来」和「widget 资源加载不到」正好被这两步覆盖。四、关键演进从本地网页服务到原生 widgetCowart 早期版本的形态是Codex 启动一个本地 Vite 服务默认端口43217用户在浏览器或 in-app browser 里打开http://127.0.0.1:43217/。这条路走得通但代价不小——需要切窗口端口可能冲突服务生命周期要人管画布和对话在两个上下文里。现在的主线换成了 MCP widgetCodex 调用render_cowart_canvas_widget画布直接在 Codex 内部渲染。scripts/start-canvas.sh只作为本地开发的 fallback 保留。这次调整背后有一个通用结论值得记下来Agent 的图形界面应该由宿主渲染而不是由插件另开一个世界。原因有三上下文同源。widget 与对话在同一容器里选择状态、剪贴板、截图能通过 widget bridge 双向流动不需要走 HTTP 端口做进程间通信。权限与安全边界清晰。本地起服务意味着开一个监听端口在企业环境里是需要解释的行为widget 资源由 MCP 通道交付边界收敛。心智负担低。用户不需要理解「插件其实是个网页」这件事。mcp/lib/下的两个文件名把这条链路写得很直白widget-resource.mjs负责把构建产物声明为 MCP 资源cowart-static-widget.mjs负责将其组装成宿主可直接渲染的静态 widget。plugin-root.mjs解决的是另一个经典麻烦——插件被安装到任意路径后如何稳定定位自身根目录以读取产物和资源。五、数据模型为什么画布必须存在用户项目里画布数据的落盘路径是这样的你的项目目录/ └── canvas/ └── pages/ └── page-id/ ├── cowart-canvas.json # 画布本体 └── assets/ # 图片、生成的 HTML 等资源默认写入当前用户项目而不是插件仓库。三个环境变量控制这套行为变量作用默认值COWART_PORT本地开发服务端口43217COWART_PROJECT_DIR画布数据归属的项目目录当前项目COWART_CANVAS_DIR画布数据目录$COWART_PROJECT_DIR/canvas这个设计看着朴素实际影响很大画布随项目走。A 项目的画布和 B 项目的画布天然隔离不会互相污染。你在做官网改版时的所有草图就躺在官网仓库里。画布进版本控制。cowart-canvas.json是纯文本能 commit、能 diff、能 review、能跟着分支切换。设计过程第一次和代码变更处在同一条时间线上。这一点对团队协作的意义比听起来更大——设计稿不再是 Figma 里的一个孤立链接而是仓库的一部分。资源本地化。生成的图片和 HTML 落在assets/不依赖任何云端存储离线可用也不会因为服务下线而失效。卸载或更新插件不丢数据。代码和数据分离插件目录随时可以删了重装。如果要给「Agent 工具的数据应该放哪」下一条经验放在用户的工作目录用纯文本格式让版本控制接管历史。不要放在插件自己家里也不要急着上云。六、四条核心工作流6.1 打开画布在 Codex 里用自然语言说明意图即可Open the Cowart canvas for this project.Codex 通过render_cowart_canvas_widget打开原生 widget。首次使用建议在安装后新开一个对话让新注册的 skill 和 MCP 工具完整加载——这是插件类工具的通用坑工具清单通常在会话初始化时快照。6.2 生成图片让框的尺寸成为 prompt 的一部分流程是三步在画布上创建并选中一个AI 图片框在弹出的生成面板里写 prompt可选择一张或多张画布上已有的图作为参考图发送。关键在于发送出去的载荷不只是文字。Cowart 会把prompt 参考图 选中框的位置与尺寸信息一起交给 Codex。Codex 按这个框的比例生成图片然后把占位框替换成普通图片形状。这里有两个容易被忽略的巧思尺寸即约束。你不需要在 prompt 里写「16:9」「竖版海报」把框拉成什么形状图就按什么比例出。空间约束用空间方式表达。参考图来自画布本身。画布上任何一张图都能被指定为参考风格延续变成一次点选而不是一段「保持和上一张一致的插画风格、同样的配色、同样的线条粗细……」的祈祷文。6.3 按标注改图这是整个项目的灵魂步骤在画布上对图片做标注——箭头、圈选、文字说明用 tldraw 原生的绘图工具选中被标注的图片点击按标注修改Cowart 导出一张包含原图、箭头和标注文字的合成截图通过 widget bridge 发给 Codex。Codex 读取截图里的标注意图生成一张去掉标注痕迹的新图放在原图旁边。原图和标注不会被删除或移动。值得展开的是这条链路的技术选择。要把「改这里」传达给模型理论上有几种方案坐标 JSON把标注序列化成结构化数据框选区域、箭头起止点、附加文字随 prompt 一起送出。精确但需要模型在文字空间里重建视觉布局且要求模型严格遵循自定义 schema。mask inpainting生成蒙版做局部重绘。技术上最「正统」但依赖特定的图像编辑接口且对「把这个元素往左移」这类结构性修改无能为力。合成截图把标注烧进像素让多模态模型直接看图理解。Cowart 选了第三种用html2canvas完成导出。这个选择的合理性在于现代多模态模型本来就擅长看图让它看一张画满批注的图与让人类设计师看同一张图理解路径是一致的。不需要定义中间协议不需要模型学习任何私有格式能力天花板直接对齐视觉模型本身的水平。代价是精度上限受模型视觉理解能力约束且需要「重新生成整图」而非局部修补细节可能漂移。但对绝大多数「构思—迭代」场景这个权衡是划算的。还有一个体贴的细节修订图放在原图旁边而不是覆盖原图。设计迭代天然是分叉的保留分支比追求整洁更重要。同时这也支持一种手工用法——你自己截一张带标注的 Cowart 图发给 Codex走的是同一条修订流程。6.4 AI HTML 与 AI Slides把画布变成产出容器这是 Cowart 从「改图工具」向「工作台」跨出的一步。AI HTML创建并选中一个AI HTML框默认1024 × 57616:9输入 prompt 与参考图Codex 生成完整可运行的单文件 HTML直接嵌入这个框。生成的 HTML 作为画布中的嵌入页面存放在当前 page 的assets/目录。选中后可以下载渲染图、直接编辑文本也可以继续用画布标注来迭代 HTML或者反过来根据 HTML 与标注生成图片。「单文件 HTML」这个约束选得很准无外部依赖、可直接iframe嵌入、可单独打开、可 commit 进仓库。它同时是产物、是素材、也是可编辑的中间态。AI Slides外框默认1048 × 600对应一页1024 × 576内容加四周各12px留白。用法有两种组装把画布上已有的图片或 HTML 拖入 Slides或复制图片后选中 Slides 粘贴进去内容自动按顺序横向排列生成选中空 Slides在生成面板里写整套演示的描述、添加参考图选择 3、5、10 页或自定义页数默认 5 页Codex 生成一组视觉与叙事连贯的独立 16:9 HTML 页面依次加入。注意一个交互规则Slides 已有内容时不再显示生成面板。这是一条防误伤的设计——避免在已有成果上意外触发批量覆盖。演示模式支持左侧缩略图预览与切换、全屏播放、方向键/空格/点击翻页并且保留 HTML 自身的按钮、链接和表单交互播放控制栏固定在顶部。也就是说做出来的不是静态幻灯片而是一串可交互页面。七、Skill 机制Agent 侧的行为契约Cowart 注册了三个 skillSkill触发场景行为cowart:cowart-open-canvas用户要求打开画布渲染原生画布 widgetcowart:cowart-image-gen需要生成、填充、替换画布上的图片接收画布内 prompt 与参考图生成图片替换选中的AI 图片框无选中框时插入到当前页面cowart:cowart-image-edit用户提供 Cowart 标注截图依据标注生成修订图Skill 和 MCP 工具的分工是理解这类插件的关键MCP 工具是能力——读取选择状态、保存画布、插入图片或 HTML、写入页面资源目录。它们是确定性的、可校验的原子操作入参由 zod 把关。Skill 是判断力——什么时候该调哪个工具、参数怎么组织、多步操作如何编排、边界情况如何降级比如「没有选中框时怎么办」。把「能力」和「判断」分开写好处是两侧可以独立演进加一个新的画布形状只需扩工具改变 Agent 的行为倾向只需改 skill 文本。这也是 Agent 插件相较传统插件的结构性差异——一部分逻辑是用自然语言写的可以被非工程师维护。八、上手实践安装两条路让 Codex 自动安装在对话里直接要求它按仓库说明装好或者手动安装。手动路径推荐把插件 clone 到 Codex personal marketplace 默认引用的位置并确认~/.agents/plugins/marketplace.json中存在 Cowart 条目{name:personal,interface:{displayName:Personal},plugins:[{name:cowart,source:{source:local,path:./plugins/cowart},policy:{installation:AVAILABLE,authentication:ON_INSTALL},category:Productivity}]}然后先注册 personal marketplace再安装插件。安装完成后开一个新对话再使用。⚠️安装类命令请以仓库 README 的当前版本为准。给 Agent 授予「自动安装插件」权限时值得先自己读一遍要执行的命令——这是所有 Agent 插件生态的共同风险面与具体项目无关。本地开发开发时仍可直接启动 Vite 画布服务并通过COWART_PROJECT_DIR指定画布数据所属的用户项目目录。改完代码后跑一遍聚合校验npmrun check# 语法自检所有 .mjsnpmrun build# 构建 widget 静态产物npmrun probe:mcp# 探活 MCP servernpmrun quality# 以上三步串联几条实用建议先把框拉对比例再写 prompt。框的尺寸是硬约束比在文字里描述比例可靠得多。标注要「说人话」。箭头指向位置文字写清动作「删掉」「换成蓝色」「放大 1.5 倍」。把它当成给外包设计师的批注来写而不是给机器写指令。把画布当版本库用。一轮迭代留一列横向铺开比反复覆盖同一张图更容易看出走向。canvas/目录记得纳入 git但注意assets/里的位图会让仓库变胖长期项目考虑 Git LFS 或定期归档。Slides 生成前先备好参考图风格一致性主要靠参考图而非文字描述维持。九、局限与思考客观地讲几条限制强依赖宿主。Cowart 是为 Codex 写的插件skill 命名、widget 渲染、图片生成能力都绑定在这套宿主上。想迁移到别的 AgentMCP 服务层和前端可以复用但 skill 层和 widget 交付方式要重做。生成质量取决于底层模型。Cowart 是交互层的创新不改变生成能力本身。标注被理解到什么程度、修订图细节保真度如何最终由多模态模型决定。整图重生成而非局部修补。「按标注改图」得到的是新生成的一张图未被标注的区域也可能发生细微变化。对精修阶段的工作不够可靠。项目仍在早期。版本号停在0.1.x贡献者集中在作者本人接口和数据格式都有变动可能。用于严肃生产前建议锁定版本。协作是单机的。画布存在本地文件里多人实时协作不在当前范围内——虽然通过 git 做异步协作反而顺畅。这些局限不影响它的价值。Cowart 真正的贡献不在功能清单而在示范了一种范式Agent 不必被困在对话框里它可以拥有为特定任务定制的图形界面而这个界面的产物直接落在用户的文件系统中。十、可迁移的五条设计经验如果你也在做 Agent 侧的工具或界面Cowart 有几条经验值得直接抄1. 为任务选对输入模态。空间问题用空间输入时间问题用时间轴输入别一律塞进文本框。表达带宽的提升往往比 prompt 调优的收益大一个量级。2. 让界面成为 prompt 的一部分。选中框的尺寸、标注的位置、拖拽的顺序都是零成本的高质量输入信号。用户已经在操作了顺手把语义收集起来就好。3. 数据放用户家里用纯文本交给 git。可 diff、可回滚、可分支、可 review插件生死不影响数据存续。4. 用宿主原生的 UI 交付方式。别自己另开端口起一个网页世界。MCP widget 这类机制在上下文共享、权限边界和心智负担上全面占优。5. 能力与判断分层。确定性操作做成带 schema 校验的工具编排与降级策略写进 skill 的自然语言里。两层各自演进改一处不牵动全局。结语Cowart 的代码量不算大思路也不复杂拿一个成熟的画布引擎接一套 MCP 工具把数据落在用户项目里再用三个 skill 教 Agent 怎么配合。但它触到了一个真问题——当 AI 的能力越过某个门槛瓶颈就从模型转移到了人机接口。过去两年我们习惯了通过打磨 prompt 来榨取模型能力。Cowart 提供了另一个方向的答案换一种更符合人类直觉的表达方式让「指哪打哪」重新成为可能。一个箭头胜过一百个形容词这在人与人之间早就是常识现在轮到人与 Agent 了。对开发者它是一份关于 MCP widget、tldraw 扩展与 Agent 插件分层的活样本对使用者它是让 Codex 从「代码助手」变成「视觉工作台」的一块拼图。无论哪一种身份都值得 clone 下来跑一遍。参考项目仓库zhongerxin/CowartMIT2026 年 6 月开源画布引擎tldraw/tldraw