RAG 多索引检索架构
核心思想
传统 RAG 中,每个 chunk(文本段)只有一个向量入口——chunk 原文的 Embedding。当用户的提问措辞与 chunk 原文差异较大时,召回率会明显下降。 多索引架构将索引与内容解耦:一个 chunk 可以拥有多个索引入口,每个索引独立 Embedding、独立参与检索,但命中后统一返回原始 chunk 内容。数据模型
aigc_segment — 文本段
存储文档拆分后的原始 chunk 内容,是知识的源数据。| 字段 | 说明 |
|---|---|
| id | 主键 |
| docs_id | 所属文档 |
| knowledge_id | 所属知识库 |
| content | chunk 原文(LLM 实际读到的内容) |
| enabled | 是否启用 |
aigc_segment_index — 索引条目
存储每个 chunk 的多个索引入口,是检索的匹配依据。| 字段 | 说明 |
|---|---|
| id | 主键 |
| segment_id | 关联 aigc_segment.id |
| index_hash | 唯一标识,写入向量库元数据,用于过滤 |
| content | 索引文本(被 Embedding 的内容) |
| type | 索引类型 |
| enabled | 是否启用 |
| status | 向量化状态 |
索引类型
| 类型 | 来源 | 说明 |
|---|---|---|
| DEFAULT | 系统自动 | chunk 原文,文档拆分时自动创建,每个 segment 必有一条 |
| TITLE | LLM 生成 / 回退截取 | 文档标题 + LLM 生成的 chunk 核心摘要,知识库开启 index_title_enabled 后生效。若配置了 modelId 则调用 LLM 生成语义化标题;未配置时回退到截取 chunk 前 100 字 |
| AUTO_QA | LLM 生成 | 针对 chunk 内容自动生成的用户可能提问,知识库开启 index_auto_qa_enabled 后生效 |
| CUSTOM | 用户手动 | 用户在 chunk 详情中手动添加的自定义索引关键词 |
| IMAGE_DESC | 预留 | 图片描述索引(预留扩展) |
写入流程
文档导入向量化
segment_id(用于回查原始内容)。
手动添加自定义索引
检索模式(SearchMode)
系统支持三种检索模式,通过RetrievalConfig.searchMode 配置:
检索配置数据结构
配置优先级
检索配置在三个场景中使用,参数来源的优先级如下:Hybrid 模式的 minScore 处理
Hybrid 模式使用 RRF 算法,分数量纲与余弦相似度完全不同:/search接口:EmbeddingSearchService.search()中直接后置过滤- RAG 对话:
SegmentAwareContentRetriever中通过 embeddingModel 计算余弦相似度后置过滤
向量库实例与 SearchMode
不同 SearchMode 需要不同的向量库实例配置(如 PgVector HYBRID 模式需要额外创建全文索引),因此 缓存键包含 searchMode。同一个知识库在 SEMANTIC 和 HYBRID 模式下会使用不同的 VectorStore 实例。检索流程
向量搜索(/search 接口)
RAG 对话检索
索引与内容分离的核心机制
向量库中存储的 TextSegment 结构:segment_id 回查 aigc_segment 表,返回 segment.content 作为最终结果。这保证了:
- 匹配维度丰富:同一个 chunk 可通过原文、标题、问题、自定义关键词等多种路径被召回
- 返回内容一致:无论通过哪个索引命中,LLM 和用户看到的始终是完整的原始 chunk 内容
- 旧数据兼容:若 metadata 中无
segment_id(迁移前的数据),自动回退到直接返回 embedded.text()
segment_id 去重
同一个 chunk 可能有多条索引(DEFAULT、TITLE、AUTO_QA、CUSTOM)同时命中,如果不去重会返回多条完全相同的内容。因此在内容回查阶段按segment_id 去重,同一 segment 只保留得分最高的那条命中:
/search接口:EmbeddingSearchService.convertSearchResults()中使用LinkedHashMap<segmentId, result>去重- RAG 对话:
SegmentAwareContentRetriever.retrieve()中使用LinkedHashMap<segmentId, content>去重
索引增强(IndexEnhancementService)
索引增强服务在文档导入阶段为每个 chunk 自动生成额外的索引入口,提升不同表述方式下的召回率。所有增强索引共用知识库配置的modelId(增强检索 LLM 模型)。
TITLE 索引 — LLM 标题摘要
目的:为 chunk 生成一句语义化的标题描述,让用户以”主题/概念”维度提问时也能精准命中。 生成流程:AUTO_QA 索引 — LLM 问题生成
目的:预测用户可能针对 chunk 提出的问题,用”问题”作为索引入口,弥补 chunk 原文与用户问句之间的表述差异。 生成流程:| 参数 | 默认值 | 说明 |
|---|---|---|
index_auto_qa_enabled | false | 是否开启 |
modelId | - | 使用的 LLM 模型(TITLE 和 AUTO_QA 共用) |
index_auto_qa_count | 3 | 每个 chunk 生成的问题数量 |
增强索引的成本考量
- TITLE 索引:每个 chunk 调用一次 LLM(输入约 500 字,输出约 30 字),token 消耗较低
- AUTO_QA 索引:每个 chunk 调用一次 LLM(输入为 chunk 全文,输出 N 个问题),token 消耗与 chunk 大小和问题数量成正比
- 两者共用
modelId,建议配置成本较低的模型(如 GPT-4o-mini、Qwen-Turbo 等) - 增强索引在文档导入时一次性生成,不会增加检索时的 LLM 调用开销
关键类职责
| 类 | 所在包 | 职责 |
|---|---|---|
RagRetrieverBuilder | builder | RAG 检索器总调度,聚合知识库检索器、SQL 检索器和 Rerank |
KnowledgeRetrieverBuilder | builder | 构建知识库 ContentRetriever,从 RetrievalConfig 读取检索参数 |
SegmentAwareContentRetriever | builder | 包装层,将索引文本替换为原始 chunk 内容;HYBRID 模式下通过余弦相似度后置过滤 |
SqlRetrieverBuilder | builder | 构建数据源 Text2SQL 检索器 |
AiServiceBuilder | builder | AI Service 构建器,集成 RAG / Tools / Memory / SystemMessage |
PromptProcessor | builder | 系统提示词处理,变量替换与格式增强 |
EmbeddingSearchService | embedding | 独立搜索服务,供 /search 接口调用,内置内容回查和 HYBRID 后置过滤逻辑 |
IndexEnhancementService | service | 索引增强服务,负责生成 TITLE(LLM 摘要 / 回退截取)和 AUTO_QA(LLM 问题生成)索引 |
VectorStoreFactory | factory | 向量库实例创建与缓存,支持动态 SearchMode |
KnowledgeFactory | factory | 知识库门面,统一获取 EmbeddingStore 和 EmbeddingModel |
检索配置使用场景
| 场景 | 配置来源 | 核心类 |
|---|---|---|
| 手动召回测试 | EmbeddingSearchReq.searchMode/rrfK | EmbeddingSearchService |
| 应用对话 | AigcApp.retrievalConfig → LcChatReq.retrievalConfig | KnowledgeRetrieverBuilder |
| 工作流节点 | 节点 params.searchMode/rrfK | KnowledgeSearchNode → EmbeddingSearchService |
知识库配置项
| 字段 | 说明 |
|---|---|
index_title_enabled | 导入时自动生成标题索引(有 modelId 时调用 LLM 生成摘要标题,无 modelId 时截取前 100 字) |
index_auto_qa_enabled | 导入时调用 LLM 为每个 chunk 生成问题索引 |
index_auto_qa_model_id | 生成问题索引使用的 LLM 模型 |
index_auto_qa_count | 每个 chunk 生成的问题数量(默认 3) |
max_results | 检索返回的最大结果数 |
min_score | 检索相似度阈值 |

