知识库问答源码解析
整体架构
flowchart LR
A[用户提问] --> B{指定知识库?}
B -->|否| C[多知识库自动匹配]
B -->|是| D[获取知识库配置]
C --> D
D --> E[问题向量化]
E --> F{知识库模式?}
F -->|标准化/客服| G[标准问答策略]
F -->|普通| H[向量检索策略]
G --> I{匹配成功?}
I -->|是| J[返回预定义答案]
I -->|否| H
H --> K[向量检索 + 全文检索]
K --> L[重排序]
L --> M[生成答案]
M --> N[相似问题推荐]
N --> O[返回完整响应]
多知识库自动匹配
当用户未指定知识库时,系统会根据用户问题语义自动匹配最相关的知识库。
flowchart LR
A[用户提问] --> B{datasetId 为空?}
B -->|否| C[直接使用指定知识库]
B -->|是| D[调用 autoChoice]
D --> E[EmbeddingProvider.search]
E --> F[用户问题向量化]
F --> G[在业务向量库中搜索]
G --> H{找到匹配?}
H -->|否| I[返回提示选择知识库]
H -->|是| J[获取最高分知识库ID]
J --> C
自动匹配逻辑
位置: support/rule/VectorChatRule.java
private static Flux<AiMessageResultDTO> autoChoice(ChatMessageDTO chatMessageDTO) {
// 在业务向量库中搜索,过滤类型为 DATASET(知识库描述)
EmbeddingSearchResult<TextSegment> searchResult = EmbeddingProvider.search(
chatMessageDTO.getContent(),
metadataKey(EmbedBizTypeEnums.Fields.type).isEqualTo(EmbedBizTypeEnums.DATASET.getType())
);
// 未找到匹配的知识库
if (Objects.isNull(searchResult) || searchResult.matches().isEmpty()) {
return Flux.just(new AiMessageResultDTO("未找到相关知识库,请点击下方+按钮选择目标知识库"));
}
// 获取匹配度最高的知识库ID,设置到上下文
Long dataId = searchResult.matches().get(0).embedded().metadata().getLong(TEMP_ID);
chatMessageDTO.setDatasetId(dataId);
...
}
RAG 搜索流程
flowchart LR
A[开始检索] --> B[构建搜索请求]
B --> C[向量相似度搜索]
C --> D{Milvus?}
D -->|是| E[BM25 全文检索]
D -->|否| F[合并结果]
E --> F
F --> G{结果为空?}
G -->|是| H[空结果处理]
G -->|否| I{配置 Reranker?}
I -->|是| J[重排序优化]
I -->|否| K[生成答案]
J --> K
K --> L[并行任务]
L --> M[返回 SSE 流]
向量检索增强生成策略
位置: support/handler/rag/strategy/impl/VectorRetrievalAugmentedGenerationStrategy.java
@Override
public Flux<AiMessageResultDTO> processChat(Embedding queryEmbedding, AiDatasetEntity dataset,
ChatMessageDTO chatMessageDTO) {
// 1. 执行向量检索
List<EmbeddingMatch<TextSegment>> vectorMatches = performVectorSearch(queryEmbedding, dataset);
// 2. 执行全文检索(仅 Milvus 支持)
List<EmbeddingMatch<TextSegment>> fullTextMatches = performFullTextSearch(dataset, chatMessageDTO);
// 3. 合并检索结果
List<EmbeddingMatch<TextSegment>> allMatches = RagHelper.mergeSearchResults(vectorMatches, fullTextMatches);
// 4. 空结果处理
if (RagHelper.isEmpty(allMatches)) {
return RagHelper.handleEmptyResult(dataset, chatMessageDTO, recordId);
}
// 5. 重排序搜索结果
List<Content> rerankedContent = RagHelper.rerankSearchResults(dataset, chatMessageDTO.getContent(), allMatches);
// 6. 生成答案并附加参考资料
return generateAnswerWithReferences(dataset, chatMessageDTO, rerankedContent, allMatches);
}
搜索参数说明
| 参数 | 说明 | 示例 |
|---|
queryEmbedding | 用户问题的向量表示 | Vector[0.23, -0.45, ...] |
maxResults | 返回结果数量(TopK) | 5 |
minScore | 相似度阈值 | 标准问答: 0.9,向量检索: 0.75 |
datasetId | 知识库ID过滤 | 123 |
documentType | 文档类型 | "0"=ANSWER,"1"=QUESTION |
向量搜索请求构建
位置: support/handler/rag/strategy/RagHelper.java
public static EmbeddingSearchRequest buildSearchRequest(Embedding queryEmbedding, AiDatasetEntity dataset,
String documentType, Double minScore) {
// 计算相似度阈值(将百分比转换为小数,如 75 → 0.75)
double finalMinScore = Objects.nonNull(minScore) ? minScore :
NumberUtil.div(Double.parseDouble(dataset.getScore().toString()), 100.0, 2);
return new EmbeddingSearchRequest(
queryEmbedding, // 查询向量
dataset.getTopK(), // 返回数量
finalMinScore, // 相似度阈值
metadataKey(AiDocumentEntity.Fields.datasetId).isEqualTo(dataset.getId().toString())
.and(metadataKey(DocumentTypeEnums.Fields.type).isEqualTo(documentType))
);
}
重排序(Rerank)
位置: support/handler/rag/strategy/RagHelper.java
public static List<Content> rerankSearchResults(AiDatasetEntity dataset, String queryContent,
List<EmbeddingMatch<TextSegment>> embeddingMatches) {
...
// 如果没有配置重排序模型,使用默认聚合器
if (StrUtil.isBlank(dataset.getRerankerModel())) {
return DEFAULT_AGGREGATOR.aggregate(queryToContents);
}
// 使用配置的重排序模型
ScoringModel rerankerModel = modelProvider.getRerankerModel(dataset.getRerankerModel());
ContentAggregator contentAggregator = ReRankingContentAggregator.builder()
.scoringModel(rerankerModel)
.build();
return contentAggregator.aggregate(queryToContents);
}
相似问题推荐
flowchart LR
A[检索完成] --> B{启用相似问题?}
B -->|否| C[跳过]
B -->|是| D{有检索内容?}
D -->|否| C
D -->|是| E[取前3条上下文]
E --> F[构建 Prompt]
F --> G[调用 LLM 生成]
G --> H[JSON 解析]
H --> I[过滤规则]
I --> J[返回相似问题]
核心实现
位置: support/handler/rag/strategy/SimilarQuestionHelper.java
public static Mono<List<AiMessageResultDTO.SimilarQuestion>> generateSimilarQuestions(
String userQuery, List<String> retrievedContents, ChatMessageDTO chatMessageDTO) {
if (retrievedContents == null || retrievedContents.isEmpty()) {
return Mono.empty();
}
// 构建 Prompt(取前3条检索内容作为上下文)
String context = String.join("\n", retrievedContents.subList(0, Math.min(3, retrievedContents.size())));
...
// 异步调用大模型生成
return Mono.fromCallable(() -> {
String result = streamAssistant.getValue().chat(systemPrompt, userPrompt)...;
return parseAndFilterQuestions(result, userQuery);
}).subscribeOn(Schedulers.boundedElastic());
}
过滤规则
private static List<AiMessageResultDTO.SimilarQuestion> parseAndFilterQuestions(
String jsonResult, String originalQuery) {
...
for (int i = 0; i < questions.size(); i++) {
String text = questions.getStr(i).trim();
// 长度过滤:10-40 字符
if (text.length() < 10 || text.length() > 40) continue;
// 相似度过滤:与原问题相似度 < 70%
if (isSimilarToOriginal(text, originalQuery)) continue;
result.add(new AiMessageResultDTO.SimilarQuestion(text, null));
// 数量限制:最多3条
if (result.size() >= 3) break;
}
return result;
}
| 规则 | 条件 | 说明 |
|---|
| 长度过滤 | 10 ≤ 长度 ≤ 40 | 过短或过长的问题被丢弃 |
| 去重过滤 | 相似度 < 0.7 | 与原问题相似度过高被丢弃 |
| 数量限制 | 最多 3 条 | 达到 3 条后停止添加 |
并行执行
位置: support/handler/rag/strategy/impl/VectorRetrievalAugmentedGenerationStrategy.java
private Flux<AiMessageResultDTO> generateAnswerWithReferences(...) {
...
// 并行任务 1:相似问题生成(异步)
Mono<List<SimilarQuestion>> similarQuestionsMono = needSimilarQuestions
? SimilarQuestionHelper.generateSimilarQuestions(...)
: Mono.just(Collections.emptyList());
// 并行任务 2:参考资料链接构建(异步)
Mono<List<ExtLink>> extLinksMono = Mono
.fromCallable(() -> RagHelper.buildReferenceLinks(embeddingMatches, aiDocumentService))
.subscribeOn(Schedulers.boundedElastic());
// 主回复流 + 合并最终结果
return answerStream.concatWith(
Mono.zip(similarQuestionsMono, extLinksMono).map(tuple -> {
AiMessageResultDTO result = new AiMessageResultDTO();
result.setSimilarQuestions(tuple.getT1());
result.setExtLinks(tuple.getT2());
result.setFinish(true);
return result;
})
);
}
向量存储工厂
flowchart LR
A[EmbeddingStoreFactory] --> B{向量库类型?}
B -->|milvus| C[MilvusEmbeddingStoreFactory]
B -->|chroma| D[ChromaEmbeddingStoreFactory]
B -->|qdrant| E[QdrantEmbeddingStoreFactory]
B -->|neo4j| F[Neo4jEmbeddingStoreFactory]
B -->|pgvector| G[PgVectorEmbeddingStoreFactory]
| 实现类 | 向量库 | 特性 |
|---|
MilvusEmbeddingStoreFactory | Milvus | 支持 BM25 全文检索 |
ChromaEmbeddingStoreFactory | Chroma | 轻量级向量库 |
QdrantEmbeddingStoreFactory | Qdrant | 高性能向量搜索 |
Neo4jEmbeddingStoreFactory | Neo4j | 图数据库 + 向量 |
PgVectorEmbeddingStoreFactory | pgvector | PostgreSQL 扩展 |
核心类总结
| 类名 | 路径 | 职责 |
|---|
VectorChatRule | support/rule/ | RAG 入口,策略选择,多知识库自动匹配 |
StandardQuestionAnswerStrategy | support/handler/rag/strategy/impl/ | 标准问答精确匹配(0.9 阈值) |
VectorRetrievalAugmentedGenerationStrategy | support/handler/rag/strategy/impl/ | 向量检索 + 全文检索 + 重排序 + 生成 |
RagHelper | support/handler/rag/strategy/ | 搜索请求构建、BM25、重排序、答案生成 |
SimilarQuestionHelper | support/handler/rag/strategy/ | 相似问题生成与过滤 |
EmbeddingProvider | support/provider/ | 业务向量存储,知识库自动匹配 |
EmbeddingStoreFactory | support/handler/rag/ | 向量存储工厂接口 |