鸿蒙图片检索最佳实践
更新时间: 2026/09/04 17:59:48
本文主要介绍鸿蒙 AI 能力在 OCR 与视觉语义检索中的应用。
概述
云信 IM HarmonyOS SDK 自 V10.11.0 起,接入鸿蒙 AI 能力用于本地消息搜索。
鸿蒙图片搜索的价值,不在于增加一个图片筛选入口,而在于把系统 AI 能力转化为可组合、可解释、可恢复的消息检索能力。系统 OCR 负责理解图片中的文字,HarmonyOS Core Vision 负责理解图片表达的画面语义,统一的消息搜索入口再把 AI 命中结果映射回原始会话消息。业务不需要自建图片语义服务,也不需要维护独立的图片搜索数据库。
因此,用户可以在同一个会话搜索体验中完成三件事:
- 按消息文本搜索
- 按图片内文字搜索
- 按画面内容搜索
本文围绕这项鸿蒙特性,说明三种来源如何协同、需要哪些 HAR 和 Service 配置,以及接入后页面和搜索结果应呈现的效果。
产品形态
在会话图片页中,用户可以浏览消息、输入关键词并选择 文本、图片文字 或 画面内容;结果页回到原始会话消息,并标出每张图片的命中来源。

上图中的三个状态分别对应:
| 状态 | 说明 |
|---|---|
| 浏览态 | 默认展示会话图片库存,保留原有预览和消息操作 |
| 输入态 | 用户输入关键词,并可组合 图片文字 和 画面内容 两个 AI 来源 |
| 结果态 | 结果直接映射回会话图片消息,同时展示命中来源;同一张图片被多个来源命中时只展示一次 |
把这个页面效果映射到三个搜索来源,就很直观:
| 页面操作 | 用户意图 | 实际能力 | 典型结果 |
|---|---|---|---|
| 选择 文本 | 找包含某个词的消息 | 本地文本检索 | 消息正文或标题命中 |
| 选择 图片文字 | 找图片里出现的字 | 鸿蒙系统 OCR | 白板、截图、文档照片命中 |
| 选择 画面内容 | 找图片表达的场景 | 鸿蒙 Core Vision 视觉语义 | 冲浪、会议现场、咖啡等画面命中 |
例如,搜索 会议白板 时,OCR 命中的是图片中确实出现的文字;搜索 海边冲浪 时,VISUAL 命中的是海浪和冲浪板等画面语义;搜索 项目排期 时,普通文本检索继续查找消息正文。三种结果可以在同一个页面中呈现,但命中原因必须分别标识。
搜索类型
消息文本:搜索已有内容
普通文本搜索面向消息正文、标题等已有文本字段,复用原有的本地全文检索能力。它适合搜索 项目排期、周报 这类消息文本,也可以继续使用原有的会话范围、时间范围和分页语义。
图片文字:搜索 OCR 内容
OCR 回答的是 这张图片里有没有这几个字。例如,用户搜索 7319,命中的是一张包含该编号的截图;搜索 会议纪要,命中的是拍摄的白板照片。
在鸿蒙上,这条链路使用系统提供的文字识别能力。图片下载到本地后,SDK 异步识别图片,保存识别出的文本层级和布局元数据,再对归一化后的文字建立本地索引。查询阶段只查索引,不在每次搜索时重新扫描全部图片。
画面内容:搜索视觉语义
视觉语义搜索回答的是 这张图片表达了什么。例如,搜索 海边冲浪 可以召回画面中有海浪和冲浪板的图片;搜索 办公室白板 可以召回会议现场照片。
这条链路使用鸿蒙 Core Vision 的图像语义搜索能力,由系统负责图片特征和模型实现。SDK 只负责把图片纳入系统索引、发起查询并把结果映射回消息,不依赖或暴露系统内部的特征存储结构。
OCR 和视觉都接收文字 Query,但语义不同——OCR 是字面匹配,VISUAL 是跨模态语义召回。不能 用视觉搜索替代 OCR,也 不能 把 OCR 命中伪装成视觉相似度。
三合一统一入口
业务侧不需要分别调用文本搜索 API、OCR API 和视觉搜索 API。统一入口是 searchLocalMessages,来源通过位标志精确表达:
| 来源 | 位值 | 含义 | 鸿蒙能力依赖 |
|---|---|---|---|
TEXT_RECOGNITION |
1 |
普通消息文本 | SDK 本地文本索引 |
OCR |
2 |
图片内文字 | 系统文字识别能力 |
VISUAL |
4 |
图片画面语义 | API 26 + 可选视觉 HAR + Core Vision |
三个位可以任意组合,共支持七种非空组合。缺省 searchTypes 时保持原行为,只执行普通文本搜索;显式传入 0 或未知位则直接返回参数错误,避免调用方误以为 没有来源 代表 搜索全部。
flowchart LR
A[业务搜索框] --> B[searchLocalMessages]
B --> C[普通文本索引]
B --> D[OCR 本地索引]
B --> E[Core Vision 视觉索引]
C --> F[按 messageClientId 去重]
D --> F
E --> F
F --> G[统一消息结果 + 实际命中来源 + 视觉相关性]
统一入口的价值不只是 API 数量更少,更重要的是把 请求来源 和 实际命中来源 分开表达。调用方传入 searchTypes 表示希望执行哪些来源;每条结果通过 matchTypes 表示它实际被哪些来源命中。同一张图片同时被 OCR 和视觉命中时,只返回一条消息,并把两个位合并到 matchTypes 中。
搜索能力配置
只在页面里调用 searchLocalMessages,并不代表图片搜索已经生效。HarmonyOS NIM SDK 的业务能力采用 业务 HAR + 服务注册 + 服务配置 的装配方式,三个环节缺一不可。
引入对应 HAR
普通文本和 OCR 至少需要引入核心搜索 HAR:
json5{
"dependencies": {
"@nimsdk/nim": "10.11.0",
"@nimsdk/base": "10.11.0",
"@nimsdk/message": "10.11.0",
"@nimsdk/search": "10.11.0"
}
}
如需启用画面语义搜索,在 API 26 宿主中增加可选视觉 HAR,并保持所有 NIM HAR 使用同一个版本号:
json5{
"dependencies": {
"@nimsdk/search": "10.11.0",
"@nimsdk/aivision": "10.11.0"
}
}
- 本文使用的
10.11.0是图片搜索能力开始支持的 SDK 版本。接入时应将 NIM 相关 HAR 统一到10.11.0,避免核心 SDK、搜索服务和视觉 HAR 版本不一致。 @nimsdk/aivision的构建目标是 API 26。
注册搜索服务与 AIVision Service
业务 HAR 引入后,必须在创建 NIM 实例前注册真实实现。AIVisionServiceImpl 按普通业务 Service 注册。
tsimport {
NIMServiceOptions,
V2NIMProvidedServiceType
} from '@nimsdk/base'
import { NIMSdk } from '@nimsdk/nim'
import { V2NIMSearchServiceImpl } from '@nimsdk/search'
import { AIVisionServiceImpl } from '@nimsdk/aivision'
NIMSdk.registerCustomServices(
V2NIMProvidedServiceType.V2NIM_PROVIDED_SERVICE_SEARCH,
(core, serviceName, serviceConfig) =>
new V2NIMSearchServiceImpl(core, serviceName, serviceConfig)
)
NIMSdk.registerCustomServices(
V2NIMProvidedServiceType.V2NIM_PROVIDED_SERVICE_AIVISION,
(core, serviceName, serviceConfig) =>
new AIVisionServiceImpl(core, serviceName, serviceConfig)
)
const serviceOptions: NIMServiceOptions = {
searchServiceConfig: {
searchAccountIdEnabled: true
},
messageServiceConfig: {
imageTextRecognitionSearchEnabled: true,
imageVisualSemanticSearchEnabled: true
}
}
const nim = NIMSdk.newInstance(context, initializeOptions, serviceOptions)
注册顺序很重要:先注册搜索服务和 AIVision Service,再调用 NIMSdk.newInstance。
打开 OCR 与视觉开关
两个开关对应两个独立 Runtime:
| 开关 | 说明 |
|---|---|
imageTextRecognitionSearchEnabled: true |
允许 SDK 建立和查询 OCR 索引 |
imageVisualSemanticSearchEnabled: true |
允许 SDK 使用视觉语义搜索能力 |
关闭其中一个开关只关闭对应来源,不会影响普通文本或另一个来源。推荐在产品配置中显式写出两个开关,而不是依赖默认值,这样线上排查时可以直接确认 是未开启、未装配,还是系统能力不可用。
完成上述三步后,普通文本和 OCR 搜索即可由核心搜索服务处理。要启用画面语义搜索,还需要在 API 26 宿主中引入并注册 AIVision 业务服务,同时打开视觉语义搜索开关。
建立索引
图片搜索依赖图片先完成本地化,但不需要等待全部图片完成索引。附件落盘后,SDK 会在后台异步建立 OCR 和视觉索引;查询只读取已经完成的对应索引,首次结果可能随索引进度逐步补充。
推荐的生命周期是:
- 图片附件完成下载并落盘,得到可访问的本地绝对路径。
- 消息生命周期通知 SDK 图片路径已就绪。
- OCR 和视觉分别提交自己的索引任务。
- 任务状态持久化,支持重试、对账和恢复。
- 用户发起搜索时,读取已经完成的对应索引,后台任务继续处理剩余图片。
如果业务使用自定义下载器,不能只修改内存中的 attachment.path。文件真正落盘后,应调用 SDK 提供的 本地附件路径已就绪 更新入口,让消息生命周期继续触发索引。这样可以避免应用重启后索引丢失,也能保证重复通知不会生成重复任务。
OCR 与视觉共享图片到消息的绑定关系,但索引状态、任务代次和失败恢复彼此独立。视觉模型需要重建时,不应清理 OCR 数据;OCR 初始化失败时,也不应阻塞普通文本和视觉搜索。
构造搜索参数
当请求包含 OCR 或 VISUAL 时,查询必须限定在一个会话内,并传入 1 至 5 个非空关键词:
tsconst result = await messageService.searchLocalMessages({
conversationId,
keywordList: ['会议', '白板'],
keywordMatchType: V2NIMSearchKeywordMathType.V2NIM_SEARCH_KEYWORD_MATH_TYPE_OR,
messageTypes: [V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE],
searchTypes: V2NIMMessageSearchType.OCR |
V2NIMMessageSearchType.VISUAL,
limit: 50
})
重要约束:
| 约束 | 说明 |
|---|---|
keywordList |
必须包含 1 至 5 个 trim 后的非空关键词 |
keywordMatchType |
支持 OR 和 AND,用于控制多关键词关系 |
limit |
范围 1...100 |
messageTypes |
显式传入时必须包含图片消息类型 |
| 不支持过滤 | OCR/视觉请求不支持仅适用于普通 FTS 的发送者、方向、子类型和非零时间范围过滤 |
分页约束:
- 分页时必须原样传递上一次结果返回的
nextPageToken。 - 修改关键词、来源或会话后必须从首页重新搜索。
- 不要先截取视觉搜索结果,再在业务层做二次过滤,这会造成漏召回。
普通文本请求则继续保留原有多关键词、时间范围、分页和搜索策略语义。两类请求的参数规则不同,业务层最好根据 searchTypes 做一次明确的请求构造,而不是复用一个 万能参数对象。
搜索结果
图片搜索的结果不应该只有缩略图和标题。至少要展示两类信息:
- 命中来源:文本、图片文字、画面内容,可同时展示多个标签。
- 视觉相关性:只有真实视觉命中时才展示
visualSimilarity,不能用0代表 没有视觉命中。
当所有请求来源都不可用时,搜索调用会以错误结束,错误详情用于说明具体原因;单个来源不可用时,其他可用来源仍可返回真实结果。
搜索效果
完成 HAR、注册和配置后,业务页面仍然只需要调用统一的 searchLocalMessages。变化发生在 SDK 内部和结果模型中,而不是业务层增加一套新的图片数据库或额外搜索调用。
以会话 项目讨论 为例:
| 用户输入 | 选择来源 | 典型命中 | 页面应展示 |
|---|---|---|---|
会议白板 |
图片文字(OCR) | 白板照片中识别出的文字 | 图片缩略图 + 图片文字 标签 |
海边冲浪 |
画面内容(VISUAL) | 海浪、冲浪板等画面语义 | 图片缩略图 + 画面内容 标签 + 视觉相关性 |
会议白板 |
图片文字 + 画面内容 | 同一张图片被两个来源共同命中 | 只展示一张图片 + 两个命中标签 |
项目排期 |
普通文本 | 消息正文或标题 | 原有文本搜索结果 |
实际使用时,图片不会在用户点击搜索按钮的瞬间才开始处理。附件落盘后,SDK 会在后台建立派生索引;组合查询中某一个来源不可用时,其他可用来源仍可返回结果。
这也是 接入成功 和 用户体验生效 的区别:
- 接入成功:HAR 已引入、服务已注册、开关已打开,NIM 实例能够识别并调度对应来源。
- 体验生效:图片已经有本地路径、索引任务已经完成,查询结果包含真实的
matchTypes,页面能解释命中来源。




