给 AI 生图接上风格库:两天、976 条提示词和七个坑
两天时间只做一件事:让我攒的风格提示词库,能真正进到生图链路里。链路最后通了,但中间有三个「看起来生效了」的假象,其中一个到今天还没解决。
一、起点:能出图,但出的不是我要的
我的工作台里有个图片工作区(media-desk),点几下就能写策划、出图、排版。问题在于:它出的图,风格每次都随机。
于是我做了两件事:
- 把另一个生图插件(dsh-image-gen)内置的两套预设全部导出来——541 条案例提示词 + 435 条手绘风格/排版词,共 976 条;
- 给它们打标签、建倒排索引,做成一个可检索的风格库。
想法很简单:出图前先检索,把选中的风格描述拼进 prompt。
结果第一步就卡住了。
二、第一天:工作台根本不认那个插件
我把 976 条导出来之后,去工作台看「订阅生图」的接入状态,得到的提示是:
未找到订阅生图工具,或默认执行器不支持直接调用;请检查插件和执行器后刷新工作台。
但 dsh-image-gen 明明能用——我刚用它出了一张 1086×1448 的图。
翻它的设置文档才看到那句被我一眼扫过去的话:
其他国内外服务的订阅也需由插件接入生图功能,并在工作台适配范围内;仅登录订阅或接入聊天模型并不代表支持生图。
坑 1:能出图 ≠ 工作台能调用它。
工作台不直接调模型,它调的是「遵守某个约定的生图插件」。dsh-image-gen 自己有一套出图入口,但没有实现工作台要的那个约定,所以工作台看不见它。
这不是 bug,是能力边界。我的误解在于把「这个插件能生图」等同于「这个插件能被工作台生图」。类似的误解在实际项目里很常见:两个东西都能做同一件事,不代表它们可以互相替换。
坑 2:两个生图工具并存,选择权不在我手里。
当时机器上同时有 dsh-image-gen 和 subscriptions(后者提供 image_generate 工具,走的是订阅账号)。两者都能出图,配置文件里只有一个 image.source 字段决定用谁。
我的处理是停用而不是卸载——保留回滚路径。判断依据很实在:新链路(subscriptions + 风格库)还没验证过,旧链路是唯一能出图的路径,不能先拆桥。
坑 3:「我怎么知道该用什么风格」,是产品问题不是技术问题。
导出 976 条之后,我立刻遇到一个新问题:我不会用。
- 让我自己指定风格编号?我不知道 #278 长什么样,#123 和 #313 差在哪。
- 让模型随便挑?它会挑一个「看起来合理」但和我内容不搭的。
所以真正的需求不是「有个风格库」,而是「有个能根据我的内容推荐风格的东西」。这一步决定了后面所有工作:必须给每条打上 use / kind / palette / mood / platform 标签,让检索能先按场景过滤、再按关键词排序,最后由模型给出 3–5 个候选、我来拍板。
一句记法:检索是给模型用的,选择是给人留的。
三、第二天:链路接上了,但有三个假象
第二天的目标很明确:让 media-desk 出图时,自动带上风格库拼好的前缀。
3.1 第一个坑:链路上没有「入口」
读代码发现,media-desk 调用生图工具时只传三个参数:
1 | const args = { prompt, size, quality }; // image-tool.js:64-75 |
它不读配置里的 image.extra(那只有 HTTP 直连通道才生效,images.js:126),不传参考图,prompt 还是它自己的策划模型生成的。
也就是说,我把 976 条风格库做得再漂亮,这条链路上没有一个地方能把它塞进去。
两条出路:
| 方案 | 代价 |
|---|---|
| 外部先拼好完整 prompt,再喂给它 | 每张图都要人工介入,等于没自动化 |
| 给插件加一个固定前缀字段 | 要改 node_modules,会被升级覆盖 |
我选了后者,但只改 10 行,并且设计成空值时行为与官方完全一致:
1 | // image-tool.js:66 |
配套在 settings.js 的默认值(:7)与校验(:79,trim().slice(0, 2000))里加同一个字段。
关键设计不是那 10 行代码,而是「零风险超集」这个约束:字段为空 → 与官方一模一样;字段有值 → 多一段前缀。这样补丁失效时最坏情况是「回到原版」,而不是「崩」。
3.2 第二个坑:改 node_modules 的补丁,会静默失效
改完之后我立刻意识到一个更麻烦的问题:插件一升级,补丁就没了,而且不会报错。
它不会炸,只会安静地回到原版行为——你看到图不对劲,但不知道是哪一步坏的。
所以补丁目录里放的不是一个 diff,而是四件套:
1 | patches/media-desk-prompt-prefix/ |
check.mjs 的判读规则也写进文档:
| 输出 | 含义 | 动作 |
|---|---|---|
版本相同 + patch OK | 一切正常 | 不用管 |
版本变了 + anchors=OK | 结构还兼容 | 跑 reapply.mjs + 重启 |
版本变了 + anchors=LOST | 源码结构变了 | 不要盲跑脚本,手工重新定位 |
版本相同 + patch=MISSING | node_modules 被重装过 | 重放 + 重启 |
还有一条最容易忘的:补丁在宿主进程启动时加载,必须重启才生效。
3.3 第三个坑(未解决):试画生效了,正式产出没有
这是这两天最值得写的一段,因为它是一个假象。
重启后我点「试画」,出来的画面确实是风格库前缀要求的那种手绘线稿风格,一切完美。
然后我换了一个前缀(黑白铅笔风格),再试画——画面完全变了,符合预期。
但真正跑正式产出时,出来的还是写实摄影,前缀根本没送到。
检查过的事实:
- 设置确实读到了(前缀 737 个字符)
- 直连生图工具两次对照,前缀生效
- 两条路径走的是同一个函数
- 代码里没有分支差异
目前的怀疑是 renderAsset 的时序——正式产出可能在设置加载完成之前就取了默认值。
我的处理是不假装它已经好了。
交接文档里写的原文是:
卡点:produce 路径前缀未送达——settings 读到 737 字、直连两次对照都生效、试画也生效,但 media-desk produce 路径出写实摄影;代码同函数,尚未定位(怀疑 renderAsset 时序)。
这类问题最怕记成「已修」。下次再遇到,会从零重查一遍。
顺带一个我自己提出来、后来被验证的怀疑:「试画是不是写死的,只有真实调用才读前缀?」 当时只是个猜测,结果它基本命中了——试画和正式产出确实走了不同的状态准备路径。
这个坑的元教训:一个入口验证通过,不代表整条链路通过。要验,就验最终交付的那个入口。
3.4 另外四个小坑
画幅由参数决定,prompt 里写「竖版」没用。 而且服务端还会改比例:请求 1024×1024,实际返回 1536×1024。结构性参数(尺寸、比例、数量)不要交给自然语言。
打标时大部分条目是 unknown。 976 条里,palette 有 49%、mood 有 27% 判不出来(模型复核 316 条候选时,palette unknown 140 条、mood unknown 43 条)。所以过滤时必须保留 unknown 条目——如果按「必须有 mood」过滤,一半库会凭空消失,而它们其实都可用。
固化技能时撞上 ENOTEMPTY。 把草稿目录采纳为正式技能时,目标目录已存在且非空,rename 直接失败:
1 | ENOTEMPTY: directory not empty, |
解法是先清空/合并目标目录再采纳,别指望 rename 覆盖。
检索分数要有人能读懂的阈值。 加权方案定成:标题 6 / styles 3 / category 2.5 / prompt 1,中文按 bigram。然后给一个判读标准:≥12 强相关、6–12 可用、<6 直接丢。没有阈值,检索结果就是一堆「看起来都还行」的噪声。
四、跑通之后的样子
链路最终是这样:
1 | 风格库(976 条) |
实际跑通的几组:
- 「窗边读书的女孩」——按风格前缀出图,风格可控
- 橘猫 + 水彩——换前缀,风格跟着变
- 一组 6 张的图文卡(3:4 竖版 1086×1448,复古暖调)——第一张作锚点,后 5 张用它作参考图,保证一组图风格一致,每张都读图核对了画面文字
最后一步特别重要:AI 生成的图,中文文字一定要逐字读图核对。尺寸对、构图对、风格对,字写错一个,整张就废了。
五、五条能带走的经验
能力边界比能力本身更容易误判。 「这个插件能生图」和「这个插件能被工作台生图」是两件事。接入前先问:它遵守的是谁的约定?
改
node_modules就必须配套自检脚本。 补丁失效是静默的。至少要能一条命令回答「现在是原版还是打过补丁」。验证要验最终入口。 试画、直连、单元测试都是中间入口。用户实际点的那个按钮,才是唯一算数的验证点。
未解决就写「未解决」。 交接文档里那句话,比任何漂亮的完成报告都有价值。
检索给人候选,不替人拍板。 风格这种事,模型可以排序,但不能定稿。
本文的链路搭建与排障全程由 AI 编程助手实现,我负责定方向、做选择、逐条验收。
