1703 Star 的 anidoodle:把画画写成可复现的代码,我在它的确定性契约里看到了工程之美
生成式 AI 画图有个让人不安的特性:同一套提示词跑两次,得到的是两张不同的图。做单张海报也许能接受,但如果你要做一部 1410 帧的短片、一套需要风格统一的品牌插画,这种"每次都重新掷骰子"的不确定性就是工程灾难。最近我读到一个 1703 Star 的开源项目,它从根上拒绝了这种不确定性——anidoodle,口号是"手绘的艺术,用代码写成"。
它不是又一个画图模型,而是一个装进 Claude Code、Codex、Grok 的 Agent 插件,让 AI 用写代码的方式作画:每一笔是一个函数,每一个音符是一次算术运算,于是同一份源码在任何机器、任何尺寸下都会重新画出同一张画,永远如此。我把它 8 万多行的引擎和那份极其较真的确定性契约读了一遍,这篇讲讲它是怎么做到"分毫不差"的,以及这种思路为什么值得每个做内容工程的人了解。
一、先定性:它是 Agent 技能,不是独立软件
和传统绘图工具不同,anidoodle 的交付物是一个标准 skill 文件夹(skills/anidoodle/SKILL.md)加上一套插件清单,分别提供 .claude-plugin、.codex-plugin、.grok-plugin 三种适配。它本身不训练模型,真正写代码的是你的编码 Agent,anidoodle 提供的是方法论、绘图引擎和质量门禁。
我统计了引擎规模:engine/ 下 1139 个 TypeScript 文件、约 8.1 万行代码,tools/ 里有 84 个工具脚本,测试目录里有 89 个测试文件、共 104 个测试相关文件。这不是个玩具,而是一个有完整构建、渲染、校验、音乐合成链路的生产级引擎。安装也很直接,以 Claude Code 为例:
/plugin marketplace add alexgreensh/anidoodle
/plugin install anidoodle@alexgreensh-anidoodle
之后用大白话说需求即可,比如"给招聘页面画一张孔版印刷风格的灯塔,1600×900",它会自动路由到合适的工作流和风格配方。
二、核心思想:每一帧都是纯函数的一次调用
理解 anidoodle 的钥匙,是它把"生成艺术"和"生成式模型"做了彻底切割。

用图像模型做视频,每渲染一帧都要调一次模型、烧一次 token,而且帧与帧之间风格、角色容易漂移。anidoodle 的路线是让 Agent 一次性写出一个纯函数绘图引擎,之后所有渲染都在本地跑、零模型成本。README 里那个 47 秒、1410 帧的示例短片就是一个程序:第一遍大约每帧花 60 个 token 的代码量,之后在任何机器、任何尺寸下重渲染全部画面和立体声配乐都是免费的,而且每次分毫不差。一张静态插画不过几百行代码,一整个系列的图像,本质上只是一份种子列表。
它把三种产物统一到了同一个抽象:成品插画是纯函数被调用一次,循环贴纸是同一个函数循环调用,短片则是这个函数配上故事和配乐。三者背后是同一套确定性保证。
三、确定性契约:把"可复现"写成强制规则,而不是口号
这是整个项目最硬核、也最值得程序员细读的部分。references/determinism-and-contract.md 开篇就写道:“确定性,否则它不是一部电影,只是一场现场表演。“我把契约条款梳理出来:
renderFrame(frame, env)必须是纯函数。帧之间不携带状态,模块级不许有触碰像素的可变状态;音频同理,采样是采样率的纯函数。- 随机数只能来自
rng(seed)。核心里永远不许出现Math.random,也不许用Date、performance.now——时间属于宿主,不属于绘图核心。 - 受限的画布能力。只允许路径、填充、描边、裁剪、变换、合成、渐变、预加载字体的
fillText等,任何形式的ctx.filter都被禁止(因为 Safari 不支持,会破坏跨端一致),也不许碰 DOM、window、new Image,画布表面只能由env.canvas()提供。 - 仓库里不放素材,只有三个具名例外。普通图像、字体、音频都不进仓库——文字是笔画、纹理是带种子的噪声、音乐是算术,这让整个仓库就是整部片子,没有素材授权问题。三个例外(vendored 的 three.js、一个 base64 内嵌字体、烘焙的旁白音频)全部用哈希锁定,构建不可能在影片脚下悄悄变化。
- 缓存键必须命名像素依赖的每一个输入。缓存帧和冷启动渲染同一帧号必须像素一致。文档还老实交代了两个靠机器扫描(而非肉眼)发现的违规:一个标注层的键漏了曲柄角度、一个漏了姿势。
- 位图永远只许缩小不许放大。近景要在该视角重新着墨,避免插值带来的不一致。
校验手段也很硬:在不同页面正向、反向哈希若干探针帧,冷帧对比缓存帧,标准是视觉一致且 PSNR 高于 45dB,优先用免费的哈希相等性判断。它甚至提醒你半色调(halftone)对亚像素位移极度敏感、一次错位就掉 17dB——这种踩坑细节,是真做过片子才写得出来的。
四、32 种画风 + 21 种曲风,而且都"自己会画出来”

styles.gen.json 里确实是 32 套完整风格配方,从铅笔水彩、马克笔漫画、孔版印刷、木刻浮世绘、水墨、刮画,到像素画、玩具积木、低多边形 3D、橡皮管动画。每种风格模拟的不只是观感,还有这种媒介的真实落笔顺序:水彩先打铅笔稿再由浅到深铺色,木刻先印主版再逐一套色版,像素画先搭纯色形状再加阴影。每种风格都配一段延时影片,逐笔展示画面如何诞生,而且影片最后一帧和静态图逐像素一致,没有用淡入淡出糊弄。
音乐侧同样是代码合成、没有采样和录音:21 种曲风(低保真电子、House、合成器浪潮、管弦、爵士、民谣等)提供音色、律动与和声词汇,但和弦、动机、曲式每次重新创作。它建模了施坦威钢琴、Rhodes/Wurlitzer 电钢、弦乐铜管、吉他贝斯和多套鼓组,还有 12 位采样器、磁带晃动、黑胶噼啪这些低保真质感。node tools/music.mjs check 会在任何人试听前审核调性、响度、掩蔽、声部平衡和新颖度——听起来像已有曲子的配乐不会通过。
五、工作流与工具链:先画后动,校验先行

它的工作方法(working-method.md)强调先用一张静态图验证效果,再一路做到底,把审批留给出错代价高的环节。84 个工具覆盖了完整链路:scaffold 搭骨架、still 出静态图、render 渲染 GIF/MP4/带透明通道的 WebM、music 作曲、gate 一次性检查确定性/契约/静音、verify-export 解码校验最终交付文件、emit 打出一个约 100KB、双击就能离线播放且自带声音的 HTML。
四个后端——playwright、html-player、remotion、hyperframes——共用同一个绘图核心,核心根本不关心是谁在调用它。运行环境需要 Node 20+,涉及动态内容还要 ffmpeg。它还内置了产品发布片工作流(launch.mjs),一条时间线输出 16:9、1:1、4:5、9:16 四种画幅并各自重新构图,自动生成封面帧和 srt/vtt 字幕,检查音画同步和峰值电平,确保没有一帧的文字被边缘切掉。
六、祛魅:边界,以及难得的工程诚实

先说边界。第一,它是 Agent 插件不是开箱 App,你得有 Claude Code/Codex/Grok 这类环境,最终审美上限仍取决于你的引导和底层模型。第二,8 万行引擎的深度定制有学习曲线,动态渲染依赖 Node、ffmpeg 和浏览器。第三,它的照片重绘是把照片测量成明暗和方向后用代码笔触重画,是风格化作品而非照片级写实。第四,许可是 Apache-2.0 而非 MIT,带有 NOTICE 和署名义务,商用和再分发要保留归属声明。
但我最想讲的是它的工程文化。确定性文档里专门记录了一次"无法解释的首跑失败”:全新工作区第一次 gate 报 0/12 帧全部不一致,之后七次复测又全部 12/12 通过、无法复现。换作很多项目,重跑到绿灯就当无事发生;但作者写下了整段经过,并断言"一次失败随后通过是最危险的结果,重跑直到变绿然后闭嘴是不可接受的,一份诚实的’无法复现,这是我尝试过的方法’本身就是结论"。文档里还留着大量"每条规则都是一次画砸换来的"的伤疤注释,比如行注释 // 曾吞掉语句后半段、三次搞挂构建,所以密集单行代码库里强制用 /* */。
我的判断
anidoodle 真正的价值,不在"AI 能画画"这个已经不新鲜的结论,而在于它把生成艺术拉回了软件工程的轨道:用纯函数、种子随机、哈希锁定和帧级校验,把"灵感"变成可复现、可版本管理、可团队协作、可零成本重渲染的资产。对追求品牌视觉一致性的团队、做产品发布片和讲解视频的开发者、以及相信"过程可审计"的人,这套思路比又一个黑箱绘图模型有价值得多。
它当然不是万能的:照片级写实、零门槛开箱、脱离编码 Agent 独立运行,都不是它的目标。但如果你认同"一件作品应该能被一份源码完整重建",那 anidoodle 示范的,正是 AI 创作里稀缺的那种确定性与诚实。