# dsh-tool-describe-image 测试教程 本教程覆盖从代码到真实产品的全部验证环节。**所有命令都在仓库根目录 `D:\deepseek-harness` 下运行**(`pnpm exec` 需要那里的 package.json;vitest 的 `root` 已锚定到插件目录,从仓库根运行即可找到测试文件)。 测试分五层,建议按顺序执行: | 层 | 目的 | 耗时 | | --- | --- | --- | | 1. 静态检查 | 类型、构建、bundle 产物正确 | ~1 分钟 | | 2. 单元测试 | 纯逻辑(magic bytes、HTTP 构造、错误路径) | 秒级 | | 3. 真实 API | 百炼 qwen-vl 端到端(需 key + 图片) | ~10 秒 | | 4. 集成安装 | 插件挂进 profile 并被组合树接受 | ~30 秒 | | 5. Web 端到端 | 粘贴图片 → 自动识别 → 进输入框 | ~1 分钟 | --- ## 第 1 层:静态检查(不联网) ### 1.1 类型检查(host + client 全量) ```powershell cd D:\deepseek-harness pnpm exec tsc --noEmit -p plugins/dsh-tool-describe-image/tsconfig.json ``` **预期**:无输出,退出码 0。 **检查范围**:`src/index.ts`(工具 + RPC)、`src/client.ts`(浏览器半区)、`src/dashscope.ts`、`src/deepseek.ts`(余额查询)、`src/rpc-contract.ts`。 ### 1.2 Host 构建(生成 lib/) ```powershell pnpm exec tsc -p plugins/dsh-tool-describe-image/tsconfig.build.json ``` **预期**:无输出;`lib/` 下生成 `index.js` / `dashscope.js` / `rpc-contract.js` 及对应 `.d.ts`。 ### 1.3 Client 声明构建(生成 lib/types/client.d.ts) ```powershell pnpm exec tsc -p plugins/dsh-tool-describe-image/tsconfig.client.json ``` **预期**:`lib/types/client.d.ts` 存在,`exports["./client"]` 能解析到它。 ### 1.4 Client bundle 构建(生成 lib/client.js) ```powershell pnpm exec tsdown --config plugins/dsh-tool-describe-image/tsdown.config.ts --env.DSH_BUILD_FACE client ``` **预期**:`lib/client.js`(约 5 KB)和 `lib/client.js.map` 生成;输出末尾显示 `Build complete`。 **验证 bundle 契约**(可选):打开 `lib/client.js` 前几行,应看到: ```js window.__ModuleLoader__.load({ id: "dsh-tool-describe-image", factory: (require) => { ``` **验证构建产物可加载**(可选): ```powershell cd plugins/dsh-tool-describe-image @" import { name, RPC_CHANNEL, RPC_ENDPOINT_DESCRIBE, describeImageRpc } from './lib/index.js' console.log(name, RPC_CHANNEL, RPC_ENDPOINT_DESCRIBE, typeof describeImageRpc) "@ | Out-File -Encoding utf8 load-test.mjs node load-test.mjs ``` **预期**:打印 `describe-image /dsh-describe-image describe function`,然后删除 `load-test.mjs`。 --- ## 第 2 层:单元测试(不联网) ### 2.1 vitest 单测(dashscope + balance 共 20 个用例) ```powershell cd D:\deepseek-harness pnpm exec vitest run --config plugins/dsh-tool-describe-image/vitest.config.ts ``` **预期**(`pet-engine.spec.ts` 是未提交的 WIP,随机表情 + 时间参数未调稳,可先忽略该 spec): ``` Test Files 2 passed (2) Tests 20 passed (20) ``` 覆盖:`detectMediaType`(PNG/JPEG/WebP/GIF 魔数 + 拒绝未知字节)、`describeImage`(正常请求、question 拼接、非 2xx 抛错、空响应抛错)、`parseBalanceResponse`(标准载荷、缺字段容错、坏项跳过、非法载荷抛错)、`checkDeepSeekBalance`(GET 路径/Bearer 头、尾斜杠归一、非 2xx 抛错、空 key 抛错)、`resolveDeepSeekBaseUrl`。 ### 2.2 纯逻辑冒烟(9 项,Node 直跑) ```powershell pnpm exec node --experimental-strip-types plugins/dsh-tool-describe-image/scripts/smoke.mjs ``` **预期**: ``` detectMediaType: OK describeImage happy path: OK describeImage question: OK describeImage html mode: OK describeImage non-2xx: OK describeImage empty payload: OK parseBalanceResponse: OK checkDeepSeekBalance happy path: OK checkDeepSeekBalance non-2xx: OK ALL SMOKE CHECKS PASSED ``` > 说明:`smoke.mjs` 是 `dashscope.spec.ts` 的单进程镜像,专为受限沙箱/CI 环境设计(vitest 的 worker 池在某些受限环境会被拦)。 --- ## 第 3 层:真实 API(需百炼 key + 一张本地图片) ### 3.1 准备 key ```powershell $env:DASHSCOPE_API_KEY = "sk-你的百炼key" ``` > ⚠️ 不要把这个 key 粘贴到聊天/公开渠道。测试完可 `Remove-Item Env:DASHSCOPE_API_KEY` 清掉。 ### 3.2 运行真实描述 ```powershell cd D:\deepseek-harness pnpm exec node --experimental-strip-types plugins/dsh-tool-describe-image/scripts/test-real.mjs "C:\Users\Administrator\Pictures\test.jpg" "图里有什么?" ``` **预期**:先打印图片信息(字节数、检测到的媒体类型),然后调用百炼,最后打印模型返回的文字描述。 **参数说明**: - 第一个参数:图片路径(必填) - 第二个参数:可选问题 - 环境变量 `DESCRIBE_IMAGE_MODEL`:可选,覆盖默认模型 `qwen-vl-plus` **常见错误**: | 输出 | 原因 | | --- | --- | | `DASHSCOPE_API_KEY is not set` | key 未设置,先执行 3.1 | | `unsupported image bytes` | 图片不是 PNG/JPEG/WebP/GIF(本插件按字节检测,不信任扩展名) | | `DashScope request failed: HTTP 401` | key 无效或未开通模型服务 | | `DashScope request failed: HTTP 400 ...` | 模型名错误、图片过大或参数不合法 | ### 3.3 真实 DeepSeek 余额查询(可选) ```powershell $env:DEEPSEEK_API_KEY = "sk-你的DeepSeek key" cd D:\deepseek-harness pnpm exec node --experimental-strip-types plugins/dsh-tool-describe-image/scripts/test-balance.mjs ``` **预期**:先后打印 `is_available` 与各币种余额(total / granted / topped-up),最后 `BALANCE CHECK PASSED`。 **常见错误**: | 输出 | 原因 | | --- | --- | | `DEEPSEEK_API_KEY is not set` | key 未设置,先设环境变量 | | `DeepSeek balance check failed: HTTP 401` | key 无效 | | `DeepSeek balance check failed: HTTP 402/403` | 账户欠费被停 / 无权限 | --- ## 第 4 层:集成安装(挂进 profile) ### 4.1 安装到 web profile ```powershell cd D:\deepseek-harness dsh plugin --profile web add D:\deepseek-harness\plugins\dsh-tool-describe-image ``` **预期**:`dsh plugin` 检测到包的 `dsh.bundle` 声明,自动把它加入 profile 的 bundles 层;再次运行会提示已存在。 > 未发布到 npm 前用本地路径;发布后改为 `dsh plugin --profile web add dsh-tool-describe-image`。 ### 4.2 确认已挂载 ```powershell dsh --profile web --dump-config | findstr describe ``` **预期**: ``` # == dsh-tool-describe-image - id: describe-image name: dsh-tool-describe-image ``` ### 4.3 确认 key 可见(web profile 会 fail loud) ```powershell $env:DASHSCOPE_API_KEY = "sk-你的百炼key" ``` 或写入 `~/.dsh/profiles/web/.env`(`loadLayeredEnv` 会自动加载): ``` DASHSCOPE_API_KEY=sk-你的百炼key ``` ### 4.4 重启 Web ```powershell dsh web ``` **预期**:正常启动无报错(插件 fail loud,任何问题会在启动日志立即暴露)。浏览器打开 http://127.0.0.1:3080/。 > 关键:**改了 client bundle 后必须重启 `dsh web`**,client-modules 才会 serve 最新版本。 --- ## 第 5 层:Web 端到端(粘贴图片自动识别) ### 5.1 模型路径方式(已有能力验证) 在 Web 会话输入: ``` 描述一下 C:\Users\Administrator\Pictures\test.jpg ``` **预期**:模型自动调用 `describe_image` 工具(聊天里出现工具调用卡片),然后基于返回的文字回答。 ### 5.2 粘贴即识别(本次新增能力)⭐ 1. 任意截图工具截一张图(或复制一张现有图片,`Ctrl+C`) 2. 在 Web 输入框 `Ctrl+V` 3. 观察: **预期流程**: - 输入框不出现图片缩略图(被插件拦截,因为 DeepSeek 不支持 image part) - 约 1–3 秒后,**识别出的文字描述自动填入输入框** - 检查文字无误后回车发送,模型基于描述回答 **判断标准**: | 现象 | 结论 | | --- | --- | | 粘贴后输入框出现文字描述 | ✅ 全链路通 | | 粘贴后无反应,但 5.1 的路径方式正常 | ❌ 浏览器半区未生效(查 5.3) | | 输入框出现图片缩略图(未被拦截) | ❌ paste 监听未抢先(capture 阶段失效) | ### 5.3 排查浏览器半区问题 按 F12 打开开发者工具: 1. **Network 面板**:粘贴后应看到对 `/dsh-describe-image` 的请求。 - 无此请求 → client bundle 未加载。检查: - `dsh web` 是否在改动 client bundle 后重启过; - `lib/client.js` 是否存在(重新跑 1.4); - 浏览器 Sources 面板搜 `describe-image-paste` 是否在已加载脚本里。 2. **Console 面板**:识别失败会打印 `describeFailed` 文案。 3. **检查 boot manifest**:浏览器地址栏访问 `http://127.0.0.1:3080/` 的页面源码,搜 `describe-image`,应看到 `/plugins/dsh-tool-describe-image/client.js?rev=...` 出现在 `__DSH_BOOT__` 注入里。 - 没有 → client-modules 没发现该包:确认包已 `dsh plugin add`(4.1)且重启过。 ### 5.4 常见问题速查 | 问题 | 检查点 | | --- | --- | | 启动 `dsh web` 报错 | 是否缺 `DASHSCOPE_API_KEY`(插件 fail loud) | | 粘贴后没文字 | 重启 Web 了吗?`lib/client.js` 在吗?Network 里有请求吗? | | 输入框出现图片缩略图 | paste capture 监听未生效(React 合成事件先于 document 监听) | | RPC 请求返回 403 | authority 校验——只允许 loopback 浏览器,确认访问的是 127.0.0.1 | | 识别内容不对 | 换 `qwen-vl-max`(patch 里改 `config.model`),或调整 `prompt` | --- ## 完整回归命令(一键跑前 4 层) ```powershell cd D:\deepseek-harness # 静态 + 单测 + 冒烟 pnpm exec tsc --noEmit -p plugins/dsh-tool-describe-image/tsconfig.json && ` pnpm exec tsc -p plugins/dsh-tool-describe-image/tsconfig.build.json && ` pnpm exec tsc -p plugins/dsh-tool-describe-image/tsconfig.client.json && ` pnpm exec tsdown --config plugins/dsh-tool-describe-image/tsdown.config.ts --env.DSH_BUILD_FACE client && ` pnpm exec vitest run --config plugins/dsh-tool-describe-image/vitest.config.ts && ` pnpm exec node --experimental-strip-types plugins/dsh-tool-describe-image/scripts/smoke.mjs # 集成(需已 4.1 安装过) dsh --profile web --dump-config | findstr describe ``` 第 5 层(Web 粘贴)必须人工验收——它验证的是浏览器与 host 的实时交互,无法脚本化替代。