语义搜索的底层原理

为什么需要 Embedding

传统搜索是关键词匹配:你搜"苹果"只能找到包含"苹果"两个字的文档。但语义上相近的"iPhone""果公司""Apple Inc."都搜不到。

Embedding 的目标:让计算机理解"相似的意思",不只是"相同的字"。

核心概念:向量就是一个数组

// 一个 Embedding 向量,长度通常是 512~3072
const vector: number[] = [0.123, -0.456, 0.789, ...]

每个词/句子都被映射成一个这样的高维数组。神奇的地方是:

embed("狗") ≈ [0.1, 0.8, 0.3, ...]
embed("犬") ≈ [0.11, 0.79, 0.31, ...]   // 很接近
embed("汽车") ≈ [-0.5, 0.2, -0.9, ...]   // 差很远

语义相近的文本,向量距离也近。这就是 Embedding 的全部魔法。

调用 Embedding API

import OpenAI from 'openai'

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

const res = await client.embeddings.create({
  model: 'text-embedding-3-small', // 1536 维,便宜
  input: '前端开发者学习 AI',
})

console.log(res.data[0].embedding)
// [0.0123, -0.0456, 0.0789, ..., 总共 1536 个数]

开源替代:BGE(北智源,中文效果好)、m3e。可以本地部署,完全免费。

计算相似度:余弦相似度

两个向量的相似度,最常用的算法是余弦相似度(Cosine Similarity)

function cosineSim(a: number[], b: number[]): number {
  let dot = 0, na = 0, nb = 0
  for (let i = 0; i < a.length; i++) {
    dot += a[i] * b[i]
    na += a[i] * a[i]
    nb += b[i] * b[i]
  }
  return dot / (Math.sqrt(na) * Math.sqrt(nb))
}

结果范围 [-1, 1]

  • 1:完全相同方向(几乎同义)
  • 0:无关
  • -1:完全相反(实际很少见)

实践中,> 0.7 通常算强相关,具体阈值要根据你的数据校准。

一个完整的小例子

import OpenAI from 'openai'
const client = new OpenAI()

async function embed(text: string) {
  const res = await client.embeddings.create({
    model: 'text-embedding-3-small',
    input: text,
  })
  return res.data[0].embedding
}

const documents = [
  '如何使用 React Hooks',
  'Vue 3 Composition API 详解',
  '烤蛋糕的家常做法',
  'TypeScript 泛型深入',
  '和女朋友去哪里度假好',
]

// 预计算所有文档的向量
const docVecs = await Promise.all(documents.map(embed))

// 用户提问
const query = 'React useEffect 怎么用'
const queryVec = await embed(query)

// 排序
const ranked = documents
  .map((doc, i) => ({ doc, score: cosineSim(queryVec, docVecs[i]) }))
  .sort((a, b) => b.score - a.score)

console.log(ranked)
// 1. React Hooks   0.84
// 2. TypeScript    0.61
// 3. Vue 3         0.58
// ...

看,就算文档里没有 "useEffect" 这几个字,也能找到最相关的那条。

向量数据库:Embedding 的家

文档少的时候数组里一把梭就够了。几万条以上,你需要向量数据库

数据库特点
pgvectorPostgreSQL 扩展,既有 SQL 又有向量
Pinecone托管服务,免运维
Chroma轻量本地库,适合原型
QdrantRust 写的,高性能
Milvus分布式,大规模生产用
Weaviate带混合搜索

对前端/全栈开发者的推荐路线:pgvector(你已有 Postgres 就零成本)或Chroma(零配置本地跑)。

-- pgvector 示例
CREATE EXTENSION vector;

CREATE TABLE docs (
  id serial PRIMARY KEY,
  content text,
  embedding vector(1536)
);

-- 查找最相似的 5 条
SELECT content
FROM docs
ORDER BY embedding <=> $1::vector  -- <=> 是余弦距离操作符
LIMIT 5;

向量检索的底层:ANN

当数据量到百万级,逐个算余弦距离就慢了。向量数据库内部用**近似最近邻(ANN)**算法:

-HNSW(图结构):最流行,精度速度兼顾 -IVF(倒排索引) -LSH(局部敏感哈希)

作为前端开发者,知道有这回事即可,向量库会替你搞定。

Embedding 的实际应用场景

不只是 RAG,Embedding 能做的事很多:

1.语义搜索:站内搜索、文档搜索 2.推荐系统:相似商品/文章推荐 3.去重:发现重复/相似的内容 4.分类:把文本映射到预定义标签(零样本分类) 5.聚类:自动发现内容主题 6.异常检测:找和整体风格不一样的文本

成本参考

text-embedding-3-small 每百万 Token $0.02(2026 年价格)。 把整个项目代码(几十万 Token)全 embed 一遍不到 1 毛钱。

动手作业

给你自己的博客/笔记做一个语义搜索:

  1. 读取所有 markdown 文件
  2. 按标题+正文 embed 每篇文章
  3. 写一个命令行工具:输入查询关键字,返回最相关的 5 篇
  4. 对比关键词搜索(grep)和语义搜索的差异

参考资料

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:Embedding 与向量:把文字变成数字

本文链接:https://www.sshipanoo.com/blog/ai/ai-for-frontend/04-Embedding与向量/

本文最后一次更新为 天前,文章中的某些内容可能已过时!