主题
混合检索引擎
本文引用的源码文件
本文引用的文件
目录
引言
本技术文档聚焦 RAG 混合检索引擎,围绕语义搜索与关键词匹配的融合算法、记忆系统与 RAG 的集成、标签系统与分层存储对检索效果的影响、检索性能优化策略,以及检索质量评估方法进行系统化说明。该实现采用双通道设计:向量语义通道负责语义相似度排序,关键词通道提供精确匹配增强;两者通过重排序与阈值过滤协同工作,并结合长期记忆的注入分级与标签优先级调整,最终输出稳定、可解释且可扩展的检索结果。
项目结构
RAG 与长期记忆相关代码主要位于 agent 子项目中:
- RAG 检索层:
agent/src/rag/searcher.js、agent/src/rag/store/vectra.js、agent/src/rag/embedding/index.js、agent/src/rag/config.js、agent/src/rag/document/chunker.js - 长期记忆服务:
agent/src/memory/service.js、agent/src/memory/injection.js、agent/src/memory/tokens.js、agent/src/memory/fusion.js、agent/src/memory/tier.js、agent/src/memory/tags.js
graph TB subgraph "RAG 检索层" Searcher["Searcher<br/>混合检索入口"] Embedding["EmbeddingProvider<br/>查询/文档嵌入"] Store["VectraStore<br/>向量索引与元数据"] Chunker["chunkText<br/>文本分块"] Config["config<br/>默认参数与路径"] end subgraph "长期记忆层" MemoryService["MemoryService<br/>store/recall/manage"] Injection["selectMemoriesForInjection<br/>三级注入"] Tokens["keywordScore / extractMemoryTerms<br/>关键词匹配"] Fusion["selectSemanticHits / rrfFuse<br/>语义采信与排名融合"] Tier["resolveMemoryTier<br/>召回档位决策"] Tags["applyTagRankLift / filterByTags<br/>标签命中与名次加成"] end Searcher --> Embedding Searcher --> Store Searcher --> Config Store --> Chunker MemoryService --> Injection MemoryService --> Tokens MemoryService --> Fusion MemoryService --> Tier MemoryService --> Tags
图表来源
- agent/src/rag/searcher.js#L27-L77
- agent/src/rag/embedding/index.js#L10-L57
- agent/src/rag/store/vectra.js#L13-L83
- agent/src/rag/document/chunker.js#L31-L75
- agent/src/memory/service.js#L143-L150
- agent/src/memory/injection.js#L84-L127
- agent/src/memory/tokens.js#L127-L158
- agent/src/memory/fusion.js#L38-L59
- agent/src/memory/tier.js#L13-L37
- agent/src/memory/tags.js#L49-L61
章节来源
- agent/src/rag/searcher.js#L1-L162
- agent/src/rag/config.js#L1-L70
- agent/src/rag/store/vectra.js#L1-L124
- agent/src/rag/embedding/index.js#L1-L68
- agent/src/rag/document/chunker.js#L1-L76
- agent/src/memory/service.js#L1-L800
- agent/src/memory/injection.js#L1-L129
- agent/src/memory/tokens.js#L1-L159
- agent/src/memory/fusion.js#L1-L61
- agent/src/memory/tier.js#L1-L38
- agent/src/memory/tags.js#L1-L63
核心组件
- 混合检索器(Searcher):封装向量检索 + 关键词命中增强 + 归一化 + 阈值过滤 + 旁路探测。
- 嵌入提供者(EmbeddingProvider):统一查询侧与文档侧嵌入接口,支持本地 BGE 与 OpenAI 兼容远端模式。
- 向量存储(VectraStore):基于 vectra 的本地索引适配器,提供 upsert、query、list、remove 等能力。
- 长期记忆服务(MemoryService):提供 store/recall/manage 三大能力,并集成关键词通道、语义通道、RRF 融合、标签优先级与三级注入。
- 关键词匹配(tokens.js):字段权重、单元制计分、文档频率过滤、整句原样加分。
- 语义融合(fusion.js):相对落差门限与 top-K 封顶,RRF 排名融合。
- 召回档位(tier.js):none/local/remote 三档决策。
- 标签系统(tags.js):有 query 时作为名次加成,无 query 时作为硬过滤。
- 注入策略(injection.js):Tier1/Tier2/Tier3 三级加载,预算控制与主题标签索引。
章节来源
- agent/src/rag/searcher.js#L27-L77
- agent/src/rag/embedding/index.js#L10-L57
- agent/src/rag/store/vectra.js#L13-L83
- agent/src/memory/service.js#L473-L740
- agent/src/memory/tokens.js#L127-L158
- agent/src/memory/fusion.js#L38-L59
- agent/src/memory/tier.js#L13-L37
- agent/src/memory/tags.js#L49-L61
- agent/src/memory/injection.js#L84-L127
架构总览
混合检索引擎由两条主通道组成:
- 语义通道:使用 EmbeddingProvider 将查询转为向量,调用 VectraStore.search 进行余弦相似度检索,返回候选集。
- 关键词通道:在知识库或长期记忆中按词边界与中文最长片段匹配,计算单元分数并进行文档频率过滤,避免高频噪声。
两条通道的结果通过以下机制融合:
- 知识库侧:Searcher 对向量候选进行关键词抬分,再统一阈值过滤,空结果时触发旁路探测放宽阈值。
- 长期记忆侧:当启用向量通道时,关键词与语义分别产生名次,再通过 RRF 融合得到最终顺序;标签在有查询时仅做名次加成。
sequenceDiagram
participant Client as "调用方"
participant Searcher as "Searcher"
participant Embedding as "EmbeddingProvider"
participant Store as "VectraStore"
participant Keyword as "关键词匹配"
participant Fusion as "RRF/阈值过滤"
Client->>Searcher : search(query, options)
Searcher->>Embedding : embedQuery(query)
Embedding-->>Searcher : queryVec
Searcher->>Store : search(queryVec, query, topK*2)
Store-->>Searcher : candidates
Searcher->>Keyword : _boostByKeywords(query, candidates)
Keyword-->>Searcher : boostedCandidates
Searcher->>Fusion : 阈值过滤 + 旁路探测
Fusion-->>Searcher : results
Searcher-->>Client : {results, fallback, hasContext}
图表来源
- agent/src/rag/searcher.js#L46-L77
- agent/src/rag/embedding/index.js#L45-L47
- agent/src/rag/store/vectra.js#L74-L83
章节来源
- agent/src/rag/searcher.js#L46-L77
- agent/src/rag/embedding/index.js#L45-L47
- agent/src/rag/store/vectra.js#L74-L83
详细组件分析
混合检索器(Searcher)
Searcher 是 RAG 检索的核心入口,负责:
- 查询嵌入维度校验,防止模型/配置变更导致索引不匹配。
- 向量检索获取候选集,随后执行关键词命中增强。
- 统一阈值过滤,并在结果为空时触发旁路探测(放宽阈值)。
- 返回标准化结果结构,包含 total、hasContext、fallback 标志。
flowchart TD
Start(["开始"]) --> Embed["查询嵌入"]
Embed --> Validate{"维度匹配?"}
Validate --> |否| Error["抛出 embeddingDimensionMismatch"]
Validate --> |是| VectorSearch["向量检索 topK*2"]
VectorSearch --> Boost["关键词命中增强"]
Boost --> Filter["阈值过滤"]
Filter --> Empty{"结果为空?"}
Empty --> |否| Return["返回结果"]
Empty --> |是| Fallback["旁路探测放宽阈值"]
Fallback --> Return
图表来源
章节来源
关键词匹配与文档频率过滤
关键词匹配在知识库与长期记忆两侧共用同一套逻辑:
- 拉丁词按词边界匹配,避免子串误伤。
- 中文取最长命中片段,并按长度加权计分单元。
- 文档频率过滤剔除高频单元(如“项目”),低频豁免(df ≤ 2)。
- 字段权重:正文 > 标题 = 标签,整句原样命中额外加分。
flowchart TD
Start(["开始"]) --> Extract["提取查询单元"]
Extract --> MatchLatin["拉丁词边界匹配"]
Extract --> MatchCjk["中文最长片段匹配"]
MatchLatin --> DF["文档频率过滤"]
MatchCjk --> DF
DF --> Score["字段权重计分"]
Score --> Threshold{"是否过门槛?"}
Threshold --> |否| Drop["丢弃"]
Threshold --> |是| Keep["保留并排序"]
图表来源
章节来源
语义通道与 RRF 融合
长期记忆侧在启用向量通道时,关键词与语义分别产生名次,再通过 RRF 融合:
- 语义采信门:只保留距 top1 不超过 margin 的头部条目,并限制 maxTake。
- RRF 融合:仅消费名次,不消费绝对分数,避免跨模型刻度不可比问题。
flowchart TD Start(["开始"]) --> CosPairs["计算余弦相似度"] CosPairs --> Select["selectSemanticHits<br/>相对落差门限 + top-K 封顶"] Select --> RankMap["构建语义名次映射"] RankMap --> KWRank["关键词名次映射"] KWRank --> Fuse["rrfFuse<br/>RRF 排名融合"] Fuse --> Final["最终候选列表"]
图表来源
章节来源
标签系统与优先级调整
标签系统在两种场景下行为不同:
- 无查询时:标签作为唯一选择器,进行硬过滤。
- 有查询时:标签不再剔除候选,仅按重叠比例上抬名次(最多固定位数)。
flowchart TD
Start(["开始"]) --> HasQuery{"是否有查询?"}
HasQuery --> |否| FilterTags["filterByTags<br/>硬过滤"]
HasQuery --> |是| LiftTags["applyTagRankLift<br/>名次加成"]
FilterTags --> End(["结束"])
LiftTags --> End
图表来源
- agent/src/memory/tags.js#L24-L61
- agent/src/memory/service.js#L517-L524
- agent/src/memory/service.js#L640-L644
章节来源
- agent/src/memory/tags.js#L24-L61
- agent/src/memory/service.js#L517-L524
- agent/src/memory/service.js#L640-L644
记忆注入与分层存储
长期记忆注入采用三级策略:
- Tier1:常驻注入,importance ≥ 阈值且 origin 为用户,单条长度受限。
- Tier2:预算内填充,按价值降序贪心装入字符与条数预算。
- Tier3:按需召回,未注入的记忆转由系统提示词索引行引导 recall。
flowchart TD Start(["开始"]) --> Tier1["筛选 Tier1<br/>user origin + importance ≥ 阈值"] Tier1 --> Tier2["筛选 Tier2<br/>价值降序 + 预算限制"] Tier2 --> Tier3["Tier3<br/>按需召回 + 主题标签索引"] Tier3 --> End(["结束"])
图表来源
章节来源
向量存储与分块策略
VectraStore 提供事务批量写入、向量检索、文档分块列出与删除能力。分块策略优先自然断点,兜底字符窗口,并记录起止偏移以预留 BM25 混合检索。
classDiagram
class VectraStore {
+ensureCreated()
+upsertChunks(documentId, chunks, embeddings, metadata)
+search(queryVector, queryText, options)
+listDocumentChunks(documentId)
+removeDocument(documentId)
+listAllItems()
+recreate()
}
class Chunker {
+chunkText(text, options)
}
VectraStore --> Chunker : "使用分块结果"
图表来源
章节来源
依赖关系分析
- Searcher 依赖 EmbeddingProvider 与 VectraStore,并通过 config.js 获取默认检索参数。
- MemoryService 依赖 tier.js 决定召回档位,依赖 tokens.js 进行关键词匹配,依赖 fusion.js 进行语义融合,依赖 tags.js 处理标签优先级。
- EmbeddingProvider 支持本地与远端两种模式,查询侧自动附加前缀,文档侧不加前缀,确保非对称检索一致性。
graph LR Searcher --> EmbeddingProvider Searcher --> VectraStore Searcher --> Config MemoryService --> Tier MemoryService --> Tokens MemoryService --> Fusion MemoryService --> Tags EmbeddingProvider --> LocalEmbedding EmbeddingProvider --> OpenAICompatEmbedding
图表来源
- agent/src/rag/searcher.js#L11-L13
- agent/src/rag/embedding/index.js#L7-L8
- agent/src/memory/service.js#L10-L19
章节来源
- agent/src/rag/searcher.js#L11-L13
- agent/src/rag/embedding/index.js#L7-L8
- agent/src/memory/service.js#L10-L19
性能与优化
- 查询缓存:MemoryService 对缺失向量进行懒补齐,失败冷却 60s,避免重复网络开销。
- 预计算索引:VectraStore 使用 beginUpdate/endUpdate 事务批量写入,减少落盘次数。
- 并行检索策略:Searcher 先执行向量检索,再并行扫描关键词通道(受 KEYWORD_SCAN_LIMIT 限制),避免大库卡顿。
- 嵌入超时保护:MemoryService 对嵌入调用设置超时上限,失败降级为关键词单通道。
- 旁路探测:Searcher 在阈值过滤后为空时放宽阈值,区分“无数据”与“被过滤”。
章节来源
- agent/src/memory/service.js#L174-L187
- agent/src/rag/store/vectra.js#L39-L65
- agent/src/rag/searcher.js#L17-L21
- agent/src/rag/searcher.js#L62-L68
质量评估方法
- 相关性评分:关键词通道使用字段权重与单元制计分,语义通道使用余弦相似度与 RRF 融合名次。
- 召回率计算:通过 selectSemanticHits 的 top-K 封顶与 RRF 融合,控制语义命中数量,避免无关查询填满 limit。
- 用户反馈收集:MemoryService 维护 accessCount 与 lastAccessAt,用于后续价值评估与淘汰建议。
章节来源
- agent/src/memory/tokens.js#L127-L158
- agent/src/memory/fusion.js#L38-L59
- agent/src/memory/service.js#L693-L712
故障排查指南
- 维度不匹配:Searcher 在 embedQuery 后校验向量维度,若不匹配抛出 embeddingDimensionMismatch,需重建索引。
- 嵌入超时:MemoryService 对嵌入调用设置超时,失败降级为关键词通道,检查远端端点可用性。
- 标签误杀:若标签与词面门槛叠加导致结果全空,确认是否启用了有查询时的标签硬过滤(应改为名次加成)。
- 旁路探测无效:若放宽阈值仍无结果,检查向量索引是否完整或阈值是否过高。
章节来源
- agent/src/rag/searcher.js#L50-L55
- agent/src/memory/service.js#L174-L187
- agent/src/memory/tags.js#L24-L61
- agent/src/rag/searcher.js#L62-L68
结论
本混合检索引擎通过语义与关键词双通道融合,结合标签优先级与三级注入策略,实现了高召回、高精确且可扩展的检索能力。通过查询缓存、预计算索引与并行检索优化,系统在大规模知识库与长期记忆中保持良好性能。未来可进一步引入 BM25 混合检索与更细粒度的用户反馈闭环,以提升检索质量与用户体验。
附录:配置与调参示例
- 默认检索参数:topK=5,threshold=0.3(config.js)
- 分块参数:chunkSize=400,overlap=80(config.js)
- 嵌入模式:local(BGE 中文)或 openai-compat(远端 API)(embedding/index.js)
- 记忆注入预算:budgetChars=1500,budgetCount=12(injection.js)
- 语义融合参数:margin=0.05,maxTake=2,RRF_K=60(fusion.js)
章节来源