概述
知识库是LangChat Pro RAG(检索增强生成)功能的核心数据源,用于存储和管理文档、向量、检索配置等。支持多种向量数据库,提供完整的文档管理、向量化、检索能力。核心实体
AigcKnowledge(知识库)
Copy
public class AigcKnowledge {
private String id;
private String name; // 知识库名称
private String description; // 描述
private String vectorStoreId; // 向量库ID
private String embeddingModelId; // Embedding模型ID
private String rerankModelId; // Rerank模型ID
private Integer topK; // 检索数量
private Double similarityThreshold; // 相似度阈值
private String splitStrategy; // 分块策略
private Integer chunkSize; // 分块大小
private Integer overlapSize; // 重叠大小
private Boolean enabled; // 是否启用
private String userId; // 用户ID
}
AigcDocs(文档)
Copy
public class AigcDocs {
private String id;
private String knowledgeId; // 知识库ID
private String name; // 文档名称
private String fileUrl; // 文件URL
private String fileType; // 文件类型
private Long fileSize; // 文件大小
private Integer segmentCount; // 分段数量
private String status; // 状态:processing, completed, failed
private String errorMessage; // 错误信息
}
AigcSegment(文本分段)
Copy
public class AigcSegment {
private String id;
private String docsId; // 文档ID
private String content; // 文本内容
private List<Float> embedding; // 向量表示
private Integer chunkIndex; // 分段索引
private String metadata; // 元数据(JSON)
}
核心服务
AigcKnowledgeService
接口路径:langchat-aigc/langchat-aigc-api/src/main/java/cn/langchat/aigc/api/service/AigcKnowledgeService.java
实现路径: langchat-aigc/langchat-aigc-biz/src/main/java/cn/langchat/aigc/biz/service/impl/AigcKnowledgeServiceImpl.java
核心方法:
Copy
public interface AigcKnowledgeService {
/**
* 创建知识库
*/
String createKnowledge(AigcKnowledge knowledge);
/**
* 删除知识库
*/
void deleteKnowledge(String id);
/**
* 获取用户的知识库列表
*/
List<AigcKnowledge> listByUserId(String userId);
/**
* 批量创建知识库
*/
List<String> batchCreate(List<AigcKnowledge> knowledges);
}
AigcDocsService
核心方法:Copy
public interface AigcDocsService {
/**
* 上传文档
*/
String uploadDocs(String knowledgeId, MultipartFile file);
/**
* 删除文档
*/
void deleteDocs(String id);
/**
* 获取知识库的文档列表
*/
List<AigcDocs> listByKnowledgeId(String knowledgeId);
/**
* 处理文档(解析、分块、向量化)
*/
void processDocs(String docsId);
/**
* 批量处理文档
*/
void batchProcess(List<String> docsIds);
}
AigcSegmentService
核心方法:Copy
public interface AigcSegmentService {
/**
* 批量创建分段
*/
void batchCreate(List<AigcSegment> segments);
/**
* 删除文档的所有分段
*/
void deleteByDocsId(String docsId);
/**
* 检索相关分段
*/
List<AigcSegment> search(String knowledgeId, String query, Integer topK);
}
文档处理流程
完整流程
Copy
文档上传
↓
保存文件到OSS
↓
创建AigcDocs记录
↓
异步处理任务
↓
┌─────────────────┐
│ 文档解析 │
│ - PDF/Word等 │
└────────┬────────┘
↓
┌─────────────────┐
│ 文本提取 │
│ - 纯文本内容 │
└────────┬────────┘
↓
┌─────────────────┐
│ 文本分块 │
│ - 分段策略 │
└────────┬────────┘
↓
┌─────────────────┐
│ 向量化 │
│ - Embedding │
└────────┬────────┘
↓
┌─────────────────┐
│ 存储向量库 │
│ - PGVector等 │
└─────────────────┘
↓
更新文档状态:completed
代码实现
Copy
@Service
public class DocsProcessor {
@Resource
private DocsParser docsParser;
@Resource
private TextSplitter textSplitter;
@Resource
private LcEmbeddingService embeddingService;
@Resource
private VectorStoreService vectorStoreService;
@Async
public void process(String docsId) {
// 1. 获取文档信息
AigcDocs docs = docsService.getById(docsId);
try {
// 2. 更新状态为processing
docs.setStatus("processing");
docsService.updateById(docs);
// 3. 下载文件
String filePath = downloadFile(docs.getFileUrl());
// 4. 解析文档
String text = docsParser.parse(filePath, docs.getFileType());
// 5. 文本分块
AigcKnowledge knowledge = knowledgeService.getById(docs.getKnowledgeId());
List<String> chunks = textSplitter.split(
text,
knowledge.getSplitStrategy(),
knowledge.getChunkSize(),
knowledge.getOverlapSize()
);
// 6. 批量向量化
List<List<Float>> embeddings = embeddingService.embedBatch(
knowledge.getEmbeddingModelId(),
chunks
);
// 7. 创建分段
List<AigcSegment> segments = new ArrayList<>();
for (int i = 0; i < chunks.size(); i++) {
AigcSegment segment = new AigcSegment();
segment.setDocsId(docsId);
segment.setContent(chunks.get(i));
segment.setEmbedding(embeddings.get(i));
segment.setChunkIndex(i);
segments.add(segment);
}
// 8. 批量插入向量库
vectorStoreService.batchInsert(knowledge.getVectorStoreId(), segments);
// 9. 保存分段记录
segmentService.batchCreate(segments);
// 10. 更新文档状态为completed
docs.setStatus("completed");
docs.setSegmentCount(chunks.size());
docsService.updateById(docs);
// 11. 清理临时文件
cleanTempFile(filePath);
} catch (Exception e) {
log.error("文档处理失败: docsId={}", docsId, e);
// 更新文档状态为failed
docs.setStatus("failed");
docs.setErrorMessage(e.getMessage());
docsService.updateById(docs);
}
}
}
文档解析
DocsParser
职责: 解析各种格式的文档 支持的格式:- Word (docx, doc)
- Excel (xlsx, xls)
- PowerPoint (pptx, ppt)
- Text (txt, md)
- HTML
Copy
@Component
public class DocsParser {
@Resource
private TikaParser tikaParser;
@Resource
private PoiParser poiParser;
@Resource
private MinerUParser minerUParser;
/**
* 解析文档
*/
public String parse(String filePath, String fileType) {
return switch (fileType.toLowerCase()) {
case "pdf" -> parsePdf(filePath);
case "docx", "doc" -> parseWord(filePath);
case "xlsx", "xls" -> parseExcel(filePath);
case "pptx", "ppt" -> parsePowerPoint(filePath);
case "txt", "md" -> parseText(filePath);
case "html" -> parseHtml(filePath);
default -> throw new IllegalArgumentException("不支持的文件类型: " + fileType);
};
}
/**
* 解析PDF
*/
private String parsePdf(String filePath) {
// 优先使用MinerU(OCR支持)
if (minerUParser.isEnabled()) {
return minerUParser.parse(filePath);
}
// 降级到Tika
return tikaParser.parse(filePath);
}
/**
* 解析Word
*/
private String parseWord(String filePath) {
return poiParser.parseWord(filePath);
}
/**
* 解析Excel
*/
private String parseExcel(String filePath) {
return poiParser.parseExcel(filePath);
}
}
文本分块
TextSplitter
职责: 将长文本分割为合适的块 分块策略:1. 固定大小分块
Copy
public List<String> splitBySize(String text, int chunkSize) {
List<String> chunks = new ArrayList<>();
for (int i = 0; i < text.length(); i += chunkSize) {
int end = Math.min(i + chunkSize, text.length());
chunks.add(text.substring(i, end));
}
return chunks;
}
2. 按段落分块
Copy
public List<String> splitByParagraph(String text) {
return Arrays.stream(text.split("\\n\\s*\\n"))
.filter(StrUtil::isNotBlank)
.map(String::trim)
.collect(Collectors.toList());
}
3. 滑动窗口分块(推荐)
Copy
public List<String> splitByWindow(String text, int windowSize, int overlap) {
List<String> chunks = new ArrayList<>();
// 先按段落分割
String[] paragraphs = text.split("\\n\\s*\\n");
// 合并段落形成滑动窗口
StringBuilder current = new StringBuilder();
for (int i = 0; i < paragraphs.length; i++) {
current.append(paragraphs[i]).append("\n\n");
// 每隔windowSize个段落创建一个chunk
if ((i + 1) % windowSize == 0 || i == paragraphs.length - 1) {
chunks.add(current.toString().trim());
current = new StringBuilder();
// 重叠部分段落
for (int j = Math.max(0, i - overlap + 1); j <= i && j < paragraphs.length; j++) {
current.append(paragraphs[j]).append("\n\n");
}
}
}
return chunks;
}
向量存储
VectorStoreService
职责: 管理向量数据库 支持的向量库:- PGVector
- Milvus
- Elasticsearch
- Redis Vector
- Neo4j
Copy
public interface VectorStoreService {
/**
* 批量插入向量
*/
void batchInsert(String vectorStoreId, List<AigcSegment> segments);
/**
* 删除向量
*/
void deleteByDocsId(String vectorStoreId, String docsId);
/**
* 相似度检索
*/
List<SearchResult> search(
String vectorStoreId,
List<Float> queryVector,
int topK,
double threshold
);
/**
* 删除知识库的所有向量
*/
void deleteByKnowledgeId(String vectorStoreId, String knowledgeId);
}
PGVector实现示例
Copy
@Service
public class PGVectorStoreService implements VectorStoreService {
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public void batchInsert(String vectorStoreId, List<AigcSegment> segments) {
String sql = """
INSERT INTO langchat_segment (id, docs_id, content, embedding, chunk_index)
VALUES (?, ?, ?, ?::vector, ?)
""";
jdbcTemplate.batchUpdate(sql, segments.stream()
.map(segment -> new Object[]{
segment.getId(),
segment.getDocsId(),
segment.getContent(),
vectorToString(segment.getEmbedding()),
segment.getChunkIndex()
})
.collect(Collectors.toList()));
}
@Override
public List<SearchResult> search(
String vectorStoreId,
List<Float> queryVector,
int topK,
double threshold
) {
String sql = """
SELECT id, content, embedding <=> ?::vector as similarity
FROM langchat_segment
WHERE embedding <=> ?::vector < ?
ORDER BY embedding <=> ?::vector
LIMIT ?
""";
String vectorStr = vectorToString(queryVector);
double maxDistance = 1 - threshold;
return jdbcTemplate.query(sql, new Object[]{vectorStr, vectorStr, maxDistance, vectorStr, topK},
(rs, rowNum) -> new SearchResult(
rs.getString("id"),
rs.getString("content"),
1 - rs.getDouble("similarity") // 转换为相似度
));
}
private String vectorToString(List<Float> vector) {
return "[" + vector.stream()
.map(String::valueOf)
.collect(Collectors.joining(",")) + "]";
}
}
配置说明
知识库配置
Copy
langchat:
knowledge:
# 文档处理配置
max-file-size: 10485760 # 最大文件大小(10MB)
allowed-types: # 允许的文件类型
- pdf
- docx
- doc
- xlsx
- xls
- pptx
- ppt
- txt
- md
# 分块配置
default-chunk-size: 500 # 默认分块大小
default-overlap: 100 # 默认重叠大小
default-strategy: window # 默认分块策略
# 处理配置
thread-pool-size: 5 # 处理线程池大小
batch-size: 50 # 批量处理大小
最佳实践
1. 文档管理
- 文件大小: 控制在10MB以内
- 文件格式: 优先使用PDF、DOCX等格式
- 内容质量: 确保文档内容清晰、结构化
- 分类组织: 按主题创建不同知识库
2. 分块策略
| 场景 | 推荐策略 | 参数 |
|---|---|---|
| 技术文档 | paragraph | - |
| 小说/长文 | window | window=5, overlap=2 |
| 短文章 | size | size=1000 |
3. 向量化优化
- 批量处理: 每批50-100个分块
- 模型选择: 根据语言和场景选择
- 维度考虑: 兼顾精度和性能
- 缓存策略: 缓存重复文档的向量
4. 检索配置
- Top-K: 通常5-10
- 相似度阈值: 0.7-0.8
- Rerank: 提高准确性
- 混合检索: 结合全文检索
性能优化
1. 异步处理
Copy
@Async("docsExecutor")
public void process(String docsId) {
// 异步处理文档
}
2. 批量操作
Copy
@Override
public void batchInsert(String vectorStoreId, List<AigcSegment> segments) {
jdbcTemplate.batchUpdate(sql, params);
}
3. 向量索引
Copy
-- PGVector索引
CREATE INDEX ON langchat_segment USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
4. 缓存策略
Copy
@Cacheable(value = "docs", key = "#docsId")
public AigcDocs getById(String docsId) {
// 查询逻辑
}

