企业知识库检索:把权限、切片和召回评估做成一条链路

从可追溯的文档切片出发,设计带权限的混合召回与重排流程,并区分 ANN 召回率和业务相关性,说明文档更新、撤权与模型迁移的处理边界。

·6 min
关键词检索与向量近邻检索分别召回候选,合并后经过权限控制返回结果。

员工搜索“报销发票抬头”,结果里出现一份已经作废的制度。另一个结果相关性更高,却来自他没有权限查看的部门目录。这样的检索系统即使返回得很快,也没有完成工作。

企业知识库需要把文档版本、访问权限和检索质量一起处理。更换嵌入模型只能改变其中一段。下面以内部制度和技术文档为例讨论最小架构,所有记录与参数均为设计示例。

搜索结果要带用户回到原文

切片记录既要告诉用户出处,也要让接入任务在原文变更后找到待重建的片段。固定长度截断无法独立承担这两项工作。

可以为每个切片保留如下信息:

{
  "chunk_id": "travel-policy:v3:section-2:chunk-1",
  "document_id": "travel-policy",
  "document_version": "v3",
  "section_path": ["差旅报销", "票据要求"],
  "source_locator": {
    "kind": "section",
    "value": "section-2"
  },
  "acl_version": "acl-8",
  "embedding_model": "model-version-placeholder",
  "content_hash": "hash-placeholder",
  "text": "示例正文,不是实际制度。"
}

内容接入流程负责填写版本和定位信息。PDF 可记录页码与区域,网页可记录稳定段落标识;若系统只能取得字符偏移,还需保存对应的文本版本。

切片应尽量保留标题层级、列表上下文和表格表头。把表格中的“300”与单位、适用对象分开,向量再相似也无法恢复它的业务含义。超长章节仍需要切分,但可以将章节标题附到子片段,避免孤立文本失去主语。

片段长度要按目标模型的 tokenizer 和评测结果决定。中文字符数不是 token 数;统一使用某个固定长度,也不能覆盖合同、表格和短 FAQ 的差异。重叠部分有助于保留上下文,但会增加重复结果和索引体积,重排后需要控制同一文档重复占位。

先授权,再把候选交给模型

身份服务给出用户可访问的集合,检索服务据此构造过滤条件。用户输入的部门名和模型对“我应该能看”的解释都不构成授权。

关键词召回和向量召回都要应用同一套授权约束。如果仅在最终页面隐藏无权结果,片段可能已经进入外部重排器、模型上下文、调试日志或缓存。安全边界应在这些环节之前。

只在全库 top-k 结果后过滤,还会损失检索质量:前几个名额可能被无权文档占据,过滤后可见候选不足。Elasticsearch 8.19 的 kNN 检索文档提供了检索过程中的过滤机制;实施时需确认所用 API 的过滤位置,不能把同名的后置过滤当成等价替换。

权限索引也会过期。撤权后不能等向量重建结束才停止披露。对权限变化敏感的知识库,可在向重排器、模型或用户输出内容之前,再向权威权限状态确认候选仍然可见;确认失败就拒绝输出该候选。共享缓存则必须包含有效的访问范围与权限版本,或只缓存不含受限内容的中间结果。

关键词和向量各自补一类漏检

向量召回擅长处理不同说法,但精确的错误码、工单号、函数名可能更适合关键词检索。例如“连接为什么一直断开”和“ERR_CONN_104”应分别进入质量评测,不能只测试自然语言问句。

可以先让关键词和向量各自生成候选,按切片 ID 去重,再融合排序。关键词相关性分数与余弦相似度使用不同的数值范围和分布,直接相加无法稳定解释。

可以从基于名次的 RRF 开始,计算示意如下:

score = map(default=0)
for ranking in [keywordResults, vectorResults]:
    ranking = deduplicateByChunkId(ranking)
    for rank, chunk in enumerate(ranking, start=1):
        score[chunk.id] += 1 / (c + rank)

merged = sortByScoreDescending(score)

c 是控制名次影响程度的正数,应固定后用评测集选择。某个切片未出现在一路结果时,该路贡献为零。相同分数再按稳定 ID 排序,便于比较版本。算法原理可参照 Elastic 的 RRF 说明

需要重排时,只对有限候选运行较贵的模型,并将重排开销计入总预算。重排器无法找回两路召回都没有提供的片段,所以优先调查候选缺失,再调整重排模型。

ANN 召回率回答不了业务问题

ANN 召回率衡量近似索引相对同一向量空间中精确近邻搜索的损失,适合用于选择索引参数。精确近邻本身仍可能与业务问题无关。

业务 Recall@k 则需要人工判断哪些文档能回答问题。如果一个问题有四份相关文档,前 k 个去重结果找到其中三份,该问题的文档级召回率就是 3/4。这是定义示例,不是系统成绩。切片级与文档级指标应分开,避免同一篇文档拆成许多片段后“提高”命中数。

评测集要包含有明确答案的问题、需要跨段落的问题、只有过期答案的问题,以及知识库中没有答案的问题。标注相关性时同步记录用户权限和语料版本,否则不同实验的搜索范围不同,分数无法比较。

新增词表、调整切片或更换模型时,保留一组未参与调参的检查样本。除了平均相关性,还要检查精确代码查询、少数部门的文档和撤权后的表现。整体分数改善不代表某个关键类别没有退化。

版本切换和撤权要能追踪

源文档变更后,应根据版本和内容摘要决定重建哪些片段,构建完成后再切换可见版本。索引里同时保留旧片段和新片段,却没有版本过滤,会让用户看到互相矛盾的答案。

删除或撤权优先停止可见性,随后清理向量、关键词索引和缓存。仅删除向量库里的记录不够,旧结果还可能留在缓存或会话中。已输出内容无法收回,这也要求下游系统限制保留与传播。

更换嵌入模型时,文档向量与查询向量必须使用兼容的模型和预处理流程。维度相同也不代表向量空间可混用。新索引可以旁路重建,按同一组问题比较结果,再决定是否切换。

第一版至少提供显示来源与版本的搜索入口、可追踪的更新链路和一组人工确认的检索问题。运营与工程人员应能据此查清漏检原因和越权结果的来源,再接入生成回答或代理流程。