RAG 向量检索:搜“番茄”,为什么能找到“西红柿”?

3 阅读4分钟

《AI 知识卡片》第 09 期 · 把文字变成坐标,意思近的就挨在一起

  比如你家有一本秘制私房菜谱,做成 AI 能查的知识库——这些菜只有你家会做,AI 训练时压根没见过。

  你输入“番茄炒蛋要放糖吗”,它却能找出了菜谱里《西红柿炒鸡蛋》那一页。

  注意,“番茄”和“西红柿”这两个词,一个字都不挨着。换成关键词搜索,这就是零结果,但 RAG 的检索找到了。这一期讲清它凭什么能“按意思”找,以及它凭什么知道这俩是一回事。

关键词匹配检索的弊端

  RAG 第一步就是“检索”:拿问题去资料库找相关片段,那怎么算“相关”?——老办法是关键词匹配:你搜的词和文档里的词对上了就算命中,但有个死穴,换个说法就抓瞎了

  • “番茄”对不上“西红柿”,“土豆”对不上“马铃薯”。同一样东西,两个完全不重合的词条;
  • “怎么做”对不上“制作方法”。一个意思,词不同也没戏;

  这种关键词匹配的问题我们叫做词汇鸿沟:字面不重合,机器就默认你俩没关系。

把每段文字变成一串“坐标”

  RAG 换了思路:别对字了,对“意思”。做法是,把菜谱里每一段文字都变成一串数字,相当于给它在一张巨大的语义坐标图上标一个坐标。这个动作正式叫 Embedding(中文译作“嵌入”),得到的那串数字叫向量

  一段文字有多少个数字?这取决于模型的规格——常用的中文 Embedding 模型bge-small-zh512 个。关键在于它是定长的:不管你喂进去 5 个字还是 500 个字,出来永远是这 512 个数。比如:

[0.0135, -0.0587, 0.0232, 0.0376, -0.0008, ... ]   共 512 个

两个反直觉的点:

  • 单个数字没有含义。文字意思是被这 512 个数字整体表达的,单看一维毫无意义。
  • 文字不是“查表”查出来的,是“算”出来的。每段文字都要真的跑一遍 Embedding 模型(这里的“模型”不等同于LLM,完全是两个东西,职责也不一样)才得到坐标。

  这张坐标图最妙的规矩是:意思越近的文字,坐标离得越近。

图2-嵌入过程.webp

它凭什么知道“番茄=西红柿”?

  没人告诉过它,它也没查任何同义词词典。这张坐标图是它自己“学”出来的。它学到的不是“字面像”,而是“用法像、语境像”。

  Embedding 模型在训练时读过海量文本。它会发现:“番茄炒蛋”和“西红柿炒鸡蛋”这两个说法,出现的语境几乎一模一样。既然它们总在同样的场合、被同样的词围绕,模型就把它们的坐标拽到了一起

训练时的核心手段叫对比学习,说白了就一句:把相关的拉近,把不相关的推远。

  • 喂进“一个问题 + 它的正确答案”这种成对的相关文本 → 拉近
  • 再随便配一段无关文本 → 推远
  • 千万次这样拉拉推推之后,一张“意思近就挨着”的坐标图就成型了;

本地实测结果对比

  我在本地拿 bge-small-zh 跑了一组,比较不同提问和它的余弦相似度(0~1,越大越近):

  第二行是重点:“番茄”和“西红柿”一个字都不挨着,相似度却有 0.61,远远高于“红烧肉”的 0.43。在语义坐标图上,它俩确实是邻居。

语义检索的短板

  语义检索的本事是“找意思差不多的”——这既是它的强项,也正是它的短板:它只会“差不多”,不擅长“一模一样”

  • 该精确的时候它不精确。你搜一个准确的产品型号、一个函数名,要的是一字不差那一条,它可能把几个“长得很像”的一起端上来。
  • 谁排第一它常拿不准。相关的内容它能一并捞进来,但真正最贴题的那条,未必就在第一位。

  所以实际工程里很少让它一条腿走路:一般再搭一条关键词检索兜住精确匹配,捞回来之后还会再重排一遍,把最贴题的顶到前面。后续有机会可以深入聊聊。

一句话总结

  关键词搜索对比的是“字”,语义检索对比的是“意思”。RAG 把每段文字变成语义坐标图,而这张图不是谁编的词典,是模型从海量文本里把相关的拉近、不相关的推远,一点点学出来的。