RAG 处理图片的正确姿势:索引时描述,而不是查询时看图

做 RAG 的人大多有个盲区:眼睛只盯着文字。 你花了两周调 chunk size,换了三种 embedding 模型,reranker 也加上了——《Rerank 在 RAG 中的角色:Bi-Encoder vs Cross-Encoder》里讲的那些优化你全做了。但回头一看,技术文档里的截图、架构图、参数表格,这些图片承载的信息,你的系统压根没碰。 这不是小问题。kapa.ai 的团队最近在 Hacker News 上分享了他们的生产实践(89 points),标题直白:How we index images for RAG。他们服务 200+ 客户,知识库里有数百万张图片,每天处理百万级查询。这篇文章把他们的方案拆开来看,顺便聊聊 ColPali 等另一条路线,以及你到底该选哪条。 图片在技术文档里到底干了什么 kapa.ai 把文档图片分成两类,这个分类非常实用。 说明型(illustrative):文字说"点击设置图标",旁边的截图标出了图标在哪、长什么样。图片让答案更直观,但信息本身在文字里也有——用户不看图也能做事,只是要多找一会儿。 承重型(load-bearing):接线图、参数规格表、认证矩阵、颜色可选表——这些图里的数值只存在于图片中。你不看图,就根本不知道答案。 他们跑了上千条真实客户问题验证:有图片上下文时,LLM 评委(McNemar 检验,p < 0.05)显著偏好带图片的答案。效果不是微调能弥补的那种——是用户能感知到的质变。 但问题来了:怎么把这些图片喂给 LLM? 查询时看图:一个看起来对、实际上贵到用不起的方案 直觉反应是:检索到相关 chunk 后,把它们引用的图片一起丢给多模态模型(GPT-5.1、Claude 4.6 Sonnet 等)。kapa.ai 测试了这个方案,发现三个结构性问题: 1. 贵得离谱。 原始图片让 GPT 单次查询成本增加 27%,Claude 增加 51%(Claude 把一张图编码成约 975 tokens,GPT 约 716)。kapa.ai 每天百万级查询,这笔钱根本花不起。 2. 塞不进去。 一个典型问题检索 10-30 个 chunk,平均引用 20-30 张图片,长尾能到 130 张。Claude 的 payload 上限 30 MB,OpenAI 的 50 MB——超过 20 张图就撞墙了。 ...

June 3, 2026 · 2 min · Hypho

向量数据库已经很快了,为什么还要重排?RAG 系统中 Bi-Encoder 与 Cross-Encoder 的工程对决

一个让工程师失眠的 Bad Case 2025 年中,某金融科技公司在内部知识库问答系统中引入 RAG(检索增强生成)。系统上线后,用户反馈普遍不错——直到某天,一个风控团队的用户问:“我们有哪些客户曾经有过信用违约记录?” RAG 系统检索返回了三条"高相关"文档,AI 基于这些文档给出了自信满满的答案。风控经理看完后直接冷汗:系统返回的内容全是关于"信用良好客户"的正面案例,和用户的查询意图完全相反。 事后排查发现,问题出在向量检索阶段。用户的查询"信用违约记录"和文档中大量出现"信用"“违约"字眼的高频正面记录产生了极高的余弦相似度,而真正描述违约事件的文档因为语言表达更隐晦,反而相似度偏低,被 Top-N 过滤掉了。 这是一个典型的向量检索只看词不看语义关系的失败案例。1 向量检索的物理课:两个不相识的学生在各自考试 理解为什么向量检索会"看走眼”,需要先理解它的工作原理。 向量检索的核心是Bi-Encoder(双编码器)架构。当你把文档存入向量数据库时,每个文档都会通过一个 Encoder 被压缩成一个固定长度的向量——通常 768 维或 1024 维。这个过程发生在入库时,与用户未来的查询完全无关。 当用户发起查询时,查询文本同样通过 Encoder 生成一个查询向量。然后,数据库在高维空间中做最近邻搜索(通常用余弦相似度或内积),找出与查询向量"距离最近"的 N 个文档。 这个过程有一个非常关键的特征:Query 和 Document 的编码是独立完成的,它们从未"见面"。 斯坦福大学 NLP 组有一个很直观的比喻:就像两个学生分别在不同的考场同时参加考试,学生 A(Query 编码器)看了一眼题目后把答案写成压缩笔记,学生 B(Document 编码器)提前把教科书内容写成压缩笔记。考试结束后,系统只是比较这两份笔记的"形状"有多像,而不知道题目问的是什么。 这解释了为什么"信用违约记录"的查询会匹配到"信用良好客户"文档:两份文档都高频出现"信用"字眼,它们的向量在语义空间中距离很近,而模型根本不知道"违约"和"良好"是反义词。 重排模型:让 Query 和 Document 当面对质 Cross-Encoder(交叉编码器)采用了完全不同的策略。 它不做"独立压缩",而是把**[Query + Document] 拼接成一段完整文本**,一次性通过一个深度神经网络(如 BERT)。在这个过程中,模型的注意力机制(Self-Attention)会在每一个 token 层级做交叉比对——当读到 Query 中的"违约"时,它会立刻去 Document 中寻找是否存在"违约"、是否存在语义矛盾、是否存在否定结构。 这种"当面对质"的方式,让 Cross-Encoder 能捕捉到 Bi-Encoder 完全无法处理的关系: 语义否定:Query “如何不用 Python” vs Document “Python 教程”——Bi-Encoder 会给高分,Cross-Encoder 能识别"不"的否定作用 长距离依赖:查询涉及某个条件组合,文档在开头提到条件A、结尾提到条件B,Cross-Encoder 的注意力能跨越全文找到同时满足两个条件的文档 细微差异:两份文档语意相近但立场相反(如上面的违约案例),Cross-Encoder 能识别出差异 用一个直观的对比表总结: ...

March 19, 2026 · 2 min · Hypho