在流媒体广告组踩了一年 search / RAG 的坑:Azure AI Search vs Lakebase
经验总结
先交代下背景,免得后面显得我在空谈。我在公司流媒体广告这块做数据/检索相关的东西,说白了就是给一堆广告主素材、投放上下文、用户行为做检索和召回,上层再挂 RAG 去做一些解释、问答、还有内部工具。这一年 search 这块的坑我基本都踩了一遍,正好趁周末整理一下,也算给后面接手的人省点事。
写得比较随意,想到哪写到哪,有不对的地方欢迎拍。
一、Azure AI Search 里 keyword / vector / semantic 到底差在哪
刚上手的时候我一直搞混一件事:semantic search 不等于 vector search。这俩在 Azure AI Search 里是完全两个层面的东西,很多人(包括当时的我)会以为「语义搜索」就是「embedding 搜索」,其实不是。
拆开说:
Keyword search(也就是 full-text) 底层是 BM25,走的是倒排索引。看的是词频、文档长度、还有整个 corpus 的 IDF 那一套。它的强项是精确匹配——广告位 ID、活动 code、错误码、品牌名这种,keyword 一打一个准。弱点也很明显,用户换个说法它就抓瞎,你搜「大促」它不知道「双十一」是一回事。
Vector / embedding search 就是把 query 和文档都过一遍 embedding model,然后在向量空间里做 ANN(Azure 用的是 HNSW)。它抓的是「意思」,query 和文档一个字不重叠也能召回。但它有个反直觉的坑:对精确 token 反而不敏感。你搜一个具体的 SKU 或者一串 ID,embedding 会给你一堆「语义上很像但根本不是那个东西」的结果。我们早期纯 vector 上线,被 PM 追着问为什么搜精确广告 ID 搜不到,就是这个原因。
Semantic search(semantic ranker) 是另一个 level 的东西。它不负责「召回」,它负责「重排」。Azure 的这个 ranker 本质是从 Bing 那边拿过来的一个 transformer cross-encoder,跑在召回结果之上。整个链路其实是分层的:L1 先用 keyword 或 vector 快速召回一批(上千条),L2 用 RRF 把不同召回路的结果合并,L3 才轮到 semantic ranker 上场,拿最靠前的大概 50 条做深度重排,还能顺手给你 caption 和 answer。
所以真相是:semantic ranker 是挂在 keyword / vector 之上的一层,它自己不召回。这点想清楚之后,很多设计上的纠结就没了。
二、那加了 embedding(hybrid)到底值不值
结论先放这:在我们的场景里,纯 keyword 或纯 vector 都不如 hybrid,但 hybrid 的收益 90% 来自那个 semantic ranker,而不是 embedding 本身。
Hybrid 的做法是 keyword 和 vector 两路并行召回,然后用 RRF(Reciprocal Rank Fusion)合并。RRF 有意思的地方是它不看两边 score 的绝对值,只看排名的倒数,所以你不用去纠结「BM25 的分和余弦相似度怎么对齐」这种头疼事,它天然把两个不同量纲的东西揉到一起。顺便提醒一句,hybrid 出来的 RRF score 数值普遍偏低,别被吓到,这是算法特性不是你配错了。
我踩过的一个具体坑:filter 和 semantic ranker 的配合。semantic ranker 最理想的输入是 50 条,你 prefilter / postfilter 如果切太狠,喂给 ranker 的可能就剩十几条,重排质量直接掉下来。我们后来统一改成尽量 prefilter + 保证 k=50 进 ranker,效果稳定不少。
所以如果你现在纯 keyword,我的建议是别急着上 embedding,先把 semantic ranker 加上,投入产出比最高;embedding 那一路是锦上添花,且要额外掏 embedding 的钱和向量存储的钱。
三、对比 Lakebase 的 search
后来我们评估要不要把一部分检索从独立的 search service 挪到 Lakebase,这里单独说下,因为思路很不一样。
Azure AI Search 是一个独立的检索服务,你要 ETL 把数据推进去、维护一套索引、query 的时候跨服务调用。Lakebase 走的是**「搜索直接长在数据库里」**的路子——它本质是个 serverless 的 Postgres(OLTP),search 能力是靠两个扩展加上去的:
lakebase_vector:ANN 向量检索,是 pgvector 的 drop-in 增强,类型和距离算子完全兼容。底层不是 HNSW 而是 IVF 分区 + RaBitQ 量化,好处是单索引能扛十亿级向量、建索引比 HNSW 快很多,而且索引是落在对象存储上的,scale-to-zero 之后再起来不用 warmup。
lakebase_text:把 BM25 做进了 Postgres,兼容标准 tsvector,但换掉了 GIN,针对对象存储的顺序读做了优化,不吃那么多内存。
Hybrid 也是 RRF,但关键区别在于——它是在一条 SQL 里完成的。向量、关键词、再 join 你的业务表、再按 tenant 过滤,一个 query 全干完。这个对我们这种要按广告主/租户隔离、检索完还要 join 一堆 metadata 的场景,简直是降维打击,省掉了一大堆胶水代码。
对我们更实际的一点是它和 Lakehouse 打通带来的反馈闭环:可以零成本地 branch 出多 TB 的数据集去试不同的 chunk 策略和 hybrid 权重,agent 的反馈还能直接回流到 Lakehouse 做离线评估。这个后面第六节还会说。
那是不是无脑迁 Lakebase?也不是。Azure AI Search 那个 Bing 血统的 semantic ranker(L3 cross-encoder 重排)目前还是它的护城河,Lakebase 这边你想要同等的重排质量得自己接 reranker。所以我们最后的选择是混着来:召回和存储往 Lakebase 收敛(省钱、省胶水、和业务数据同源),重排质量要求高的链路继续用外部 reranker。
四、对工业级方案的一点看法 + 怎么设计省钱的 RAG
聊点主观的。现在市面上一堆「企业级 RAG 方案」,我的感受是大部分成本不是花在该花的地方。真正能把 cost 打下来的,就几个动作:
1. 别什么都塞 embedding。 向量存储和 embedding 调用是持续烧钱的,很多字段用 keyword 就够了。我们的做法是先跑纯 hybrid(keyword 召回 + semantic ranker 重排),只有 recall 确实上不去的字段才补 embedding。省下来的是真金白银。
2. 分层检索,别一上来就全量重排。 cross-encoder / reranker 很贵,一定要放在最后一层、只处理 top-K。前面用便宜的 BM25 / ANN 把候选从百万缩到几百,reranker 只碰最后 50 条。上面 Azure 那个 L1/L2/L3 就是这个思路,值得抄。
3. 缓存要做到 semantic 层。 广告场景里大量 query 是高度相似的(同一个 campaign 各种问法)。用 pgvector 做个 semantic cache,query 先去向量库找有没有「意思一样」的历史命中,命中就直接返回,省掉一整次检索 + 生成。这个 ROI 高得离谱,很多团队都漏了。
4. chunk 策略当成超参来调,而不是拍脑袋定死。 chunk size / overlap 对召回质量和 token 成本影响巨大。能像 Lakebase 那样零成本 branch 数据反复试当然最好,退一步至少也要有一套离线评估集,别上线了再靠 PM 投诉来调。
五、Python pipeline:Polars / DuckDB / Ray,和 Spark 的取舍
这块是我这一年感受最深的。不是所有 pipeline 都值得上 Spark。
Spark 强在真·大数据 + 集群,但它的启动开销、序列化开销、还有那套 JVM 调优,在中等数据量下完全是负资产。我们把一批「其实单机内存塞得下」的预处理从 Spark 迁走之后,速度和成本都好看很多:
Polars:DataFrame 层面直接替代 pandas / 中小规模 Spark,多线程 + lazy execution,同样的清洗逻辑常常快一个数量级,而且 API 干净。数据能塞进单机内存(现在动辄几百 G 内存的机器很常见)的场景,优先它。
DuckDB:当你的逻辑本质是 SQL(join、聚合、window),DuckDB 直接怼 Parquet 就能跑,不用起集群。我们很多「以前用 Spark SQL 写的中间表」现在就是 DuckDB 一把梭。
Ray:真正需要分布式、而且是 ML-heavy 的那部分——比如给几千万条素材批量算 embedding、批量推理——Ray 比 Spark 顺手太多。Spark 做 ML pipeline 一直很别扭(要么 UDF 绕,要么 pandas_udf 各种坑),Ray 就是为这个场景生的,task/actor 模型、GPU 调度、和 Python 生态无缝。想降 embedding 成本 + 提 scale 的话,把这段从 Spark 挪到 Ray 收益很直接。
我现在的心智模型大概是:能单机就 Polars / DuckDB,必须分布式且是 ML 计算就 Ray,Spark 留给真正的重型 ETL 和已有的 Spark 生态。别一把 Spark 走天下。
(顺带说下,embedding 批处理那段用 Ray 还有个隐藏好处:可以按 GPU 利用率动态调 batch,比 Spark 固定 partition 那套省卡。)
六、feedback loop / user context / session 怎么搭
检索做得再好,没有反馈闭环都是一次性的。这块我觉得比前面的检索选型还重要,但恰恰最容易被跳过。
Feedback loop:核心是把「用户对检索结果的反应」变成可训练/可评估的信号。我们的做法是——线上的点击、停留、后续 query 改写、以及 LLM 生成后的用户操作,全部打点回流。如果你用 Lakebase,这件事会顺很多,因为 agent 的反馈能直接落回 Lakehouse 做离线评估,不用再搭一套单独的数据回流管道。有了这个信号,你才能做 reranker 的持续微调、chunk 策略的 A/B、以及 hybrid 权重的调参。
User context:把用户/租户的画像、历史偏好当成检索的一部分,而不是事后拼 prompt。具体点就是 prefilter(按 tenant、按权限、按投放阶段先过滤)+ 上下文注入(把用户近期行为作为 query 的补充信号)。Lakebase 那种「search 和业务表同源、一条 SQL 里 join + filter」在这里特别香,你不用把 context 从另一个库捞出来再手动拼。
Session:多轮场景一定要持久化 session,别只靠内存。我们现在直接把每一轮的 message、检索到的 context、以及模型输出都存进 Postgres(Lakebase 本身就把 agent 的 chat session 持久化当成一等公民的用例),用户可以断了重连、agent 也能基于历史轮次推理。存下来的另一个好处是——这些 session 本身就是最好的评估数据和微调数据,和上面的 feedback loop 是一套东西。
一句话总结这节:检索、context、session、feedback 不是四个独立模块,是一个环。 数据同源(比如都在 Lakebase / Lakehouse)能让这个环转起来的成本低一个量级。
最后
大概就这些,都是这一年实打实趟出来的,不敢说是最优解,我们组的场景(流媒体广告、强租户隔离、检索后重 join)也未必和你的一样。几个我自己还在纠结、也想听听大家意见的点:
有没有人 Azure semantic ranker 和自建 reranker(比如 bge-reranker 系列)做过正经对比?质量差多少、成本差多少?
Lakebase Search 现在还是 beta,有上生产的老哥吗?大规模下 IVF + RaBitQ 的召回质量实测怎么样?
semantic cache 的命中阈值你们怎么定的?定松了乱返、定紧了没命中,这个度挺难。
抛砖引玉,欢迎评论区接着聊。有帮到的话点个赞攒攒积分(狗头)。
写得比较随意,想到哪写到哪,有不对的地方欢迎拍。
一、Azure AI Search 里 keyword / vector / semantic 到底差在哪
刚上手的时候我一直搞混一件事:semantic search 不等于 vector search。这俩在 Azure AI Search 里是完全两个层面的东西,很多人(包括当时的我)会以为「语义搜索」就是「embedding 搜索」,其实不是。
拆开说:
Keyword search(也就是 full-text) 底层是 BM25,走的是倒排索引。看的是词频、文档长度、还有整个 corpus 的 IDF 那一套。它的强项是精确匹配——广告位 ID、活动 code、错误码、品牌名这种,keyword 一打一个准。弱点也很明显,用户换个说法它就抓瞎,你搜「大促」它不知道「双十一」是一回事。
Vector / embedding search 就是把 query 和文档都过一遍 embedding model,然后在向量空间里做 ANN(Azure 用的是 HNSW)。它抓的是「意思」,query 和文档一个字不重叠也能召回。但它有个反直觉的坑:对精确 token 反而不敏感。你搜一个具体的 SKU 或者一串 ID,embedding 会给你一堆「语义上很像但根本不是那个东西」的结果。我们早期纯 vector 上线,被 PM 追着问为什么搜精确广告 ID 搜不到,就是这个原因。
Semantic search(semantic ranker) 是另一个 level 的东西。它不负责「召回」,它负责「重排」。Azure 的这个 ranker 本质是从 Bing 那边拿过来的一个 transformer cross-encoder,跑在召回结果之上。整个链路其实是分层的:L1 先用 keyword 或 vector 快速召回一批(上千条),L2 用 RRF 把不同召回路的结果合并,L3 才轮到 semantic ranker 上场,拿最靠前的大概 50 条做深度重排,还能顺手给你 caption 和 answer。
所以真相是:semantic ranker 是挂在 keyword / vector 之上的一层,它自己不召回。这点想清楚之后,很多设计上的纠结就没了。
二、那加了 embedding(hybrid)到底值不值
结论先放这:在我们的场景里,纯 keyword 或纯 vector 都不如 hybrid,但 hybrid 的收益 90% 来自那个 semantic ranker,而不是 embedding 本身。
Hybrid 的做法是 keyword 和 vector 两路并行召回,然后用 RRF(Reciprocal Rank Fusion)合并。RRF 有意思的地方是它不看两边 score 的绝对值,只看排名的倒数,所以你不用去纠结「BM25 的分和余弦相似度怎么对齐」这种头疼事,它天然把两个不同量纲的东西揉到一起。顺便提醒一句,hybrid 出来的 RRF score 数值普遍偏低,别被吓到,这是算法特性不是你配错了。
我踩过的一个具体坑:filter 和 semantic ranker 的配合。semantic ranker 最理想的输入是 50 条,你 prefilter / postfilter 如果切太狠,喂给 ranker 的可能就剩十几条,重排质量直接掉下来。我们后来统一改成尽量 prefilter + 保证 k=50 进 ranker,效果稳定不少。
所以如果你现在纯 keyword,我的建议是别急着上 embedding,先把 semantic ranker 加上,投入产出比最高;embedding 那一路是锦上添花,且要额外掏 embedding 的钱和向量存储的钱。
三、对比 Lakebase 的 search
后来我们评估要不要把一部分检索从独立的 search service 挪到 Lakebase,这里单独说下,因为思路很不一样。
Azure AI Search 是一个独立的检索服务,你要 ETL 把数据推进去、维护一套索引、query 的时候跨服务调用。Lakebase 走的是**「搜索直接长在数据库里」**的路子——它本质是个 serverless 的 Postgres(OLTP),search 能力是靠两个扩展加上去的:
lakebase_vector:ANN 向量检索,是 pgvector 的 drop-in 增强,类型和距离算子完全兼容。底层不是 HNSW 而是 IVF 分区 + RaBitQ 量化,好处是单索引能扛十亿级向量、建索引比 HNSW 快很多,而且索引是落在对象存储上的,scale-to-zero 之后再起来不用 warmup。
lakebase_text:把 BM25 做进了 Postgres,兼容标准 tsvector,但换掉了 GIN,针对对象存储的顺序读做了优化,不吃那么多内存。
Hybrid 也是 RRF,但关键区别在于——它是在一条 SQL 里完成的。向量、关键词、再 join 你的业务表、再按 tenant 过滤,一个 query 全干完。这个对我们这种要按广告主/租户隔离、检索完还要 join 一堆 metadata 的场景,简直是降维打击,省掉了一大堆胶水代码。
对我们更实际的一点是它和 Lakehouse 打通带来的反馈闭环:可以零成本地 branch 出多 TB 的数据集去试不同的 chunk 策略和 hybrid 权重,agent 的反馈还能直接回流到 Lakehouse 做离线评估。这个后面第六节还会说。
那是不是无脑迁 Lakebase?也不是。Azure AI Search 那个 Bing 血统的 semantic ranker(L3 cross-encoder 重排)目前还是它的护城河,Lakebase 这边你想要同等的重排质量得自己接 reranker。所以我们最后的选择是混着来:召回和存储往 Lakebase 收敛(省钱、省胶水、和业务数据同源),重排质量要求高的链路继续用外部 reranker。
四、对工业级方案的一点看法 + 怎么设计省钱的 RAG
聊点主观的。现在市面上一堆「企业级 RAG 方案」,我的感受是大部分成本不是花在该花的地方。真正能把 cost 打下来的,就几个动作:
1. 别什么都塞 embedding。 向量存储和 embedding 调用是持续烧钱的,很多字段用 keyword 就够了。我们的做法是先跑纯 hybrid(keyword 召回 + semantic ranker 重排),只有 recall 确实上不去的字段才补 embedding。省下来的是真金白银。
2. 分层检索,别一上来就全量重排。 cross-encoder / reranker 很贵,一定要放在最后一层、只处理 top-K。前面用便宜的 BM25 / ANN 把候选从百万缩到几百,reranker 只碰最后 50 条。上面 Azure 那个 L1/L2/L3 就是这个思路,值得抄。
3. 缓存要做到 semantic 层。 广告场景里大量 query 是高度相似的(同一个 campaign 各种问法)。用 pgvector 做个 semantic cache,query 先去向量库找有没有「意思一样」的历史命中,命中就直接返回,省掉一整次检索 + 生成。这个 ROI 高得离谱,很多团队都漏了。
4. chunk 策略当成超参来调,而不是拍脑袋定死。 chunk size / overlap 对召回质量和 token 成本影响巨大。能像 Lakebase 那样零成本 branch 数据反复试当然最好,退一步至少也要有一套离线评估集,别上线了再靠 PM 投诉来调。
五、Python pipeline:Polars / DuckDB / Ray,和 Spark 的取舍
这块是我这一年感受最深的。不是所有 pipeline 都值得上 Spark。
Spark 强在真·大数据 + 集群,但它的启动开销、序列化开销、还有那套 JVM 调优,在中等数据量下完全是负资产。我们把一批「其实单机内存塞得下」的预处理从 Spark 迁走之后,速度和成本都好看很多:
Polars:DataFrame 层面直接替代 pandas / 中小规模 Spark,多线程 + lazy execution,同样的清洗逻辑常常快一个数量级,而且 API 干净。数据能塞进单机内存(现在动辄几百 G 内存的机器很常见)的场景,优先它。
DuckDB:当你的逻辑本质是 SQL(join、聚合、window),DuckDB 直接怼 Parquet 就能跑,不用起集群。我们很多「以前用 Spark SQL 写的中间表」现在就是 DuckDB 一把梭。
Ray:真正需要分布式、而且是 ML-heavy 的那部分——比如给几千万条素材批量算 embedding、批量推理——Ray 比 Spark 顺手太多。Spark 做 ML pipeline 一直很别扭(要么 UDF 绕,要么 pandas_udf 各种坑),Ray 就是为这个场景生的,task/actor 模型、GPU 调度、和 Python 生态无缝。想降 embedding 成本 + 提 scale 的话,把这段从 Spark 挪到 Ray 收益很直接。
我现在的心智模型大概是:能单机就 Polars / DuckDB,必须分布式且是 ML 计算就 Ray,Spark 留给真正的重型 ETL 和已有的 Spark 生态。别一把 Spark 走天下。
(顺带说下,embedding 批处理那段用 Ray 还有个隐藏好处:可以按 GPU 利用率动态调 batch,比 Spark 固定 partition 那套省卡。)
六、feedback loop / user context / session 怎么搭
检索做得再好,没有反馈闭环都是一次性的。这块我觉得比前面的检索选型还重要,但恰恰最容易被跳过。
Feedback loop:核心是把「用户对检索结果的反应」变成可训练/可评估的信号。我们的做法是——线上的点击、停留、后续 query 改写、以及 LLM 生成后的用户操作,全部打点回流。如果你用 Lakebase,这件事会顺很多,因为 agent 的反馈能直接落回 Lakehouse 做离线评估,不用再搭一套单独的数据回流管道。有了这个信号,你才能做 reranker 的持续微调、chunk 策略的 A/B、以及 hybrid 权重的调参。
User context:把用户/租户的画像、历史偏好当成检索的一部分,而不是事后拼 prompt。具体点就是 prefilter(按 tenant、按权限、按投放阶段先过滤)+ 上下文注入(把用户近期行为作为 query 的补充信号)。Lakebase 那种「search 和业务表同源、一条 SQL 里 join + filter」在这里特别香,你不用把 context 从另一个库捞出来再手动拼。
Session:多轮场景一定要持久化 session,别只靠内存。我们现在直接把每一轮的 message、检索到的 context、以及模型输出都存进 Postgres(Lakebase 本身就把 agent 的 chat session 持久化当成一等公民的用例),用户可以断了重连、agent 也能基于历史轮次推理。存下来的另一个好处是——这些 session 本身就是最好的评估数据和微调数据,和上面的 feedback loop 是一套东西。
一句话总结这节:检索、context、session、feedback 不是四个独立模块,是一个环。 数据同源(比如都在 Lakebase / Lakehouse)能让这个环转起来的成本低一个量级。
最后
大概就这些,都是这一年实打实趟出来的,不敢说是最优解,我们组的场景(流媒体广告、强租户隔离、检索后重 join)也未必和你的一样。几个我自己还在纠结、也想听听大家意见的点:
有没有人 Azure semantic ranker 和自建 reranker(比如 bge-reranker 系列)做过正经对比?质量差多少、成本差多少?
Lakebase Search 现在还是 beta,有上生产的老哥吗?大规模下 IVF + RaBitQ 的召回质量实测怎么样?
semantic cache 的命中阈值你们怎么定的?定松了乱返、定紧了没命中,这个度挺难。
抛砖引玉,欢迎评论区接着聊。有帮到的话点个赞攒攒积分(狗头)。
已获得 109 大米


+2
共12条回复
✨ 您正在体验新版论坛UI

