turbopuffer 于 2026 年 9 月 29 日上线原生嵌入:在文本属性的 schema 里加一个 embed 字段,Linear 每次查询省下 150 毫秒、Readwise 中位嵌入延迟降到八分之一,前置部署 Forward Deployed Engineer(FDE)要照抄的是 30 行嵌入批次与 4 个嵌入属性这两个硬上限
turbopuffer 于 2026-09-29 上线原生嵌入:Linear 每次查询省下 150 毫秒,Readwise 中位嵌入延迟降到八分之一。
这条教程落在「前置部署」Forward Deployed Engineer(FDE)的检索层交付段:企业 RAG enterprise RAG 的第一跳永远是嵌入管道,而「前置部署」Forward Deployed Engineer(FDE)在客户现场最常被投诉的就是这一跳的延迟与 429 错误。把嵌入搬进搜索引擎本身、让向量与源文档存在同一行里,是「前置部署」Forward Deployed Engineer(FDE)可以少维护一个服务的选项,代价是把模型选型这件事从可随时替换的服务变成不可轻改的数据决策。
改动前的做法是 BYOE,也就是自带嵌入(bring your own embeddings)。turbopuffer 官方博文写明:嵌入在 turbopuffer 之外的机器上完成,对查询计划器完全不可见;更麻烦的是嵌入与查询执行被序列化——在客户端拿到查询向量之前,turbopuffer 无法给任何文档向量打分,客户端也只能干等。原来的分布式 trace 是三段串行:先跑 embedding,再发 query,两段时间没有重叠。对「前置部署」Forward Deployed Engineer(FDE)来说,这意味着延迟预算里有一段完全不在自己掌控之内,而且失败要自己重试。
新做法是「原生嵌入」native embedding:在属性 schema 上写一个 embed 字段指定模型,之后对该属性的写入与查询都会即时转成向量,不需要写任何对接嵌入服务商的集成代码。这篇博文由 turbopuffer 工程师 Ben Linsay 署名发布,发布日期是 2026 年 9 月 29 日。官方给出的机制解释是并行化:读「命名空间」元数据不依赖向量,所以可以放在 GPU 忙着做嵌入的同一条时间线上,trace 因此变成 S3 fetch 与 embedding 并行、scan 最后等向量。官方明确承认 scan 仍然要等嵌入完成,这次优化只是把依赖往后推,并称会继续下推。写入侧同样做了并行化。长期方向是在后台异步重嵌入整个命名空间,并借此在新模型上跑评估、自动迁移到表现最好的模型——这一步官方标注为「stay tuned」,尚未可用。
两个客户在 beta 阶段给出了数字。Linear 在嵌入管道上省掉 150 毫秒;Readwise 把中位嵌入延迟降低 8 倍,博文原文写作 reduced their median embedding latency by 8x。官方称原生嵌入正式可用后观察到更多类似收益,但没有给这批新数字。turbopuffer 自述的规模是 1T+ documents、10M+ writes/s、25k+ queries/s,也就是 1 万亿级以上文档、每秒 1000 万次以上写入、每秒 25000 次以上查询;这三个数字全部是厂商自述,没有第三方复核。
写入侧的最小配置只有一处。
在 write 请求里给文本属性加 embed:请求体的字段是 upsert_rows 与 schema,schema 写成 text 的 type 为 string、embed 为模型名,例如 nvidia/nemotron-3-embed-8b;embed 也可以写成对象形式,同时指定 model、dims 与 attribute,把向量落到指定的向量属性名,例如 vector 或 embed_text。同一请求里要带 distance_metric 为 cosine_distance。schema 只需要在首次写入时给一次,之后的写入不必再传 schema,被设置了 embed 的文本属性会在每次写入时自动转成向量。原文的示例还保留了一个细节:自动生成的嵌入列可以直接用 include_attributes 读出来。
查询侧用 Embed 函数配 ANN 或 kNN,官方给了三种可以共存的模式。
第一种是写入与查询都交给 turbopuffer:rank_by 写成数组,第一项是文本属性名、第二项是 ANN、第三项是 Embed 函数加查询文本,返回字段用 include_attributes 显式列出 id、text 与自动生成的 embed_text。第二种是只让查询走原生嵌入 query-only:文档向量沿用自建模型,查询时在 Embed 函数里显式带上 model,并且查询指定的模型必须与文档所用的模型一致,否则官方原话说「结果可能会有些奇怪」。第三种是「预计算向量」自备:设置 embed 之后仍然可以在行里直接带 vector,带向量的行按原样存储、不会发给嵌入服务商,因此可以在同一次写入里混用自备向量与原生嵌入,把写入路径逐步切过来,不必一次性切换所有写入方。
第一个必须提前知道的坑:给属性设置 embed 不会自动重嵌入已有数据。官方原文写明只有后续写入受影响,所以一旦决定更换嵌入模型,就必须重新摄取该命名空间的数据。官方给的迁移顺序是先把应用的读切到查询时嵌入,再更新 embed 字段把写切过去。对「前置部署」Forward Deployed Engineer(FDE)来说,这句话翻译成项目动作就是:嵌入模型是一个必须在上线前评估阶段定死的交付决策,而不是一个可以事后替换的实现细节。另外官方提醒:把一个已有向量属性的表接上原生嵌入时,系统只校验模型是否支持现有向量的形状与数据类型,既不校验语义是否合理,也不会重新嵌入整个命名空间,所以开启之前要先自己确认在用的模型与原生嵌入模型是否同一族。
第二组坑是一批硬上限,写进容量规划才不会在压测时才撞上。
每个命名空间最多 4 个「嵌入属性」,官方限制表写作 Maximum number of embedded attributes per namespace 为 4;单次携带嵌入属性的 upsert 批次最多 30 行,限制表写作 Max upsert batch of embedded attributes 为 30 rows;单次 upsert 请求体上限 512 MB;每个命名空间最多 8 个向量列;稠密向量的「维度上限」是 10752。文档量上限是每个命名空间 128B 文档、256 TB,其中 128B 指 1280 亿条文档,需要靠「分片」sharding 撑起来;未固定 unpinned 的命名空间最多 16 个并发查询;官方标注的向量检索 recall@10 落在 90% 到 100% 区间,不是一个精确值。
第三组坑是输入超长与限流,这两个都不报明确错误,最容易在客户现场翻车。
超过模型上下文长度的输入默认被截断(truncated by default),也就是从文档开头开始嵌入,而不是拒绝整条写入——如果一篇长文档的关键结论在后半段,那部分会被静默丢掉,检索也会跟着漏。限流按组织维度管理,官方给出的新组织上限是每分钟 1024 个请求、每个模型每分钟 200 万个 token,超限返回 HTTP 429。官方强调两件事:额度不透明、没有任何接口能查还剩多少;而且每个请求是整体接受或整体限流,一个大批次写入可能比一个小查询更小,却仍然被拒。官方自称把长时间限流当成 bug,可以直接联系提额。对所有支持的原生嵌入模型,官方声明零数据保留 ZDR,但数据确实会从 turbopuffer 的云账户发到所选模型的服务商。
第四组坑关于混用与官方尚未支持的能力。
turbopuffer 不校验你提供的向量是否来自所配置的模型,也不校验它与源文本是否对应,官方明确警告在同一个属性里混用不同模型的向量会降低检索质量。只提供向量、不提供源文本的行虽然可以写入,但这些行之后无法被重新嵌入,所以手里有原文时应把原文一起写进去。官方目前只支持「定长向量」,会自行分块、产生可变数量向量的上下文模型(contextual models)尚未支持。多模态是进行中的工作,图像与视频没有专门的类型,必须把源数据存成 base64 编码的字符串属性。官方还把「自动缓存对同一文档的重复嵌入并将节省返还用户」列为未来版本的能力,也就是说现在重复嵌入仍然照价收费。
「嵌入计费」按模型嵌入的 token 数计算,不是「按行计费」,官方给的经验法则约 4 字节英文文本对应 1 个 token,嵌入消耗的 token 数会出现在写入与查询的响应里,可以直接抓出来对账。官方计算器的示例是:文档 3 kB、约 750 个 token,查询 512 字节、约 128 个 token,按每 100 万 token 收费 0.06 美元的 voyage/voyage-4 计算,每月 100 万次写入约 45 美元、100 万次查询约 8 美元,合计约 53 美元。可选的托管模型共 18 个,价格从 baai/bge-m3 的每 100 万 token 0.01 美元到 google/gemini-embedding-2 的 0.20 美元之间,另有 zeroentropy/zembed-1 标价 0.05 美元。voyage/voyage-4-large 标价 0.12 美元、RTEB 综合分 79.70 为官方表里最高,而 voyage/voyage-code-3 的 RTEB 只有 23.15,说明通用榜单分高并不等于代码检索分高,模型必须按自己的语料选。官方也承认选模型既难又难改,推荐的做法是跑评估而不是看榜单,RTEB 只当作判断不了时的参考。
适用条件必须说清楚,否则容易把一次优化做成一次退化。
原生嵌入适合官方描述的主流负载:每一次写入和每一次读取都需要一个新嵌入。反过来,如果查询向量可以在一段时间内、或者跨命名空间复用,就不要每次读取都重新嵌入——再优化的单次调用也不如彻底省掉这次调用。官方为此留了三种粒度:只让写入走原生嵌入、只让查询走原生嵌入、或者都不走。「前置部署」Forward Deployed Engineer(FDE)在客户现场判断的依据,就是这个负载里有没有可复用的查询向量:RAG 问答与代码检索通常没有,批量回填与固定模板查询通常有。另外如果系统已经在自建嵌入服务上做了领域微调(金融、法律、代码),把嵌入交给通用托管模型会损失这部分收益,官方也承认过去让客户自带嵌入正是为了保住这种灵活性。
作者与官方自己承认的局限,以及公开材料留下的空白,必须写全。
第一,150 毫秒与 8 倍都是客户自述的 beta 期数字,官方没有给测试口径、数据规模、基线定义,也没有第三方复核,不能当成承诺指标。
第二,官方没有公布嵌入在整个查询延迟里的占比,也没有给出嵌入本身的 p50 与 p99。读者算不出自己那套系统能省多少,只能自己按本集群的模型与语料实测。
第三,turbopuffer 自己的 tradeoffs 页面写明,把状态只放在对象存储上换来的代价是更高的写延迟,512 KB 的 upsert p90 为 248 毫秒;以及未缓存或未固定数据的偶发冷查询,100 万文档上 p90 为 1214 毫秒。原生嵌入降低的是嵌入这一跳的延迟,并没有改变这两条结构性约束。
第四,「限流阈值」不透明:官方只给了 1024 请求/分钟与每模型 200 万 token/分钟这两个新组织上限,既没有分模型的明细,也没有查询接口,容量规划只能靠压测反推。
第五,原生嵌入会把源文档与向量一起存进 turbopuffer,对「前置部署」Forward Deployed Engineer(FDE)来说这意味着数据驻留与出境要提前过合规;官方声明所有模型零数据保留,但推理服务商列表与地区覆盖仍要以官方子处理商页面为准。
该行动的是两类人,动作今天就能做。
第一类是在客户现场搭企业 RAG、被嵌入管道的延迟与重试折磨的「前置部署」Forward Deployed Engineer(FDE);第二类是已经自建嵌入服务、正在算「自己维护还是交给搜索引擎」这笔账的检索平台团队。
具体做法五步。第一步,先在现有命名空间上按 query-only 模式试,只把查询侧切到原生嵌入,自建模型写进去的文档不用动。第二步,用 Embed 函数跑一组离线评估,把 RTEB 高的模型与最便宜的模型各选一个做对照,官方本身就推荐用评估而不是榜单来定模型。第三步,模型定死后,再更新 embed 字段把写入侧切过去,切忌在评估阶段就放开写入。第四步,按 30 行的嵌入批次上限重排写入流水线,把大批次拆小,避免整体被 429 拒掉。第五步,把嵌入消耗的 token 数从写入与查询响应里采集出来,用它核对「嵌入计费」账单,再决定是否下线自建嵌入服务。这五步的核心是顺序:先读后写、先评估后定模型、先小批次后放量。
回滚很简单:把属性 schema 里的 embed 设为 null 即可。官方写明设置成功后,未来的写入需要显式传向量,未来的 ANN 查询需要在 rank_by 里传向量;已经嵌入的列,包括自动生成的嵌入列,不受影响,可以照常查询。也就是说回滚不需要重建数据,风险只落在下一次写入路径的切换上。建议提前写三条回滚阈值:嵌入服务商持续 429 且提额无果;离线评估显示新模型的召回明显低于自建模型;或者 token 账单超过自建嵌入服务的运维成本。FDE 在变更前就把这三条阈值与基线延迟一起写进交付文档,回滚之后才能立刻判断改动是否真的生效。
上线自检清单,逐条勾选,每条都对应上面某一段的结论。
- FDE(前置部署 Forward Deployed Engineer):先只切查询侧。
- FDE(前置部署 Forward Deployed Engineer):模型定死再切写入。
- FDE(前置部署 Forward Deployed Engineer):批次拆到 30 行以内。
- FDE(前置部署 Forward Deployed Engineer):记录嵌入 token 数。
- FDE(前置部署 Forward Deployed Engineer):核对 4 个「嵌入属性」上限。
- FDE(前置部署 Forward Deployed Engineer):核对 8 个向量列上限。
- FDE(前置部署 Forward Deployed Engineer):核对 10752「维度上限」。
- FDE(前置部署 Forward Deployed Engineer):确认长文档的截断位置。
- FDE(前置部署 Forward Deployed Engineer):429 时整批重试。
- FDE(前置部署 Forward Deployed Engineer):不混用不同模型向量。
- FDE(前置部署 Forward Deployed Engineer):有原文就写原文。
- FDE(前置部署 Forward Deployed Engineer):禁用时把 embed 置空。
- FDE(前置部署 Forward Deployed Engineer):不指望自动重嵌入。
- FDE(前置部署 Forward Deployed Engineer):迁移先读后写。
- FDE(前置部署 Forward Deployed Engineer):用 RTEB 做初筛。
- FDE(前置部署 Forward Deployed Engineer):用离线评估定选。
- FDE(前置部署 Forward Deployed Engineer):把峰值 QPS 对上「请求限流」。
- FDE(前置部署 Forward Deployed Engineer):查冷查询的 p90。
- FDE(前置部署 Forward Deployed Engineer):查 248 毫秒写延迟。
- FDE(前置部署 Forward Deployed Engineer):固定热度高的命名空间。
- FDE(前置部署 Forward Deployed Engineer):确认零数据保留条款。
- FDE(前置部署 Forward Deployed Engineer):多模态先转 base64。
- FDE(前置部署 Forward Deployed Engineer):先排除上下文模型需求。
- FDE(前置部署 Forward Deployed Engineer):对上 16 个并发查询。
- FDE(前置部署 Forward Deployed Engineer):对上 512 MB 请求体。
- FDE(前置部署 Forward Deployed Engineer):盯住 90% 到 100% 的召回区间。
- FDE(前置部署 Forward Deployed Engineer):和自建成本逐项对比。
- FDE(前置部署 Forward Deployed Engineer):保留 embed 置空的回滚开关。
- FDE(前置部署 Forward Deployed Engineer):记录改动前的基线延迟。
- FDE(前置部署 Forward Deployed Engineer):把阈值写进交付文档。
原始来源:
- https://turbopuffer.com/blog/native-embedding
- https://turbopuffer.com/docs/embedding
- https://turbopuffer.com/docs/limits
评论(0)
- 暂无评论。