Embedding API 中转常被当成“换个地址、便宜一点”的选择。但向量化是批量任务,一次迁移可能涉及几十万条文本;中转一旦限流、截断或悄悄换了模型,损失不是一次请求,而是整个索引的可信度。
本文讲四个问题:中转有哪些形态、批量请求怎么设计、计费怎么核对、迁移和验真怎么做。具体模型与价格以站内模型价格为准。
说明:OpenAI 及其他模型、服务名称是其各自权利人的商标,本文与相关厂商无隶属关系。中转市场形态变化较快,本文不构成对任何服务方的背书。
一、中转、代理、网关不是一回事
- 网络代理:只解决网络可达性,转发 HTTP 或 HTTPS 请求,Key 仍由自己管理。
- 商业中转:由服务方转发到上游,可能附带计费、限额、日志和发票;市场上也有共享或转售 Key 的形态,稳定性取决于运营方。
- 统一 AI 网关:提供 OpenAI 兼容入口,聚合多个模型,发放独立 Key,带控制台和用量统计。
- 自建代理:自己部署反向代理或网关,控制力最强,但运维和上游账号都要自己承担。
签约前先确认对方提供哪一层服务、Key 归谁、数据经过哪里。概念不清,后面所有对比都会失焦。
| 维度 | 官方直连 | 自建代理 | 商业中转 | 统一 AI 网关 |
|---|---|---|---|---|
| 计费口径 | 厂商账单,规则清楚 | 上游账单加自担成本 | 取决于运营方,需对账 | 站内统一账单 |
| 批量吞吐 | 受账号限额约束 | 可排队,仍受上游限制 | 可能共享池,需确认 | 看网关限额与调度 |
| Key 归属 | 自己持有 | 自己持有 | 可能共享或转售 | 通常独立 Key |
| 数据边界 | 自行评估 | 自行负责 | 需审查条款与留存 | 需审查条款与资质 |
| 发票与合同 | 视主体而定 | 无第三方合同 | 看服务方能力 | 通常可开票 |
| 运维成本 | 中 | 高 | 低到中 | 低 |
二、批量请求:先压测,再谈价格
Embedding 的吞吐由三个变量决定:单请求条数、单条文本长度、并发数。不同服务方对单请求条数、总 Token 和请求体大小都有上限,超出时可能返回参数错误,也可能被中间层直接截断。
压测要用生产数据。取一批真实文档块,分别按小批、中批、大批组装请求,观察单批耗时、P95 延迟、429 比例和错误类型。批量不是越大越好:批次过大时,一次失败重跑的成本高,超时也更难控制。
建议的分批流程:
- 先统计文本长度分布,按 Token 估算而不是按条数硬切。
- 设定单批上限,同时留出余量应对长文本。
- 并发从低开始,每轮增加,记录吞吐与错误率。
- 达到目标吞吐后停止加并发,避免把余量吃满。
- 把每批的输入哈希、Token 用量和结果落盘,便于对账和续跑。
三、重试与部分失败
Embedding 请求是只读操作,重试不会产生重复写入,但会把失败放大成账单。429 和瞬时 5xx 适合指数退避加随机抖动;401、403、404 和参数错误应立即停止,先修 Key、地址或请求体。
部分失败要单独处理。如果一批 200 条中有 3 条因超长被拒,不要整批无限重试;应拆出问题文本,修正或截断后单独提交,其余结果正常入库。入库用内容哈希做 upsert,确保重跑不会产生重复记录。
import hashlib
import os
import time
from openai import OpenAI
client = OpenAI(
api_key=os.environ["RELAY_API_KEY"],
base_url=os.environ["RELAY_BASE_URL"],
timeout=60.0,
max_retries=0,
)
MODEL = os.environ["EMBEDDING_MODEL"]
def key_of(text):
return hashlib.sha256(text.encode("utf-8")).hexdigest()
def embed_with_retry(batch, attempts=4):
for i in range(attempts):
try:
resp = client.embeddings.create(model=MODEL, input=batch)
store = {
key_of(batch[d.index]): d.embedding
for d in resp.data
}
return store, resp.usage
except Exception as exc:
if i == attempts - 1:
raise
time.sleep(min(2 ** i, 30))
texts = ["文档块一", "文档块二", "文档块三"]
store, usage = embed_with_retry(texts)
print(len(store), len(next(iter(store.values()))), usage)
四、计费透明度
Embedding 通常按输入 Token 计费,没有输出 Token,这一点和聊天模型不同。对账时要问清:
- Token 如何计算,是否与厂商 tokenizer 一致。
- 失败请求、重试请求是否计费。
- 是否有批量折扣、缓存或阶梯价。
- 用量能否按项目、Key、模型导出。
- 预付款是否有有效期,余额能否退款。
- 发票内容、币种和汇率如何确定。
“倍率低”不等于总成本低。若中转把失败重试也计费,或用量统计与响应 usage 对不上,低价会被隐性损耗抵消。具体价格与规则以模型价格和对方账单为准,不要采信口头承诺。
五、Key 风险与数据边界
中转最容易被忽略的是 Key 归属。共享 Key、转售额度或来源不明的余额,可能带来四个问题:上游封禁时业务一起中断;多租户共用导致限流互相影响;请求内容经过第三方,日志和留存不透明;服务条款不允许转售时,纠纷缺少依据。
选服务方时至少确认公司主体、服务条款、隐私政策、数据保留位置、发票能力和故障联系方式。含个人信息、合同原文或密钥的文本,不要发给无法说明数据去向的中转。强监管场景优先评估直连、私有部署或可签数据处理协议的服务。
六、迁移清单
从直连迁到中转或网关,建议按顺序做:
- 盘点现有模型、维度、批量参数和索引结构,记录基线成功率和 P95 延迟。
- 用固定样本在直连上跑一遍,保存向量、usage 和耗时。
- 新建测试 Key,设置预算和告警,不要直接复用生产 Key。
- 只改
base_url和api_key,在预发环境跑通最小请求。 - 用同一批样本调用中转,比较维度、条数、usage 和余弦相似度。
- 灰度 5%–10% 的批量任务,观察 3–7 天,保留直连作为回退。
- 全量迁移后对账一周,确认账单、入库条数和失败重跑记录一致。
- 旧索引至少保留一个版本,确认检索效果稳定后再清理。
如果中转换的是不同模型,旧向量不能直接混用。应建立并行索引,全量重算后对比检索效果,再决定是否切换。
七、怎么验证一个中转是否靠谱
- 向量一致性:同一输入分别走直连和中转,比较维度与余弦相似度;明显偏离时先排查是否换模型或截断。
- 用量一致性:响应 usage 与后台账单逐批核对,重点看失败和重试是否重复计费。
- 延迟与错误率:用真实批量在晚高峰测试,记录 P50/P95、429 和 5xx 比例。
- 批量上限:确认单请求条数、总 Token 和请求体大小限制,避免生产批次被截断。
- Key 隔离:确认是否独立 Key、能否轮换、额度和限流是否与其他客户共享。
- 合同与支持:查主体、条款、发票和工单响应;只有个人联系方式、拒绝提供用量明细,都是红旗。
响应里的 model 字段不能单独作为证据,它可以被改写。组合验证比单点检查可靠。
八、什么时候不该用中转
数据敏感且无法审查留存策略、需要厂商直接支持、调用量很小、团队没有对账人力时,中转未必划算。中转也不能绕开上游限流和服务条款。若只是偶尔做小规模向量化,直连加简单脚本可能更省心。
需要统一入口和多模型对比,可以在中商起航 AI 网关创建测试 Key,先用固定样本验证向量一致性,再迁移批量任务;可选模型与计费方式见模型价格。
相关阅读:
常见问题
Embedding API 中转和聊天中转有什么不同?
聊天中转关注流式输出和首 Token 延迟;Embedding 中转关注批量吞吐、单请求上限、失败续跑和向量一致性。对账时还要注意 Embedding 通常只计输入 Token。
中转返回的向量和直连能混用吗?
只有在同一模型、同一版本的前提下才建议混用。不同模型的向量空间不同,即使维度相同也不能混入同一索引。迁移前用固定样本做余弦相似度对比。
批量请求失败一半怎么办?
先判断失败类型:限流和瞬时 5xx 可退避重试;超长或参数错误要拆出问题文本单独处理。用内容哈希做幂等键,重跑时只补缺失部分,避免重复入库。
中转计费按什么算?
多数按输入 Token 计费,但 Token 口径、失败与重试是否计费、是否有批量折扣,各家不同。以账单和用量明细为准,签约前把这些写进对比表。
迁移到中转需要重新向量化吗?
同一模型、同一版本通常不需要;换模型则必须重建索引。即使不需要重算,也建议保留旧索引和回退路线,并对固定样本做一致性验证。
