跳转到主要内容
Zeus 的知识库检索采用 向量语义搜索 + BM25 关键词匹配 的双路召回策略,通过 RRF(Reciprocal Rank Fusion) 融合排序,兼顾语义理解和精确匹配。

双路召回 + RRF 融合

检索流程

阶段说明
1. 向量检索pgvector 余弦相似度搜索,按 user_id + knowledge_base_id 元数据过滤,获取 top_k × 3 候选
2. BM25 检索jieba 中文分词 + BM25Okapi 关键词匹配,同样获取 top_k × 3 候选
3. RRF 融合Reciprocal Rank Fusion 合并两路结果,公式:score = vector_weight / (rank + 60) + keyword_weight / (rank + 60)
4. 排序截取按融合分数降序,返回 Top K 结果

参数配置

参数默认值说明
top_k5返回的最大结果数
vector_weight0.6向量搜索在 RRF 中的权重
keyword_weight0.4关键词搜索在 RRF 中的权重
use_reranktrue启用时获取 3 倍候选再融合排序;关闭则直接返回向量结果

降级策略

rank-bm25jieba 未安装时,系统自动降级为纯向量搜索模式,只使用 pgvector 进行语义检索。日志会输出 ⚠️ BM25 依赖不可用,使用纯向量搜索模式

BM25Store 三层缓存

BM25 索引采用三层缓存架构,支持懒加载和多实例部署:

缓存层级

层级存储特点
L1进程内存最快,Dict 结构,进程重启丢失
L2Redis多实例共享,TTL 自动过期,序列化存储
L3PostgreSQL(bm25_indexes 表)持久化,zlib 压缩 + pickle 序列化

索引生命周期

事件行为
首次检索懒加载 — 获取知识库所有 chunks,构建 BM25 索引,写入三层缓存
文档新增/删除invalidate(kb_id) — 清除三层缓存中该知识库的索引
下次检索自动重建索引
Redis TTL 过期从 PostgreSQL 恢复,或重新构建

索引数据结构

@dataclass
class BM25IndexData:
    bm25: BM25Okapi          # BM25 索引实例
    corpus: List[List[str]]   # jieba 分词后的语料库
    documents: List[str]      # 原始文档内容
    doc_ids: List[str]        # 文档 ID 列表(用于结果映射)
    created_at: float         # 创建时间戳
    doc_count: int            # 文档数量

PostgreSQL 持久化表

CREATE TABLE IF NOT EXISTS bm25_indexes (
    kb_id VARCHAR(255) PRIMARY KEY,
    index_data BYTEA NOT NULL,        -- zlib 压缩 + pickle 序列化
    doc_count INTEGER NOT NULL DEFAULT 0,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

多知识库检索

当用户在对话中关联多个知识库时,系统对每个知识库独立执行双路检索,然后合并所有结果按分数排序: 每个知识库有独立的 BM25 索引,互不干扰。

RAGService

RAGService 是检索的服务层封装,提供:
方法说明
search(query, knowledge_base_id, user_id, top_k, score_threshold)单知识库检索
search_multiple_knowledge_bases(query, knowledge_base_ids, user_id, top_k)多知识库检索 + 合并排序
format_for_context(chunks, max_chars)格式化为 LLM 上下文(带来源标注)
format_for_tool_output(chunks, max_chunks)格式化为 Agent 工具输出
get_retriever(user_id, knowledge_base_id, k)获取 LangChain VectorStoreRetriever