文本嵌入
文本生成能回答问题、写文章,但有些任务不是生成文字,而是理解文字之间的关系。比如判断两句话意思是否相近、从一堆文档里找出和某个问题最相关的那篇。这类任务靠关键词匹配效果差,要用文本嵌入把文字变成向量,再用数学方法算相似度。这篇讲清楚嵌入是什么、Workers AI 怎么调用嵌入模型、怎么计算语义相似度。
什么是文本嵌入
文本嵌入(Text Embedding)是把一段文字映射成一组数字的过程。这组数字叫向量,通常有几百到几千个维度。关键在于,意思相近的文字生成的向量也相近,意思无关的文字向量距离远。
一段文字变成向量的过程
"今天天气很好" → [0.12, -0.34, 0.56, ..., 0.78] (比如 768 个数字)
"今日气候不错" → [0.11, -0.32, 0.55, ..., 0.77] (向量很接近)
"我想买台电脑" → [0.89, 0.21, -0.43, ..., 0.05] (向量差很远)
这和关键词匹配的区别
| 对比项 | 关键词匹配 | 向量嵌入 |
|---|---|---|
| 工作原理 | 字面是否包含某个词 | 语义层面的意思是否相近 |
| 同义词 | 匹配不到 | 能识别为相近 |
| “汽车””轿车” | 当成无关词 | 向量距离近 |
| 拼写错误 | 直接失效 | 仍能匹配到相近语义 |
| 计算方式 | 字符串包含判断 | 向量距离或余弦相似度 |
嵌入是语义搜索、推荐系统、RAG(检索增强生成)的基础。把文档全部转成向量存起来,用户提问时把问题也转成向量,找距离最近的几篇文档就是最相关的结果。
嵌入模型
Workers AI 提供了几个嵌入模型,都来自 BAAI(北京智源人工智能研究院)的 BGE 系列。文本生成模型输出的是文字,嵌入模型输出的是向量,调用方式类似但参数和返回结构不同。
可用的嵌入模型
| 模型 ID | 向量维度 | 语言支持 | 特点 |
|---|---|---|---|
| @cf/baai/bge-small-en-v1.5 | 384 | 英文 | 最快最省,适合大量数据 |
| @cf/baai/bge-base-en-v1.5 | 768 | 英文 | 精度和速度平衡 |
| @cf/baai/bge-large-en-v1.5 | 1024 | 英文 | 精度最高,速度最慢 |
| @cf/baai/bge-m3 | 1024 | 多语言含中文 | 中文场景首选 |
选型看两点。语言决定选哪个系列,中文内容用 bge-m3,纯英文按精度需求选 small 或 base。维度影响存储成本和检索速度,维度越高信息越丰富但存储和计算开销越大。
基础嵌入调用
调用嵌入模型传 text 参数,可以传单个字符串,也可以传字符串数组批量处理。
export default {
async fetch(request, env) {
const embeddings = await env.AI.run("@cf/baai/bge-m3", {
text: ["今天天气很好", "我想买台电脑"],
});
return Response.json({
shape: embeddings.shape,
// 向量太长只展示前 5 个数字
preview: embeddings.data[0].slice(0, 5),
});
},
};
返回结构
{
"shape": [2, 1024],
"data": [
[0.012, -0.034, 0.056, "...共 1024 个数字"],
[0.089, 0.021, -0.043, "...共 1024 个数字"]
]
}
字段说明
| 字段 | 含义 |
|---|---|
| shape | 向量矩阵的形状,[文本数, 维度数] |
| data | 二维数组,每个元素是一段文本的向量 |
批量传 text 比循环单条调用效率高,网络往返少,神经元消耗也更省。一次最多建议几十条,太多可能超时。
pooling 参数
BGE 系列模型有个 pooling(池化)参数,决定怎么把模型中间层输出聚合成最终向量。它接受 cls 或 mean 两个值。
const embeddings = await env.AI.run("@cf/baai/bge-base-en-v1.5", {
text: ["Hello world"],
pooling: "cls",
});
两种池化方式对比
| pooling 值 | 原理 | 精度 | 注意事项 |
|---|---|---|---|
| cls | 取句首标记的向量 | 更准确 | Cloudflare 推荐用这个 |
| mean | 对所有 token 向量求平均 | 略低 | 默认值,向后兼容旧版本 |
这里有个坑。cls 和 mean 产生的向量不兼容,不能混用。如果索引库里的向量是用 mean 生成的,查询时也必须用 mean,否则相似度计算全是错的。新建项目直接用 cls,精度更好。老项目迁移要重新生成所有向量。
相似度计算
拿到向量后,怎么判断两段文字意思近不近。最常用的是余弦相似度(Cosine Similarity),衡量两个向量方向的夹角。夹角越小越相似。
余弦相似度公式
cos(A, B) = (A·B) / (|A| × |B|)
A·B 是两个向量的点积,就是对应位置相乘再求和。|A| 和 |B| 是各自的模长。结果范围是 -1 到 1,越接近 1 表示越相似。
三种相似度算法对比
| 算法 | 计算方式 | 适合场景 | 是否受向量长度影响 |
|---|---|---|---|
| 余弦相似度 | 向量夹角 | 语义搜索、文本匹配 | 否 |
| 点积 | 对应位相乘求和 | 已归一化的向量 | 是 |
| 欧氏距离 | 空间直线距离 | 聚类、异常检测 | 是 |
嵌入模型输出的向量通常没归一化,所以用余弦相似度最稳妥。如果预先把向量归一化(除以模长),点积的结果就等于余弦相似度,计算更快。
实现相似度计算
在 Worker 里写一个余弦相似度函数,比较两段文本的语义接近程度。
function cosineSimilarity(a, b) {
let dot = 0;
let normA = 0;
let normB = 0;
for (let 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));
}
export default {
async fetch(request, env) {
const { text1, text2 } = await request.json();
const embeddings = await env.AI.run("@cf/baai/bge-m3", {
text: [text1, text2],
pooling: "cls",
});
const score = cosineSimilarity(embeddings.data[0], embeddings.data[1]);
return Response.json({
text1,
text2,
similarity: score,
});
},
};
测试几组文本能直观看到效果
| 文本 A | 文本 B | 相似度(参考值) |
|---|---|---|
| 今天天气很好 | 今日气候不错 | 约 0.85 |
| 今天天气很好 | 这家餐厅很好吃 | 约 0.55 |
| 今天天气很好 | 服务器配置文件 | 约 0.25 |
注意相似度是相对值,不是绝对阈值。不同模型、不同 pooling 方式算出来的数值范围不一样。实际应用要先用自己的数据测一批,定一个合适的阈值,比如 0.7 以上算相关。
语义搜索示例
嵌入最典型的用途是语义搜索。把一批文档预先转成向量存起来,用户搜索时把查询也转成向量,找相似度最高的几篇返回。
// 假设这是预先存好的文档向量,实际项目存到 Vectorize 或 D1
const DOCS = [
{ id: 1, text: "Workers 是 Cloudflare 的边缘计算服务", vector: [] },
{ id: 2, text: "R2 是 Cloudflare 的对象存储", vector: [] },
{ id: 3, text: "D1 是 Cloudflare 的 SQLite 数据库", vector: [] },
];
export default {
async fetch(request, env) {
const { query } = await request.json();
// 把查询转成向量
const queryEmbedding = await env.AI.run("@cf/baai/bge-m3", {
text: [query],
pooling: "cls",
});
const queryVector = queryEmbedding.data[0];
// 算每篇文档和查询的相似度
const results = DOCS
.map((doc) => ({
id: doc.id,
text: doc.text,
score: cosineSimilarity(queryVector, doc.vector),
}))
.sort((a, b) => b.score - a.score);
return Response.json({ query, results });
},
};
实际项目中文档向量要存到专门的向量数据库,Cloudflare 自己有 Vectorize(Cloudflare 的向量数据库服务)可以做这件事。纯计算几百篇文档在 Worker 里算还能接受,上万篇就必须用向量数据库的索引能力了。
小结
文本嵌入把文字变成向量,让机器能从语义层面衡量文本的相似程度。Workers AI 提供 BGE 系列嵌入模型,中文用 bge-m3,英文按精度需求选 small 或 base。调用方式和文本生成类似,传 text 数组批量转换更高效。拿到向量后用余弦相似度算接近程度,配合向量数据库就能搭语义搜索和推荐系统。下一篇讲结构化输出,让模型按指定 JSON 格式返回数据,方便程序直接消费。
上一篇 流式输出
下一篇 结构化输出