大可的 AI Builder 手记
跟随系统
颜色主题
长沙小桌实战
大可 Dako
大可 Dako大可 DakoAI Builder · Agent 实践者用 AI 做真正能用的产品。专注探索 Agent 工程化落地、Vibe Coding 实践与商业化交付。
内容平台即将上线,敬请期待
相似度衡量的是像不像,不是能不能回答像不像文字压成坐标后比距离,讲同一件事就靠得近衡量的是措辞和话题的接近程度能不能答取决于这一段里有没有那个事实,和像不像无关答案常常出现在措辞完全不同的段落里把两者当一回事等于假设最像的就是最有用的,这个假设经常不成立这是检索里最大的单一误差来源距离只能筛掉明显无关的,不能替你决定哪一段有用
距离近说明两句话在讲同一件事,不说明其中一句回答了另一句。

向量与相似度

向量把意思变成坐标,所以意思相近的靠得近。但相似度衡量的是像不像,不是能不能回答——检索翻车大多从这一步开始。

向量把意思变成了坐标,所以意思相近的靠得近。但这只是第一层——距离近说明两句话在讲同一件事,不说明其中一句回答了另一句。把相似当成相关,是检索里最大的单一误差来源。

读完你能回答:

  • 为什么用户换个说法,检索就找不到了?
  • 为什么最像的那几段,往往答不了问题?
  • 哪两类内容向量读不出来,必须靠别的办法补?
  • 上线一个向量检索,第一步该看哪个指标?

只认字面,换个说法就漏

先看老办法的毛病。关键词检索是字面匹配:用户输入什么词,系统就去找含这些词的文档。可是同一个意思的说法多得很——用户打「电脑开不了机」,手册里写的是「终端设备无法启动的报修流程」;用户说「晚点再弄」,文档里写「顺延」。几串字彼此毫无重合,指的却是同一件事,只认字面的检索对此束手无策。

向量要解决的正是这个问题。它绕开字面,直接看意思。 文字送进一个专门的向量模型,出来的是一串几百到几千个数字,也就是一个坐标。意思靠得近的文字,坐标也挨得近。

这张按意思排布的地图,你可以把它想成语义空间:报修相关的内容挤成一片,采购和报销聚在另一片,团建活动则远在别处。检索做的事情,是把用户的问题也放到这张图上,看哪一片离它最近。

产出坐标的向量模型,和你熟悉的文本模型是两类不同的东西:文本模型吃进去文字、吐出来的还是文字;向量模型吃进去文字,吐出来的是一串数字。两个坐标之间的距离通常用余弦相似度来量,你不需要弄懂它背后的数学,只要知道它给出一个落在 0 到 1 之间的数,数越大表示越像。openai-embeddings

最近的那一段,未必能回答问题

这一篇最要紧的一条,从一个小场景说起。用户问「去年没休完的年假还能用吗」,库里召回了几段,相似度排在最前面的两段是这样:一段标题叫《年假使用说明》,「年假」两个字密集出现,正文却只有一串小标题;另一段讲的是离职时未休年假怎么折算,「年假」同样密集,回答的却是另一个问题。真正能答的那句话写的是「未休完的假期顺延到次年三月三十一日前使用」——措辞和用户的问题并不重合,相似度排不进前列。

召回结果里的失误大致有三种。第一种是空壳:标题堆满关键词,正文其实没有内容,可它的用词和问题重合度最高。第二种是邻近话题:同一片语义区域里的另一个问题,坐标确实很近,但答的不是这一问。第三种最难识别,是过期版本——库里同时留着去年和今年的两份说明,用词几乎一样,坐标自然也几乎重叠,差别只在几个数字上。坐标里没有时间这个概念,光靠相似度分不出哪份是现行的,只能从数据这一层解决:旧版本下线,或者给每段带上生效日期并在检索时过滤。

相似度衡量的是「像不像」,不是「能不能回答」。一段话像不像问题,取决于它的用词和话题是否接近;一段话能不能回答问题,取决于它里面有没有那个事实。这两件事经常不一致,而且两个方向都会出问题:

  • 像但没用。标题里堆满关键词的索引页、每个页面都带的例行声明,用词和问题高度重合,会把真正的答案挤下去。
  • 有用但不像。能回答问题的片段可能用了完全不同的措辞:用户说口语,文档用术语;用户说简称,文档写全称。

为什么这一条最要紧?因为检索后面所有环节都建在这个前提上。你按相似度取前几段,等于假设「最像的就是最有用的」。这个假设一旦不成立,模型拿到的材料就是错的——而它照样会基于错误材料给出一个语气笃定的答案,甚至因为标了出处显得更可信。

坐标是学出来的,所以它带着语料的偏好

这些坐标并非由人手工指定,而是模型从海量文本里训练出来的:总在一起出现的那些词句,坐标会彼此靠拢。换句话说,它记录的是语料里人们怎么使用这些词,谈不上什么客观真理——这个性质到后面会变成一个坑。word-embedding-wiki

好消息是这一层花费很少。在所有模型类型里,向量模型的成本属于最低的一档,比文本模型通常要低一到两个数量级。这意味着把全部文档都做成向量,几乎不构成成本问题。

真正要提前定下的是三件工程上的事,它们决定这套东西能不能长期用:

  • 这是一次性的离线预处理。文档先切成小段、逐段算向量、存进向量库。用户提问时只给用户的问题算一次向量、再比距离,所以查询很快。
  • 文档改了要重算。一篇文档改动之后,它对应的向量必须重新生成。这一步在项目里很容易被漏掉,漏掉的后果是知识库还在拿几个月前的旧内容作答。
  • 换向量模型等于全部重算。不同模型产出的坐标系彼此不通用,混着用等于拿两张不同的地图量距离。所以选型值得慎重,因为换一次的代价不低。

否定和精确串读不出来,长文会被稀释

两条明确的盲区,值得直接写进设计检查表:

一、对否定不敏感。「此操作需要双人复核」和「此操作无需双人复核」用词几乎一样,坐标往往靠得很近。合规、政策、医疗这类内容里这是真风险:它找到的是措辞最像的那一段,而不是恰好把意思反过来的那一段。

二、精确串是弱项。合同编号、物料批次号、员工工号——这类东西在语料里没什么语义,转成坐标之后挤成一团,分不出彼此。而用户恰恰最常拿这些来问。

还有一条不算盲区但要知道:长文本会被稀释。 把一大段文字压进同一个坐标,等于把它涉及的各个主题取了平均,平均之后没有一个主题还能冒出来。因此怎么切分,直接决定检索准不准,RAG 那一篇会展开。

用它召回,别用它排序

把上面几条落成动作:

一、别把相似度当最终排序。它能可靠地筛掉明显无关的,但分不出「像」和「有用」。用相似度召回一批,再用一个更贵的步骤挑出真正能回答的那几段。

二、把精确匹配补上。编号、批次号、专有名词交给关键词检索,那一路对这些东西的判断比向量准得多。两路找法各有盲区讲的就是怎么把两路拼起来。

三、先量一遍检索层。在改提示词之前,先看正确的那一段有没有进召回、排在第几。这一步不需要调用模型,快且便宜,能告诉你问题到底出在找还是出在答。

向量把「意思」变成了一门可以计算的距离,这一步不可替代——没有它,换个说法就找不到。但它算出来的只是像不像。要把像变成有用,靠的是混着找、再排一次,那是下一层的事。

回到这一章的主线:模型自己不记得任何东西,它知道的一切都是你的系统在这一次请求里放进去的。放进去的那几段是不是有用的,取决于你在这一步的判断,而不是模型的能力。

还有一层比调相似度更根本的问题:在复杂场景里,真正决定判断对不对的往往是那些措辞和问题不相似的约束——状态机不能跳转、这个接口只有某个服务能调。它们相似度不高,却是必须知道的。检索解决的是「找出可能相关的内容」,保证不了「该知道的都拿到了」,这中间的差距在知识库要先对齐领域里展开。

参考资料

  1. Vector embeddings — OpenAI
  2. Word embedding — Wikipedia