多模态 RAG 实战(二):一张图片,为什么我给它建立了两条向量索引?
上一篇文章聊了一个多模态 RAG 中遇到的问题图片明明被检索到最终回答却可能没有正确展示。这次想把视角往前移聊聊图片是如何被检索到的。在项目中我通过 MinerU 解析 PDF将图片保存到 MinIO。但图片存下来只是第一步更重要的是当用户用自然语言提问时系统应该如何找到对应的图片我最终采用的方案是Caption VLM 双路文本 Embedding再通过 RRF 融合检索结果。这篇文章就结合实际代码聊聊为什么这么做以及后来重新审视这个方案时的一些思考。一、图片怎么才能被自然语言检索先考虑一张操作手册里的 SSH 配置截图。文档作者可能给它写了一个图注图14 SSH 登录配置但用户的问题可能是如何进行 SSH 网络登录MobaXterm 的 Remote host 在哪里填写SSH 登录默认使用什么端口这些问题可能指向同一张图片却有不同的语义侧重点。如果我们采用普通文本 Embedding 模型就无法直接将原始图片作为输入。一种比较自然的做法是先将图片转换为文字描述再使用文本 Embedding让用户问题与图片描述在同一个文本向量空间中进行匹配。于是我首先考虑使用 VLM 来理解图片内容。但实际文档中还有一份现成的语义信息值得利用图片的原始图注Caption。MinerU 解析 PDF 时可以提取部分图片对应的图注。我认为这部分信息很有价值因为它通常是文档作者对图片用途或整体内容的概括。而 VLM 更擅长补充图片中实际可见的信息例如按钮、命令、界面元素和操作内容。于是我选择同时保留两种语义表示。Caption 主要保留文档赋予图片的语义VLM 则主要补充图片自身的视觉语义。二、Caption VLM 双路 Embedding 怎么实现这里需要先说明双路 Embedding 并不是使用两个视觉模型分别编码图片而是构造两段不同的文本分别调用同一个文本 Embedding 模型。1. Caption 路径这一条路径比较简单。在代码中caption_embedding_text()主要提取原始图注和所属章节路径形成类似下面的文本Source caption: SSH 登录配置 Section path: 使用前的准备 / SSH 网络登录之所以附带章节路径是因为有些图片的图注很短单独一个图片名称可能不足以表达它所属的操作场景。而对于没有原始图注的图片如果仍有章节路径这条路径也可能生成索引只是语义信息相对有限。2. VLM 路径另一条路径更复杂。离线阶段会把图片送入 VLM同时提供原始图注、章节信息和辅助上下文要求模型输出结构化描述。其中重点包括summary图片整体内容visible_text图片中可见的文字key_elements关键界面元素user_action图片直接支持的操作visual_evidence可观察到的视觉事实search_keywords辅助检索的关键词例如一张 SSH 配置截图可能被表示为Summary: MobaXterm SSH 会话配置界面 Visible text: Remote host; SSH; 22 Key elements: 主机地址输入框; 端口输入框 User action: 配置 SSH 远程连接 Keywords: SSH; MobaXterm; 远程登录这里是用于说明结构的示例并非实际 VLM 调用日志。值得注意的是VLM 描述并非完全独立于 Caption。生成时提供了图注和上下文因此两条语义表示可能存在信息重合。3. 分别向量化入库生成两种文本之后核心处理逻辑可以简化为texts { caption: caption_embedding_text(image), vlm: vlm_embedding_text(image), } for route, text in texts.items(): if not text: continue vector (await async_embedding_func([text]))[0] # 将向量及 route、asset_id 等信息写入数据库在项目中向量存储使用 PostgreSQL pgvector图片原文件则通过 MinIO 管理。图片向量表采用的联合主键是(document_id, asset_id, route)因此同一张图片可以对应不同的索引记录但仍然通过相同的asset_id关联到原始图片资产。三、在线阶段两条检索结果如何融合离线索引建好后在线阶段需要解决两个问题第一同一张图片可能同时被两条路径召回。第二两条路径的检索分数不一定适合直接相加。我的处理方式是分路检索、按 Asset ID 合并再使用 RRF 排名融合。1. 两路独立检索用户 Query 首先经过文本 Embedding得到查询向量。随后分别在 Caption 和 VLM 索引中进行余弦相似度检索各自返回 Top-K 候选。在源码的image_route_rows()中就是分别查询route captionroute vlm并记录图片在各自路径中的排名。如果同一个 Asset ID 在两路中都有命中就把两条排名信息合并到同一张图片上。2. 使用 RRF 融合排名RRF 全称 Reciprocal Rank Fusion即倒数排名融合。它主要依据各路的排序位置而不是直接相加原始相似度。项目里的核心逻辑是image_ranks row.get(_image_route_ranks) or {} if image_ranks: score sum( 1 / (RRF_K rank) for rank in image_ranks.values() )其中RRF_K 60。举个简单的例子图片Caption 排名VLM 排名RRF 得分A1未命中0.0164B10100.0286C230.0320注表格是根据公式构造的示例并非实际测试结果。可以发现图片 B 虽然没有在任何一条路径中排第一但因为两路都有命中最终得分仍然高于只在 Caption 中排名第一的图片 A。这也体现了 RRF 的一个特点它会让多个通道共同命中的候选获得累计排名贡献。当初选择这套方案主要是因为我认为 Caption 和 VLM 都具有检索价值不希望提前给某一路设置更高权重。不过RRF 只是图片候选的融合环节。实际系统还会根据问题意图将图片候选与文本、表格候选进行配额组装并不会直接把图片 RRF 排名当成最终图文回答的展示顺序。四、回过头看双路索引一定比单路好吗现在重新审视这个设计我觉得需要考虑两个问题。1. 为什么不直接拼接 Caption 和 VLM 描述如果把两段文本拼在一起再生成一个向量整个流程确实会更简单。但当初我担心的是不同语义信息混在一起后可能难以突出各自的检索重点。例如Caption 偏向图片用途VLM 描述偏向具体界面元素。将它们合并到一个向量中可能使某些细粒度信息的匹配能力受到影响。不过这并不意味着拼接一定更差。对于一张语义比较集中的图片拼接后的文本可能同样连贯而且一个向量就能覆盖大部分查询。双路索引更明确的价值是保留了两种语义表示的独立检索入口而不是已经证明它具有更高准确率。2. 通用性和检索质量如何平衡实际知识库可能接入不同类型的文档。技术手册中的图片可能只有简短图注学术论文的图注可能非常详细PPT 里的示意图又可能完全没有图注。很难提前判断 Caption 和 VLM 哪一路始终更有价值。因此我当初更倾向于采用统一的双路处理方式尽可能兼顾不同文档的特点。但它也有成本和局限两路 Embedding 意味着额外的向量存储与检索开销VLM 描述也可能遗漏细节或产生错误。另外当前代码中即使缺少有效视觉描述只要存在章节路径VLM 路径也可能构建出一条信息量较低的索引。如果两条路径包含高度重复的章节信息RRF 的共同命中奖励也不一定代表真正获得了互补证据。所以通用性不能简单理解为所有图片都无条件采用两路索引。后续更值得探索的是如何结合图注完整度、视觉描述质量和文档类型判断哪一路值得参与检索。当然也不只有文本化图片这一种方案。视觉 Embedding、OCR 文本提取以及先检索文本再关联图片都是值得比较的路线。只是这些方案我目前还没有做过完整的对照实验。最后回顾这套 Caption VLM 双路索引设计我最大的感受是多模态 RAG 中图片能否被准确检索不只是 Embedding 模型的问题更是图片语义如何构建与组织的问题。Caption 提供原始文档中的语义线索VLM 补充视觉内容两路分别建立索引再通过 RRF 融合排序是我当时为了兼顾不同文档而做出的工程选择。但它是否真的优于 Caption Only、VLM Only或者直接拼接文本后生成单向量还需要在相同查询集上进行验证。后续我也想围绕这些方案做一次更具体的检索对照。毕竟能解释一个方案为什么这样设计很重要能通过实验知道它在什么情况下有效同样重要。

相关新闻

研究中合成数据使用闸门:失败含义与当天最小实验

研究中合成数据使用闸门:失败含义与当天最小实验

图:披露、种子复现、来源标记、漂移声明与结论边界五闸门,加合规打包。 千笔-AIWritePaper https://www.aiwritepaper.com 合成与 AI 生成数据能补样本、护隐私、做情景,但也会冒充实测、污染下一代训练集。欧盟出版署 data.europa academy …

2026/10/12 5:42:21 阅读更多 →
写时复制、读写分离:一文吃透CopyOnWriteArrayList原理与实战

写时复制、读写分离:一文吃透CopyOnWriteArrayList原理与实战

如果你在并发环境里用过ArrayList,大概率被ConcurrentModificationException教育过:一边遍历一边改数据,轻则抛异常,重则读到脏数据。很多人第一反应是加锁,加synchronized关键字或者直接套一层Collections.synchroniz…

2026/10/12 5:42:21 阅读更多 →
从ReAct到多Agent协作:AI Agent工程化落地关键实践

从ReAct到多Agent协作:AI Agent工程化落地关键实践

1. 从一套“能干活”的提示词说起这几年 AI Agent 的概念红得发紫,但很多团队拿着大模型 API 折腾了个把月,最后发现所谓的 Agent 就是个“会聊天的脚本”。真正把 Agent 推向生产环境,绕不开两个关键词——ReAct和多 Agent 协作。我参与过几…

2026/10/12 5:42:21 阅读更多 →

最新新闻

短线交易生存指南:模式内交易、仓位管理与止损铁律

短线交易生存指南:模式内交易、仓位管理与止损铁律

我不确定各位做短线交易多久了,但如果你在交易社区里泡过一阵,应该会发现一个特别直观的现象:晒收益截图的人换了一茬又一茬,今天还在涨停板上来回横跳的那位,第二年基本就没了声音。短线交易之所以是淘汰率最高的领域…

2026/10/12 6:25:44 阅读更多 →
Mongoose入门教程:用TaoToken统一Key打通Node.js与MongoDB开发链路

Mongoose入门教程:用TaoToken统一Key打通Node.js与MongoDB开发链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 6:25:44 阅读更多 →
傅里叶算子结合SVM的手势识别源码详解与调参实战

傅里叶算子结合SVM的手势识别源码详解与调参实战

简介:面向手势识别与计算机视觉学习场景,这份完整源代码基于Python实现,并附带已构建好的样本库,适合机器学习初学者、课程设计或毕业设计者借鉴。代码运行于Win10 Python3.7环境,完整覆盖图像平滑、OTSU阈值肤色分割…

2026/10/12 6:25:44 阅读更多 →
CAMStoWRF完全指南:从CAMS数据下载到WRF-Chem初边界场配置

CAMStoWRF完全指南:从CAMS数据下载到WRF-Chem初边界场配置

做空气质量模拟的人应该都干过这件事:把全球化学模式的输出结果塞进WRF-Chem里当初始场和边界场。早些年大家满世界找MOZART的nc文件,后来慢慢有人开始用CAMS(哥白尼大气监测服务)的再分析数据。CAMS数据覆盖面广、化学物种相对齐…

2026/10/12 6:25:44 阅读更多 →
多角色管理与押金自动退:一站式租赁商城小程序源码系统解析

多角色管理与押金自动退:一站式租赁商城小程序源码系统解析

做租赁类小程序这几年,我见过太多项目死在同一个坑里:商品、支付都接好了,结果押金体系没设计好,客人退押金要催、商家扣款要吵、平台两边受气。今天聊的这套“多角色管理、押金自动退的一站式线上租赁商城小程序源码系统”&#…

2026/10/12 6:25:44 阅读更多 →
开源+私有化:打造能主动干活的企业AI工作伙伴

开源+私有化:打造能主动干活的企业AI工作伙伴

1. 从"只会聊天"到"能干活":企业AI落地的真实断层在哪过去两年,我参与过好几个企业内部的AI助手项目,几乎每一个都经历过同样的尴尬:上线第一周大家图新鲜,问天气、写周报、翻译邮件,用…

2026/10/12 6:24:44 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →