# llm-deepseek 适配器的 OCR 缝(接口契约) 本插件自身不产生任何效果:它依赖 `dsh-llm-deepseek` 适配器里的一个**扩展点(缝)**。 如果你的部署还没有这个缝,插件装上也不会被调用。本文描述缝的**接口契约**, 供你在自己的部署上对照实现(这是对 DeepSeek 官方适配器的本地小改动,不随本仓库分发补丁)。 ## 背景 DeepSeek API 当前不接收图片输入。适配器在把会话消息序列化为 API 请求时, 默认会把图片块编码成 `image_url` 直传。本缝的作用是:**当会话里存在 `ocr` 服务时, 先把图片本地 OCR 成文本,再以文本块发给模型**——这样文本模型就"看见"了图片。 ## 契约 ### 服务 - 服务名:`ocr`(`ctx.provide('ocr', { ocrImage, ocrDeep })`,均可选) - 回调签名:`(ref, signal?) => Promise` - `ref` 为附件对象:`{ attachmentId, name, mediaType }` - 返回该图片的 OCR 文本;空字符串表示识别失败/无文字 - **解析时机**:每次请求时 `ctx.get('ocr')`,不要在 apply() 时缓存。 这样插件装上即接管、停用即回退,无需重启。 ### 序列化行为(在 serializeMessages / wireContent / ocrOrImagePart 一带实现) 1. 消息内容顶层无图片块时,行为不变(纯文本拼接)。 2. 有图片块时,逐块转换: - 文本块 → `{ type: "text", text }` - 图片块 → 见下 3. 图片块转换逻辑: - 若"深度模式"且 `ocrDeep` 可用:`text = await ocrDeep(ref)`; 非空 → `{ type: "text", text: "[深度OCR 附件 ]\n" + text }` - 否则若 `ocrImage` 不可用:回退 `image_url` 直传(`data:;base64,...`) - 否则:`text = await ocrImage(ref)`; 非空 → `{ type: "text", text: "[图片OCR 附件 ]\n" + text }`; 空 → 内置的 fallback 标记文本(如 `[图片OCR 失败 ]`) 4. **深度模式判定**:`useDeep = 有 ocrDeep && 用户消息纯文本包含 "深度识图"`。 (会话标题、压缩等内部用途建议强制跳过深度 OCR,避免每次深度推理耗时过长。) 5. 工具结果(`tool-result`)里出现的图片块走同一套逻辑; 没有任何 ocr 服务时保持纯文本断言(图片工具结果不允许混入请求)。 ### 配置接线(apply() 中) - 每请求解析:`const external = ctx.get('ocr')` - `ocrImage = external?.ocrImage ?? 内置 createOcrImage`(内置实现可保留原直传逻辑) - `ocrDeep = external?.ocrDeep ?? 内置 createOcrDeepImage` ## 验证方法 1. 装上本插件(install.sh)后,向会话发一张图片: - 抓请求体应看到 `[图片OCR 附件 ...]` 文本块,而不是 `image_url`。 2. 消息文本里带上 `[深度识图]` 再发同一张图: - 应看到 `[深度OCR 附件 ...]` 标记(深度通道耗时会明显高于快速通道)。 3. 停用 `ocr-provider` 组合行后重发: - 请求体应恢复为 `image_url` 直传(回退内置实现)。