向量检索面试题
向量检索面试题
Qdrant / 向量检索、Neo4j / 知识图谱各三档,每档 20 题。★ 是高频题。入门讲概念,中等讲实现,高难按「现象→定位→方案→取舍→验证」练口述。没有标「项目」的架构方案不代表项目已经采用。
向量检索 入门(20 题)
1. ★ 向量检索解决什么问题?
把文本、图片等变成数值向量,按距离找相近内容,适合表达不同但意思相近的查询。它找到的是模型空间里的相近,不自动等于事实正确,也不能替代精确的编号过滤。
2. ★ embedding 是什么?
模型把输入映射为固定维数的表示。入库和查询应使用兼容模型、维度及预处理;两个模型即使都是 1024 维,也不能直接混搜。模型版本要随索引记录。
3. 稠密向量和稀疏向量有什么区别?
稠密向量通常每维有值,擅长语义;稀疏向量只有少数非零维,可表达词项及权重,擅长实体名、编号等字面匹配。稀疏不一定就是 BM25,SPLADE 等也是稀疏表示。
4. ★ BM25 为什么适合补充语义检索?
它根据词频、逆文档频率、文档长度等给关键词匹配打分。产品型号和错误码可能被语义模型忽略,BM25 能补上;中文分词、同义词和停用词处理会影响结果。
5. Qdrant 的 collection、point、payload 是什么?
collection 类似一组统一配置的数据集合;point 包含 ID、向量和 payload;payload 存文档 ID、租户、来源等元数据。它能过滤,但不是关系数据库的外键和事务替代品。
6. ★ 余弦、点积、欧氏距离怎么选?
余弦关注方向,点积还受模长影响,欧氏距离看空间距离。先遵循 embedding 模型的训练与使用约定;向量归一化后它们可能产生等价排序,但分数范围和阈值仍有区别。
7. top_k 和分数阈值有什么区别?
top_k 控制最多取几条,阈值控制最低匹配程度,两者同时作用可能一条也没有。不同距离和模型的分数不可照抄;阈值应在代表性查询和无答案样本上校准。
8. ★ 为什么要分块?
长文档可能超过模型输入长度,整篇一个向量也会稀释局部信息。按标题、段落、表格语义切块,保留文档 ID、章节和原文位置,便于命中后扩展上下文并提供出处。
9. chunk overlap 有什么作用?
相邻块共享一点文字,减少信息在边界被切断的损失。重叠越多存储和重复召回越多;不能盲目固定一半重叠,应该结合段落结构和评测选比例。
10. ★ ANN 和精确搜索有什么区别?
精确搜索比较全部候选,成本高但可作为召回率基线;ANN 用索引缩小搜索范围,在速度、内存和召回率间取舍。评测 ANN 时,要在同样过滤条件下和精确结果比较。
11. HNSW 的基本思路?
构建多层邻近图,高层快速找到大致区域,低层逐步靠近查询点。它通常有较好延迟,但图索引占内存,构建和更新也有成本;不是把数据排序后做二分。
12. ★ payload filter 能做什么?
按用户、组织、项目、文档类型等限制候选范围。过滤条件应由服务端可信身份构造;不能接收模型给出的任意 org_id 就当授权通过。常用过滤字段可建 payload 索引。
13. upsert 为什么适合文档重试入库?
相同 point ID 再写会更新而不是新增一份,便于幂等重试。ID 应由稳定的文档版本和块标识生成;重切块后块数变少,还要清除旧版本残留,upsert 不会自动替你删旧块。
14. ★ 混合检索是什么?(项目)
同时做语义检索与关键词检索,再合并候选。项目文档链路把 dense 和 BM25 sparse 作为两路 Prefetch,交给 Qdrant 的 Fusion.RRF。不是先拿两路原始分数直接相加。
15. ★ RRF 是什么?
按名次而非原始分数融合:某条记录每进入一个榜单,就贡献 1 / (常数 + 名次),同一 ID 累加。它避开分数量纲差异,但不利用第一名比第二名强多少的信息。
16. ★ rerank 和 embedding 检索有什么区别?
embedding 用预计算表示快速粗选;reranker 同时看查询与候选,通常更慢但可做更细的相关性判断。常见流程是先召回较多,再重排少量;它不能把未召回的文档变出来。
17. Recall@K 和 Precision@K 怎么算?
Recall@K 是相关材料被前 K 条找回的比例;Precision@K 是前 K 条中相关材料的比例。必须先定义标注单位是文档还是块;同一篇的五个重复块不能当成五篇相关材料。
18. 为什么相似度不能当置信度?
分数反映模型和距离函数下的接近程度,不是答案为真的概率。一个完全错误的问题也会有最近邻;要结合出处、无答案识别和实际标注判断是否足够支持回答。
19. Qdrant 和 PostgreSQL 各负责什么?(项目)
向量索引负责召回,PG 保存业务归属、权限等权威状态。索引可能滞后,命中后仍要复核文档是否存在及可读;向量库出现旧 payload 不能让旧授权继续生效。
20. ★ 向量检索和知识图谱怎么配合?
向量先定位可能相关的实体或材料,图谱沿明确关系补上下文。回答「谁负责哪个项目」时图遍历有价值;只要找一段相似原文时,向量检索更直接,没必要为所有文本强建图。
向量检索 中等(20 题)
1. ★ HNSW 的 M 和 ef 分别控制什么?
M 影响每个节点连接数,通常越大图越占内存;ef_construct 影响建图质量和耗时;查询 ef 影响搜索范围、召回率和延迟。先固定数据集画召回率与 P95 曲线,再选参数。
2. payload 索引为什么能提速?
索引帮助估计和定位满足条件的点,避免过滤时反复检查大量 payload。不是每个字段都要建;高频租户/项目/状态过滤值得优先考虑,同时记录索引构建和更新成本。
3. ★ 先过滤还是检索后过滤?
检索时过滤能避免 top_k 被无权数据占满;检索后复核能处理索引过时。两者都要:先带可信过滤召回,再按权威源复核,必要时有上限地补召回,不能越权凑满 K。
4. 多租户放一个 collection 还是拆开?
共享集合便于统一运维,小租户成本低,但过滤和配额必须可靠;独立集合隔离强,集合与索引管理成本高。按租户大小和合规边界选择,可让大租户独立,小租户共享。
5. ★ 同一个 collection 能存多个向量吗?(项目)
可以用 named vectors 为不同表示配置名字和维度。项目图节点用 name-embedding 和 summary-embedding 两个名字,各自查询;它们不是简单拼接成一条长向量再算距离。
6. ★ 为什么不能把 BM25 分数加到余弦分数?
它们范围和分布不同,直接相加会让量纲较大的一路支配结果。可用 RRF,或在标注集上学习归一化权重;调整权重必须用固定查询集验证,不能只看一个示例。
7. 项目的两种 RRF 有什么区别?(项目)
文档 dense/BM25 在 Qdrant 服务端融合;图节点 name/summary 在 Python 里融合,_RRF_K=60。这个 60 是项目图融合的常数,不能说成所有 Qdrant 服务版本的默认值。
8. 召回 K 和最终 K 为什么分开?
最终只展示 5 条,不代表只需召回 5 条。重排、去重、权限复核都会损失候选;可以先取几十条再筛成 5 条,但要给成本与延迟设上限,并评测新增候选是否真正提高覆盖。
9. ★ 如何做文档更新的一致性?
PG 记录文档版本和入库状态,索引按版本写入并校验;新版本就绪后切换可见版本,再清理旧块。通过 outbox/重试/对账补偿跨库窗口,不把 PG 和 Qdrant 当成一个原子事务。
10. 怎么生成稳定的 point ID?
可由租户、文档 ID、版本、块标识生成 UUID,保持重试相同 ID。不要只用段落内容哈希,否则不同文档可能合并掉权限和出处;内容去重和业务身份需要分别设计。
11. 小块命中后为什么要扩展父块?
小块有利于定位,但单独一句可能缺主语、时间或表头。命中后按父章节取适度邻文,把来源位置一并带回;扩展仍需做权限检查并受上下文预算限制。
12. ★ MMR 解决什么问题?
在相关性与结果多样性间取舍,减少前几条都是同一段话。可在候选上迭代选取与已选集合不太重复的条目;不适合把精确查编号的结果强行多样化。
13. 查询改写怎样避免跑偏?
保留原始查询作为一路,改写只补同义表达、上下文缺失信息。对型号、日期、否定词做保真检查;各路带上查询版本便于对比,不能把模型猜测当成用户给定条件。
14. 向量缓存的键包含什么?
模型及版本、输入预处理版本、归一化后的文本哈希。结果缓存还需权限范围、文档索引版本和检索参数;只按问题缓存可能把 A 用户可见内容返回给 B 用户。
15. 量化怎么省资源?
将高精度向量近似压缩成更少位数,减少内存或计算;代价是距离误差和潜在召回下降。可扩大候选后用原始向量复算,量化前后都要在相同过滤条件下评测。
16. on_disk 就是不占内存吗?
不是。向量、索引和 payload 的磁盘配置分别影响工作集,操作系统页缓存仍占内存,随机读还受磁盘延迟影响。容量估算要分别列原始向量、图结构、payload 和缓存。
17. ★ 入库失败怎么重试?
分清解析、embedding、索引写入和状态回写阶段;以文档版本/分块 ID 幂等重试。不要失败后整个文档无限重跑;记录失败块、原因、次数和下次时间,区分限流与不可解析数据。
18. rerank 超时怎么降级?
保留粗排结果,给重排设置独立 deadline 和候选上限;超时按策略返回粗排并记录降级原因。权限复核属于必需步骤,不能和 rerank 一起跳过;记录质量与耗时损失。
19. 分页为什么不能反复取 top_k 后切片?
检索期间数据和近似排序可能变化,重复切片会漏或重;大 offset 还可能扩大预取成本。浏览全量数据用 scroll,搜索分页用服务支持的方式或短期冻结候选,明确一致性语义。
20. ★ 怎么建一个检索评测集?
覆盖同义表达、型号、否定、跨段、无答案、多租户和权限撤回。记录预期文档/片段及可见范围,同时看召回、排序、无答案误报、P95 和成本;保留一组不用于调参的样本。
向量检索 高难(20 题)
1. ★ 一亿条向量怎么估容量?
先算 条数×维度×每维字节,例如一亿×1024×4 约 409.6GB,仅是原始向量十进制大小。再加副本、HNSW、payload、WAL、迁移双份和磁盘余量;按实际样本测索引膨胀率。
2. ★ P99 从 100ms 涨到 2s 怎么查?
拆 embedding、过滤召回、网络、rerank、PG 复核耗时。看并发、磁盘缺页、索引构建和租户热点;用同一批 query 比较冷/热缓存。先找到慢段,再改索引参数或扩容。
3. ★ 过滤越严格反而越慢?
候选子集稀疏时,图搜索可能难以找到足够满足条件的点。检查 payload 索引、过滤选择性和实际执行路径;比较精确扫描小子集、增大搜索范围及租户分片,不能只说提高 ef。
4. embedding 升级怎么不停服?
新建版本索引,历史回填与增量双写并行,固定评测集比较新旧效果;灰度切读,留旧索引可回退。换模型时查询向量与索引必须一起切,不能只换查询端然后混用旧空间。
5. ★ 租户被移除成员后还搜到片段?(项目)
payload 可能滞后,缓存也可能含旧结果。按当前可信身份重新解析 read scope,命中后回 PG 复核文档归属与权限,无 original_id 的片段不返回;缓存键和失效机制也要跟着改。
6. 重排效果好但总延迟超标怎么办?
量化重排收益集中在哪类查询,再限制候选、批处理、分层路由和超时降级。比较最终答案质量与成本,避免为了平均 NDCG 提升,让精确查询也多等一次昂贵模型调用。
7. ★ 索引写成功但 PG 状态回写失败?
不要简单删掉向量,因为提交结果可能未知。用版本/幂等键重查索引和业务状态,再安全地完成或标记孤儿;后台对账清理。将「写入成功」与「可供查询」分成明确状态。
8. 删除 PG 文档却漏删向量怎么办?
先让业务删除状态立即参与权限复核,防止索引残留泄露;再可靠投递索引删除并对账。保留 tombstone 或删除版本,避免旧重试又写回;只发一次异步删除不够。
9. 热租户把整个集群拖慢怎么处理?
按租户看 QPS、候选量、重排 token 和尾延迟,再限并发与预算;大租户可独立分片/集合或实例。扩容前先处理无界查询和重试,不然只是把瓶颈推到 embedding 服务。
10. 多副本和读写一致性怎么选?
副本提高可用性和读能力,但更新传播、写确认和读一致性有取舍,具体能力按版本确认。权限撤回不依赖向量副本立刻同步,仍回权威源校验;用故障演练测恢复时的旧读窗口。
11. ★ ANN recall 很高但用户仍说找不到?
ANN recall 是相对精确向量邻居的接近程度,未必是业务相关性。检查解析漏表、切块、模型领域适配、权限误过滤和评测标注;分别测检索相关性与 ANN 算法召回,别混成一个指标。
12. 中英混合、代码、图片怎么统一检索?
先确认模型覆盖输入类型与语种,必要时不同表示分路召回再融合。代码保留符号和路径,图片带 OCR 与版面信息;不能把所有模态都当作截断后的普通中文句子。
13. 用户问题含多个约束,检索只命中一半怎么办?
把可精确判断的日期、对象、权限转成结构化过滤,语义部分可分解召回再验证约束。区分「材料分别相关」和「同一证据同时满足」;若证据不足,明确缺失条件。
14. ★ 怎么降低重复块挤占上下文?
按文档/父章节聚合,近重复检测后保留最有信息量的块,控制单文档份额;为被删块保留出处映射。用多样性和答案证据覆盖衡量,不只看返回条数有没有达到 K。
15. RRF 常数怎么调?
较小常数更重视榜首差距,较大常数让不同名次贡献更接近。固定各路候选深度,比较 NDCG、覆盖和无答案误报;常数与候选量应联合验证,不能因为图代码用 60 就所有链路照搬。
16. 索引重建期间怎样控制资源?
后台限速并预留磁盘与峰值内存,观察写延迟和查询尾延迟;必要时分片滚动重建。提前演练失败重试和回退,避免旧索引未清、新索引又重来导致容量翻倍后耗尽。
17. ★ 如何验证备份真的可恢复?
在隔离的本机实例恢复 snapshot,并核对 collection 配置、点数、payload 索引及代表性检索。再从 PG/原文补齐快照之后的增量;向量快照不包含外部原始文件和业务权限真相。
18. 检索服务超时能直接让模型凭知识回答吗?
按业务约定区分知识问答与必须有内部证据的问题。内部制度、合同数字等应返回证据暂不可用或降级检索;若允许通用回答,清楚说明来源边界,不能编内部出处。
19. 多路查询导致成本爆炸怎么办?
给查询改写数、每路候选、重排 token、总 deadline 设预算;简单查询走短路径,复杂查询按需扩展。监控每次多路的边际收益与失败率,避免递归改写和无界补召回。
20. ★ 面试问「你们怎么证明 RAG 有用」?
拿固定样本比较纯模型、关键词、向量、混合、混合加重排五条路径;看答案证据支持率、无答案拒答、延迟和费用。项目实现能讲源码;没有真实业务指标就讲评测方法,不编提升百分比。
项目事实与源码入口
核对日期:2026-10-21,路径相对 ../xknow-ai/。
| 事实 | 依据 |
|---|---|
| 文档稠密向量 + BM25 稀疏向量,两路 Prefetch,服务端 Fusion.RRF | app/extended/ext_vector.py 的 hybrid_search;BM25 使用 Qdrant/bm25 |
| 图节点 name-embedding / summary-embedding 分别检索,再在 Python 按排名融合,常数 60 | app/core/graph/graph_store.py 的 search_nodes_semantic(融合常数见 _RRF_K) |
| 检索参数可带 payload filter;授权不能只信 payload | app/extended/ext_vector.py、app/services/agent_read_scope.py、app/core/agent/tools/vector_tools.py;源码要求命中文档按 PG 归属复核 |
| 图写工具 update_node / update_relation / deprecate_node 仅 ariadne 可用,图只读工具已向主 Agent 放开 | app/core/agent/tools/__init__.py、graph_edit_tools.py |
| qwen3-vl-rerank 有模型迁移记录和历史 rerank profile 配置 | app/migrations/versions/f2a3b4c5d6e7_dashscope_model_pricing.py、app/scripts/legacy_config/models.yaml;实际当前模型由配置中心/数据库决定,不能仅据此断言运行时永远是它 |
官方核对入口:Qdrant 混合检索、Qdrant 过滤、Neo4j MERGE、Neo4j 执行计划。API 与可用功能要按实际服务版本核对。
第一部分:Qdrant 与向量检索
六、Qdrant(向量检索)
入门
- 向量数据库是干什么的? 存向量,按相似度找最近的 K 个(ANN,近似最近邻)。
- Qdrant 的基本概念? collection(类似表)、point(一条记录 = id + 向量 + payload)、payload(附带的 JSON 元数据,可过滤)。
- 相似度度量? Cosine(余弦,最常用)、Dot(点积)、Euclid(欧氏距离)。向量归一化后余弦等于点积。
中等
- HNSW 是什么? 分层可导航小世界图:多层图,上层稀疏用来快速定位大致区域,下层稠密做精细查找。参数:
m:每个节点连几条边(默认 16),越大越准、越占内存;ef_construct:建索引时的搜索宽度(默认 100),越大索引质量越好、建得越慢;ef(查询时):查询时的搜索宽度,越大越准越慢。召回率和延迟的主要调节旋钮。
- 带过滤条件的向量检索怎么做? 先建 payload 索引(
create_payload_index)。Qdrant 的 HNSW 是「可过滤」的:过滤条件很宽时在图上边走边过滤;很窄时(只剩几百条)直接对候选暴力算。没有 payload 索引,带过滤的检索会非常慢。我们的检索都带权限过滤(个人 / 组织 / 项目),这些字段都建了索引。 - 混合检索怎么做? 一个 point 同时存稠密向量和稀疏向量(BM25),用 Query API 的
prefetch分别召回,再fusion: rrf融合。我们就是这么做的。
高难(场景模拟)
场景:1 亿条文档片段,1024 维向量,要求 P99 < 100ms,带租户过滤
第一步,先算账(一定要先算,面试官最看重这个):
- 原始向量:1 亿 × 1024 维 × 4 字节(float32)≈ 400GB;
- HNSW 图:约 1 亿 × m(16) × 2(层0双倍)× 4 字节 ≈ 13GB(量级估算);
- 全放内存需要 400GB+,单机不现实。
第二步,压缩(量化):
- 标量量化(int8):每维从 4 字节变 1 字节,压到 约 100GB,精度损失很小(约 1% 召回);
- 二值量化(binary):每维 1 位,压到 约 12.5GB,32 倍压缩,精度损失大,适合高维模型(OpenAI 那种 1536 / 3072 维效果较好);
- 乘积量化(PQ):压缩比更高,精度损失也更大。
- 配合 oversampling + rescore:用量化向量召回 3 倍候选(比如要 10 条召回 30 条),再用原始向量(放在磁盘上)重新打分。量化向量常驻内存,原始向量
on_disk=true走 mmap。
第三步,分布式:
- Qdrant 集群模式:collection 分成多个 shard(
shard_number),每个 shard 多副本(replication_factor)。查询会发到所有 shard 再合并结果。 - 比如 6 个节点、12 个 shard、2 副本,每个节点承担约 2 亿 /6 ≈ 3300 万条(含副本)。
第四步,多租户:
- 少量大租户:按租户分 collection,或用**自定义分片键(shard key)**把同一个租户放在同一组 shard 上,查询只打到那几个 shard;
- 大量小租户:共用 collection,payload 里放
tenant_id并建索引,标记为租户字段(is_tenant=true),Qdrant 会按租户优化存储布局; - 绝对不要每个小租户一个 collection(collection 太多管理开销巨大)。
第五步,写入:
- 批量导入时先关掉索引构建(
indexing_threshold=0或m=0),全部写完再开,比边写边建快很多; upsert用批量(每批几百到几千),wait=false异步写;- 更新 embedding 模型 = 全量重建:新建一个 collection 后台写,写完用 alias 原子切换,旧的再删。
第六步,召回质量:
- 量化 + 低 ef 会掉召回 → 离线建一份标准问题集,用暴力检索结果当标准答案,测 recall@10,按目标调
ef和 oversampling; - 混合检索 + rerank 弥补向量检索的精确匹配短板。
一万 QPS 怎么扛? 加副本(读请求分摊到副本);热点查询结果缓存;embedding 计算本身也要算进延迟(自建 embedding 服务 GPU 批处理)。
