Embedding API 不生成文字,它把文本映射成一串数字。语义搜索、RAG、聚类、去重和推荐都靠这串数字工作。接入本身不难,难的是维度、批量、归一化和索引这几处细节,一旦处理错,检索结果会悄悄变差。
本文以 OpenAI 兼容接口为例,讲清请求与响应、Python 与 curl 调用、批量策略、向量存储、RAG 串联、常见报错和迁移检查。
说明:OpenAI、PostgreSQL 及其他文中提及的品牌均为其权利人的商标。本文仅用于技术说明,与相关厂商无隶属关系;接口、模型和价格以官方文档及站内页面为准。
一、接口与鉴权
OpenAI 兼容的 embedding 接口是:
POST https://ai.zhongshangqihang.com/v1/embeddings
Authorization: Bearer sk-你的密钥
Content-Type: application/json
Base URL 只写到 /v1,不要自己拼 /embeddings 后再交给 SDK。SDK 会根据方法自动补路径,重复拼接会得到 404。
请求体最少只需要两个字段:
{
"model": "text-embedding-3-small",
"input": ["退货运费谁承担", "退货的物流费由谁支付"]
}
input 可以是单个字符串,也可以是字符串数组。数组会按顺序返回多个向量,适合批量处理。空字符串通常会被拒绝,清洗阶段要过滤掉。
二、请求与响应字段
| 字段 | 位置 | 说明 |
|---|---|---|
model |
请求 | 必填,模型名以平台文档和 模型价格 为准 |
input |
请求 | 字符串或字符串数组;单条不要超过模型上限 |
dimensions |
请求 | 仅部分模型支持,用于缩短输出维度 |
encoding_format |
请求 | 通常为 float,也可用 base64 减少传输体积 |
data[].embedding |
响应 | 向量数组,顺序与输入一致 |
data[].index |
响应 | 输入在数组中的下标 |
usage |
响应 | prompt_tokens 与 total_tokens,用于对账 |
响应里的 index 很重要。批量请求失败重试或并发合并时,不要假设返回顺序永远和输入一致,按 index 对齐更稳。
三、Python 调用与相似度
安装 SDK 后,最小示例:
import os
from math import sqrt
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ZSQH_API_KEY"],
base_url="https://ai.zhongshangqihang.com/v1",
)
resp = client.embeddings.create(
model="text-embedding-3-small",
input=["退货运费谁承担", "退货的物流费用由谁支付"],
)
a, b = resp.data[0].embedding, resp.data[1].embedding
cos = sum(x * y for x, y in zip(a, b)) / (
sqrt(sum(x * x for x in a)) * sqrt(sum(y * y for y in b))
)
print(len(a), round(cos, 4), resp.usage.total_tokens)
这段代码同时验证了三件事:Key 可用、维度符合预期、两条相近文本的相似度合理。正式项目建议用 NumPy 或向量库自带的距离函数,不要在大批量数据上跑纯 Python。
四、维度、归一化与模型一致性
常见 OpenAI embedding 模型的维度:
text-embedding-3-small:1536 维。text-embedding-3-large:3072 维。text-embedding-ada-002:1536 维。
dimensions 参数可以把 text-embedding-3 系列的输出缩短,例如从 3072 降到 1024,以节省存储和带宽。缩短会损失一部分信息,是否值得要用自己的评测集验证,不要只看维度数字。
OpenAI 文档称其 embedding 已归一化到长度为 1,因此余弦相似度可以用点积直接算。但如果你用了自建模型、第三方模型或手动截断维度,就要重新做 L2 归一化。最稳妥的做法是:写入和查询使用同一套归一化逻辑,并在代码里写清楚。
还有一个硬约束:向量库里的所有向量必须来自同一个模型、同一维度、同一归一化方式。换模型后旧向量不能继续混用,否则相似度没有可比性。
五、批量请求怎么切
批量能减少 HTTP 开销,但请求不是越大越好。OpenAI 当前文档给出的边界是:单条输入不超过约 8K Token,单次请求所有输入合计不超过 300,000 Token;具体限制以服务商文档为准。
实践上按 Token 预算切批,而不是固定条数:
- 先清洗文本,去掉空白、重复和超长段落。
- 用分词器估算每条 Token,超长文本先切块。
- 按总 Token 预算组批,例如每批控制在几万 Token,留出重试余量。
- 对 429 和 5xx 使用指数退避重试,4xx 参数错误不要盲目重试。
- 记录每批的模型、维度、Token 和耗时,便于对账和复现。
重试要有幂等思路。可以用“文本哈希 + 模型名 + 维度”做缓存键,避免同一段文本重复计费。批量任务失败后,先确认哪些 index 已经成功,再补缺失部分。
六、存进向量库
以 PostgreSQL 的 pgvector 为例,表结构可以简化成:
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
content text NOT NULL,
source text,
embedding vector(1536)
);
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);
查询时用余弦距离:
SELECT id, content
FROM chunks
ORDER BY embedding <=> $1
LIMIT 5;
除了向量,至少还要存:原文、来源、所属租户或权限、切块位置、模型名和维度。没有这些元数据,回答无法引用来源,也无法做权限过滤。生产环境还要考虑索引类型、召回参数、备份和增量更新,不要等数据量上来才补。
七、RAG 串联顺序
一个能跑通的 RAG 流程通常是:
- 文档解析与清洗,去掉页眉页脚和重复内容。
- 按标题或段落切块,块之间保留少量重叠。
- 对每个块调用 embedding,批量写入向量库。
- 用户提问时,用同一个模型把问题向量化。
- 按余弦相似度召回 Top-K,再按权限和来源过滤。
- 需要时加关键词检索做混合召回,或加重排模型。
- 把检索结果和问题一起交给聊天模型,并要求带引用回答。
切块大小没有统一答案。中文文档常见的起点是 300 到 800 字,再根据问答效果调整。切得太大,检索不精准;切得太小,上下文不完整。与其纠结参数,不如先做一套小评测集,固定问题和标准答案,每次改流程都跑一遍。
八、常见报错与排查
401 Unauthorized:Key 错误、被删除、没带 Bearer,或环境变量为空。404 Not Found:Base URL 少了/v1,或路径重复拼接。model not found:模型名不对或账号没有该模型权限。400输入过长:单条超过模型上限,先切块再请求。400空输入:数组里有空字符串,清洗阶段过滤。429:频率或并发超限,降低并发并做指数退避。- 维度不一致:写入和查询用了不同模型或不同
dimensions。 - 相似度全是 0 或异常接近:检查是否归一化、是否拿错字段、是否把零向量写进库。
排查时先打印模型名、输入条数、单条长度和返回维度。大多数问题不是“模型效果差”,而是请求和存储的某个环节不一致。
九、迁移与上线检查
从官方 API 或其他网关迁移到 OpenAI 兼容网关,通常只改两处:base_url 和 api_key。但 embedding 有一个额外成本:模型或维度一变,历史向量就要重建。
上线前至少检查:
- 新平台是否支持你正在用的模型名和维度。
- 是否需要重新向量化全库,重建窗口和费用能否接受。
- 查询侧和写入侧是否同时切换,避免新旧向量混用。
- 是否有灰度方案,可以先切一部分流量对比召回效果。
- 监控是否覆盖错误率、延迟、Token 消耗和空结果率。
- 是否有回滚路径,旧 Key 和旧索引在切换后保留一段时间。
如果只是换 Base URL、模型和维度不变,迁移会简单很多;如果连模型一起换,就把它当成一次数据重建项目来排期。具体模型和价格以 模型价格 为准。
常见问题
Embedding 接口和 Chat Completions 有什么区别?
Embedding 返回固定长度的向量,用于检索、聚类和相似度计算;Chat Completions 返回生成的文本。两者鉴权方式通常相同,但请求体和响应结构完全不同。
一次可以传多少条文本?
接口接受字符串数组,但真正的限制是总 Token 和单条长度。OpenAI 当前文档给出的单次请求上限是 300,000 Token,单条约 8K Token。实际批次建议按 Token 预算切,不要卡着上限跑。
向量需要自己归一化吗?
如果平台明确说明向量已归一化,余弦相似度可以用点积计算。自建模型、手动截断维度或不确定来源时,写入和查询都应按同一方式做 L2 归一化。
换了 embedding 模型,旧向量还能用吗?
不能直接混用。不同模型的向量空间不可比,维度相同也不代表语义对齐。换模型后要重建索引,并在元数据里记录模型名和维度。
中文文本需要先分词吗?
通常不需要。现代 embedding 模型直接处理原始文本,分词反而可能丢失上下文。需要处理的是清洗、切块、去重和长度控制。
下一步
想先跑通一条请求,可以从 注册页 创建低额度 Key,把上面的 curl 或 Python 示例改成自己的文本。正式选型时再看 模型价格,并用一小批真实业务文本对比召回效果。价格和模型会调整,以站内当前展示为准。
相关阅读:
