Vision & Multimodal
dsh-zimage-gen
aefbda/dsh-zimage-gen · ★ 0
DeepSeek Harness 工具插件:用阿里云百炼 Z-Image-Turbo 出图,并统计本地调用次数与费用。
README body is shown in Chinese.
Manifest details
安装
dsh plugin --profile web add 'dsh-zimage-gen@0.2.0'从 npm 安装预构建包
For install and environment setup, plus plugin development tutorials, see DSH community docs ↗
README
Table of contents
DeepSeek Harness(DSH)工具插件:把阿里云百炼(DashScope)的 Z-Image-Turbo 文生图能力 做成两个 Host 工具,并在本地记账。
English — A DeepSeek Harness tool plugin that generates images with Alibaba Cloud Model Studio Z-Image-Turbo (
zimage_generate) and tracks local call counts and cost (zimage_quota). Zero runtime dependencies, no build step.
| 工具 | 作用 |
|---|---|
zimage_generate |
调用 Z-Image-Turbo 出图;提示词过短时先用当前会话的 DSH LLM 自动扩写;图片渲染成图片卡片 |
zimage_quota |
读取本地调用记录,统计次数与费用(按 prompt_extend 分档 ¥0.1 / ¥0.2) |
- 零运行时依赖:只用
node:内置模块和 DSH 的ctx服务,没有dependencies、没有构建 步骤,指向源码目录就能装。 - 短提示词自动扩写:提示词不足 30 字符时,用当前会话的 LLM 扩写,并按风格切换模板 (照片 / 插画 / 示意图各用各的扩写结构);扩写失败只降级,不影响出图。
- 风格预设:
style=photographic | illustration | diagram,会追加对应的肯定式渲染约束。diagram默认关闭图中文字,配合"无字底图 + 人工加字"使用。 - 公开自己的能力边界:见 适用范围与局限——哪些能用、哪些别用, 都按实测写清楚,避免把"看起来像"当成"画对了"。
- 图片卡片:生成的图片下载后登记为 DSH 附件,直接渲染在会话里(附件服务缺失时降级为 URL)。
- 本地记账:每次成功出图追加一条记录,按两档价目统计次数与费用,金额按“分”累加避免浮点误差。
环境要求
| 项 | 要求 |
|---|---|
| Node.js | >= 20(用到全局 fetch) |
| DSH | 0.1.5-rc.x(在 0.1.5-rc.3 上开发验证) |
| 凭据 | 一个百炼 API-KEY(不是 AccessKey ID/Secret,见 鉴权) |
| 网络 | 能访问 dashscope.aliyuncs.com 与返回图片的 OSS 域名 |
安装
插件是 bundle 形态:package.json 声明 dsh.bundle.patch,装进某个 profile 后其
cordis.patch.yml 会被合进该 profile 的层栈。因为零依赖、无构建,指向源码目录即可。
CLI 可管理的 profile(web、headless、自定义 profile …)
git clone https://github.com/aefbda/dsh-zimage-gen.git
dsh plugin --profile web add ./dsh-zimage-gen
dsh --profile web --dump-config | grep zimage # 确认出现 id: zimage-gen 层
装完后重启该 profile 的 host;新插件通常会被热加载,刷新页面即可。
桌面应用使用的 desktop profile
该 profile 由 Electron 应用独占,CLI 会拒绝管理
(profile "desktop" is managed exclusively by the Electron application),需要手工复刻
dsh plugin 做的两件事:用应用自带的 pnpm 装上,再把包名加进 dsh.profile.bundles。
$prof = "$env:USERPROFILE\.dsh\profiles\desktop"
$pnpm = "<DeepSeek Harness 安装目录>\resources\runtime\pnpm\bin\pnpm.cjs"
Copy-Item "$prof\package.json" "$prof\package.json.bak" # 先备份
Push-Location $prof
& node $pnpm add "/path/to/dsh-zimage-gen"
Pop-Location
# 然后编辑 $prof\package.json,把 "dsh-zimage-gen" 追加进 dsh.profile.bundles
运行中的 host 会热加载新 bundle,一般无需重启。
鉴权
按顺序解析:
- 环境变量
DASHSCOPE_API_KEY(百炼 API-KEY); ~/.alibabacloud/credentials(INI)里的[default]段,字段名可以是api_key/apikey/dashscope_api_key;段名可用ZIMAGE_CREDENTIALS_PROFILE覆盖。
[default]
api_key = sk-xxxxxxxxxxxxxxxx
凭据是每次调用时重新读取的,所以改完文件不需要重启 host(环境变量才需要重启,因为进程只在 启动时继承一次)。
AK/SK 不可用。 如果凭据段里只有
access_key_id/access_key_secret,插件会明确报错而不是 拿错凭据去发请求——Z-Image 走的是百炼多模态生成的 REST 端点,鉴权方式是Authorization: Bearer <API-KEY>,REST 端点不做 AK/SK 签名。
请求形状(与官方文档一致):
{
"model": "z-image-turbo",
"input": { "messages": [{ "role": "user", "content": [{ "text": "<prompt>" }] }] },
"parameters": { "size": "<宽>*<高>", "prompt_extend": false }
}
配置
cordis.patch.yml 里的 config 可直接覆盖,省略即用默认值:
- insert:
- id: zimage-gen
name: 'dsh-zimage-gen'
config:
defaultSize: '1024*1024'
autoExpandBelow: 30
downloadImages: true
| 键 | 默认 | 说明 |
|---|---|---|
baseUrl |
DashScope 多模态生成端点 | 端点覆盖 |
model |
z-image-turbo |
模型 id |
defaultSize |
1024*1024 |
未传 size 时的分辨率 |
credentialsFile |
~/.alibabacloud/credentials |
凭据文件(支持 ~ 展开) |
quotaFile |
~/.dsh/zimage-quota.json |
调用记录文件 |
autoExpandBelow |
30 |
提示词短于该字符数时自动扩写;0 关闭 |
expandMinChars / expandMaxChars |
200 / 600 |
扩写目标长度区间 |
expandProvider / expandModel |
空 | 扩写模型路由覆盖;空则用当前 Agent 的模型选择 |
expandTimeoutMs / expandMaxTokens |
60000 / 1024 |
扩写调用参数 |
requestTimeoutMs |
120000 |
生图请求超时 |
downloadImages |
true |
是否把图片下载并登记为附件(false 只返回 URL) |
maxImageBytes / imageTimeoutMs |
8388608 / 60000 |
附件登记限制 |
priceStandard / priceExtended |
0.1 / 0.2 |
计价单价(元/张) |
freeQuota |
0 |
>0 时 remainingFreeQuota 才给出数字,否则为 null(未知) |
环境变量可覆盖关键项(这些需要 host 重启才可见):ZIMAGE_BASE_URL、ZIMAGE_MODEL、
ZIMAGE_QUOTA_FILE、ZIMAGE_CREDENTIALS_FILE、ZIMAGE_EXPAND_PROVIDER、
ZIMAGE_EXPAND_MODEL、ZIMAGE_CREDENTIALS_PROFILE。
用法
zimage_generate(prompt="一只坐在窗台上的橘猫,午后阳光") # 短提示词 → 自动扩写
zimage_generate(prompt="<长提示词>", size="1280x720") # size 接受 * / x / ×
zimage_generate(prompt="<长提示词>", prompt_extend=true) # 开启服务端改写(¥0.2)
zimage_generate(prompt="<示意图描述>", style="diagram") # 结构示意图:纯平面 + 默认关字
zimage_generate(prompt="<描述>", style="illustration") # 扁平矢量插画
zimage_generate(prompt="<描述>", style="photographic") # 写实质感
zimage_quota() # 全部记录
zimage_quota(since="2026-10-01") # 时间过滤
zimage_generate 返回:
{
"imageUrl": "https://.../out.png",
"rewrittenPrompt": "响应里的 text 字段",
"finalPrompt": "实际发给模型的提示词",
"promptExpanded": true,
"size": "1024*1024",
"promptExtend": false,
"style": "diagram",
"textSuppressed": true,
"timestamp": "2026-10-08T11:13:50.818Z",
"costCNY": 0.1,
"durationMs": 3889,
"requestId": "…",
"image": { "attachmentId": "…", "mediaType": "image/jpeg", "bytes": 137433, "width": 1024, "height": 1024 },
"notes": []
}
image 为 null 表示附件登记失败(例如没有 ctx.attachments),此时只有 imageUrl 可用,
失败原因写在 notes 里。style 是解析后的实际风格(auto 会被解析成具体值),
textSuppressed 表示是否已要求画面不出现任何文字。
size 的单边取值范围是 512~2048,推荐 1024*1024、1280*720、720*1280。
风格预设(style)
style 决定两件事:追加哪种渲染语言约束,以及短提示词扩写用哪套模板。
Z-Image-Turbo 没有 negative prompt,所以所有约束都用肯定式描述表达。
style |
追加的渲染语言 | 适用 |
|---|---|---|
auto(默认) |
按提示词关键词自动判断 | 不想手动选 |
photographic |
真实摄影质感、自然光照、浅景深、细腻颗粒 | 照片、人像、写实场景 |
illustration |
扁平矢量插画、纯平涂色块、均匀照明、无阴影无景深 | 插画、图标、卡通 |
diagram |
扁平矢量示意图、纯平面均匀照明、无阴影无景深无噪点、几何精确、纯色背景 | 示意图、架构图、结构图 |
diagram 还额外管图中文字:
- 默认关闭图中文字(实测标签文字是 Z-Image 的系统性弱点),
textSuppressed为true; - 若提示词里出现「标注 / 标签 / 写上 / 文字 / label / text」等词,改为只允许短标签
(英文单词或不超过四个汉字),
textSuppressed为false; - 显式传
style会覆盖自动判断。
适用范围与局限(实测)
以下结论来自 5 张对照实验(合计 ¥0.5,单张 3.1–3.8 秒),不是主观印象:
| 任务 | 版面/构图 | 文字保真 | 结构/语义正确 |
|---|---|---|---|
| 单位圆 + 正弦波(3b1b 风格) | ✅ 左圆右波 + 贯通横轴 | ❌ θ→3、sinθ→A、±1→8/2 |
❌ 投影关系不成立 |
| 同上,短提示词走自动扩写 | ✅ | ❌ | ❌ 扩写更完整,几何仍不对 |
| Transformer 架构图 | ✅ 很像真图 | ⚠️ ~95%,Feed Forward→Feed Forwar、Multi-Hhaid |
❌ 无编码器/解码器之分 |
| 三层神经网络 + 中文标签 | ⚠️ | ✅ 输入层/输出层 正确,复杂字畸变 |
❌ 节点数 3/4/2 → 实际 6 个 |
| 神经网络抽象概念图(无字) | ✅ | ✅(无字) | ✅ 直接可用 |
能直接用的:封面图、章节标题图、概念插画、PPT 氛围背景、文章配图。
推荐工作流 —— 无字底图 + 人工加字:把文字需求从提示词里彻底拿掉,生成干净的图形底图,
再用 PPT / Figma / draw.io / LaTeX 叠加真实标签。style="diagram" 默认就按这个思路工作:
zimage_generate(
prompt="扁平矢量示意图,画面左侧一个圆形,右侧一条波形曲线,两者用水平坐标轴相连,极简风格",
style="diagram",
size="1280*720"
)
不要用它做:精确数学图示(公式、坐标数值)、需要语义正确的架构图/流程图、3b1b 那种动画。 这类需求请用 Manim 一类的矢量/可编程工具——图像模型只能给"看起来像"的位图,给不了 "算得对"的图形。
失败模式(便于预判):它画的是"这类图的典型样子",不是"你要的那张图";广为人知的视觉 范式(左单位圆 + 右正弦波)能复现,但具体约束(节点数、投影几何、标签内容)会被平均化; 英文短标签有稳定的近似拼写错误,数学符号基本失效。
提示词优化
当 prompt 字符数 < autoExpandBelow 时,用当前会话的 DSH LLM(ctx.llm.stream)扩写,
并按 style 切换模板——用「主体→场景→光照」的摄影模板去扩写示意图会引入景深、镜头这类
术语,实测会让结构图跑偏:
| 解析后的风格 | 扩写模板 |
|---|---|
photographic |
主体 → 场景 → 构图 → 光照 → 风格 → 约束 |
illustration |
主体与动作 → 视觉风格 → 配色 → 构图 → 线条与色块语言 → 画质 |
diagram |
整体布局 → 元素与数量 → 相对位置与连接关系 → 线条与配色 → 画面约束(禁用摄影术语) |
- 目标长度 200~600 字符;
- Z-Image-Turbo 不支持 negative prompt,因此 system 指令要求全部约束写成肯定式语言 (例如「画面干净通透」而不是「不要模糊」);
diagram+ 关字策略时,扩写模板会明确要求不要描述任何文字、标签或公式,避免扩写结果 和"画面不出现文字"的约束互相打架;- 路由解析优先级:
expandProvider/expandModel配置 > 当前 Agent 的options.provider/modelctx.agentDefaultModel.currentSelection(); - 任何失败(无 llm 服务、无路由、超时、模型报错、输出为空)都只记入
notes并回退原始提示词, 不会让生图失败。
计费与配额
每次 zimage_generate 成功后向 quotaFile 追加一条记录:
{ "version": 1, "records": [
{ "timestamp": "2026-10-08T11:13:50.818Z", "size": "1024*1024", "promptExtend": false, "cost": 0.1 }
] }
写入采用「临时文件 + rename」的原子替换,并在进程内串行化以抵御并发调用。官方计价为
prompt_extend=false ¥0.1/张、=true ¥0.2/张,速率上限 120 RPM;官方调价时改
priceStandard / priceExtended 即可。
开发与测试
npm test # 离线自检:67 项检查,不访问网络、不消耗任何 API 额度
npm run check # node --check + npm test
自检用 globalThis.fetch 桩替身跑通完整的 execute 路径,覆盖:size 校验、请求体契约、
响应解析、凭据解析(含 AK/SK 拒绝路径)、配额持久化/统计/并发/since 过滤、扩写契约与降级、
风格预设与文字策略、附件登记与降级、插件装载与工具定义契约、连续调用后的记账核对,
以及包自洽性(零依赖、可发布字段、cordis.patch.yml 与包名一致、repository 与包名同步等)。
若本机装有 DSH,自检会额外用 @deepseek-ai/dsh-tools 的真实校验器
(assertSupportedJsonSchema / validateJsonSchemaValue)校验 output.schema 与返回值;
找不到时自动跳过,可用 DSH_TOOLS_PATH 显式指定
…/@deepseek-ai/dsh-tools/lib/index.js。CI 在 Node 20 / 22 / 24 上跑同样的检查。
目录结构
dsh-zimage-gen/
package.json 声明 dsh.bundle.patch -> cordis.patch.yml;零 dependencies
cordis.patch.yml bundle patch:插入 id=zimage-gen 的插件行及其 config
index.js 插件入口:name / inject 导出 + apply(ctx, config)
lib/credentials.js DASHSCOPE_API_KEY / ~/.alibabacloud/credentials 解析
lib/zimage-api.js size 校验、请求体组装、响应解析、HTTP 错误归类
lib/prompt-expander.js 短提示词自动扩写(ctx.llm.stream)
lib/quota.js 调用记录持久化与统计(原子写 + 进程内串行化)
lib/image-attachment.js 下载图片并登记为 DSH 附件
lib/paths.js ~ 展开等路径小工具
lib/style-presets.js 风格预设、风格自动判断与「图中文字」策略
lib/tools.js 两个工具的 definition(parameters / output.schema / render / execute)
locale/{en,zh}.json 插件卡片显示文本
icon.svg 插件图标
test/offline-selfcheck.mjs 离线自检
.github/workflows/ci.yml CI
实测记录
在 DSH 桌面应用(desktop profile)里连续调用 3 次,结果与离线自检的预期完全一致:
| 次序 | 提示词 | size 入参 | prompt_extend | 本地扩写 | 耗时 | 计费 |
|---|---|---|---|---|---|---|
| 1 | 一只坐在窗台上的橘猫,午后阳光(15 字) | 默认 | false | ✅ 触发 | 3.9s | ¥0.1 |
| 2 | 一只柯基犬在雨后的青石板街道上奔跑…(40 字) | 默认 | true | ⏭ 未触发 | 22.3s | ¥0.2 |
| 3 | 雪山日出,云海翻涌(9 字) | 1280x720 |
false | ✅ 触发 | 3.6s | ¥0.1 |
zimage_quota返回总次数 3 · 总费用 ¥0.4 · 标准 2 · 改写 1,与逐条记录相加一致;since过滤边界正确;三张图都以图片卡片渲染;size分隔符x被归一化为*。
设计说明与已知限制
- 零运行时依赖是刻意的:只用
node:内置模块和ctx服务,不 import 任何@deepseek-ai/*包。profile 里安装的 bundle 运行时不保证能解析这些裸包名(社区插件同样只 依赖ctx),因此这里直接构造ctx.tools.register()需要的定义对象——形状与defineTool的产物一致({ name, description, parameters, output: { schema, render }, execute }), 参数校验由lib/tools.js自己完成。自检里有一条断言守住这个约束。 - 未声明
Config:声明 Config 需要@deepseek-ai/schemastery,与零依赖冲突;配置在normalizeConfig()里做默认值合并与类型纠正,validateConfig()在加载时只告警不阻断。 ctx.tools.register()不做参数校验(那是defineTool的包装行为),所以插件自己校验 形状与取值;模型传入非法参数会得到一条Error: …结果,而不会中断本轮对话。- 图片卡片依赖
ctx.attachments;该服务缺失或图片下载失败时降级为纯文本 +imageUrl。 附件库中的对象是归一化后的副本(统一 JPEG、可能缩放、无扩展名),不是百炼返回的原始字节。 - 图片 URL 会过期(OSS 签名链接),需要长期留存请用附件副本或自行转存。
remainingFreeQuota只有在配置了freeQuota > 0时才是数字,否则返回null(百炼免费额度是账号维度、随活动变化,插件不猜测)。
许可证
MIT。变更记录见 CHANGELOG.md。