FastGPT 引申:借鉴 FastGPT 基于MySQL + ES 实现知识库(含表结构以及核心代码)
一、整体思路
- 知识库查询能力:引入 Elasticsearch (ES) 提供向量存储和检索能力。
- 三层逻辑结构:
- 数据集:使用模块 ID 关联应用。
- 数据:存储具体的知识内容。
- 索引:提供高效的查询能力。
二、存储结构
2.1 MySQL 表结构
(1) knowledge_base_dataset
存储数据集基本信息
字段名 | 类型 | 描述 |
---|---|---|
id | BIGINT | 主键 |
type | INT | 0:手动数据集 |
name | VARCHAR | 数据集名称 |
es_index_name | VARCHAR | ES 索引名称 |
extract_qa_prompt | TEXT | 抽取 QA 问答对 Prompt |
extract_summary_prompt | TEXT | 抽取摘要 Prompt |
extract_param_prompt | TEXT | 抽取参数 Prompt |
is_deleted | TINYINT | 逻辑删除标志 |
(2) knowledge_base_data
存储具体知识数据项
字段名 | 类型 | 描述 |
---|---|---|
id | BIGINT | 主键 |
dataset_id | BIGINT | 关联 dataset 表 |
main_content | TEXT | 主要内容 |
auxiliary_data | TEXT | 辅助数据 |
is_deleted | TINYINT | 逻辑删除标志 |
(3) knowledge_base_index
存储索引项
字段名 | 类型 | 描述 |
---|---|---|
id | BIGINT | 主键 |
data_id | BIGINT | 关联 data 表主键 |
index_type | INT | 0-默认,1-用户指定,3-提取问题,4-相关摘要 |
index_content | TEXT | 索引内容 |
(4) ai_kb_relation
存储数据集与应用关联
字段名 | 类型 | 描述 |
---|---|---|
id | BIGINT | 主键 |
module_id | BIGINT | 关联的模块 ID |
dataset_id | BIGINT | 知识库数据集 ID |
2.2 Elasticsearch Mapping 结构
(1) ES索引
存储知识数据的向量索引:ai_knowledge
{
"mappings": {
"properties": {
"data_id": {
"type": "long",
"index": true
},
"index_id": {
"type": "long",
"index": true
},
"dataset_id": {
"type": "long",
"index": true
},
"vector_index": {
"type": "dense_vector",
"dims": 1024,
"index": true,
"similarity": "cosine"
},
"text_index": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}
}
(2) 字段说明
字段名称 | 类型 | 说明 |
---|---|---|
data_id | long | 关联 knowledge_base_data 主键 ID |
index_id | long | 关联 knowledge_base_index 主键 ID |
dataset_id | long | 关联 knowledge_base_dataset 主键 ID |
vector_index | dense_vector | 1024 维度稠密向量,支持余弦相似度计算 |
text_index | text | 采用 ik_max_word 分词器进行索引 |
三、代码实现
3.1 调用 Embedding 服务
public List<Float> getVector(String text) {
ResponseEntity responseEntity = xxxx;
if (responseEntity != null && responseEntity.getCode() == 0) {
List<List<Double>> vectorList = (List<List<Double>>) responseEntity.getData();
if (vectorList != null && !vectorList.isEmpty()) {
return vectorList.get(0).stream()
.map(Double::floatValue)
.collect(Collectors.toList());
}
}
return null;
}
3.2 向量检索(ES KNN 查询)
public List<Knowledge> searchByVector(List<Float> vector) throws IOException {
log.info("Searching knowledge with vector similarity using KNN");
try {
return client.search(s -> s
.index(INDEX_NAME)
.knn(k -> k
.field("vector_index")
.queryVector(vector)
.k(DEFAULT_SIZE)
.numCandidates(100)
),
Knowledge.class
).hits().hits().stream()
.map(Hit::source)
.collect(Collectors.toList());
} catch (Exception e) {
log.error("Error searching knowledge by vector: {}", e.getMessage(), e);
throw e;
}
}
3.3 RRF 排序(融合向量检索和文本检索结果)
private Map<String, Double> calculateRRFScores(
List<Hit<KnowledgeCommon>> vectorHits,
List<Hit<KnowledgeCommon>> textHits) {
Map<String, Double> rrfScores = new HashMap<>();
int rank = 1;
for (Hit<KnowledgeCommon> hit : vectorHits) {
String id = hit.id();
double score = VECTOR_WEIGHT * (1.0 / (rank + RRF_CONSTANT));
rrfScores.put(id, rrfScores.getOrDefault(id, 0.0) + score);
rank++;
}
rank = 1;
for (Hit<KnowledgeCommon> hit : textHits) {
String id = hit.id();
double score = TEXT_WEIGHT * (1.0 / (rank + RRF_CONSTANT));
rrfScores.put(id, rrfScores.getOrDefault(id, 0.0) + score);
rank++;
}
return rrfScores;
}
四、总结
- 采用 MySQL 存储基础数据,ES 存储向量及文本索引。
- ES 结合 KNN 和分词搜索进行知识检索。
- 使用 RRF 算法融合向量和文本搜索结果,提高查询质量。
此方案支持灵活的知识管理和高效检索,可扩展至更大规模的知识库应用。