从面试中学习:什么是向量检索?
背景
最近面试了一位候选人,聊到了向量检索在组件库文档搜索中的实践。听起来很高级,但本质上是个很实用的技术。这篇文章用最通俗的话解释一下。
先说结论
向量检索:把文字转换成”数字”,然后在这个数字空间里找”意思相近”的内容。
简单说:让机器理解你的意图,而不是死板地匹配文字。
什么是向量检索?
一个生活化的比喻
想象你在图书馆找书。
传统搜索:
店员问:”你要什么书?”
你说:”《挪威的森林》。”
店员精确找到这本书。
向量检索:
店员问:”你想看什么类型的书?”
你说:”我想看关于一个人经历了很多困难,最后成长起来的故事。”
店员不仅找到了《挪威的森林》,还推荐了《少年维特的烦恼》《平凡的世界》… 因为它们”讲的是类似的事情”。
为什么需要它?
你有没有遇到过这种情况:
| 场景 | 搜索结果 |
|---|---|
| 搜”手机” | 只找到含”手机”两个字的内容 |
| 搜”苹果” | 不知道你要水果还是公司 |
| 搜”怎么把手机变成热点” | 搜不到”如何开启手机热点” |
| 打错字 | 什么都搜不到 |
传统搜索就是”字面对字面”,你说什么我就找什么,一字不差。
向量检索能解决什么?
| 传统搜索 | 向量检索 |
|---|---|
| 搜”快乐”找不到”开心” | 语义相近都能找到 |
| 搜”苹果”可能混淆 | 理解上下文(水果or公司) |
| 搜法不同就搜不到 | 同一意思不同说法都能搜到 |
它是怎么工作的?
整个过程可以拆成三步:
第一步:转成数字(Embedding)
把一段文字变成一串数字,这串数字就是”向量”。1
2"如何实现按钮点击" → [0.12, -0.34, 0.56, 0.89, ...] (1536个数字)
"按钮的点击事件怎么做" → [0.11, -0.32, 0.58, 0.88, ...] (1536个数字)
关键是:**意思相近的文字,生成的数字也会”长得像”**。
第二步:找相似的
在所有已转换的文字中,找到数字”距离近”的。1
2
3"如何实现按钮点击" → [0.12, -0.34, 0.56, ...]
"按钮的点击事件怎么做" → [0.11, -0.32, 0.58, ...]
↓ 距离很近(相似度 95%)
什么是”距离近”?
想象在一个巨大的空间里,每个向量都是一个点:1
2
3
4
5
6
7
8 开心
★
/
/
快乐 ---★--- 悲伤
/ \
/ ★
高兴 难过
相近的词在这个空间中的位置也近。计算”距离”有几种方法:
| 方法 | 适用场景 | 了解更多 |
|---|---|---|
| 余弦相似度 | 语义搜索(方向相近) | 余弦相似度 - 维基百科 |
| 欧氏距离 | 绝对距离敏感 | 欧几里得距离 |
| 点积 | 计算效率优先 | 点积 - Khan Academy |
第三步:返回结果
把最相似的几个结果返回给你。
底层原理:文字是怎么变成数字的?
1. 最早的做法:独热编码
最早的词向量很简单,但没什么用。1
2
3
4假设我们有 10000 个词:
"苹果" → [1, 0, 0, 0, ..., 0] (第1位是1,其他都是0)
"香蕉" → [0, 1, 0, 0, ..., 0] (第2位是1,其他都是0)
问题:每个词都是”孤立的”,苹果和香蕉的距离 = 苹果和手机的距离,完全无法表达语义。
2. 从上下文学习
后来研究发现:一个词的意义,由它的上下文决定。
📚 参考:Word2Vec 论文 - Google (2013)
如果两个词经常出现在相似的上下文里,它们意思就相近。1
2
3
4
5
6
7"我吃了一个苹果" → "苹果"是水果
"我吃了一个香蕉" → "香蕉"是水果
"我买了一部苹果手机" → "苹果"是公司
模型学习后发现:
- "苹果"和"香蕉"经常出现在相似的上下文 → 位置相近
- "苹果"和"手机"有时也在一起 → 有一定关系
3. 现代做法:Transformer
现在的 Embedding 模型用的是 Transformer 架构,更加强大。
📚 参考:Attention Is All You Need - Transformer 原始论文
📖 通俗解读:Transformer 模型详解 - Jay Alammar
**核心是”注意力机制”**,让模型理解上下文:1
2
3
4
5
6
7
8句子:"苹果的营养价值很高"
传统方法:每个词独立看
- "苹果" → 固定向量
Transformer:结合上下文
- "苹果" 在这句话里 → [0.12, -0.34, ...] (理解为水果)
- "苹果" 在另一句话里 → [0.11, -0.31, ...] (可能理解为公司)
Attention 的比喻:
读一句话时,你会”关注”不同的词。
读”苹果手机很好用”:
- 你关注”手机” → 理解这里”苹果”是公司
读”苹果很甜很好吃”:
- 你关注”甜” → 理解这里”苹果”是水果
Transformer 就是模拟这个过程。
4. 一句话总结
| 阶段 | 方法 | 特点 |
|---|---|---|
| 早期 | 独热编码 | 每个词独立,无法表示语义 |
| 后来 | 上下文学习 | 语义相近则向量相近 |
| 现在 | Transformer | 结合上下文,理解深层语义 |
能做什么?
身边的产品例子
你可能已经用过向量检索了,只是没意识到:
1. Google 搜索
当你搜索”手机死机怎么办”时:
- 早期 Google:精确匹配”手机死机怎么办”这几个字
- 现在的 Google:理解你的意图,返回”手机卡顿解决方法””手机开不了机怎么解决”等相关文章
📖 参考:Google AI 搜索技术介绍
Google 从多年前开始大量使用语义搜索技术(BERT、Muennighoff 等)。
2. AI 搜索产品
现在的 AI 搜索(比如 Perplexity、秘塔搜索、知乎直达):
🔗 体验:Perplexity | 秘塔搜索 | 知乎直达
1 | 你问:"为什么早起对身体好?" |
3. 电商搜索
在淘宝搜”显白的裙子”:
- 传统搜索:找不到”显白”这个词
- 向量检索:理解你要”显肤色白”,返回白色、浅色、粉色等裙子
4. 代码搜索
在 GitHub 搜”发送网络请求”:
- 传统搜索:只找含”发送网络请求”的文件
- 向量检索:找到 fetch、axios、XMLHttpRequest 相关的代码
🔗 GitHub 已经在使用语义代码搜索,可以试试 GitHub Code Search
实际应用场景
| 场景 | 用法 |
|---|---|
| 智能客服 | 用户问”怎么退货”,找到退货相关的FAQ |
| 文档搜索 | 搜”怎么做表格”,找到Excel教程 |
| 代码搜索 | 搜”发送请求”,找到HTTP相关代码 |
| 内容推荐 | 给你推荐”你可能感兴趣的文章” |
一个具体例子
假设你在做一个组件库文档搜索:
用户搜索:”点击按钮没反应”
传统搜索:只找含”点击按钮没反应”这几个字的内容
向量检索:找到
- “按钮点击事件无效”
- “button点击没有响应”
- “点击button没反应怎么办”
因为它们”说的是同一件事”。
如何搭建一个向量检索系统?
第一步:准备数据
把所有文档拆分成小块。比如一篇长文档,可以按章节、按段落拆分。
📖 参考:LangChain 文本分割器
1 | 原文档 |
第二步:调用 Embedding API
把每块内容转换成向量。这步不需要自己写算法,调用现成的服务就行。1
2
3
4
5
6
7
8
9
10
11
12
13import OpenAI from "openai"
const client = new OpenAI()
// 把文字转成向量
const response = await client.embeddings.create({
model: "text-embedding-3-small",
input: "按钮组件用于触发操作事件"
})
// 拿到这1536个数字(1536维向量)
const vector = response.data[0].embedding
// [0.12, -0.34, 0.56, 0.89, ...]
1536维是什么意思?
想象一个坐标系,每个向量有1536个坐标。这1536个数字一起定义了它在”语义空间”中的位置。
第三步:存入向量数据库
把每块内容及其对应的向量存到数据库里。1
2
3
4
5
6
7
8// 存入向量数据库(以Pinecone为例)
await index.upsert({
vectors: [
{ id: "chunk-1", values: vector1, metadata: { content: "按钮组件用于触发操作事件" } },
{ id: "chunk-2", values: vector2, metadata: { content: "button组件支持onClick事件" } },
// ...更多内容
]
})
第四步:处理用户搜索
当用户搜索时,同样的逻辑:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 1. 把用户的搜索词转成向量
const queryVector = await client.embeddings.create({
model: "text-embedding-3-small",
input: "按钮点击怎么做"
})
// 2. 在向量空间里找相似的
const results = await index.query({
vector: queryVector.data[0].embedding,
topK: 5 // 返回最相似的5个结果
})
// 3. 返回结果
console.log(results.matches)
// [
// { id: "chunk-1", score: 0.95, metadata: { content: "按钮组件用于触发操作事件" } },
// { id: "chunk-2", score: 0.89, metadata: { content: "button组件支持onClick事件" } },
// ...
// ]
完整流程图
1 | 用户搜索"按钮点击怎么做" |
常用工具
| 用途 | 工具 | 参考链接 |
|---|---|---|
| Embedding 生成 | OpenAI | OpenAI Embeddings 文档 |
| Claude | Anthropic Embeddings | |
| Google Embeddings API | ||
| 开源模型 | Hugging Face Sentence Transformers | |
| 向量数据库 | Pinecone | Pinecone 官网 |
| Milvus | Milvus 官网 | |
| Chroma | Chroma 官网 | |
| Qdrant | Qdrant 官网 |
术语表
| 术语 | 解释 |
|---|---|
| 向量(Vector) | 一串数字,代表文字在语义空间中的位置 |
| Embedding | 把文字转换成向量的过程或模型 |
| 向量检索 | 在向量空间中找相似内容的技术 |
| 语义搜索 | 理解意图的搜索,而不是关键词匹配 |
| 相似度(Similarity) | 两个向量”距离近”的程度 |
| 余弦相似度 | 最常用的相似度计算方法 |
| Transformer | 现在的Embedding模型用的架构 |
| Attention | Transformer的核心,理解上下文 |
| Chunk | 把大文档拆成的小块 |
| TopK | 返回最相似的K个结果 |
| Reranking | 对检索结果重新排序提升精度 |
总结
向量检索解决的核心问题是:让机器理解你的意图,而不是死板地匹配文字。
它的工作流程:
- 转成数字 - 把文字变成向量
- 找相似的 - 在数字空间里找”距离近”的
- 返回结果 - 把最相关的返回
什么时候用它?当你需要语义搜索而不是关键词搜索的时候。
扩展阅读
想深入了解?这些资源可以帮助你:
入门系列
- 向量搜索入门 - Pinecone
- 语义搜索介绍 - Elasticsearch
视频教程
- 3Blue1Brown - 神经网络系列
- 李宏毅 - Transformer 课程
实践教程
- OpenAI Embeddings 教程
- LangChain 向量存储
论文
- Word2Vec 原始论文
- Attention Is All You Need
- BERT 论文