Skip to content

混合检索引擎 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与优化
  8. 质量评估方法
  9. 故障排查指南
  10. 结论
  11. 附录:配置与调参示例

引言 ​

本技术文档聚焦 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

图表来源

章节来源

核心组件 ​

  • 混合检索器(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 三级加载,预算控制与主题标签索引。

章节来源

架构总览 ​

混合检索引擎由两条主通道组成:

  • 语义通道:使用 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}

图表来源

章节来源

详细组件分析 ​

混合检索器(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

图表来源

章节来源

记忆注入与分层存储 ​

长期记忆注入采用三级策略:

  • 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

图表来源

章节来源

性能与优化 ​

  • 查询缓存:MemoryService 对缺失向量进行懒补齐,失败冷却 60s,避免重复网络开销。
  • 预计算索引:VectraStore 使用 beginUpdate/endUpdate 事务批量写入,减少落盘次数。
  • 并行检索策略:Searcher 先执行向量检索,再并行扫描关键词通道(受 KEYWORD_SCAN_LIMIT 限制),避免大库卡顿。
  • 嵌入超时保护:MemoryService 对嵌入调用设置超时上限,失败降级为关键词单通道。
  • 旁路探测:Searcher 在阈值过滤后为空时放宽阈值,区分“无数据”与“被过滤”。

章节来源

质量评估方法 ​

  • 相关性评分:关键词通道使用字段权重与单元制计分,语义通道使用余弦相似度与 RRF 融合名次。
  • 召回率计算:通过 selectSemanticHits 的 top-K 封顶与 RRF 融合,控制语义命中数量,避免无关查询填满 limit。
  • 用户反馈收集:MemoryService 维护 accessCount 与 lastAccessAt,用于后续价值评估与淘汰建议。

章节来源

故障排查指南 ​

  • 维度不匹配:Searcher 在 embedQuery 后校验向量维度,若不匹配抛出 embeddingDimensionMismatch,需重建索引。
  • 嵌入超时:MemoryService 对嵌入调用设置超时,失败降级为关键词通道,检查远端端点可用性。
  • 标签误杀:若标签与词面门槛叠加导致结果全空,确认是否启用了有查询时的标签硬过滤(应改为名次加成)。
  • 旁路探测无效:若放宽阈值仍无结果,检查向量索引是否完整或阈值是否过高。

章节来源

结论 ​

本混合检索引擎通过语义与关键词双通道融合,结合标签优先级与三级注入策略,实现了高召回、高精确且可扩展的检索能力。通过查询缓存、预计算索引与并行检索优化,系统在大规模知识库与长期记忆中保持良好性能。未来可进一步引入 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)

章节来源