Vision & Multimodal

dsh-zimage-gen

aefbda/dsh-zimage-gen · ★ 0

aefbda

DeepSeek Harness 工具插件:用阿里云百炼 Z-Image-Turbo 出图,并统计本地调用次数与费用。

README body is shown in Chinese.

License
MIT
Added
2026-10-30
Maintenance
active
Install kind
npm
npm
dsh-zimage-gen
Manifest details
Manifest status
Declared
Manifest path
package.json#dsh
Manifest checked
2026-10-30T09:38:19.352Z
Lifecycle scripts
None found

Static manifest checks report declarations only; they are not compatibility or security review.

安装

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

Source README · body: zh · Read Chinese README

Table of contents
  1. 环境要求
  2. 安装
  3. CLI 可管理的 profile(web、headless、自定义 profile …)
  4. 桌面应用使用的 desktop profile
  5. 鉴权
  6. 配置
  7. 用法
  8. 风格预设(style)
  9. 适用范围与局限(实测)
  10. 提示词优化
  11. 计费与配额
  12. 开发与测试
  13. 目录结构
  14. 实测记录
  15. 设计说明与已知限制
  16. 许可证

CI

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,一般无需重启。

鉴权

按顺序解析:

  1. 环境变量 DASHSCOPE_API_KEY(百炼 API-KEY);
  2. ~/.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/model

    ctx.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。