切换主题
长期免费的Embedding向量化API——零成本基建系列
做知识库、语义搜索或 RAG 时,Embedding 往往是第一笔容易被忽略的成本。把文本切块以后,每一块都要变成向量;文章多一点、重新建一次索,调用次数很快就上去了。
这次只留下现在还能反复调用免费档、并且实际跑通的服务。注册送一笔 token 的试用不算进来,没测过的也不拿来凑表。测试用了同一组中英文语义检索样本:给“退款”问题找对应的退款政策,再和一段无关的服务器日志比较余弦相似度。
text
中文查询:我买了东西,想退货怎么办?
中文相关文档:退款政策:购买后30天内可申请全额退款。
中文无关文档:服务器日志保留七天,超过期限自动清理。
English query: I bought something and want a refund. How do I do that?
Relevant document: Refund policy: purchases can be fully refunded within 30 days.
Unrelated document: Server logs are retained for seven days and then removed.| 服务 | 这次实测模型 | 免费规则 | 中英文检索结果 |
|---|---|---|---|
| 硅基流动 | BAAI/bge-m3 | 当前价格页标为免费 | 两种语言都把退款政策排在日志前面 |
| Cloudflare Workers AI | @cf/baai/bge-m3、@cf/qwen/qwen3-embedding-0.6b | 每天 10,000 Neurons | 两个模型的中英文检索都通过 |
| Gemini Developer API | gemini-embedding-001、gemini-embedding-2 | Free Tier 可调用 | 两个模型的中英文检索都通过 |
这一轮不是基准测试,分数也不能跨模型横向比较。它只验证一件事:对同一模型来说,退款问题和退款政策的相似度高于和服务器日志的相似度。结果够用来排除“接口能返回向量但检索方向明显不对”的情况。
硅基流动:BGE-M3
硅基流动的价格页现在仍把 BAAI/bge-m3 标为免费,价格页。接口是 OpenAI 兼容风格,地址用 .cn:https://api.siliconflow.cn/v1/embeddings。bge-m3 单条输入上限是 8,192 token;Qwen3-Embedding 系列虽然能用更长上下文和自定义维度,但价格页没有明确标成免费,就不把它算进零成本方案。Embedding 接口文档
测试返回的向量长度是 1024。中文里,相关文档的余弦相似度是 0.6978,无关文档是 0.5045;英文是 0.6933 和 0.4575。差距不算夸张,但排序是对的。
js
const response = await fetch('https://api.siliconflow.cn/v1/embeddings', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SILICONFLOW_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'BAAI/bge-m3',
input: '退款政策:购买后30天内可申请全额退款。',
encoding_format: 'float'
})
});
const { data } = await response.json();
const vector = data[0].embedding;
console.log(vector.length); // 1024国内项目如果只想给网站或小工具补一个中文语义搜索,我会先用它。之前测语音接口时已经踩过一次域名问题:旧示例里的 .com 和现在接口文档指定的 .cn 不能混用,这里也一样。
Cloudflare Workers AI:两个模型都能用
Cloudflare Workers AI 的免费计划每天给 10,000 Neurons,UTC 零点重置。@cf/baai/bge-m3 和 @cf/qwen/qwen3-embedding-0.6b 目前都是每百万输入 token 消耗 1,075 Neurons。官方定价页
这次两个模型都返回 1024 维向量。BGE-M3 的中文、英文结果和硅基流动非常接近,毕竟跑的是同一个模型;Qwen3 的两组相关/无关差距更大一些:中文 0.5890 / 0.2353,英文 0.6228 / 0.1573。这不代表 Qwen3 一定更好,只说明在这四段很短的测试文本里,它把无关日志拉得更远。
Workers 里直接绑定 AI 后,调用不需要绕 REST API:
ts
export interface Env {
AI: Ai;
}
export default {
async fetch(request, env) {
const result = await env.AI.run('@cf/qwen/qwen3-embedding-0.6b', {
text: [
'我买了东西,想退货怎么办?',
'退款政策:购买后30天内可申请全额退款。'
]
});
return Response.json(result);
}
} satisfies ExportedHandler<Env>;如果要从本地脚本或别的服务器调用,换成 Cloudflare 的 REST API 即可。@cf/baai/bge-m3 是多语言模型,Cloudflare 的模型页给出的上下文窗口是 60,000 token;Qwen3 这次按接口返回的 1024 维数据使用即可,不要把不同模型生成的向量混到同一个索引里。BGE-M3 模型页
js
const url = `https://api.cloudflare.com/client/v4/accounts/${process.env.CLOUDFLARE_ACCOUNT_ID}/ai/run/@cf/baai/bge-m3`;
const response = await fetch(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ text: '服务器日志保留七天,超过期限自动清理。' })
});
const { result } = await response.json();
console.log(result.data[0]);我自己的网站已经在 Cloudflare Pages 上,因为之前接过 Worker、D1,所以比较顺手。
Gemini:001 和 Embedding 2 不是同一种写法
Gemini Developer API 的 Free Tier 现在可以调用 gemini-embedding-001 和 gemini-embedding-2。001 只做文本;Embedding 2 可以把文本、图片、音频、视频和 PDF 放进同一个向量空间。Gemini 定价页注明免费层提交的内容可能被用于改进产品,业务数据不适合直接拿去跑免费层。
我把输出维度固定为 768。001 的中文相关/无关分数是 0.7327 / 0.5867,英文是 0.7332 / 0.5480;Embedding 2 分别是 0.7450 / 0.5982 和 0.7837 / 0.5831。它们同样把相关文档排在前面。
这里有一个兼容性坑。gemini-embedding-001 可以用 taskType 区分 RETRIEVAL_QUERY 和 RETRIEVAL_DOCUMENT;Embedding 2 不接受这个参数,官方建议把查询角色写进文本。两个模型的向量空间也不兼容,已经入库的数据不能只改模型名就继续用,必须重新向量化。Embedding 文档
js
// gemini-embedding-001:查询和文档分别声明用途
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:embedContent?key=${process.env.GEMINI_API_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
content: { parts: [{ text: '我买了东西,想退货怎么办?' }] },
taskType: 'RETRIEVAL_QUERY',
outputDimensionality: 768
})
}
);
const { embedding } = await response.json();
console.log(embedding.values.length); // 768Embedding 2 的文本检索请求改成下面这样,查询和文档用不同前缀:
js
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-2:embedContent?key=${process.env.GEMINI_API_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
content: {
parts: [{ text: 'task: search result | query: 我买了东西,想退货怎么办?' }]
},
outputDimensionality: 768
})
}
);Gemini 2 的多模态也实际跑了一次。我用了前面文生图文章中生成的窗边橘猫 PNG,直接把图片的 base64 放进请求;再用中文和英文的“窗边橘猫”查询去匹配它,并和无关的服务器日志文本比较。中文查询对图片的相似度是 0.7086,对无关文本是 0.5772;英文是 0.7419 和 0.5525。两次都把图片排在了无关文本前面。
js
import { readFile } from 'node:fs/promises';
const image = await readFile('orange-cat.png');
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-2:embedContent?key=${process.env.GEMINI_API_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
content: {
parts: [{
inline_data: {
mime_type: 'image/png',
data: image.toString('base64')
}
}]
},
outputDimensionality: 768
})
}
);
const { embedding } = await response.json();
console.log(embedding.values.length); // 768这只验证了一张 PNG 和两条描述。把图片、PDF 和文本混在一起检索时,切分方式、图片质量和向量库的过滤条件都会影响结果;因此可不能这么简单的用在自己的知识库里哦。
API测试方式
测试脚本没有引入向量数据库,直接计算余弦相似度。做一轮很小的冒烟测试,这样最省事:
js
function cosine(a, b) {
let dot = 0;
let aa = 0;
let bb = 0;
for (let i = 0; i < a.length; i += 1) {
dot += a[i] * b[i];
aa += a[i] * a[i];
bb += b[i] * b[i];
}
return dot / Math.sqrt(aa * bb);
}
console.log(cosine(queryVector, refundVector));
console.log(cosine(queryVector, logVector));真正做知识库时,别只拿一条退款政策验收。应该把自己的标题、缩写、产品名、错别字和跨段提问都放进去,再看召回结果。
怎么选
中文网站或国内小工具,硅基流动的 BGE-M3 最省事。已经在 Cloudflare 上开发的,直接用 Workers AI,少维护一个供应商的 Key。需要多模态检索,或者后面想把图片、PDF 一起接进来,可以从 Gemini Embedding 2 开始试。


