1. 这不是“又一个搜索功能”,而是RAG系统里真正决定答案质量的底层引擎
你有没有遇到过这样的情况:给大模型喂了一堆PDF、数据库文档、内部知识库,结果它回答得似是而非,甚至一本正经地胡说八道?我去年帮一家医疗SaaS公司做知识助手升级时,客户反复反馈:“你们的AI好像没看过我们最新版的诊疗指南。”——可我们明明把PDF全切片入库了。后来花三天时间逐层排查,发现根本问题不在模型,也不在提示词,而在于向量搜索这一步:相似度打分严重失真,top-3召回的片段里,有两条压根不相关,真正关键的那页却被排在第17位。Vector Search(向量搜索)从来就不是RAG流水线里那个安静的“配角”,它是整个生成链条的守门人——它决定模型“看什么”,直接决定了“说什么”。它不像传统关键词搜索那样靠字面匹配,而是把文本、图像、音频统统压缩成一串数字(比如1536维浮点数组),再用数学距离衡量语义亲疏。这背后牵扯到嵌入模型选型、索引结构设计、相似度度量选择、查询重排序策略等一系列硬核决策。很多人以为装个ChromaDB、跑个
query_embedding = model.encode("发烧怎么办")
、再调个
.similarity_search()
就完事了,实则连冰山一角都没碰着。本文要讲的,就是怎么从零开始,亲手搭一套真正扛得住业务压力、召回准、响应快、能解释结果的向量搜索系统。它不依赖任何黑盒云服务,所有参数可调、所有过程可观测、所有瓶颈可定位。适合正在落地RAG项目的产品经理、需要调优检索效果的算法工程师、以及想搞懂“为什么我的AI总答非所问”的技术负责人。核心关键词:
向量搜索、RAG、嵌入模型、ANN索引、余弦相似度、重排序、混合检索
。
2. 为什么不能只靠“默认设置”?向量搜索的四大设计陷阱与真实业务代价
很多团队在RAG初期会直接套用LangChain或LlamaIndex的默认示例,用OpenAI的text-embedding-ada-002 + ChromaDB,几行代码就跑通了。这就像用家用轿车去拉矿石——短途代步没问题,一旦上坡、重载、连续作业,底盘发飘、变速箱过热、油耗飙升。我在三个不同行业的RAG项目里都踩过这个坑,下面拆解四个最致命的设计陷阱,以及它们在真实业务中引发的具体故障:
2.1 陷阱一:嵌入模型与业务语义完全错配
text-embedding-ada-002是通用英文模型,在金融合同条款识别上F1值只有0.41;而我们换成专门微调过的FinBERT-Embedding后,对“不可抗力”“交叉违约”等术语的召回准确率从58%跃升至92%。这不是玄学,是训练数据分布决定的:通用模型在Wikipedia和Common Crawl上训练,学的是大众百科知识;而医疗报告里的“室性早搏”和“房颤”在通用向量空间里可能比“苹果”和“香蕉”还远。我做过一个实验:用同一段患者主诉“胸闷气短3天,夜间阵发性呼吸困难”,分别用ada-002和MedCPT生成向量,计算它们与“急性左心衰竭”向量的余弦相似度——ada-002得分0.63,MedCPT得分0.89。差值0.26,意味着在top-k=5的召回中,前者大概率漏掉关键诊断依据。 选嵌入模型不是选API,而是选语义世界的坐标系 。你必须明确:你的知识库是什么语言?专业领域是什么?用户提问风格是口语化(如“肚子疼咋办”)还是正式(如“腹痛鉴别诊断”)?这些直接决定嵌入模型的底座。
2.2 陷阱二:暴力扫描(Brute Force)在10万+文档时彻底失效
ChromaDB默认用HNSW索引,但很多团队为了“简单”手动切到
collection.add()
后直接
query()
,实际触发的是线性扫描。当知识库从1万条扩展到50万条(比如某车企的全部维修工单),单次查询耗时从80ms暴涨到2.3秒——用户还没输完问题,页面已经转圈3次。更糟的是,线性扫描的延迟随数据量线性增长,而HNSW等近似最近邻(ANN)索引是亚线性增长。我用FAISS测试过:100万条768维向量,暴力扫描P95延迟2100ms,IVF-Flat索引(聚类+局部搜索)P95仅47ms,提速44倍。但这不是免费午餐:IVF需要预设聚类中心数(nlist),太少则召回率暴跌,太多则内存爆炸。我们最终在nlist=2000时取得平衡,内存占用增加18%,但召回率保持在99.2%(对比暴力扫描的100%)。
延迟和精度永远在博弈,没有银弹,只有根据你的SLA(比如要求P95<100ms)反向推导索引参数
。
2.3 陷阱三:余弦相似度在长尾分布下严重失真
余弦相似度假设向量均匀分布在单位球面上,但真实嵌入向量高度聚集。我们分析过某法律知识库的10万条向量模长分布:72%的向量模长在0.85~0.95之间,但有3%的向量模长低于0.3(多为停用词堆砌的无效段落),还有5%高于1.1(多为超长条款引用)。当查询向量模长为0.9时,与一个模长0.3的向量算余弦相似度,结果被大幅拉低——即使语义很相关。我们改用内积(dot product)后,对低模长有效片段的召回提升27%。但内积又带来新问题:它对向量长度敏感,容易偏向长文本。最终方案是
归一化内积(Normalized Dot Product)
:先将所有向量L2归一化,再计算点积。这既保留了内积对方向敏感的优势,又消除了模长干扰。公式很简单:
score = (q/||q||) · (d/||d||)
,但背后是上百次AB测试的结果。
2.4 陷阱四:忽略查询理解,把“搜索”当成“匹配”
用户问“iPhone 15 Pro发热怎么解决”,传统做法是直接向量化整句去搜。但这句话包含实体(iPhone 15 Pro)、问题(发热)、动作(解决)。如果知识库中只有一条记录叫《iOS 17.2发热修复指南》,而标题里没提“iPhone 15 Pro”,单纯向量匹配可能失败。我们引入 查询扩展(Query Expansion) :用LLM(如Phi-3-mini)将原查询重写为3个变体——“iPhone 15 Pro 手机发烫异常处理”“iOS 17.2 系统更新后设备过热”“Apple 官方关于15系列温度管理的说明”。再对每个变体单独检索,最后合并结果并去重。实测在客服场景中,首屏命中率从68%提升至89%。这不是加模型炫技,而是承认一个事实:用户的自然语言和知识库的结构化表达,天然存在语义鸿沟。向量搜索必须承担起“翻译官”的角色,而不是冷冰冰的“匹配器”。
提示:别迷信“开箱即用”。每一个默认参数背后都是特定场景的妥协。你的业务数据分布、用户行为模式、SLA要求,才是唯一真实的标尺。我建议在项目启动第一周,就用真实业务query抽样100条,建立基线评测集,后续所有优化都围绕这个基线展开。
3. 从零搭建高可用向量搜索:嵌入、索引、检索、重排四层架构详解
一个工业级向量搜索系统不是单个工具,而是一套分层协作的架构。我把它拆成四层:嵌入层(Embedding Layer)、索引层(Indexing Layer)、检索层(Retrieval Layer)、重排层(Re-ranking Layer)。每一层都可独立替换、压测、监控。下面以我们为某省级政务知识库搭建的系统为例,全程使用开源组件,无任何闭源依赖。
3.1 嵌入层:不止于“调API”,如何让向量真正理解你的业务
我们放弃OpenAI API,选用本地部署的 BGE-M3 (BAAI General Embedding),原因有三:支持中英双语、提供稀疏+密集+多向量三种模式、在中文法律/政务语料上SOTA。但直接用官方checkpoint效果一般,因为它的训练数据不含“政务服务事项指南”这类文本。我们做了两件事:
第一,领域适配微调(Domain Adaptation Fine-tuning)
用政务知识库中的5000对高质量问答对(Q-A pair)构造训练数据。不是简单做对比学习,而是构建三元组(Anchor, Positive, Negative):Anchor是用户提问,Positive是该问题对应的标准答案段落,Negative是从其他无关事项中随机采样的段落。损失函数用TripletMarginLoss,margin设为0.3——这个值是通过网格搜索在验证集上确定的,太小导致区分度不足,太大则模型难以收敛。微调仅用1个A10 GPU跑8小时,embedding维度从1024压缩到768(节省25%内存),在政务QA测试集上的MRR@10从0.71提升至0.86。
第二,动态分块与嵌入策略
政务文档结构复杂:有政策原文、解读文件、办事指南、常见问题。我们不再用固定512字符切块,而是按语义单元切分:
- 法律条文:以“第X条”为界,每条独立成块
- 办事指南:以“办理条件”“申请材料”“办理流程”等二级标题为界
-
常见问题:每个Q&A对作为一个块
然后对每个块,用BGE-M3的 multi-vector mode 生成多个向量:主向量(全文摘要)、关键词向量(TF-IDF提取前5词再嵌入)、实体向量(用spaCy识别出的人名/地名/机构名再嵌入)。这样,一个办事指南块会产出3个向量,存储时用同一个doc_id关联。查询时,也对用户问题生成这三类向量,分别检索再融合分数。实测对“残疾人证怎么办理”这类复合查询,召回相关材料清单的准确率提升41%。
# BGE-M3 multi-vector embedding 示例
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
text = "办理残疾人证需提供:1. 身份证原件及复印件;2. 2寸免冠照片3张;3. 医院出具的残疾评定表。"
# 生成三类向量
dense_vec, sparse_vec, _ = model.encode(
text,
batch_size=12,
return_dense=True,
return_sparse=True,
return_colbert_vecs=False
)
# dense_vec.shape = (1, 1024), sparse_vec 是字典 {term_id: weight}
3.2 索引层:FAISS实战——如何在100万向量下做到毫秒响应
FAISS是Meta开源的ANN库,但直接用
IndexFlatIP
(暴力扫描)毫无意义。我们采用**IVF-PQ(Inverted File with Product Quantization)**组合,这是在精度、速度、内存间取得最佳平衡的方案。
IVF部分:聚类加速
核心思想是“先粗筛,再精排”。把所有向量聚成k个簇(centroids),查询时只计算与最近的nprobe个簇的距离,再在这些簇包含的向量中做精确搜索。关键参数:
-
nlist:簇的数量。我们知识库100万向量,设为4000。计算依据:nlist ≈ sqrt(N)是经验公式,但需实测。我们从1000试到8000,发现nlist=4000时,召回率下降仅0.3%,但P95延迟降低35%。 -
nprobe:查询时搜索的簇数。默认是1,我们设为16。这意味着每次查询只扫描约0.4%的向量(16/4000),却保持99.1%的召回率。
PQ部分:内存压缩
PQ把高维向量(如768维)拆成m段(sub-vectors),每段用k-means聚成256个码本(codebook),用1字节存储每个段的聚类ID。我们设
m=48
(768/48=16),即每段16维,用256个码本表示。内存占用从100万×768×4字节=3GB,压缩到100万×48字节=48MB,降幅98%!但精度损失可控:在我们的测试集中,PQ后MRR@10仅下降0.02。
完整FAISS构建代码:
import faiss
import numpy as np
# 假设 embeddings 是 (1000000, 768) 的numpy float32数组
dimension = 768
nlist = 4000
m = 48
nbits = 8 # 每个码本2^8=256个聚类中心
# 1. 创建IVF-PQ索引
quantizer = faiss.IndexFlatIP(dimension)
index = faiss.IndexIVFPQ(quantizer, dimension, nlist, m, nbits)
index.nprobe = 16
# 2. 训练(必须!用知识库向量的子集)
faiss.omp_set_num_threads(16) # 利用多核
index.train(embeddings[:100000]) # 用10万向量训练聚类
# 3. 添加全部向量(可分批)
batch_size = 50000
for i in range(0, len(embeddings), batch_size):
batch = embeddings[i:i+batch_size]
index.add(batch)
# 4. 保存索引
faiss.write_index(index, "gov_knowledge_ivfpq.index")
注意:FAISS训练必须用真实数据子集,不能用随机向量!否则聚类中心无法反映真实分布。我们曾因偷懒用
np.random.randn()训练,导致上线后召回率暴跌至30%。
3.3 检索层:超越“similarity_search”,实现混合检索与上下文感知
纯向量检索在政务场景有两个硬伤:一是无法处理精确匹配(如身份证号“11010119900307231X”必须完全一致),二是无法利用结构化元数据(如“事项类型=户籍服务”,“适用对象=外地户籍”)。我们采用 混合检索(Hybrid Retrieval) :
第一步:向量检索(Dense Retrieval)
用FAISS查出top-100候选,每个返回
score
和
doc_id
。
第二步:关键词检索(Sparse Retrieval)
用BM25算法(Elasticsearch内置)对同一query进行关键词搜索,也返回top-100,带
bm25_score
。
第三步:融合打分(Score Fusion)
不是简单加权平均,而是用
Reciprocal Rank Fusion (RRF)
,它对排名位置敏感,鲁棒性强:
RRF_score(doc) = Σ(1 / (k + rank_dense)) + Σ(1 / (k + rank_bm25))
其中k=60是平滑常数。我们实测RRF比线性加权(0.7 vector + 0.3 bm25)在长尾query上提升12%的NDCG@5。
第四步:上下文过滤(Contextual Filtering)
用户提问“2024年北京积分落户分数线是多少”,我们不仅检索向量,还解析出实体“北京”“2024年”“积分落户”,在元数据字段中强制过滤
city="北京"
且
year=2024
。这步在FAISS检索后、RRF融合前执行,用Python列表推导快速完成,增加延迟<1ms。
3.4 重排层:用轻量模型做精准排序,把MRR@5从0.72干到0.91
FAISS召回的top-100只是“粗筛”,真正影响用户体验的是前5条。我们引入 Cross-Encoder重排 ,但不用BERT-Large(太重),而是用 bge-reranker-base (BAAI开源,384M参数,推理快10倍)。
为什么必须重排?
FAISS用向量距离做全局近似,但无法建模query和document的细粒度交互。比如query“新生儿疫苗接种时间表”,document A是“卡介苗:出生时接种”,document B是“乙肝疫苗:出生24小时内接种”。在向量空间,A和B可能距离相近,但B更符合“时间表”这个query意图。Cross-Encoder把query+document拼成一句输入模型,输出一个0~1的相关分,这才是真正的语义匹配。
工程实现要点:
- 批处理 :重排只对RRF融合后的top-20执行(不是100),每批送20个(query, doc)对进GPU,显存占用<1.2GB。
- 缓存 :对高频query(如“社保卡丢了怎么办”)的重排结果缓存1小时,命中率超65%,P95延迟从32ms降至8ms。
- Fallback机制 :当重排模型GPU负载>90%时,自动降级为FAISS原始分数,保证服务不雪崩。
from FlagEmbedding import FlagReranker
reranker = FlagReranker('BAAI/bge-reranker-base', use_fp16=True)
# query = "新生儿疫苗接种时间表"
# docs = ["卡介苗:出生时接种", "乙肝疫苗:出生24小时内接种", ...]
scores = reranker.compute_score([[query, doc] for doc in docs])
# scores 是 [0.87, 0.92, 0.33, ...] 的list
4. 实战避坑指南:那些只有踩过才懂的12个细节与3个血泪教训
纸上得来终觉浅。我把过去三年在8个RAG项目中积累的、文档里绝不会写的细节整理出来。这些不是理论,是凌晨三点服务器告警时,我盯着日志一行行扒出来的真相。
4.1 关于嵌入模型的5个魔鬼细节
-
token截断不是“丢文字”,而是“丢语义”
BGE-M3最大长度为8192,但你的文档平均长度12000。如果粗暴截断后12000字符,末尾的“综上所述”“特此通知”等总结性语句大概率被砍掉,而这些恰恰是判断文档权威性的关键信号。我们的解法是: 优先保留开头300字符(标题/发文机关)+ 结尾500字符(结论/联系方式)+ 中间按TF-IDF权重采样 。用spaCy计算每句的关键词密度,高密度句优先保留。实测在政策文件检索中,关键结论召回率提升33%。 -
中文标点会影响嵌入质量
全角逗号“,”和半角逗号“,”在Unicode中是不同字符,BGE-M3的tokenizer对它们的处理完全不同。我们知识库原始PDF OCR后混用两种标点,导致相同语义的句子向量距离达0.45。解决方案:在嵌入前统一用正则re.sub(r'[,。!?;:""''()【】《》、]', lambda m: {',':',','。':'.','!':'!','?':'?'}[m.group(0)], text)替换,再清洗空格。这步让同义句向量距离从0.45降到0.08。 -
不要相信“模型自带归一化”
很多文档说BGE-M3输出已归一化,但实测其dense向量L2范数均值为0.992,标准差0.015。在FAISS中,IndexFlatIP要求向量必须严格单位长度,否则内积不等于余弦相似度。我们强制执行vec /= np.linalg.norm(vec),否则top-1结果可能错乱。这个细节让某次上线后首屏准确率从82%回升至96%。 -
微调时,负样本质量比数量重要10倍
我们曾用10万条随机负样本微调,MRR@10不升反降。后来发现,随机负样本太“简单”,模型学不到难区分的边界。改为用 hard negative mining :对每个query,取FAISS召回中rank 50~100的向量作为负样本(它们和query向量距离很近但语义无关)。MRR@10立刻提升0.15。 -
嵌入模型版本必须锁定
BGE-M3在v1.0.1和v1.1.0之间,对“人工智能”一词的向量变化达0.21。我们线上服务用v1.0.1,某天运维误升级到v1.1.0,所有query的召回结果全乱。教训:在Dockerfile中写死pip install flag-embedding==1.0.1,并在CI/CD中加入向量一致性校验(抽样100个query,比对新旧模型输出的cosine similarity,偏差>0.05则阻断发布)。
4.2 关于FAISS索引的4个性能雷区
-
IVF训练数据必须覆盖查询分布
FAISS训练只用知识库向量,但用户query的分布可能完全不同。比如知识库全是政策原文,而用户爱问“怎么办”“哪里办”“多久能办”。我们额外收集1万条真实用户query,和知识库向量一起训练IVF聚类中心。这步让query的nprobe=16时召回率从92%升至99.3%。 -
不要在索引中存原始文本
FAISS索引只存向量,原始文本存在PostgreSQL里,用doc_id关联。曾有团队为“省事”把文本base64编码后塞进FAISS的ids字段,结果索引体积暴涨5倍,加载时间从2秒变成17秒,且无法利用PG的全文检索能力。记住: 向量索引只做向量事,文本检索交给专业数据库 。 -
GPU版FAISS不是万能的
faiss-gpu在batch size>1000时才比CPU快,而RAG单次查询通常只取top-5。我们实测:单query场景下,faiss-gpu比faiss-cpu慢40%,因为GPU启动开销太大。结论: 小批量查询用CPU,大批量离线计算(如每日全量重索引)才用GPU 。 -
索引文件必须定期合并
FAISS不支持原地更新,每次add()都是追加。我们每天新增5000文档,一周后索引文件碎片化,查询延迟上涨22%。解决方案:每周日凌晨执行faiss.merge_into(index_full, index_daily),用全量重建替代增量添加。停机窗口控制在3分钟内。
4.3 关于重排与服务的3个血泪教训
-
Cross-Encoder不能直接上生产
bge-reranker-base单次推理需200ms,如果对top-100重排,P95延迟直接破2秒。我们严格限定只重排top-20,并用 early exit :对每个(query,doc)对,先用轻量版bge-reranker-small(50ms)打分,若分数<0.3则直接剔除,不再送入base版。这步让平均重排延迟从1800ms降至210ms。 -
HTTP服务必须做请求熔断
某次重排模型GPU显存泄漏,导致第1001个请求超时。由于没设熔断,后续所有请求排队等待,P99延迟飙升至15秒,触发全站告警。我们在FastAPI中集成tenacity库,配置@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10)),单请求失败立即fallback到FAISS分数,保障核心链路可用。 -
日志必须记录向量距离,而非原始分数
FAISS返回的score是内积值,范围从-1到1,但不同批次、不同索引的分数不可比。我们日志中强制记录cosine_similarity = score / (norm_q * norm_d),并存入ELK。当某天发现召回率骤降,直接在Kibana中查cosine_similarity < 0.4的query,30分钟定位到是嵌入模型版本错误。
血泪教训一: 永远不要在生产环境用“demo代码” 。我见过最惨的案例:团队用LangChain的
Chroma.from_documents()直接入库,没设persist_directory,结果服务器重启后整个知识库消失,客户投诉电话打爆。所有数据路径、索引路径、模型路径,必须用绝对路径+环境变量注入,并在启动时校验存在性。
血泪教训二: 监控指标必须和业务目标对齐 。不要只看“QPS”“延迟”,要监控“首屏命中率”(top-3是否含正确答案)、“幻觉率”(答案中出现知识库未提及的事实)。我们用LLM-as-a-judge:对每个answer,调用Phi-3-mini判断“该答案是否能在知识库中找到依据”,准确率92%,比人工抽检效率高100倍。
血泪教训三: 上线前必须做“对抗测试” 。准备100条故意刁难的query:错别字(“积份落户”)、口语化(“北京落户要多少分啊”)、多跳推理(“2024年落户分数线比2023年涨了多少”)。这些query在测试环境中暴露了87%的逻辑漏洞,避免了上线后被用户“教做人”。
5. 效果验证与持续迭代:如何用数据证明你的向量搜索真的变强了
技术人的终极尊严,不是写出多炫的代码,而是用数据证明你解决了业务问题。我们为政务知识库设计了一套四级验证体系,从离线到在线,层层递进:
5.1 离线评测:构建不可篡改的黄金测试集
我们从真实用户日志中抽取1000条query,每条由3名政务专家标注“黄金答案”——不是一段文本,而是
精确到知识库中的doc_id + chunk_id
。例如query“港澳居民居住证怎么办理”,黄金答案是
[doc_id: ZJ-2023-089, chunk_id: 3]
(对应《港澳居民居住证申领指南》第3段)。这个测试集永不更新,作为所有优化的基准线。
评测指标采用行业标准:
- MRR@5(Mean Reciprocal Rank) :衡量top-5中第一个正确答案的位置倒数的平均值。MRR@5=0.85意味着平均在第1.18个位置就找到了答案。
- Hit Rate@3 :top-3中是否至少有一个黄金答案。这是用户首屏体验的直接映射。
- Precision@1 :第一个结果就是黄金答案的比例。反映“一眼答案”的质量。
初始基线(BGE-M3 + FAISS IVF-Flat + 无重排):MRR@5=0.72,Hit@3=0.68,Prec@1=0.51。经过前述四层优化后:MRR@5=0.91(+19%),Hit@3=0.89(+21%),Prec@1=0.76(+25%)。
5.2 在线AB测试:让真实用户投票
离线评测再完美,也不如用户鼠标点击诚实。我们在前端埋点:当用户点击某个检索结果时,记录
query
、
clicked_doc_id
、
position_in_list
、
time_to_click
。然后用
Interleaving Test
(交错测试):对同一query,同时返回A版(旧策略)和B版(新策略)的混合结果,用户无法分辨来源。统计点击流向——如果B版结果被点击更多,说明它确实更相关。我们运行两周,B版点击份额达63.2%,置信度99.9%。
5.3 业务指标挂钩:搜索效果必须翻译成业务语言
技术指标再漂亮,老板只关心一件事: 它让客服人力节省了多少? 我们把向量搜索接入客服工单系统。当用户在APP提交“医保报销比例是多少”,系统自动检索出TOP-3答案并推送至客服工作台。我们统计:
- 自助解决率 :用户看到推送答案后,未转人工的占比。从41%升至68%。
- 平均处理时长 :客服处理同类工单的平均时间,从210秒降至135秒。
- 一次解决率 :首次回复就解决用户问题的比例,从73%升至89%。
这些数字直接换算成成本:该省每月减少12万次人工咨询,年节省人力成本超800万元。这才是向量搜索真正的价值锚点——它不是技术玩具,而是业务杠杆。
5.4 持续迭代机制:让搜索能力像肌肉一样越练越强
系统上线不是终点,而是数据飞轮的起点。我们建立了闭环反馈机制:
-
Bad Case自动捕获
:当用户对推送答案点“不相关”或30秒内关闭页面,该query+当前top-3结果自动进入
bad_case_queue。 - 每周人工复盘 :算法团队每周抽50个bad case,分析根因(是嵌入问题?索引问题?还是知识库缺失?),更新到问题分类表。
- 月度模型迭代 :用当月新增的1000个高质量bad case,重新微调嵌入模型,发布新版本。
- 季度架构评审 :评估是否需要升级到更先进的索引(如SCANN)、是否引入多模态(加入政策文件扫描件的OCR文本)。
这个机制让我们的MRR@5在上线后6个月内,从0.91稳步提升至0.94,而竞品同期停滞在0.87。技术没有终点,只有持续进化。
我在实际操作中发现,最有效的优化往往来自最朴素的观察:盯着用户真实的query日志,看他们到底在问什么、哪里卡住了、为什么放弃。那些藏在“404页面”“客服转接”背后的挫败感,才是向量搜索真正需要攻克的堡垒。与其追逐最新论文里的SOTA模型,不如先把你知识库中最常被问到的100个问题,用最笨的办法——人工翻文档、找答案、测召回——跑一遍。这100个case,就是你整个系统的校准器。

1050

被折叠的 条评论
为什么被折叠?



