多模态内容怎么入库
知识库里不只有文字。产品图、参数表、说明视频、带截图的教程——这些才是企业文档的大头,而它们的处理方式完全不同。
一条贯穿全文的原则:能被检索的必须是文本或向量,原件只用于展示与引用。
让一张图、一页 PDF 变得可检索,现在有两条路,不是只有"转成文字"这一条:
描述路径 —— 用 OCR 或视觉模型把图片转成一段文本,把这段文本嵌入后入库。通用、便宜、结果人能读懂、检索链路完全复用现有那套;代价是转写必然丢信息,版面、位置、图形关系全没了。
视觉检索路径 —— 用 ColPali / ColQwen 这类模型把整页图像切成 patch,每个 patch 编一个向量,检索时算 MaxSim(late-interaction,见《重排修不了召回》)。页面图像直接进索引,表格、多栏、公式都原样保留;代价是存储和算力都重得多——一条文档从 1 个向量变成上百个。
两条路的选型可以按内容分:扫描件和复杂版式的 PDF 走视觉检索(要检索的正是版式里承载的结构信息,转文本必然丢);产品图、照片、示意图走描述路径(用户要检索的是"这是什么",不是版面)。
1. 图片:两条进索引的路
1.1 路径一:转成文本再入库
OCR / 版面解析:提取图片里的可见文字。适合截图、扫描件、带文字的图表。这条路便宜、快、结果可核查。
视觉模型生成描述:让多模态模型看图写一段描述。适合照片、示意图、流程图这类没有文字或文字不足以表达内容的图。
两种办法可以叠加:OCR 拿文字,视觉模型补语义,合成一段描述文本。
1.2 路径二:视觉检索,页面图像直接进索引
ColPali / ColQwen 这类模型做的是:把一页文档渲染成图像,切成固定数量的 patch(通常上千个),每个 patch 编一个向量;查询时把 query 也编成一组 token 向量,对每个 query token 找最匹配的 patch 再加总(MaxSim)。
好处是不需要解析。表格不用重建、多栏不用还原阅读顺序、公式不用 OCR——模型看到的就是人看到的那一页。对扫描件和版式复杂的 PDF,这条路能省掉最麻烦的一段解析工作。
代价有三条,都要先算清楚:
- 存储。 一页从 1 个向量变成几百上千个,索引体积高一个数量级;通常需要配合多向量压缩或量化。
- 算力。 入库要过一遍视觉模型,比 OCR 贵;查询时的 MaxSim 计算量也远大于单次点积。
- 生态。 支持多向量 late-interaction 的库比支持单向量的少,pgvector 这类单向量方案接不住(维度与存储的限制见《HNSW 还是 IVFFlat》)。
1.3 描述要写成"可被检索"的样子
这一步最容易被写成"生成一段自然语言描述",然后检索效果很差。
区别在于:检索时用户问的是具体问题("XX 型号的电池容量是多少"),而模型生成的描述往往是泛泛的介绍("这是一张产品图,展示了一个电子设备")。
所以描述提示词要明确:
- 包含实体信息:型号、编号、数值、单位
- 包含可见文字的原文,尤其错误信息、界面文案
- 避免"这张图展示了…"这类无检索价值的套话
- 说明"什么时候会有人需要这张图"
最后一条最有用。它让描述从"图里有什么"变成"什么查询应该命中它"——和《Agent Skills 三层加载》里 description 的写法要求是同一个道理。
1.4 原图单独存
切片里存的是描述文本,索引也是它。原图存在对象存储,通过 URL 引用。
好处是索引体积小、检索快;引用时能把原图和描述一起给用户看。用户看到的应该是原图,而不是那段描述文字。
2. 表格:别压成一段话
表格是最容易处理坏的一类。常见错误是把整张表拍平成一长串文本,然后按字符切分——结果一行被切到两个切片里,两行之后就不知道哪列是哪列了。
2.1 保留结构
三种可选粒度:
整表一个切片:适合小表(几十行以内)。检索命中就给全表,上下文完整。
按行切片,每行带表头:适合大表。每一行是独立切片,但都携带列名,这样单独召回一行也能读懂。
按行组切片:几行一组,平衡上下文与粒度。
选择依据是表的用途:用户是要"查某个值"(按行),还是要"看整体趋势"(整表)。
2.2 为什么表头必须跟着走
一个独立的行切片如果只有 | 42 | 3.7 | 2024-03 | 这串数字,模型和用户都读不懂。带上表头变成"型号:X42;容量:3.7;日期:2024-03",才可被理解、可被检索。
这一点和《切片决定检索上限》里说的"切片要能自解释"是同一条规则,只是表格场景更容易违反。
2.3 转成 Markdown 还是保留结构
取决于下游。如果消费方是 LLM,Markdown 表格通常够用且省 token;如果需要程序化读取(比如提取某个字段做计算),保留结构化数据(JSON/CSV)更好,用另一条路径访问。
两者都想要时,可以存两份:文本版进索引,结构化版供程序读取。
3. 音视频:先转写,再按时间切
3.1 转写是前置步骤
音频和视频里没有可直接检索的文本,必须先 ASR 转写。转写质量直接决定检索质量——转错了,之后全错。
几个实践要点:
- 转写要带时间戳,否则无法回溯
- 说话人分离(diarization)在会议、访谈场景很有用,能让"谁说的"进入元数据
- 标点要恢复。没有标点的长文本切片效果很差
3.2 按时间切而不是按字符切
转写文本按说话的自然段落切,每段带起止时间。这样引用可以跳回具体时间点——用户点引用能直接跳到视频的第 12 分 30 秒,这才是有用的引用。
按固定字数切会切在句子中间,也会打散时间边界。
3.3 视觉信息别忘了
视频里的关键信息经常在画面上(PPT、演示、界面操作),不在语音里。纯 ASR 会漏掉这部分。
如果内容重要,需要抽帧 + 视觉模型描述,和转写文本一起入库,并标注时间点。
4. 混合文档:解析是第一步
PDF、Word、网页这类容器里同时有文字、表格、图片。处理顺序是:
解析(保留结构)→ 按结构分块 → 各块按自己的类型处理 → 统一入库
解析这一步的工具选择很关键,而且要看许可(见《切片决定检索上限》里的对比):Docling(MIT)是可商用的;MinerU 用的是基于 Apache-2.0 的自定义许可,带规模与署名门槛;有的工具是 GPL 或权重限商用,商用前要确认。
解析质量决定了后面所有步骤的上限——表格被解析成乱文本,后面再怎么切片都没救。
5. 元数据要更丰富
多模态内容的元数据比纯文本更重要,因为用户需要判断"这个结果是不是我要的那个"。建议至少:
| 字段 | 用途 |
|---|---|
modality | 文本 / 图片 / 表格 / 音视频,用于结果展示形态 |
source_url | 原件位置 |
page / timestamp | 页码或时间点,用于精确跳转 |
parent_id | 属于哪个文档或哪个表 |
caption | 图片描述或表格标题 |
lang | 语种(见《跨语言检索的三条路》) |
其中 parent_id 很实用:召回一个表格行时,能通过它找到整张表;召回一帧时,能找到整个视频。
6. 引用怎么展示
多模态内容的引用比纯文本更需要设计:
- 图片:缩略图 + 描述 + 点击看原图。不要只给一段文字描述——用户要确认的是图本身
- 表格:展示相关行 + 表名,必要时展开整表
- 音视频:时间戳 + 该时间点的转写文本 + 跳转播放
共同点是:引用要能验证。用户点了引用之后应该能立刻判断"这个来源确实说了这件事"。做不到这点的引用等于没引用(见《答案要能追溯到出处》)。
7. 一条容易忽略的风险
图片描述是模型生成的,而生成的内容可能包含原文里没有的信息——视觉模型会"补全"它认为合理的细节。
如果这个描述被当成事实检索出来并展示给用户,就变成了幻觉的一个新来源。
缓解办法:
- 描述里明确区分"图中可见"与"推断"
- 引用展示时优先展示原图,让用户自己看
- 关键数值(价格、规格、日期)优先取 OCR 结果,而不是模型描述
第三条最实用:OCR 拿到的数字是可核查的,模型生成的数字不是。
8. 清单
- 每种模态有对应的转文本路径(OCR / 视觉描述 / 转写)
- 扫描件与复杂版式 PDF 评估过视觉检索路径(存储与算力算过账)
- 图片描述写成"可被检索"的形式,含实体与数值
- 表格按行切且每行带表头,或整表成片
- 音视频带时间戳,引用可跳转到时间点
- 原文与描述分开存,引用指向原件
- 元数据含 modality / 来源 / 位置 / 父文档
- 关键数值优先用 OCR 结果
- 解析工具的许可确认过
最后补一句:先做文字,再加多模态。 纯文本链路没跑通之前加图片和音视频,会让排查变得极其困难——你分不清问题是检索坏了还是解析坏了。