id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
| id |
title |
type |
domain |
stack |
tags |
level |
prerequisites |
est_hours |
related |
week |
| backend.elasticsearch.scenario.kb-search-sync |
知识库混合检索:数据库到 ES 的同步、权限过滤、分词词典和检索效果评估 |
scenario |
backend |
| elasticsearch |
| postgresql |
| mq |
| rag |
|
| 混合检索 |
| 数据同步 |
| CDC |
| outbox |
| 权限过滤 |
| IK 词典 |
| 召回率 |
|
intermediate |
| backend.elasticsearch.search-hybrid |
| ai.rag.hybrid-rerank |
|
2.5 |
| backend.elasticsearch.interview |
| ai.rag.scenario.permission-aware-kb |
| backend.mq.reliability |
|
|
知识库混合检索:数据库到 ES 的同步、权限过滤、分词词典和检索效果评估
业务背景与问题
企业知识库 RAG 一开始只用了 pgvector 向量检索,问题:
- 用户搜产品型号、错误码、合同编号,向量检索经常找不到;
- 文档在后台修改后,问答仍引用旧内容;
- 偶尔检索到用户无权查看的部门文档;
- 每次调整分词或检索参数,不知道效果是变好还是变差。
约束
- 文档的主数据在 PostgreSQL(含权限)。
- 修改后 1 分钟内可检索到。
- 不能引入复杂的大数据组件。
方案设计
- 检索层:ES 索引同时保存分块的文本(IK 分词 + 业务词典:产品型号、内部术语)和向量;查询时 BM25 和 kNN 两路检索,应用层 RRF 融合,再用重排模型取前 5。
- 权限:每个分块冗余
tenant_id、dept_ids、visibility 等 keyword 字段;查询时把当前用户的权限转换成 filter,两路检索都带上;返回前再按数据库做一次最终校验(防止同步延迟导致的权限变更未生效)。
- 同步:
- 文档修改时在同一个数据库事务中写入 outbox 表;
- 后台任务读取 outbox 发到消息队列;
- 消费者重新分块、计算向量、按“文档 id + 分块序号”写入 ES(幂等),删除多余的旧分块;
- 失败重试,超过次数进入死信并告警;
- 每天对账:数据库与 ES 的文档版本号比对,修复差异。
- 变更映射:通过别名 + 新索引重建,不停机。
- 评估:维护一份标注集(问题 → 应该命中的文档),每次调整分词词典、权重、
num_candidates 都计算召回率@K 和 MRR,比基线差就不上线。
关键代码
权限条件同时作用于两路检索:
完整可运行的基础示例:examples/hybrid-search;权限设计详见权限感知知识库。
取舍
| 选择 |
优点 |
缺点 |
| ES 做混合检索(本方案) |
关键词能力强、过滤和聚合成熟、向量也支持 |
多一套集群和同步链路 |
| 只用 pgvector + PG 全文检索 |
组件少、强一致 |
中文分词和相关性调优能力弱一些 |
| 专用向量库 + ES |
各自最强 |
两套检索系统,复杂度最高 |
易错点
- 只同步新增和修改,忘记处理删除和权限变更。
- 消费者不是幂等的,重试后出现重复分块。
- 业务词典更新后没有重建索引(已写入的数据仍按旧分词)。
- 只凭几个例子判断效果,没有标注集和指标。
面试怎么讲
“纯向量检索对型号、错误码这种精确词召回很差,我们引入 ES 做混合检索:IK 分词加业务词典,BM25 和 kNN 两路召回,应用层 RRF 融合后再重排。权限字段冗余到每个分块,作为 filter 同时作用于两路检索,返回前再校验一次。同步用 outbox + 消息队列,消费者按文档 id+分块号幂等写入,每天对账。每次调参都用标注集跑召回率@K 和 MRR,比基线差不上线。”