Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.
Coding
WorkBuddy 自定义模型能力位排查(读图/工具/推理)
Try it排查并修复 WorkBuddy 里自定义模型「不支持读图 / 不调用工具 / 不能推理」的能力位标错问题,以及新增自定义模型(OpenCode Zen、自建或其他厂商的 OpenAI 兼容端点)时一次性探测能力位并生成配置。当用户说「为什么说不支持读图」「模型读不了图片」「附件发不进去」「custom-local 模型能力不对」,或问「加新模型要测什么」「怎么知道这个模型支持读图/思考强度」时使用。含通用探测器 probe_model.py(一条命令测出读图/工具调用/effort 档位)、能力位定位法、直连端点自证法、以及「改完必须重启」这一必踩的坑。
What it does
**关键判读**:这是**本地元数据拦截**,不是网络错误、不是模型真的不支持。 请求根本没发出去。
The skill document
WorkBuddy 自定义模型能力位排查
症状
用 Read 读图片(png/jpg/jpeg/gif/webp/bmp)时返回:
Error: Current model (custom-local:) does not support reading images
关键判读:这是本地元数据拦截,不是网络错误、不是模型真的不支持。 请求根本没发出去。
根因
C:\Users\DELL\.workbuddy\models.json 里每个模型的能力位被标错,最常见的是 supportsImages: false。
判定代码(v5.6.2 实测)在
<安装目录>\resources\app.asar.unpacked\cli\dist\codebuddy-lite-wb.mjs 的 Read 工具分支:
if(await this.isImageFile(el, ea.context)){
let ei = await nY.ModelUtils.getCurrentModel(ea.context);
if(ei?.supportsImages === !1){ // ← 严格等于 false 才拦
return { content: `Error: Current model (${ei.id}) does not support reading images`, ... }
}
注意是 === false(严格)。理论上把字段删掉(undefined)也能绕过,但不建议 —— 应用会在启动时重写该文件。
getModelsPath() 的读取位置:
- WorkBuddy 产品:
~/.workbuddy/models.json - 其他产品:
~/.codebuddy/models.json - 可用环境变量
CODEBUDDY_CONFIG_DIR覆盖
排查步骤
0. 加新模型 / 改配置前,先跑通用探测器(推荐第一步)
本技能自带 probe_model.py,探测读图、工具调用、思考强度档位三件事,
--apply 可直接把结论写回 models.json(自动备份):
# 探测 models.json 里全部模型(只读,不改文件)
python probe_model.py
# 探测 + 直接写回配置(改前自动备份到 ~/.workbuddy/backups/)
python probe_model.py --apply
# 加一个全新模型:先追加条目,再探测,再按探测结果填能力位
python probe_model.py --add --model my-new-model \
--url https://api.example.com/chat/completions --key sk-xxx \
--name "My Model" --vendor "Example"
# 常用组合
python probe_model.py --model deepseek-v4-flash # 只探一个
python probe_model.py --model xxx --no-vision # 纯文本模型,省一次大请求
python probe_model.py --model xxx --image D:\pics\t.jpg # 用自己的图
python probe_model.py --apply --default-effort high # 强制指定默认档位
--apply 的安全设计(都已实测):
- 只改
supportsImages/supportsToolCall/supportsReasoning/reasoning, 绝不碰id/name/url/apiKey - 写前自动备份到
~/.workbuddy/backups/models.json.bak-probe-<时间戳> - 结论与现状一致 → 不重写文件(避免无谓的重启要求)
- 探测结果全部不完整(网络故障/限流伪装成"全不支持")→ 拒绝写入,需
--force才强写 - 单项探测异常 → 保留原值,绝不把已验证的
true降级成false - 保留你已有的
defaultEffort(只要它还在探测到的可用档位里) - 探测确认不支持推理 → 自动删除遗留的
reasoning块,避免自相矛盾配置
回归测试(改动脚本后务必跑):
python test_apply_logic.py # 17 项,覆盖写入/幂等/降级保护/备份
python test_vision_stability.py # 读图判定稳定性(需联网)
能照抄结论就别手写探测脚本 —— 手写一次要花掉半小时,且极易漏掉假阴性那个坑。
1. 看当前模型和标记
python -c "
import json
d=json.load(open(r'C:\Users\DELL\.workbuddy\models.json',encoding='utf-8'))
for m in d: print(m['id'].ljust(22),'images =',m.get('supportsImages'))
"
报错信息里的 custom-local: 就是当前模型 id。
2. 直连端点自证「模型其实支持图片」
不要凭猜测改配置,先实测端点。用该模型自己的 url / apiKey:
⚠️
max_tokens必须 ≥ 800。reasoning 模型会先把 token 花在思维链上, 给小了会出现「HTTP 200 但 content 为空」的假阴性,让你误判成"模型不支持读图"。 这个坑我踩过一次:DeepSeek 读图明明支持,却因为max_tokens: 400被误判为不支持。
python -c "
import base64,json,urllib.request
p=r'<图片绝对路径>'
b=base64.b64encode(open(p,'rb').read()).decode()
body=json.dumps({'model':'','messages':[{'role':'user','content':[
{'type':'text','text':'Look at the image. Answer exactly two things on separate lines:\nANIMAL: \nSTYLE: '},
{'type':'image_url','image_url':{'url':'data:image/png;base64,'+b}}]}],
'max_tokens':900}).encode() # ← 必须 800+,否则 reasoning 吃光 token
req=urllib.request.Request('<模型 url>',data=body,headers={
'Content-Type':'application/json',
'Authorization':'Bearer ',
'User-Agent':'curl/8.0'}) # ← 必须带,否则 Cloudflare 挡 403 error code: 1010
r=urllib.request.urlopen(req,timeout=120)
d=json.loads(r.read().decode())
print('HTTP',r.status)
print('finish_reason:',d['choices'][0].get('finish_reason'))
print(d['choices'][0]['message'].get('content'))
print('[reasoning]',(d['choices'][0]['message'].get('reasoning_content') or '')[:200])
print('[usage]',d.get('usage'))
"
判读:
- HTTP 200 且能说出图里内容 → 模型支持,配置标错了,改
supportsImages: true。 - ⚠️ content 为空但
reasoning_content里描述了图像内容 → 也算支持。 实测 DeepSeek 用橙圆蓝底测试图时,6/6 次 content 全空,但 reasoning 里明确写着 "an orange circle on a blue background" —— 模型读到了,只是 900 token 全被推理吃掉, 轮不到写正文。只判 content 会把"支持"误报成"不支持"。 - 别用「HTTP 200」当"支持多模态"的证据 —— 必须看 content 或 reasoning 里有没有 对图像内容的实际描述。用结构化提问(问形状+颜色,而不是问动物)能一眼看出真读了图还是在编造。
- 提问要和测试图匹配:拿几何图形问"什么动物"会让模型答"没有动物",看起来像失败。
- HTTP 400/422 报 unsupported content type / invalid image → 模型真的不支持,别改。
- 完整响应体要打出来看
finish_reason/message的字段名 /usage, 有些端点会把内容放在非标准字段里。 - 纯色/极小图可能被直接拒(实测 Space Bunny 对 8×8 纯色 PNG 返回 400, 换 256×256 有内容的图就正常)→ 测试图要有明确视觉内容。
3. 改配置(改前必备份)
cp ~/.workbuddy/models.json ~/.workbuddy/models.json.bak
然后编辑目标模型的 supportsImages。改完必须校验 JSON:
python -c "
import json
d=json.load(open(r'C:\Users\DELL\.workbuddy\models.json',encoding='utf-8'))
for m in d: print(m['id'].ljust(22),'images =',m.get('supportsImages'))
print('JSON OK')
"
4. 重启 WorkBuddy(不可跳过)
改了不重启 = 无效。 已实测:
- 代码里虽写了
watchFile+[HotReload],但改文件后当前会话仍报错,~/.workbuddy/logs/daemon.log里没有 HotReload 记录。 - daemon 是独立常驻进程,模型元数据在
daemon-server:daemon-bootstrap时载入。 查 daemon 何时启动:cd ~/.workbuddy/logs && grep -a -oE '\{"timestamp":"[^"]+","level":"[^"]+","scope":"daemon-server:daemon-bootstrap"' daemon.log | tail -3 - 子代理(subagent)继承同一进程的缓存,重启前同样报错。
5. 确认重启是否吃掉你的改动
应用启动时 [LocalModelHardwareGate] 会重写 models.json。查:
cd ~/.workbuddy/logs && grep -a "LocalModelHardwareGate" main.log | tail -3
diff ~/.workbuddy/models.json.bak ~/.workbuddy/models.json
实测:重写时保留了手工改的 supportsImages。若发现被还原,说明该模型另有权威来源
(服务端 product config / UI 设置),需从 UI 侧改。
已验证事实(2026-09-30)
| 模型 | 官方是否支持图像 | models.json 默认值 | 结论 |
|---|---|---|---|
| space-bunny-free(OpenCode Zen) | 是,text/image/video,1M ctx | false | 标错,已改 true |
| deepseek-v4-flash(api.deepseek.com) | 是,实测读出图中动物+画风 | false | 标错,已改 true |
| WorkBuddy 官方内置模型(快速/均衡/极致/Hy3/Hy4/GLM/Kimi/MiniMax…) | 是 | 全部 true | 正常 |
- 前端没有「自定义模型是否支持图片」的 UI 开关(搜过 app.asar,无对应文案), 只能改 models.json。
- 已有相邻技能
claude-code-third-party-model(管 Claude Code 接第三方端点), 本技能管 WorkBuddy 侧,勿混用。
思考强度(reasoning_effort)怎么配
supportsReasoning: true 只是开权限;真正决定可用档位的是 reasoning.supportedEfforts。
三套枚举互不相同,必须实测,不能照抄任何一方。
用非法值套出服务端真实枚举(最快的一招)
传一个服务端不认的值,报错通常直接把候选列表吐出来:
# DeepSeek:422 并列出全部合法值
# {"message":"...reasoning_effort: unknown variant `__probe__`,
# expected one of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `ultra`, `max`"}
客户端 UI 的内置枚举(读自 codebuddy-lite-wb.mjs)
["off","minimal","low","medium","high","xhigh","max"] // off 走 canDisableThinking 单独处理
配置里写客户端不认识的词(如 DeepSeek 的 none/ultra)不会报错,只会被静默吞掉,
用户在 UI 里根本选不到 → 只写上面这 6 档。
客户端怎么消费 supportedEfforts
withSupportedEffortsFallback 把它转成 thinkingLevelMap(effort → effort 一对一),
再原样发给服务端。所以写服务端真实接受的词即可,不需要自己做映射。
实测结果(2026-09-30)
| 模型 | 服务端接受 | 写进配置 |
|---|---|---|
deepseek-v4-flash | none/minimal/low/medium/high/xhigh/ultra/max(8 档全通) | minimal…max(6 档) |
space-bunny-free | minimal/low/medium/high/xhigh/max(6 档);none 和 ultra 被拒 400 | minimal…max(6 档) |
Space Bunny 拒 none/ultra 时只返回一句笼统的
invalid_request_error: Upstream request failed,不会告诉你哪些词合法 → 只能逐档试。
两个反直觉的坑
- 不传
reasoning_effort不代表不推理。Space Bunny / DeepSeek 不传时照样返回 非空reasoning_content,只是档位由服务端自定。defaultEffort仍建议显式写, 这样档位才可预期。 - 档位更高 ≠ 答得更对。同一道 bat-and-ball(答案 $0.05),Space Bunny
max出现过 答 $0.10;复测 4 次,minimal2/4、low3/4、medium+ 各 4/4。 别用"档位高"当"更聪明"的依据,也别指望调高档位能修好数学错误。 - 探测工具本身会骗你。同一张图、同一个模型,判定可能因运气在 true/false 之间跳
(
max_tokens到底够不够看模型当天状态)。所以probe_model.py的读图判定 允许 reasoning 里出现描述就算通过,并且加了 3 次重试。 任何--apply之前,先看探测输出里[1/4] 读图那行到底是什么结论, 别只看最后的汇总。
其他能力位
同一文件里还有同类字段,症状不同根因相同:
supportsToolCall: false→ 模型不调工具,只聊天supportsReasoning: false→ 推理档位不生效(reasoning.supportedEfforts也不生效)- 服务端配置里另有
disabledMultimodal—— 那是内置模型的开关,自定义模型不走这里
Related skills
Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.
Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.
Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.
Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.
Write, debug, and tune Playwright specs with locator strategy, trace diagnosis, and CI-aware timeouts.
More from oracis
Browse all skills把本地 Skill 打包并发布到 SkillHub(腾讯 skillhub.cn)与 ClawHub,覆盖预检、发布、状态回读与竞品对标全链路,并在上传被拒时用二分/ddmin 脚本把「服务端 WAF 拦内容(566)」与「包结构/字段问题」精确区分开。当用户说「发布技能到市场」「上架 skill」「SkillHub 提交失败」「Failed to fetch」「566」「上传 zip 报错」「技能审核状态」「版本号被拒」时使用。也讲清「入库成功 ≠ 已公开」——用 `mine` 的 `reviewStatus`、重发探测 `409 VERSION_EXISTS`、`/versions/<v>/signature` 三个探针判断版本到底走到哪一步。
平台只有网页后台、没有上传 API 时,用本机已装的 Chrome/Edge + CDP 自动化走完上传/发布/填表流程——复用真实登录态、零安装(不下载 500MB Chromium)、能给文件选择框塞文件;并实测富文本编辑器(公众号 / CMS / 邮件模板)里到底哪些 CSS 能活下来,以及在决定是否接入平台自带的「一键排版 / AI 美化」前先抓它输出做结构化 diff。当用户说「只能网页上传」「后台发布」「网页填表自动提交」「发布到技能市场/各平台控制台」「复用已登录的浏览器」「公众号排版样式不生效/保存后样式没了」「一键排版要不要用」「点了按钮没反应/提交没成功也没报错」时使用。
一键安装并实测验证已验证可用的免费 LLM 到 WorkBuddy/Claude Code,无需注册、无需 API Key。当用户说「装免费模型」「加免费模型」「免费 LLM」「免费模型一键装」「验证免费模型可用吗」「免费模型怎么接入」「帮我配几个免费模型」「监控免费模型」「免费模型挂了吗」「kilo 网关」「kilo-auto」「nemotron 免费」「space-bunny」「智谱 glm 免费」「zhipu 免费」「glm-4-flash」「glm-4.7-flash」「glm-z1-flash」「glm-4v-flash」「探测国外免费模型」「国外平台免费 llm」「优先用免费模型」「读图桥接」「vision bridge」「给 glm-4-flash 加读图」「不读图的模型怎么看图」「阶跃星辰」「stepfun」「step 5」「step-5-preview」「限时免费模型」「ccproxy 读图转发」「给 claude code 加读图」时使用。也用于验证已有免费模型配置是否真的可用(tool calling 是否支持、是否限流、是否支持读图),以及定期 watch 监控免费模型是否下线/限流/失去工具调用能力。
在中国大陆网络环境下用 yt-dlp 下载视频(含 Windows GUI 封装)。当用户要下载 YouTube、Bilibili、SoundCloud 等 yt-dlp 支持的站点内容,或遇到「yt-dlp 装不上 / 下载报 502 / Tunnel connection failed / ffmpeg not found / 缺 JS runtime / TLS fingerprint / watch?v=&list= 把整个列表都下了 / Sign in to confirm you're not a bot / The page needs to be reloaded / 下到一半突然下不动 / 下回来的视频糊、有拖影残影 / 下载速度特别慢」等问题时使用。
聚合搜索百度网盘/夸克网盘/阿里云盘等网盘分享链接。当用户想按作者名、课程名、剧名、书名、软件名找网盘下载路径,或提到「网盘搜索」「PanSou」「盘搜」「找到下载链接」「夸克/百度/阿里网盘资源」时使用。包含已验证的 PanSou 协议端点、绕过系统代理的必要写法、以及可直接运行的本地 Web 应用 panradar.py。
在 WorkBuddy(Windows)环境里交付零依赖本地 Web 应用,并绕开官方测试技能完全不覆盖的本机专属坑。当用户要在本机跑起来一个本地 Web 应用 / 单文件工具 / 案例库 / 看板 / 小工具,或要验证刚写完的 HTML/JS/Python 交付物真的能工作时使用。独家覆盖:Bash 缺 coreutils、bash 是 WSL 启动桩、rm 是坏 shim、PowerShell 不回显 stdout、cwd 漂移、预览面板靠 HEAD 探活——这些在干净 Linux 环境里遇不到。