dsh-multimodal-bridge
DeepSeek Harness plugin bundle — 给纯文本模型补上视觉理解与图像生成能力。
把 multimodal-bridge 的 MCP 能力带进 DeepSeek Harness(dsh):
本包是一个 Cordis 插件 bundle,注册两个模型可见工具:
| 工具 | 功能 | 后端模型 |
|---|
qwen_vision(image_path, prompt) | 图片理解、OCR、结构分析 | Qwen-VL 系列(qwen-vl-max / qwen-vl-plus / qwen3-vl-flash …) |
qwen_generate(prompt, image_path?, size?, n?, negative_prompt?) | 文生图;传 image_path 时做图生图(编辑/风格转换),保存为本地 PNG | Qwen-Image 系列(qwen-image-2.0 / wan2.7-image …) |
qwen_video(prompt, image_path?, size?) | 文生视频;传 image_path 时做图生视频(首帧动画),原生异步接口,MP4 落盘,几分钟出片 | t2v: wanx2.1-t2v-turbo / wan2.6-t2v / wan2.7-t2v;i2v: wanx2.1-i2v-turbo / wan2.6-i2v / wan2.7-i2v |
qwen_chat(prompt, system?) | LLM 咨询:第二意见、质疑性审查/反驳、验证、复杂推理辅助 | qwen3.7-max / qwen-max / glm-5.2 / MiniMax-M2.5 … |
qwen_tts(text, voice?) | 语音合成:文本转语音,WAV 落盘 | qwen-tts(input.text 形状已验证) |
适用场景与 multimodal-bridge 的"备选线"相同:DeepSeek 等纯文本模型没有视觉,
qwen_vision 是它看图的唯一途径;qwen_generate 为任何模型补上出图能力;
qwen_chat 让主模型在面对复杂/高不确定性问题时获得另一模型族的第二意见与反驳
(Fusion 架构:Qwen 系多模态 + LLM 能力融合给 DeepSeek v4)。
安装
以下命令默认在仓库根目录 multimodal-bridge/ 下执行(若已 cd dsh-plugin,
则把 ./dsh-plugin 换成 .):
# 从 npm(发布后)
dsh plugin --profile <name> add dsh-multimodal-bridge
# 从本仓库 checkout(在仓库根目录执行)
dsh plugin --profile <name> add ./dsh-plugin
# 或直接从 GitHub(会自动执行 prepare 构建;pnpm 首次会要求 allowBuilds 授权)
dsh plugin --profile <name> add github:Spirit4471/multimodal-bridge#<commit-sha>
dsh plugin add 会把包追加到 profile 的 dsh.profile.bundles,其
cordis.patch.yml 作为一层配置插入 multimodal-bridge 行。
验证层已生效(不启动服务):
dsh --profile <name> --dump-config # 应看到 "# == dsh-multimodal-bridge" 注释块
dsh --profile <name> # 启动后即可让模型调用两个工具
免 profile 快速验证(开发用)
不想建 profile 时,用 overlay 直挂本地构建产物(先 npm run build,
把 overlay.example.yml 里的路径改成你的 checkout 位置——Windows 必须用
file:///C:/... URL 形式,C:/... 会被 Loader 当成 c: 协议拒绝),
在仓库根目录执行:
dsh web --patch ./dsh-plugin/overlay.example.yml --port 3081
配置与密钥安全
核心原则:配置只放"引用",密钥放在每个用户自己的凭据层里。 bundle 自带的
patch 不含任何秘密(apiKey: ''),因此可以直接共享、同步、提交。每个使用者
只需输入自己的两样东西:Key 和归属空间网关。
推荐流程(每个用户一次设置,无需改任何 patch)
-
归属空间网关(非机密) → 追加到 ~/.dsh/.env(用户级环境层,dsh 启动时自动加载):
QWEN_API_BASE=https://ws-xxxx.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
(用 DashScope 官方端点则不需要这行,默认就是 https://dashscope.aliyuncs.com。)
-
API Key(机密) → 写入 DSH 官方凭据存储 ~/.dsh/.credentials.yaml
(首次创建即为 0600 权限,永不进环境变量、不进配置树):
QWEN_DASHSCOPE_API_KEY: sk-ws-xxxxxxxx
视觉/生成分开管理时用 VISION_API_KEY、GENERATE_API_KEY(未设时回退上面的共享键)。
-
单次临时覆盖(最高优先级,仅当次进程生效):
$env:QWEN_DASHSCOPE_API_KEY = 'sk-...' # 或 VISION_API_KEY / GENERATE_API_KEY
插件每次调用时按 config apiKey → DSH 凭据存储(环境 > .credentials.yaml > .env)→ 裸环境变量 的顺序解析,轮换密钥只需改凭据文件,下一轮调用即生效,无需重启。
⚠️ 不建议把 key 写进 cordis.patch.yml(apiKey 字段是给受限部署用的最后一招):
patch 文件是普通配置,可能被分享或同步,密钥进去就等于泄露。
其他字段
bundle patch 自带默认值;需要改默认值时在 profile 的 cordis.patch.yml 里按行 id 覆盖
(注意:patch 替换整行 config,需重述保留字段):
- id: multimodal-bridge
config:
apiKey: '' # 建议留空,走上面的凭据层
apiBase: '' # 留空 = 用环境变量 QWEN_API_BASE,未设则官方端点;填了会覆盖环境变量
visionModel: qwen-vl-max
visionFallbackModels: [qwen-vl-plus, qwen3-vl-plus, qwen3-vl-flash]
generateModel: qwen-image-2.0
generateFallbackModels:
- wan2.7-image
- qwen-image-2.0
- wan2.7-image-pro
- wan2.1-t2i-turbo
videoModel: wanx2.1-t2v-turbo
videoFallbackModels: [wanx2.1-t2v-plus, wan2.6-t2v, wan2.7-t2v]
chatModel: qwen3.7-max
chatFallbackModels: [qwen-max, glm-5.2, MiniMax-M2.5]
attachGeneratedImages: false # true = 生成图注册为附件并展示在结果卡片(纯 UI,不进模型请求)
outputDir: generated
| 字段 | 默认值 | 说明 |
|---|
apiKey | '' | 显式密钥(不建议用于密钥);留空则按上面的凭据优先级解析 |
apiBase | '' | 留空 = 环境变量 QWEN_API_BASE 优先,未设则官方 dashscope.aliyuncs.com;显式填 URL 会覆盖环境变量。兼容网关(URL 含 compatible-mode / maas.aliyuncs.com)自动走 OpenAI 兼容协议 |
visionModel | qwen-vl-max | 视觉模型 |
visionFallbackModels | [qwen-vl-plus, qwen3-vl-plus, qwen3-vl-flash] | 视觉备用链(模型级拒绝自动切换) |
generateModel | qwen-image-2.0 | 首选文生图模型 |
generateFallbackModels | [wan2.7-image, qwen-image-2.0, wan2.7-image-pro, wan2.1-t2i-turbo] | 主模型遇网关侧路由错误时按序尝试的备用链;每个模型自动使用各自的官方默认尺寸 |
videoModel | wanx2.1-t2v-turbo | 首选文生视频模型(原生异步接口) |
videoFallbackModels | [wanx2.1-t2v-plus, wan2.6-t2v, wan2.7-t2v] | 视频备用链;模型级拒绝(AccessDenied / 不存在 / 额度)自动跳过换下一个 |
videoI2vModel | wanx2.1-i2v-turbo | 图生视频首选模型(qwen_video 收到 image_path 时使用) |
videoI2vFallbackModels | [wanx2.1-i2v-plus, wan2.6-i2v, wan2.7-i2v] | 图生视频备用链 |
chatModel | qwen3.7-max | qwen_chat 咨询的首选 LLM |
chatFallbackModels | [qwen-max, glm-5.2, MiniMax-M2.5] | 咨询备用链(模型级拒绝自动切换) |
ttsModel | qwen-tts | qwen_tts 首选语音合成模型(input.text 形状已验证可用) |
ttsFallbackModels | [] | 语音备用链(qwen3-tts-flash 需 voice 参数且对文本格式挑剔,暂未纳入默认链) |
attachGeneratedImages | |
行为与限制
- 大图自动降采样:超过 ~10 MB 或最长边 1568 px 的图片会用
sharp
压成 JPEG。sharp 不随包安装(避免 pnpm 构建脚本审批拖垮默认安装);
遇到超限图片且未装 sharp 时,报错会提示 dsh plugin --profile <name> add sharp
(装完在 profile 目录跑一次 pnpm approve-builds 审批其构建脚本)。
- 两种 API 协议:URL 含
compatible-mode 或以 maas.aliyuncs.com 结尾
时走百炼 OpenAI 兼容格式(视觉与生成都挂 chat/completions),否则走
DashScope 原生格式(生成是"提交任务 + 轮询"的异步流程,最长等 2 分钟)。
- 图生图(image-to-image,双通道):
qwen_generate 传 image_path 时先在
当前网关的 chat 通道尝试(部分工作空间支持);被拒(多数工作空间对图像模型只
暴露文生图,报 content length invalid / Either 'text' or 'image' 等误导性
错误)后自动回退到 DashScope 原生 multimodal-generation 编辑接口——实测
工作空间 key 在官方端点可用且支持文字引导编辑(2026-08 探测结论)。原生通道
不接受 parameters,输出尺寸为模型默认(qwen-image-2.0 编辑输出 1024×1024)。
输入图会压缩到 1024px JPEG(sharp 可用时)以满足内容长度限制。
- 尺寸默认值随模型族变化:qwen-image 模型默认
1328*1328,wanx 模型默认
1024*1024;显式传 size 时以传入值为准(可选值见工具 schema)。
- 生成自动路由(fallback):免费额度 ≠ 网关路由表——工作空间网关只挂载部分
模型。主模型遇网关侧错误(
url error、空响应、model not enabled 等)时按
generateFallbackModels 顺序自动换模型重试;401(密钥认证失败)不转移,
模型级 403(AccessDenied/额度)会转移。
- 文生视频(
qwen_video):走 DashScope 原生 video-synthesis 异步接口
(提交任务 → 轮询 ≤10 分钟 → 下载 MP4),工作空间 key 已验证可用(2026-08-16:
wanx2.1-t2v-turbo / wan2.6-t2v / wan2.7-t2v 接受任务)。视频模型在网关
聊天通道不可路由,故不走网关;模型级拒绝按 videoFallbackModels 换下一个模型。
- 模型路由原则:插件不枚举/探测你的模型——"有哪些模型、哪些有额度"以
百炼控制台的免费额度清单为准,你只需把可用模型名填进对应 fallback 配置;
插件负责按序尝试与失败转移。
- 聊天框内联展示(可选):DSH 当前的模型适配器只输出文本,且 DeepSeek 适配器
遇图片内容块会直接拒绝请求(
UNSUPPORTED_CONTENT)——所以图片不能注入对话。
开启 attachGeneratedImages 后,插件把生成图注册为持久附件,图片块只出现在
工具结果卡片(presentResult 的 generic 卡内容,纯 UI 投影,不进模型请求),
模型本身仍只拿到文件路径文本,并可调 qwen_vision 自查图片。Web 端是否渲染
卡片内的图片块取决于其 UI 适配器实现。
qwen_vision 是只读并发安全工具(可与其他工具调用并行);
qwen_generate 写文件,保持互斥执行。
- 取消:两工具都转发
exec.signal,取消会中止在途 HTTP 请求。
- 模型侧错误信息是面向模型的纯文本;
qwen_generate 的 API 失败返回
{ success: false, error } 而不是抛错,配置类错误(缺 key、n 越界)才抛出。
Fusion 使用模式(架构落地)
本插件的定位:DeepSeek v4 的感知与对抗中枢。原则与推荐工作流:
决策不分权,感知与生产分权
v4 是唯一控制平面(计划/编排/纠错/判断);Qwen 家族是能力平面(看/画/动/咨询)。
不要让 Qwen 替 v4 做决策——只让它产出信息:结构化读数、生成物、第二意见。
感知:传感器读数式查询
- 视觉结果以结构化文本过桥(表格/CSV/JSON),不是原图;
- 两级取景:先整体描述,再针对关键区域/属性聚焦提问;
- 关键事实三角验证:换问法(或换模型)二次查询交叉确认。
对抗审议流水线(qwen_chat 的正确姿势)
v4 提出结论
→ 跨族顾问红队审查(system: "你是严厉的审稿人,找出漏洞")
→ 顾问指出漏洞 → v4 综合、修正或反驳
→ 分歧大 → 第三方模型仲裁(qwq-plus 推理型)
→ 多模型一致 + v4 一致 → 高置信度
跨模型族是关键:GLM / MiniMax / Kimi 与 DeepSeek / Qwen 训练分布不同,一致才有
信号价值;同族模型互相附和是噪声。
任务画像路由预设(覆盖配置即可切换)
| 预设 | chatModel 链建议 |
|---|
| 通用 | qwen3.7-max → qwen-max → glm-5.2 |
| 代码审查 | kimi-k2.7-code → qwen3-coder-plus |
| 逻辑/数学验证 | qwq-plus → qwen-math-plus |
| 多样性投票 | glm-5.2 → MiniMax-M2.5 → kimi-k2.5 |
预算感知
免费额度有限(图像/视频是次数制):flash 系模型做初筛,贵模型只处理 v4 判定
"值得"的请求;v4 自己能推理的绝不过桥。
排障
Qwen API error 401: ... invalid_api_key:key 与端点不匹配或已过期。
- 百炼工作空间网关的 key(
sk-ws-...)只能配 https://ws-xxxx...maas.aliyuncs.com/compatible-mode/v1 网关地址;
- DashScope 官方 key(
sk-...)配默认的 https://dashscope.aliyuncs.com。
按 配置与密钥安全 的优先级检查实际生效的是哪一层的值(启动环境 > ~/.dsh/.credentials.yaml > ~/.dsh/.env),或到百炼控制台重新生成 key。
- 大图报"sharp not installed":按提示安装:
dsh plugin --profile <name> add sharp,然后在 profile 目录
($DSH_HOME/profiles/<name>)执行 pnpm approve-builds 勾选 sharp 审批其
构建脚本;或换一张 ≤10MB、长边 ≤1568px 的图。
Qwen API error 400: ... url error, please check url!:百炼兼容网关下
qwen-image-2.0 用 1024*1024 尺寸会触发这个误导性报错(网关内部路由问题)。
用官方默认尺寸 1328*1328(插件对 qwen-image 模型族已自动采用),或换 wan2.7-image
等 wanx 模型(1024*1024 正常)。不同模型族支持的尺寸不同:wanx 支持
1024*1024 / 720*1280 / 1280*720;qwen-image 支持 1328*1328 / 1536*1536 等。
- 生成报
Qwen image API returned no images:模型名在该网关没有图像路由
(如 qwen-image-max),响应里没有图片;换网关实际开通的图像模型(如 qwen-image-2.0、wan2.7-image)。
- 在 GUI/插件里报
url error 但 smoke 脚本正常:patch/overlay 配置里的
apiBase 显式值会覆盖环境变量——把它留空(apiBase: '')才会使用
~/.dsh/.env 里的 QWEN_API_BASE。工作空间 key(sk-ws-...)必须走工作空间
网关(同步通道);走官方原生端点会因工作空间的结果存储链路问题在生成时报
url error(视觉不受影响)。
dsh plugin add 后版本停在旧版 / 报 minimumReleaseAge 相关错误:
pnpm 11 起对"刚发布"的版本有约 24 小时的最小发布期保护(supply-chain 安全机制),
新版本发布后 add 可能自动回退旧版;显式 @<新版本> 也会在 lockfile 校验步
报 MINIMUM_RELEASE_AGE_VIOLATION。两个解法:① 等发布期窗口过去(约一天);
② 在该 profile 的 pnpm-workspace.yaml($DSH_HOME/profiles/<name>/)里加
minimumReleaseAge: 0 关闭门槛后重试。若同时出现 IGNORED_BUILDS,
把同文件里 allowBuilds 的对应占位符改为 true(或跑 pnpm approve-builds)。
- 视频报
403 AccessDenied / Model not exist:该模型对当前 key 未开通或
不在路由表。插件会自动跳到 的下一个模型;若整条链都失败,
到百炼控制台核对免费额度清单里该模型的实际 Code(注意 与
是不同的名字),再把可用名字填进配置。
开发
cd dsh-plugin
npm install
npm run build # tsc → lib/
prepare 脚本保证从 git 直接安装时也会自动构建(无 monorepo 上下文依赖)。
免 Harness 冒烟测试
不用启动 dsh 也能验证 Qwen 客户端(需要设置 QWEN_DASHSCOPE_API_KEY
环境变量,百炼网关可再加 QWEN_API_BASE):
node scripts/smoke.mjs path/to/image.png "描述这张图片"
node scripts/smoke.mjs gen "一只赛博朋克风格的机械猫" 1024*1024 1
发布与贡献(生态路线)
DeepSeek Harness 目前不接受外部 PR(见其 CONTRIBUTING),插件走生态路线:
- 发布到 npm(
files 已限定 lib + cordis.patch.yml + scripts/smoke.mjs):
cd dsh-plugin
npm run build
npm pack --dry-run # 复核包内容:只应有 lib/**、cordis.patch.yml、scripts/smoke.mjs
npm login # 首次
npm publish
- 发布后冒烟:
dsh plugin --profile demo add dsh-multimodal-bridge +
dsh --profile demo --dump-config 应能看到本层。
- 给仓库加
dsh-plugin topic:GitHub 仓库 Settings → Topics →
dsh-plugin,方便社区发现。
- README / 博客介绍:说明"纯文本模型 + 桥 = 视觉/出图能力"的用法,
就像本仓库 README 的双轨工作流图。
- 社区:登记 awesome-deepseek-harness,
并在 DeepSeek Harness 的 GitHub Discussions / Discord 分享。
检查清单
License
MIT