Embedding 向量化的魔法:从文本到向量的数学之旅与 Java 实战

系列文章

  1. 从零理解 RAG:检索增强生成完整指南
  2. 理解 AI Agent 的大脑:ReAct 模式从入门到实战
  3. AI Agent 的记忆系统:从 ChatMemory 到持久化记忆的 Java 实战
  4. AI Agent 的记忆力是怎么实现的——LangChain4j Memory 机制深度解析
  5. MCP 模型上下文协议:AI 的万能接口与 MCP Server 实战
  6. AI Agent 的工具箱:深入理解 Tool Use 与 Spring AI Function Calling 实战
  7. 让 AI 学会”说人话”——Spring AI 结构化输出实战
  8. AI Agent 的规划大脑:从任务分解到自适应执行策略
  9. AI Agent 的灵魂对话:Prompt Engineering 系统提示词设计的艺术与工程
  10. Embedding 向量化的魔法:从文本到向量的数学之旅与 Java 实战(本文)
  11. 当 RAG 遇上知识图谱:GraphRAG 原理与 Java 实战
  12. AI Agent 团队协作:多 Agent 系统架构设计与 Java 实战
  13. AI Agent 评估与优化:从基准测试到生产环境的质量守护实战

引言:一个困扰搜索引擎十年的问题

想象你去图书馆找一本书,主题是”如何让团队更高效地协作”。你会怎么做?

如果你搜索”高效协作”,可能会找到一本叫《高效能人士的七个习惯》的书。但如果你搜的是”团队管理”,你可能找到的是《团队的力量》。而真正能回答你问题的那本《敏捷开发实践》,它的书名里既没有”高效”也没有”协作”——但它恰恰是最相关的。

这就是传统关键词搜索的根本缺陷:它只能匹配字面,不能理解语义

在 AI Agent 的世界里,这个问题更加致命。当用户问”我想让我的应用能理解用户意图”时,Agent 需要从知识库中找到关于 NLU、意图识别、语义理解的相关文档——即使这些文档里可能从来没有出现过”理解用户意图”这几个字。

Embedding,就是解决这个问题的魔法。

它把人类的文字转换成计算机能理解的数学向量,让”意思相近的文字”在向量空间中彼此靠近。这是 RAG(检索增强生成)的基石,是语义搜索的核心,更是 AI Agent 理解世界的基础能力。

在之前的 RAG 完整指南 中,我们提到了 Embedding 是 RAG 管道的第一步。但那篇文章里,我们只是”用”了它,没有”懂”它。今天,我们就来彻底搞懂这个魔法背后的数学原理、工程实现和生产实践。


什么是 Embedding?从直觉到数学

直觉理解:给文字一个”坐标”

如果你要在一个二维平面上画出几种水果,你会怎么画?

你可能会把”苹果”和”梨”画在一起(都是水果、都圆、都甜),把”西兰花”画远一点(蔬菜),把”牛排”画在另一个区域(肉类)。你画的这个”位置”,就是一种最简单的 Embedding。

Embedding 的本质,就是给每段文字一个”坐标”——一个高维空间中的位置向量。

  • 意思相近的文字,坐标靠近
  • 意思不同的文字,坐标远离
  • 语义关系可以通过向量运算捕捉(比如:国王 - 男人 + 女人 ≈ 女王)

数学定义

形式化地说,Embedding 是一个函数:

1
f: Text → ℝⁿ

它把一段文本映射到一个 n 维实数向量。比如 OpenAI 的 text-embedding-3-small 模型输出 1536 维向量,而 text-embedding-3-large 输出 3072 维。

1
2
3
4
// 一段文本经过 Embedding 后的样子
String text = "Spring Boot 是 Java 生态最流行的微服务框架";
float[] embedding = embeddingModel.embed(text);
// embedding = [0.0234, -0.0891, 0.1456, ..., -0.0321] // 1536 个浮点数

这 1536 个数字,就是这段文字在”语义空间”中的坐标。你无法直观地理解每个数字的含义(不像二维坐标有 x 和 y),但数学可以——两个向量之间的距离,就代表两段文字的语义相似度。

为什么是高维?

你可能会问:二维不够用吗?三维呢?

想象一下,”苹果”这个词至少有以下维度需要编码:

  • 水果/非水果
  • 甜/不甜
  • 红色/非红色(水果义)
  • 科技公司(Apple 的含义)
  • ……

每个维度捕捉一种语义特征。维度越高,能区分的语义差异越精细。但维度也不是越高越好——太高的维度会带来”维度灾难”(curse of dimensionality),增加计算成本的同时不一定提升效果。实践中 768-3072 维是主流选择。


Embedding 的工作原理:Transformer 如何”理解”文字

从词到向量的旅程

Embedding 不是简单的查表(虽然最早的 Word2Vec 确实是),而是通过深度神经网络动态生成的。现代 Embedding 模型几乎都基于 Transformer 架构。让我们看看一段文字从输入到输出经历了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
输入文本: "Spring Boot 微服务框架"

1. Tokenizer: 分词 → ["Spring", " Boot", " 微", "服务", "框架"]

2. Token Embedding: 每个 token → 初始向量(查表)

3. Positional Encoding: 加入位置信息

4. Transformer Layers (12-24层):
- Multi-Head Self-Attention: 捕捉词与词之间的关系
- Feed-Forward Network: 非线性变换
- Layer Norm + Residual: 稳定训练

5. Pooling: 把多个 token 的向量合并为一个句子向量

输出: [0.0234, -0.0891, ..., -0.0321] // 1536维

Self-Attention:Embedding 的灵魂

Transformer 的核心是 Self-Attention(自注意力机制)。它解决了一个关键问题:同一个词在不同上下文中有不同含义

考虑这两个句子:

  • “我在银行存了钱” → “银行”是金融机构
  • “我在河 bank 边散步” → “bank”是河岸

Self-Attention 的做法是:让每个词”看看”句子中的其他词,然后根据相关性调整自己的向量表示。

1
2
3
4
5
6
7
8
9
10
// Self-Attention 的直觉理解(伪代码)
// 对于句子中的每个词,计算它与其他所有词的相关性分数
for (Token query : tokens) {
for (Token key : tokens) {
double attention = dot(query.vector, key.vector) / sqrt(dimension);
// "Spring" 和 "Boot" 的注意力分数高(经常一起出现)
// "Spring" 和 "框架" 的注意力分数中等(语义相关)
// "Spring" 和 "微" 的注意力分数低(关系弱)
}
}

Multi-Head Attention 则是让模型从多个”角度”同时观察——有的 head 关注语法关系(主谓宾),有的关注语义关系(同义词),有的关注长距离依赖(指代消解)。这些不同角度的信息融合在一起,形成了对文本的深层理解。

Pooling 策略:从 Token 到句子

Transformer 输出的是每个 token 的向量,但我们通常需要一个句子级别的向量。如何从多个 token 向量得到一个句子向量?

常见的 Pooling 策略:

策略 原理 适用场景
CLS Token 取第一个特殊 token [CLS] 的向量 BERT 系列模型默认方式
Mean Pooling 所有 token 向量取平均 通用场景,效果稳定
Max Pooling 每个维度取最大值 关键词匹配场景
Last Token 取最后一个 token 的向量 GPT 系列模型

生产实践建议:大多数现代 Embedding 模型(如 BGE、E5、GTE)使用 Mean Pooling,这是最稳妥的选择。如果你用的是 BERT 系列模型,默认的 CLS Token 也可以,但 Mean Pooling 通常效果更好。


主流 Embedding 模型对比:选对模型是成功的一半

2026 年的 Embedding 模型市场已经相当成熟,但选择困难症依然是开发者的第一道坎。让我做一个横向对比:

模型对比矩阵

模型 维度 最大 Token 中文支持 MTEB 排名 价格 推荐场景
OpenAI text-embedding-3-large 3072 8191 ★★★★ Top 5 $0.13/1M tokens 英文为主,多语言通用
OpenAI text-embedding-3-small 1536 8191 ★★★ Top 15 $0.02/1M tokens 成本敏感场景
BGE-M3 (BAAI) 1024 8192 ★★★★★ Top 3 免费开源 中文场景首选
Jina Embeddings v3 1024 8192 ★★★★★ Top 5 $0.02/1M tokens 多语言长文本
Cohere embed-v4 1024 128000 ★★★★ Top 3 $0.1/1M tokens 超长文档
GTE-Qwen2 1536 32000 ★★★★★ Top 2 免费开源 中文长文本
M3E (Moka Massive) 768 8192 ★★★★★ - 免费开源 中文轻量级

选型建议

中文场景:优先考虑 BGE-M3 或 GTE-Qwen2。这两个模型在中文语义理解上的表现远超 OpenAI 系列,而且开源免费,可以本地部署。

成本敏感:OpenAI text-embedding-3-small 是性价比之王,$0.02/1M tokens 的价格几乎是免费的。但如果你的量很大(每天百万级请求),本地部署 BGE-M3 可能更划算。

长文档:Cohere embed-v4 支持 128K token,适合直接 Embedding 整篇文档而不需要分块。但 128K 的上下文窗口意味着更高的计算成本。

多语言:Jina Embeddings v3 在多语言基准测试中表现优异,适合需要同时处理中英文的场景。


向量相似度:如何判断两段文字”像不像”

有了向量,下一步就是计算它们之间的”距离”。这决定了你的语义搜索质量。

三种主流相似度度量

1. 余弦相似度(Cosine Similarity)

1
2
3
4
5
6
7
8
9
10
public static double cosineSimilarity(float[] a, float[] b) {
double dotProduct = 0, normA = 0, normB = 0;
for (int i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
// 结果范围: [-1, 1],1 表示完全相同,0 表示无关,-1 表示完全相反

特点:只关注方向,不关注大小。适合大多数语义搜索场景。

2. 欧氏距离(Euclidean Distance)

1
2
3
4
5
6
7
8
public static double euclideanDistance(float[] a, float[] b) {
double sum = 0;
for (int i = 0; i < a.length; i++) {
sum += Math.pow(a[i] - b[i], 2);
}
return Math.sqrt(sum);
}
// 结果范围: [0, +∞),0 表示完全相同

特点:同时考虑方向和大小。适合需要精确匹配的场景。

3. 点积(Dot Product)

1
2
3
4
5
6
7
public static double dotProduct(float[] a, float[] b) {
double sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i] * b[i];
}
return sum;
}

特点:计算最快,但对向量的模敏感。适合归一化后的向量。

实践选择

绝大多数场景用余弦相似度。原因很简单:Embedding 模型输出的向量通常没有归一化,如果用点积,长文本(token 多,向量模大)会天然获得更高的分数,这不合理。余弦相似度消除了这个偏差。

向量数据库的选择:Milvus、Qdrant、Pinecone 都默认支持余弦相似度。如果你用的是 pgvector(PostgreSQL 扩展),注意它默认用的是欧氏距离(vector_l2_ops),需要显式指定余弦相似度(vector_cosine_ops)。


分块策略(Chunking):Embedding 之前的必修课

在实际的 RAG 系统中,你不是直接 Embedding 整篇文档,而是先把它切成小块(Chunk),然后对每个 Chunk 单独做 Embedding。分块策略的好坏,直接决定了检索质量的上限。

为什么需要分块?

  1. 模型限制:大多数 Embedding 模型的最大输入是 512-8192 token,一篇万字文档塞不进去
  2. 精度问题:把整篇文档 Embedding 成一个向量,会丢失细节。”这篇文档讲了 A、B、C 三个主题”——但用户问的是 B,你需要精准地找到 B 相关的段落
  3. 上下文窗口:检索到的 Chunk 会作为上下文塞进 LLM 的 prompt,太大的 Chunk 浪费 token,太小的 Chunk 缺少上下文

主流分块策略对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 1. 固定大小分块(最简单,但最粗糙)
public List<String> fixedSizeChunk(String text, int chunkSize, int overlap) {
List<String> chunks = new ArrayList<>();
for (int i = 0; i < text.length(); i += chunkSize - overlap) {
chunks.add(text.substring(i, Math.min(i + chunkSize, text.length())));
}
return chunks;
}

// 2. 语义分块(按语义边界切分)
public List<String> semanticChunk(String text) {
// 先按段落切分
String[] paragraphs = text.split("\n\n");
List<String> chunks = new ArrayList<>();
StringBuilder current = new StringBuilder();

for (String para : paragraphs) {
// 如果当前块加上新段落超过阈值,先保存当前块
if (current.length() + para.length() > 512) {
if (current.length() > 0) {
chunks.add(current.toString().trim());
current = new StringBuilder();
}
}
current.append(para).append("\n\n");
}
if (current.length() > 0) {
chunks.add(current.toString().trim());
}
return chunks;
}

// 3. 递归分块(LangChain 推荐方式)
public List<String> recursiveChunk(String text, int chunkSize) {
// 优先按大分隔符切分,不够再按小分隔符
String[] separators = {"\n\n", "\n", "。", ",", " "};
return recursiveSplit(text, separators, 0, chunkSize);
}

分块参数经验值

参数 推荐值 说明
Chunk Size 256-512 token 中文场景约 500-1000 字
Overlap 10-20% 防止语义在边界断裂
最小 Chunk 50 token 太短的 Chunk 缺少上下文

一个重要的原则:分块应该尽量保持语义完整性。一段话被切成两半,两半都检索不到完整信息。所以优先按段落、章节等自然边界切分,而不是机械地按字数切。


Java 实战:用 Spring AI 和 LangChain4j 做 Embedding

Spring AI 方式

Spring AI 提供了统一的 EmbeddingModel 接口,支持 OpenAI、Ollama、Azure 等多种后端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Configuration
public class EmbeddingConfig {

@Bean
public EmbeddingModel embeddingModel() {
// 使用 OpenAI
return new OpenAiEmbeddingModel(
new OpenAiApi(System.getenv("OPENAI_API_KEY"))
);
}
}

@Service
public class SemanticSearchService {

@Autowired
private EmbeddingModel embeddingModel;

/**
* 计算两段文本的语义相似度
*/
public double similarity(String text1, String text2) {
float[] vec1 = embeddingModel.embed(text1);
float[] vec2 = embeddingModel.embed(text2);
return cosineSimilarity(vec1, vec2);
}

/**
* 批量 Embedding(更高效,减少 API 调用次数)
*/
public List<float[]> batchEmbed(List<String> texts) {
EmbeddingResponse response = embeddingModel.embedForResponse(texts);
return response.getResults().stream()
.map(r -> r.getOutput())
.collect(Collectors.toList());
}

/**
* 构建简单的内存向量索引
*/
public List<SearchResult> search(String query, List<Document> documents, int topK) {
float[] queryVec = embeddingModel.embed(query);

return documents.stream()
.map(doc -> new SearchResult(
doc,
cosineSimilarity(queryVec, doc.getEmbedding())
))
.sorted((a, b) -> Double.compare(b.score(), a.score()))
.limit(topK)
.collect(Collectors.toList());
}

private double cosineSimilarity(float[] a, float[] b) {
double dot = 0, normA = 0, normB = 0;
for (int i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
}

record Document(String id, String content, float[] embedding) {}
record SearchResult(Document document, double score) {}

LangChain4j 方式

LangChain4j 同样提供了统一的 Embedding 模型接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 配置 Embedding 模型
EmbeddingModel model = OpenAiEmbeddingModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("text-embedding-3-small")
.build();

// 单条 Embedding
Response<Embedding> response = model.embed("Spring Boot 微服务框架");
float[] vector = response.content().vector();

// 批量 Embedding
List<TextSegment> segments = List.of(
TextSegment.from("Spring Boot 是 Java 微服务框架"),
TextSegment.from("React 是前端 JavaScript 框架"),
TextSegment.from("Kubernetes 是容器编排平台")
);
Response<List<Embedding>> batchResponse = model.embedAll(segments);

// 使用 EmbeddingStore 存储和检索
EmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();

// 添加向量
for (int i = 0; i < segments.size(); i++) {
store.add(batchResponse.content().get(i), segments.get(i));
}

// 语义搜索
float[] queryEmbedding = model.embed("Java 后端开发").content().vector();
List<EmbeddingMatch<TextSegment>> results = store.findRelevant(
Embedding.from(queryEmbedding), 3, 0.7 // top 3, 最低相似度 0.7
);

for (EmbeddingMatch<TextSegment> match : results) {
System.out.printf("相似度: %.4f | 内容: %s%n",
match.score(), match.embedded().text());
}

对接向量数据库

生产环境中,内存存储不够用。Spring AI 和 LangChain4j 都支持主流向量数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Spring AI + pgvector(PostgreSQL 扩展)
@Bean
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {
return new PgVectorStore(jdbcTemplate, embeddingModel,
PgVectorStore.PgVectorStoreOptions.builder()
.dimensions(1536)
.indexType(HNSW) // 近似最近邻索引
.distanceType(COSINE_DISTANCE)
.build()
);
}

// 存储文档
vectorStore.add(List.of(
new Document("Spring Boot 是 Java 微服务框架", Map.of("category", "backend")),
new Document("React 是前端 JavaScript 框架", Map.of("category", "frontend"))
));

// 语义检索
List<Document> results = vectorStore.similaritySearch(
SearchRequest.query("Java 后端开发")
.withTopK(5)
.withSimilarityThreshold(0.7)
);

性能优化与成本控制

批量请求 vs 单条请求

Embedding API 通常支持批量请求,这比逐条调用高效得多:

1
2
3
4
5
6
7
// ❌ 错误:逐条调用(100 次 HTTP 请求)
for (String text : texts) {
float[] vec = model.embed(text); // 每次都是一次 API 调用
}

// ✅ 正确:批量调用(1 次 HTTP 请求)
List<List<float>> vectors = model.embedAll(texts); // 一次搞定

性能差异:批量请求的吞吐量通常是单条请求的 10-50 倍,因为减少了网络往返和 API 限流的开销。

缓存策略

Embedding 计算是昂贵的(GPU 推理或 API 调用),但结果是确定性的——同样的文本总是产生同样的向量。所以缓存是必须的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Service
public class CachedEmbeddingService {

private final EmbeddingModel delegate;
private final Cache<String, float[]> cache;

public CachedEmbeddingService(EmbeddingModel delegate) {
this.delegate = delegate;
this.cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterAccess(Duration.ofHours(24))
.build();
}

public float[] embed(String text) {
return cache.get(text, key -> delegate.embed(key));
}
}

维度裁剪(Dimension Reduction)

OpenAI 的 text-embedding-3 系列支持维度裁剪——你可以用 3072 维的模型,但只取前 256 维,精度损失很小但存储和计算成本大幅降低:

1
2
3
4
5
6
7
8
9
// 使用 256 维(而非默认的 3072 维)
float[] fullVector = model.embed("text"); // 3072 维
float[] reduced = Arrays.copyOf(fullVector, 256); // 256 维

// OpenAI API 原生支持(更推荐,模型内部做了优化)
EmbeddingResponse response = embeddingModel.call(
new EmbeddingRequest(List.of("text"),
Map.of("dimensions", "256"))
);

本地部署 vs API 调用

维度 API 调用 本地部署
延迟 50-200ms 5-20ms
成本 按 token 计费 GPU 硬件成本
质量 最新模型 需要自己更新
隐私 数据外传 数据不出内网
适用 日均 < 100 万次 日均 > 100 万次或数据敏感

如果你选择本地部署,可以用 Sentence-Transformers(Python)或 DJL(Deep Java Library)加载 BGE-M3 等开源模型:

1
2
3
4
5
6
7
8
9
10
// DJL 加载本地 Embedding 模型
Criteria<String, float[]> criteria = Criteria.builder()
.setTypes(String.class, float[].class)
.optModelUrls("djl://ai.djl.huggingface.onnxruntime/BAAI/bge-small-zh-v1.5")
.optEngine("OnnxRuntime")
.build();

ZooModel<String, float[]> model = criteria.loadModel();
Predictor<String, float[]> predictor = model.newPredictor();
float[] embedding = predictor.predict("Spring Boot 微服务框架");

生产环境注意事项

1. 向量维度一致性

这是一个常见且致命的错误:查询时用的 Embedding 模型和入库时用的不一样。

1
2
3
4
5
6
7
8
// ❌ 入库用 OpenAI,查询用 BGE——维度不同,相似度无意义
float[] stored = openaiModel.embed("文档内容"); // 1536 维
float[] query = bgeModel.embed("用户查询"); // 1024 维
// cosineSimilarity(stored, query) → 胡乱计算,结果无意义

// ✅ 始终使用同一个模型
float[] stored = embeddingModel.embed("文档内容"); // 1536 维
float[] query = embeddingModel.embed("用户查询"); // 1536 维

建议:在配置文件中统一指定模型名称,所有组件引用同一个配置。

2. 向量归一化

有些 Embedding 模型输出的向量没有归一化(模不等于 1),这会影响点积相似度的计算。如果你的向量数据库用点积作为距离度量,记得先归一化:

1
2
3
4
5
6
7
8
9
10
public static float[] normalize(float[] vector) {
double norm = 0;
for (float v : vector) norm += v * v;
norm = Math.sqrt(norm);
float[] result = new float[vector.length];
for (int i = 0; i < vector.length; i++) {
result[i] = (float) (vector[i] / norm);
}
return result;
}

3. 异常处理

Embedding API 调用可能失败(网络超时、限流、模型过载),必须有重试机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class ResilientEmbeddingService {

private final EmbeddingModel delegate;
private final RetryTemplate retryTemplate;

public ResilientEmbeddingService(EmbeddingModel delegate) {
this.delegate = delegate;
this.retryTemplate = RetryTemplate.builder()
.maxAttempts(3)
.exponentialBackoff(1000, 2.0, 10000)
.retryOn(EmbeddingException.class)
.build();
}

public float[] embed(String text) {
return retryTemplate.execute(ctx -> delegate.embed(text));
}
}

4. 监控指标

生产环境中,务必监控以下指标:

  • Embedding 延迟:P50/P95/P99 延迟,超过阈值告警
  • 缓存命中率:低于 60% 说明缓存策略需要优化
  • API 调用量:防止意外的批量操作导致账单飙升
  • 向量维度一致性:定期校验入库向量的维度是否与配置一致

Embedding 的边界与未来

什么时候 Embedding 不够用?

Embedding 不是万能的。以下场景它表现不佳:

  1. 精确匹配:”错误码 E0042”——这种精确查询用关键词搜索更好
  2. 否定语义:”不包括 Java 的后端框架”——Embedding 会把”Java”和”后端框架”都匹配上
  3. 数字和表格:Embedding 对结构化数据的理解较弱
  4. 多跳推理:”谁写了《Spring 实战》的第五版?”——需要先找到书,再找作者,单次 Embedding 搞不定

混合检索(Hybrid Search) 是当前的最佳实践:同时使用关键词检索(BM25)和语义检索(Embedding),然后用 RRF(Reciprocal Rank Fusion)合并结果。

未来趋势

  1. 多模态 Embedding:CLIP 等模型已经能把图片和文字映射到同一个向量空间。未来,音频、视频、3D 模型都会有统一的 Embedding 空间
  2. 更长的上下文:从 512 token 到 8K 到 128K,Embedding 模型的上下文窗口在持续增长,分块策略可能会逐渐被淘汰
  3. 任务特化:通用 Embedding 模型会被任务特化模型取代——专门做代码搜索的、专门做法律文档的、专门做医学文献的
  4. Matryoshka Embedding:一种新范式,训练时同时优化多个维度(256、512、1024),推理时按需选择精度——就像俄罗斯套娃,小的嵌套在大的里面

总结

Embedding 是 AI Agent 理解世界的基石。它把人类的文字转换成计算机能处理的数学向量,让”语义相似”变成了”向量距离近”这个可计算的问题。

回顾一下核心要点:

  1. Embedding 的本质是把文本映射到高维向量空间,语义相近的文本向量距离近
  2. Transformer 的 Self-Attention 机制让 Embedding 能理解上下文,同一个词在不同句子中有不同的向量表示
  3. 选对模型比调参重要——中文场景首选 BGE-M3 或 GTE-Qwen2
  4. 分块策略决定了检索质量的上限——保持语义完整性比严格控制大小更重要
  5. 余弦相似度是语义搜索的默认选择,向量数据库要确认用的是 cosine 而不是 L2
  6. 批量请求 + 缓存是性能优化的两个基本功
  7. 混合检索(Embedding + BM25)是生产环境的最佳实践

在我们的 RAG 完整指南 中,Embedding 是检索阶段的核心。在 GraphRAG 中,Embedding 同样是构建知识图谱的基础。理解了 Embedding,你就理解了整个 AI 检索体系的数学基础。

下一篇,我们将继续深入 AI Agent 的其他核心能力。敬请期待。


参考资料