dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
游戏素材生产流水线:出图、生视频与音频,把一段视频变成连贯的精灵动画,派生配色变体,适配引擎配置,装进项目并核对代码是否真的引用了。视频转精灵动画需 ffmpeg;出图/生视频需自备图像/视频 API Key(Agnes AI / OpenAI / fal)。
把 GPT Image Playground(
gpt-image2ground)的能力模型搬进 DSH:多服务商 · 多配置档 · 文生图 · 图生图/局部重绘 · 异步任务轮询 · 自定义接口模板 —— 变成 AI 自己就能调用的工具。
独立插件,零运行时依赖(只用 node: 内置模块),装进 profile 不需要再拉依赖树。
| GPT Image Playground 里的能力 | 本插件里的形态 |
|---|---|
| 多服务商(openai / sb2api 异步 / fal / 自定义) | 内置 3 个模板 + 任意自定义 provider 模板 |
| 多配置档(每个档 = 接口 + Key + 模型) | profiles(配置档),工具与面板都能增删改、切换激活档 |
gpt-image-config.json 导入导出 | config 工具 import / export,格式完全兼容 |
| 文生图(Images API) | generate(apiMode: images) |
| Responses API 模式 | generate(apiMode: responses,tool_choice: required + image_generation 工具) |
| 图生图 / 刷蒙版局部重绘 | edit(image[] + mask,multipart;Responses 模式走 data URL) |
| 异步提交 + 轮询任务 | generate ... wait_s=0 拿 task_id → job 查状态/继续等 |
$profile.model / $params.size 模板占位 | 同一套占位语法 + 路径表达式 data.*.url / result.data.*.b64_json |
| 「哪张图对应哪句提示词」 | 落盘 <输出根>/<项目>/<素材名>_序号.png + <素材名>.json + 项目 图片清单.md |
| 出图历史 / 预览 | 面板 /gpt-image:缩略图 + 提示词 + 参数 + 状态,全部可回看 |
本插件比 Playground 多的几件事:
| 能力 | 说明 |
|---|---|
| 自动化编排(L–R) | 一个 auto 工具、36 个 action:声明式流水线(N);规则引擎+置信度分流(M);自愈(O);触发(L);优化(P);治理(R) |
| 素材工作室(F–J) | 一个 studio 工具、30 个 action:模板库/变体矩阵/成本(F);引擎契约·引用完整性·替换影响(G);并发锁·来源归属·损坏检测·备份回滚(H);健康度·漏斗·趋势(I);风格指南·共享库·风格迁移·A/B·图鉴(J) |
| 质量地基(A–E) | 一个 quality 工具、11 个 action:spec 校验+自动修复 / 协调性 / 派生变体 / 图集 / 视觉标签 / 评分路由 / 偏好 / 回归 |
| 素材变化摘要 | 画廊顶部横幅:新增 / 消失 / 内容变化 / 结论失效。首次只建基线;touch 不算变化;清空不刷屏 |
| 审核对比视图 | C 进四格对比:原图 / 实时切片 / 游戏实际尺寸 / 相邻帧。键盘流 ←/→ A/R/P |
| 批量重处理 | 规则变了但源图没变 → 结论继承、只重切;失败回滚、孤儿清理、文件锁互斥 |
| 素材目录 + 人工审核 | 画廊以磁盘为准(含切片/缩放件/外部产物)。勾选后一键标 合格/不合格/待定 |
| 余额 / 花费 · 免费档无限额度 · ratio · 重试 · 蒙版守卫 · 花钱闸门 | 见下方各节 |
Q1 装机数据回传 / Q2 效果反馈校准 / Q4 玩家数据补货 / P4 自动 A/B / J4 真实 A/B —— 这些全部依赖游戏端回传数据。单机游戏没有回传通道,所以:
| 功能 | 状态 | 替代方案 |
|---|---|---|
| Q1 使用分析 | ⚠ 降级 | 用文件级证据(代码里有没有引用)判断"死重量",并明确标注这不是玩家行为数据 |
| Q2 评分校准 | ❌ 不做 | 没有真实使用数据可校准 |
| P4/J4 A/B | ⚠ 降级 | 保留实验设计 + 显著性判定(ab-design/ab-analyze),数据由你填,不编造 |
| Q4 补货 | ✅ 本地版 | 只看本地库存(数量/合格数/被引用数) |
| Q3 淘汰 | ✅ 本地版 | 用"未被引用 + 低分 + 过保护期"判定,不依赖玩家数据 |
| 冲突 | 解法(已实现) |
|---|---|
| M2 自动 approve ↔ 责任 | 事后抽查(确定性 hash 抽样 + 保底至少 1 张)+ 批量撤销(review-undo,撤销本身也留痕可再撤) |
| Q3 自动淘汰 ↔ H3 备份 | 淘汰计划带 requires_snapshot: true;清理先移到回收目录而不是直接删 |
| C1 变体 ↔ 母图结论变更 | 级联失效(cascade-plan/apply):母图 reject → 派生件一并驳回(不让再审一遍) |
| N5 事务性 ↔ N3 并行 | 事务按批次划:某步失败 → 整批中止,不留"一半新一半旧" |
| L1 监听 ↔ H1 文件锁 | 防抖(mtime 距今 > 5s)+ 尺寸二次确认,写完才读 |
| L1 监听 ↔ 脚本失控 | 限流(超量只处理前 N)+ 熔断(连续 3 轮超量就停) |
| 漏项 | 实现 |
|---|---|
| 批量撤销 | review-undo:按批次恢复,且撤销本身也留痕(可再撤回来) |
| 试运行模式 | 所有破坏性 action 默认 dry_run=true;流水线有 require_dry_run_first |
| 审核的 undo | 同上(人的操作和机器的一样都进日志) |
| 素材库版本 | backup / restore(只备份状态文件,不备份几万张图) |
| 并发审核协调 | H1 心跳锁 + R3 权限;冲突由日志可追溯 |
| 权限 | R3:外部脚本不能改审核结论(未知身份 fail closed) |
| 自动化冷启动 | M2 降级:历史样本 < 20 时阈值提高,全部转人工,不在没数据时硬猜 |
| 想做的 | 为什么做不到 | 正确落点 |
|---|---|---|
| L3 定时任务 | 插件无常驻进程,DSH 关了就没 | 用 DSH 任务看板的 cron 卡每天调一次 auto action=maintenance |
| L1 永久目录监听 | 同上(fs.watch 只在插件活着时有效) | webhook 主动上报 / git hook / cron 触发防抖扫描 |
| R2 自动推送告警 | 插件没有推送通道(发不了邮件/IM) | checkAlerts 出清单,由 cron 卡消费 |
auto action=maintenance会给出完整维护清单(扫变更→清孤儿→查损坏→悬空结论→健康→备份→告警), 配合 DSH 的定时任务就是无人值守的日常维护。
name: 我的素材流水线
on: [manual, new_asset]
steps:
- id: postprocess
use: postprocess
opts: {defringe: true, sharpen: true}
- id: slice
use: slice
- id: validate
use: validate
- id: route
use: route
- id: auto_review
use: auto_review
opts: {min_confidence: 0.85, sample_after_approve: 0.1}
- id: install
use: install
改流程改这个配置,不改代码。pipeline-validate 会检查步骤依赖顺序(install 需要 review 前置)。
F1 模板 ──→ F2 变体矩阵 ──→ batch 执行 ──→ F3 成本汇总
↑ │
│ ↓
J1 风格指南 ←── 审核通过 ←── A2 规格/A1 后处理 ←──┘
│ │
↓ ↓
J2 共享库 C1 派生变体 → C2 图集
│
H2 来源登记 / H3 备份 / H1 并发锁
| 组 | 关键设计(都是实测踩出来的) |
|---|---|
| F1 模板 | 缺变量保留 {占位符} 而不是静默留空 —— 否则 prompt 少一截却看不出来 |
| F2 调度 | 变体是乘法的(prompt×尺寸×质量×seed),>50 张给成本预警。每个变体带 seed,好结果能精确复现 |
| F3 成本 | 均单价只看付费张数(免费图不稀释);性价比按「记录里被装机名字的比例」折算,而不是"命中一个就算整条都用上" |
| F4 反向提取 | 反推的是可复用风格特征(颜色/明暗/线条/构图),不猜"画的是什么" |
| G2 契约 | 自己读文件核尺寸与透明(不是等调用方传) |
| G3 引用完整性 | 误报比漏报更糟:实测被 diag 脚本的产出名(cdpperf.png)误报过 36 次。现按相对游戏目录的路径判定用途;冗余必须遍历「磁盘上的图」,只遍历代码引用会让它恒为空 |
| G4 影响分析 | 给出具体文件+行号与风险等级,不说"有引用"这种空话 |
| H1 并发锁 | 用心跳而不是 exists() —— 进程被强杀后锁能自动失效,不会永久卡死 |
| H2 来源归属 | 归一化路径(大小写/斜杠);补登记按「目录+日期」聚类,并声明这是启发式 |
| H4 损坏检测 | 三级:文件头 → IEND 结尾(写一半的典型特征)→ 解码 |
| H3 备份回滚 | 只备份状态文件(审核结论/快照/账本),不备份几万张图。回滚前先给当前状态留一份 —— 回错了还能回来 |
| I1–I4 可观测 | 审都还没审时优先提醒这一点;样本不足就明说"看不出趋势",不硬给方向 |
| J4 A/B | 不采集游戏数据(那要埋点)。只做实验设计与显著性判定,不编造留存/点击数字 |
| J5 图鉴 | 自包含 HTML,可按状态/类别筛选;名字转义(防 <img onerror> 注入) |
核心思路:把「能不能用」从审美问题降级成技术问题。
| 组 | 能力 | 关键设计 |
|---|---|---|
| A2 | 规格即代码 | resolveSpec:内置默认 → <游戏目录>/asset-specs.json → 「待画素材清单.md」→ 显式传入。能修的修(尺寸/透明/留白/毛边),不能修的才报人(空图/全黑/背景无法确定) |
| A1 | 出厂后处理 | applySpecFixes 固定顺序:抠背景 → 去毛边 → 裁留白 → 调色板吸附 → 锐化 → 尺寸归一。先抠再裁(背景透明了包围盒才准),最后缩放(避免锐化放大噪点) |
| B1 | 调色板锁定 + 漂移 | buildPalette 按覆盖张数排序(跨多张都出现的色才是"这套的底色");paletteDrift 按「离群色的占比」判 ok/watch/off。取样 <2% 的噪点不参与 |
| B2 | 小尺寸可读性 | 先缩到游戏尺寸再算(大图上一切清楚,问题只在缩小后暴露):对比度 p90−p10、有效像素占比、边缘密度 |
| B3 | 动画连贯性 | 相邻帧差 + 首尾闭合 + 全一样。判"突变"用中位数而非平均值 —— 平均值会被突变本身拉高(实测 0.09/0.08/0.74 → avg 0.30,0.74 只有 2.5 倍,卡在阈值上检不出来;中位数 0.09 → 8 倍,稳定检出) |
| C1 | 派生变体 | 翻转/灰度/染色/受损/高亮。染色保留明暗结构(按亮度用目标色重建,不是简单叠色);受损态用 xorshift32 确定性噪点(同 seed 同结果,可复现可测)。继承源图审核结论 —— 人审 1 张 = N 份产出 |
| C2 | 图集打包 | 同一次计算产出图集与 manifest,天然一致(手工拼容易改了顺序忘改 manifest)。注意用 fitted.drawn 而不是 fitted.width —— 后者是画布尺寸,小图会被误报成"填满整格",引擎据此放大就错了 |
| D1 | 视觉标签 | 用确定性图像特征给可检索标签(横/竖、透明底、主色系、密度、线条感),不调视觉模型猜"画的是什么"(慢且不稳)。语义描述只来自 prompt |
| D2 | prompt 血缘 | lineageOf 记录可复现参数;planRegenerate 派生相邻 seed。没有 seed 就坦白说"随机、无法复现",不假装确定性。注意 seed: 0 是合法值,不能用 ` |
| D3 | 评分路由 | 分数构成全可解释(硬伤 −60 / 可读性 −25 / 留白 −15 / 漂移 −20 / 尺寸 −5)。auto_pass ≥80 / normal 50-79 / priority <50。auto_pass 不等于"好看",只保证技术层面无硬伤 |
| E1 | 偏好学习 | 从合格/不合格记录统计最常用的 provider/model/尺寸;样本 <5 就说"别急着学",不硬给结论 |
| E2 | 回归检测 | 重画后与 .bak-before-install 对比质量分;差 ≤3 判持平(避免噪声假报变好/变差) |
工具:quality(check/batch/spec/fix/coherence/animation/variants/atlas/tags/preference/regression)
人只出现在两个地方:审核、冲突。其余全自动。
生成(本插件 / 外部脚本)
↓
磁盘上的素材
↓
审过?─否→ needsReview ──→ 【人:画廊勾选/对比视图拍板】
│是 ↓
↓ approved
直接用 ↓
↓ 自动切片(继承源图结论)
↓ ↓
└────────→ needsInstall ←──┘
↓
install 自动装(无人值守)
队列(assets.json.queue,派生数据、可重算):
| 队列 | 含义 | 谁处理 |
|---|---|---|
needsInstall | 已合格、待装 | install 自动 |
needsReview | 从未审核 | 人(唯一必须人的地方) |
needsReReview | 审过但内容变了 → 结论失效 | 人(重审一次) |
needsReview / needsReReview 不阻塞已合格部分的安装。
网格从哪来?名字 + 清单 + 磁盘观测三者定,优先级从低到高:
| 例子 | 陷阱 | 处理 |
|---|---|---|
t_path_64x16 | 16 是尺寸高度,不是 16 格 | 排除 _WxH 后缀 |
i_skull_48 | 48 是清单声明的规格(48×48) | 结尾数字等于清单宽/高 → 当尺寸 |
r67tpath_dark2 | 2 是版本号,不是 2 格 | 名字只作候选,以观测为准 |
r67_frames_8 | 名字对,且磁盘上真有 8 个切片 | 观测优先(_slices/ 里文件数) |
所以 <name>_slices/ 里实际有几个文件才是事实来源;名字解析只用于"还没切过"的情况。
工具:assets(scan/review/clear/pending/changes/queue)、reprocess、quality
接口:/api/assets、/api/changes、/api/compare、/api/review、/api/reprocess
根因:history.json 只记录本插件生成的图。而输出目录里还有大量图来自别处:
| 来源 | 例子 | 以前 |
|---|---|---|
| 切片产物 | hero_slices/hero_01.png | 看不见 |
| 缩放件 | hero_64x16.png | 看不见 |
| 带名预览图 | hero_1_labeled.png | 部分可见 |
| 截图 | shot_xxx.png | 看不见 |
| 别的脚本/agent 批量产出 | r67rest_cardart_28.png | 看不见 |
实测本机:目录里 916 张,本插件只记了 235 张 → 681 张是"只在文件夹里"的。
现在画廊改为扫磁盘,全部列出来,并给每张分类(source / slice / resized / preview / screenshot),
顶部两排筛选条可按「类别」与「审核状态」过滤。
一件勾选:
| 按钮 | 作用 |
|---|---|
| 全选本页 | 勾选当前筛选结果的全部 |
| 选未审核 | 只勾还没拍板的(逐批清最快) |
| 选外部产物 | 只勾不是本插件生成的(那批历史遗留) |
| ✓ 标为合格 / ✕ 标为不合格 / ? 标为待定 | 对已勾选项批量写结论 |
| 清空选择 | 取消勾选 |
审核结论按素材名也记一份,所以「重画同名的图」会继承你之前的判断(按文件路径的单独结论优先)。
数据落在 ~/.dsh/gpt-image/assets.json,与 history.json 分开 —— 前者是"人工判断",后者是"生成记录",
生命周期不同(重画不清掉结论,改结论也不动生成记录)。
给 AI 的约定(assets 工具 guidance 里会明说):
review=approvedunreviewed 未经人确认,rejected 不要用kind=preview(画面里带名字)与 kind=screenshot 是给人看的,不要装进游戏工具:_dsh_external_dsh_gpt_image_assets(scan / review / clear / pending)
接口:GET /gpt-image/api/assets、POST /gpt-image/api/review、POST /gpt-image/api/review-clear
免费档(单价 0)的语义是「无限额度」 —— 这是刻意的,因为 0 元没有可防的东西:
| 锁 | 付费档 | 免费档 |
|---|---|---|
dailyBudget 日预算 | 超了就拒 | 不参与(付费额度用尽也照样跑) |
confirmAboveImages 确认锁 | 超过要 confirm | 不触发(0 元没什么可确认的) |
spendDisabled 总闸 | 拒绝 | 不受影响(它是"付费总闸") |
perRunMaxImages 单次张数 | 生效 | 同样生效 —— 这是请求体量保护,与钱无关 |
免费档在 check() 返回值里会明说 free: true、quota: 'unlimited'、remaining_today: null,
并在 skipped_locks 里列出「本次跳过了哪些锁」,避免用户疑惑「为什么没拦我」。
记账也是分开的(spend.json):
{
"records": [ { "images": 5, "cost": 0, "free": true }, // 免费:只记张数
{ "images": 3, "cost": 0.12, "free": false } ], // 付费:记张数 + 金额
"totals": { "free": { "images": 5, "runs": 1 },
"paid": { "images": 3, "runs": 1, "cost": 0.12 } }
}
todayFree() / todayPaid() 分开取;todaySpent() 只算付费(免费恒 0 元,天然不挤占预算)。free 字段时按金额推断,所以历史数据不会算错。⚠️ 闸门按「本次实际使用的配置档」算价,不是激活档。 否则「激活档是免费 + 显式指定付费档」会绕过日预算与确认锁 = 漏钱。 7 处闸门调用都传
pricePerImage: priceForProfile(state, args.profile),并有回归测试钉住。
| 看到 | 含义 |
|---|---|
🎁 免费档 ∞ · 3 张 | 当前是免费档:无限额度,今日已免费出 3 张(不占付费预算) |
💰 0.20 / 1 CNY + 进度条 | 付费档:今日已花 / 日预算;条子变黄 = 用到 80%,变红 = 用尽 |
| 状态「预算用尽」 | 付费档会被拦下;等明天、调高 locks.dailyBudget,或切免费档 |
| 状态「总闸已关」 | spendDisabled=true,付费调用被拒(免费档不受影响) |
| 上游「无真实余额(免费/内部额度)」 | 该账户额度上限是占位值(如 1e8),不是真余额 —— 插件不会把它伪装成余额 |
为什么分两块看:本地闸门才是真正会拦住出图的(真实生效);上游账户计费多数中转站不提供接口, 且免费档的「额度」是占位数字。所以插件以本地为主、上游为参考,并在详情里如实标注来源。
工具:_dsh_external_dsh_gpt_image_balance(check_remote=false 只看本地不联网)
接口:GET /gpt-image/api/balance(?remote=0 跳过上游查询,面板轮询用)
自动纠错:baseUrl 少写 /v1(中转站最常见的坑)时,先按原样请求,404 自动补 /v1 重试;
probe 还会直接告诉你「建议把 baseUrl 改成 xxx」。
单价按 0.04 元/张 计(pool.json 里可改 pricePerImage),插件的工作方式是先攒后花:
scan(root="D:\AHC\doomsday-ember")
→ 体检:还缺 16 张素材 → 只登记进「需求池」,一张都不生成
pool(action="show") → 待生成 16 张 · 阈值 8(卡牌类 12)· 现在跑要花 0.64 元
pool(action="flush", dry_run=true) → 只规划:冷启动先 1 张探针、本轮最多几张、预计花费
pool(action="flush", confirm=true) → 到阈值才一次性跑,跑完自动从池子划掉
install(root="D:\AHC\doomsday-ember") → 按规格缩放(保留透明) → 写进游戏 → 立刻复核尺寸与引用
screenshot(mode="window", window="<游戏窗口>", attach=true) → 重大改动后截图给用户看
两道花费闸门:①没到阈值不给跑(除非 force=true);②一批超过 autoSpendLimit(默认 0.5 元)
必须显式 confirm=true。所有预估都写在返回里,先看数字再决定。
install 解决的正是黑屏:模型只能出 1024 级别大图,游戏要 40×60 这种规格;手工缩放很容易做出
几百字节的空图(wx_clear_tile_40x60.png 就是 450 字节 → 黑屏)。install 统一按规格缩放
(保留 alpha,实测 1024×1536 → 40×60 透明 3.4KB)→ 落到游戏真正读的位置(同名文件先备份
.bak-before-install)→ 复核,未登记的还会告诉你差哪一步。
自查真实使用数据时发现的三处「钱会漏掉」的地方,都已修好并有回归测试:
| 问题 | 后果 | 现在 |
|---|---|---|
| 判停用「已成功张数」 | 失败的张数不算数 → 整轮顶出上限,多花 | 改成按已尝试张数判停(失败也烧钱),并在返回里给出 images_attempted |
| 时间预算用平均耗时 | 实测 141s/最慢 214s 时,按 141s 排量 → 超时那部分白花 | 取最慢值估算(estimateLatencyMs),排不完整的不排 |
| 普通失败不会停批 | 只认限流;连续报错时会一直撞着烧钱 | 连续 3 波有失败就停(repeated-failures),并退回 1 路并发 |
| 单张生成不查重 | 同一句提示词再发一次 = 白付一次 | 生成前指纹预检,命中直接跳过(skipped_duplicate, spent: 0),要重画才带 force=true |
实测:拿历史里 starcraft-rts / flag_terran 的原参数再要一次 → skipped_duplicate = true, spent = 0,直接复用已有产物。
另外:硬规定不允许静默失效 —— 「画面内带名字」的预览图如果没做出来,
返回里会明确给出 label_warning + label_errors,不再悄悄跳过(你的历史里曾有 3/8 张没标签)。
approval)四道锁拦的是「单次张数/日预算」,但一次 5 元、10 元的大批采购属于"要跟人商量"的量级,单独拎出来:
batch(assets=[...6 个...], confirm=true)
→ needs_approval: true,返回待批准单(没花任何钱)
【待批准】batch · demo
要花:0.24 CNY(6 张)
用途:塔防第一批通用素材
画什么:tower_fire、tower_ice、enemy_grunt、enemy_boss、icon_coin、icon_heart
影响范围:src/game.js
单号:apr_861af39046ac6d8c
approval(action="approve", token="apr_861af39046ac6d8c")
→ 已批准。把 approved_token 传给对应工具就会执行。
approvalToken() 由参数决定 —— 参数变了单子自动作废(防止批了 A 跑了 B)。实测:6 张批准后放行;改成 7 张立刻出新单号approval action=set_threshold 可改;待批准单文本可直接贴进对话cross_style)你怀疑的「两个项目风格差异大」已经量化了,实测输出:
doomsday-ember ↔ starcraft-rts = 0.243 ← 两套美术语言,别互相参考
starcraft-rts ↔ workbuddy-deploy = 0.164 ← 差异最大
workbuddy-deploy↔ 连通性测试 = 0.496 ← 风格接近,可共享参考
警告:检测到跨项目风格差异明显:生成时请只用「同项目」的素材当风格参考。
配套强制约束:挑参考图时若没指定项目,直接拒绝跨项目挑(cross_project_blocked)——宁可没有参考图,也不要串味。面板新增「跨项目风格」页。
sheet)· 切图后自动对齐你说得对,你们本来就有这个习惯(147_whole、r42_cardart_sheet)。现在做成工具:
sheet(items=[9 个图标描述], project="tower-defense")
→ 1 个请求出 1536×1024 合集图 → 自动按 3×3 切开 → 每个图标自动对齐 → 缩放到游戏规格
→ requests_used: 1, requests_saved: 8
切图默认自动对齐(trim: true),交付的是「能直接用的图标」而不是「格子」:
| 步骤 | 说明 |
|---|---|
| ① 连通域分析 | 洪水填充找出每格里的图形块(4 邻域,迭代式不爆栈) |
| ② 抗噪 | 丢掉落单的孤立像素/碎块(实测:角落 1 个杂点被识别为 dropped_specks: 1,不会把包围盒撑满整格) |
| ③ 取主体包围盒 | 用最大连通域 + 附近的大碎块合并,得到图标真实边界 |
| ④ 居中缩放 | 裁掉多余留白 → 按 target_width/target_height 等比 contain 居中(或 stretch),四周补透明 |
| ⑤ 质量判断 | 主体占比过小/没找到主体的格子给 warnings,不硬裁成垃圾 |
实测(故意做了一张「图标大小不一、位置歪、还有空格子」的合集图):
icon_1 格内 100×100 → 内容 20×20 → 输出 48×48 居中偏差 0.0/0.0
icon_2 格内 100×100 → 内容 60×40 → 输出 48×48 内容画成 48×32 垂直居中
icon_3 格内 100×100 → 内容 30×15 → 输出 48×48
icon_4 格内 100×100 → 内容 40×60 → 输出 48×48 内容画成 32×48 水平居中
icon_5 空间格子 → 警告「没找到图标主体」
icon_6 格内 100×100 → 内容 25×25 → 输出 48×48
过程中修掉一个真 bug(坐标系):contentBox 返回的是「格内相对坐标」,
跨格裁剪必须先用 absoluteBox() 转成整图绝对坐标 —— 第一版混用了,导致
图标被裁到角落甚至整张全透明(测试图里 icon_3/icon_6 空白)。现在两个坐标系显式分开,
并有专门的回归测试(tests/autotrim.test.js)锁死。
参数:trim(默认 true)、target_width/target_height(缩放规格)、mode(contain/stretch)、
padding(格子内缩);items 支持逐格指定尺寸([{name:"icon",width:48,height:48}])。
| 工具 | 作用 |
|---|---|
_dsh_external_dsh_gpt_image_approval | 待批准清单:list / approve / reject / set_threshold |
_dsh_external_dsh_gpt_image_sheet | 图标集:generate(出图+切)/ slice / grid |
_dsh_external_dsh_gpt_image_cross_style | 跨项目风格对比与污染预警 |
| # | 优化 | 说明 |
|---|---|---|
| 1 | 修好参考图打分的死代码 | 之前 resolveStyleRefs 里有个空分支,指纹匹配从没真正生效。现在用统一的 scoreReference 打分:分类一致(2.5) + 宽高比(1.5) + 风格指纹(4) + 尺寸接近(1) —— 竖版卡牌不会再被塞一张横版背景当参考 |
| 2 | 使用热度去噪 | _labeled 预览图、_fixed 副本、.bak 不再算成独立素材(否则会误报"白花钱");同一素材的多尺寸去重成一个 |
| 3 | 热度视图给全 | 新增 warm(被引用但不多)与 all(完整列表)。之前只有 hot(≥5次) 和 cold(0次),用到的素材可能两个视图都找不到 |
| 4 | 自检结果回流提示词库 | 废图自动进避坑清单(带原因),好图进可复用模板 —— 实测你项目已自动记下 2 条避坑(全黑/空图那两条提示词) |
| 扩展 | 工具 | 价值 |
|---|---|---|
| 一键工作台 | pipeline | 把「取素材 → 生成 → 自检 → 装机 → 登记」串成一条命令。素材来源自动选(需求池 → 顺序计划)。自检发现废图直接停下不装机(装进去只会黑屏),并给该重画哪几张 + 原提示词。实测 dry_run:assets-from-pool → 规划 4 张 / 313s |
| 提示词库 | prompts | 记录每条提示词的成功/失败次数与原因。action=lookup 生成前先查;good 拿可复用模板;pitfalls 看避坑清单 |
| 使用热度 | heatmap | 哪些生成出来的图真被游戏代码用到、哪些零引用(=白花钱)。实测你项目:5 个去重素材里 3 个零引用(waste_ratio: 0.6) |
| 花费预测 | config action=spend 里的 plan_forecast | 「这批 24 张约 0.96 元,今天只够 20 张,建议分 2 天」+ 预算还能撑几天 |
heatmap 缺 import → checkProject is not defined,工具直接报错review 用错字段 → result.reviewed is not iterable(实际字段是 suspects/good_files)xxx_labeled.png 报成"白花钱"| # | 优化 | 效果 |
|---|---|---|
| 1 | 热启动 | 学习粒度从「模型+尺寸+质量」放宽到「服务商+模型」:换尺寸/质量不再重新冷启动 1 张探针,只有真·第一次才探针 |
| 2 | 真实单价校准 | config action=calibrate images=12 actual_cost=0.48 → 反推单价并写回,之后所有预算/估算都按真实价算;也能从上游 usage.total_cost 等字段自动采信 |
| 3 | 失败重试上限 | 同素材最多重试 1 次(可重试错误才重试:限流/超时/5xx),带退避;重试同样计入已尝试张数,不会偷偷超额 |
| 4 | 风格指纹 | 扫全项目给每张图算指纹(主色板/明暗/饱和度/线条密度/透明占比),生成时按「像不像」挑参考图,并把量化风格拼进提示词 |
| 5 | order 生成顺序 | 从代码里解析素材依赖,按「先通用件(UI框/图标底板/地形)、后具体件(立绘/卡面/特效)」排批次 —— 避免通用件没定稿就画一堆具体图导致全部返工 |
| 6 | 并行度 | caps.maxConcurrency 可调到 3~4;默认从 2 起,成功会自己加、失败自己退 |
| # | 扩展 | 说明 |
|---|---|---|
| 7 | review 出图自检 | 不等模型"看一眼",先用确定性规则筛废图:几乎单色 / 全黑 / 全白 / 对比度极低 / 没有边缘(画面是空的)/ 该透明却不透明 / 尺寸不符。实测在你项目里揪出 3 张(t_path_1、wx_clear_tile_1、wx_clear_tile_40x60)—— 正是黑屏元凶 |
| 8 | order 素材依赖图 | 你的项目实测:169 个素材排成 3 批通用件(ui×3、terrain×22)+ 2 批具体件(effect×14、misc×130) |
| 9 | spend 预算日历 | 按天聚合花费 + 趋势 + 预测日均 + 文本条形图;config action=spend 直接看 |
| 工具 | 作用 |
|---|---|
_dsh_external_dsh_gpt_image_style | index 建/更新风格索引 · show 看风格档案 · outliers 看跑偏素材 |
_dsh_external_dsh_gpt_image_order | 生成顺序计划(先通用后具体) |
_dsh_external_dsh_gpt_image_review | 出图自检,只列可疑的 |
_dsh_external_dsh_gpt_image_register | 写进游戏登记表(action=install)/ 审计缺图(action=audit) |
register 的一个关键保护你的 src/84_assetmap.js 和 src/96_rpgasset.js 文件头写着「由 tools/build.mjs 自动生成,请勿手改」。
写它们等于白写(下次构建覆盖),所以 register 会识别并拒绝,同时告诉你该改哪个源文件:
84_assetmap.js skipped: 这是构建生成物(文件头写着「build.mjs」),改了会被下次构建覆盖
另外 register 兼容你项目的真实登记格式:
"key":{"s":"data:image/png;base64,...","w":40,"h":60,"base":0,"g":"wx","label":"晴天叠加层"} ——
只替换 s/w/h,保留 base/g/label 等字段,写前备份 .bak-before-register。
action=audit 实测:96_rpgasset.js 里 323 项全部填了真图,没有破图。
每次要花钱的调用(generate / edit / batch / pool.flush)都必须先过 spend.js 的闸门,
记账落在 ~/.dsh/gpt-image/spend.json:
| 锁 | 默认 | 行为 |
|---|---|---|
① 总闸 spendDisabled | false | 置 true = 一切付费调用直接拒绝(急停开关) |
② 单次张数 perRunMaxImages | 12 | 超过直接拒绝并让你拆批(实测:16 张被拦) |
③ 确认锁 confirmAboveImages | 1 | 一次要花 >1 张就必须带 confirm=true(实测拦住) |
④ 日预算 dailyBudget | 1.0 元/天 | 当天累计 + 本次估算超了直接拒绝(实测拦住) |
查账 / 改锁:
config(action="spend") → 今天几张、多少钱、预算、日历、趋势
config(action="calibrate", images=12, actual_cost=0.48) → 用真实扣费校准单价
config(action="set_locks", patch={"dailyBudget":2}) → 改预算
config(action="set_locks", patch={"pricePerImage":0.03})→ 单价按 3 分算
config(action="set_locks", patch={"spendDisabled":true})→ 急停,别想花我一分钱
需求池只收真缺口:报告文档、tools/、scripts/、tests/、out/ 以及 _ 开头的临时名
全部过滤掉(实测清掉了 36 条垃圾,否则会为文档里的截图白花钱)。
风格参考:generate/batch/pool.flush 带 use_style_refs=true 时,会自动从
游戏现有素材(同分类优先)→ 本项目已生成的图里挑 1~2 张当参考图走图生图,
并追加「严格对齐参考图的画风/线条/配色」的提示词;也可显式传 style_refs=[路径]。
DSH 里只保留这一个生图插件(原来的「素材锻造炉」已整体并入本插件,不再单独安装):
| 入口 | 位置 | 内容 |
|---|---|---|
| 左侧栏「生图」图标 | 左侧图标栏(与其它主面板并列) | 点开是主区域整页:项目生图画廊 + 核对结果 |
| 会话头部「生图」按钮 | 对话 / 轨迹那一行的 actions 区 | 同一个画廊的浮层版,Esc 关闭 |
画廊里每条都带:
达标 / 规格不符 / 没被引用 / 用不上| 结论 | 含义 |
|---|---|
达标 | 规格符合项目里的清单要求,且游戏代码/工具里确实引用了 |
规格不符 | 尺寸对不上(例如清单要 40×60,生成的是 1024×1536)——缩放裁剪后再登记 |
用不上 | 缺透明通道,或文件小到只有几百字节(空图/纯色图)——游戏里就是黑屏/空白的直接原因 |
没被引用 | 图本身没问题,但游戏代码里没有任何引用,或只在清单/报告文档里被提到 |
规格要求从项目里的 待画素材清单.md 这类表格自动读(| 素材 | \key` | 40×60,透明、可平铺 |`),
引用统计只看代码/配置(md/txt 清单与报告不算「在用」)。
设置 → 插件 → 发现(Discover)→ 添加插件源,填:
https://gcore.jsdelivr.net/gh/yyfabab1/dsh-gpt-image@main/market-index.json
索引格式是 DSH 商店的 format 1;生成/校验方式见 docs/商店发布.md。
:: 先只检查环境(不改任何文件、不重启)
一键安装.bat check
:: 确认没问题后正式装(会重启 DSH)
一键安装.bat
一键脚本会:检查 Windows / Node 20+ / 定位 DSH profile → 自检插件本体(工具数、侧栏配对)→
建目录链接(失败自动改用复制)→ 重启 DSH → 打开 安装说明.html。
跳过重启:set AUTO_NORESTART=1。脚本本体是 install.ps1(UTF-8 BOM),
.bat 只做纯 ASCII 启动 —— 因为 .bat 里写 UTF-8 中文在 chcp 65001 下会被拆成碎命令(实测)。
⚠️ 跨机器风险:只在作者本机(Windows + Node 22)验证过。 完整风险清单见 安装说明.html(浏览器打开,含可点击的 API 申请入口)。
# 本地路径(目录链接,改代码即时生效)
mklink /J "%USERPROFILE%\.dsh\profiles\web\node_modules\@dsh-external\dsh-gpt-image" "D:\你的路径\dsh-gpt-image"
# 或 npm 包
dsh plugin --profile web add @dsh-external/dsh-gpt-image
装完重启 DSH,模型侧会多出 _dsh_external_dsh_gpt_image_* 工具,浏览器可开
http://127.0.0.1:3080/gpt-image 面板。
npm test :: 687 个测试应全过
node scripts\ inventory.mjs :: 27 工具 / 32 路由 / 4 侧栏页配对
完整清单(含全部网址、每家文档、免费图床可达性实测)见 docs/API_KEYS.md, 或者直接浏览器打开 安装说明.html —— 那里的按钮点了就跳到官网注册页。
速查:
| 服务商 | 申请地址 | baseUrl / 模型 |
|---|---|---|
| Agnes AI(作者在用,图+视频当前免费) | https://platform.agnes-ai.com | https://apihub.agnes-ai.com/v1 · agnes-image-2.5-flash / agnes-video-2.5-flash |
| OpenAI 官方 | https://platform.openai.com/api-keys | https://api.openai.com/v1 · gpt-image-2 |
| fal.ai | https://fal.ai/dashboard/keys | https://fal.run · openai/gpt-image-2 |
每张生成的素材都会配一张「画面里印着素材名」的预览图 —— 素材一多就知道哪张是哪张。 但预览图默认一直是原尺寸,一张 1024×1024 素材的预览图也是完整 1024×1024 PNG(约 1MB)。 实测攒了 535 张、吃掉 928MB,直接把磁盘塞满。
现在有三档(config(action="set_label")):
| mode | 效果 | 体积 |
|---|---|---|
small(默认) | 缩到最长边 maxSide 像素(默认 384),名字照样看清 | 约原尺寸的 2~3% |
on | 原尺寸(旧行为) | 100% |
off | 完全不生成(明确关掉,不是"悄悄没做") | 0 |
config(action="set_label", mode="small", max_side=384) // 默认
config(action="set_label", mode="off") // 完全不要预览图
config(action="purge_labeled", dry_run=true) // 先看有多少历史预览图
config(action="purge_labeled", dry_run=false) // 真删
关掉时插件返回 skipped: true 而不是报错 —— 「跳过」和「失败」必须分开,
否则每次生成都会挂一条假的"预览图失败"警告。
一个动画做好后,"红/蓝阵营 / 缴获版 / 受损版"是常见需求。逐变体重跑「锚点→视频→抽帧」的代价是 每变体几分钟 + 每变体细节都可能不一样。变体继承只改颜色:
spritesheet(action="variant", sheet="soldier_walk_loop_sheet.png",
variants=[{name:"walk_blue", preset:"blue"},
{name:"walk_red", preset:"red"}])
预设 red/blue/green/purple/yellow/captured/damaged/ghost;也可手调 hue/saturation/
brightness/grayscale/tint/palette。只改颜色,alpha 与几何一个像素都不动;
帧数/格子/帧率/时序全部继承源表,JSON 里记下 variant_of 与 recolor 配方。
限制:
hue是全局旋转,会连肤色/金属色一起带走。只换制服用palette精确指定颜色对。
静态图按整图宽高缩放;动画必须按格子算倍率(5×2 的表按整图缩放会得到 185.6 像素的格子 → 切帧错位)。
install(action="anim-list", project="xxx") // 有哪些动画
install(action="anim", project="xxx", root="D:\游戏",
cell_width=96, cell_height=176) // 默认 dry_run
宽高倍率不一致会拒绝(拉伸会变形)并给出建议尺寸。产出:缩放后的精灵表 +
<名字>.anim.json(帧坐标按缩放后重算)+ 同名备份 .bak-before-install。
副产物太多分不清哪个给游戏用。现在四类:
| 类别 | 去处 | 游戏引用? |
|---|---|---|
| source(干净素材/精灵表) | 原地不动 | ✅ 只有这类该被引用 |
preview(*_labeled.png) | _preview/ | ❌ |
derived(.post/.pixel/_slices/缩放件/.alpha) | _derived/ | 可能 |
intermediate(frames//*.cache.json/*.bak) | _tmp/ | ❌ |
assets(action="layout", project="xxx") // 清点
assets(action="organize", project="xxx") // 演练,默认 dry_run
整理只动 preview + intermediate,source 一个都不动(被引用着,移动即断引用); derived 默认不动(切片可能就是游戏在用的单张),要动得显式 `include_derived=true