Skip to main content

概述

知识库是LangChat Pro RAG(检索增强生成)功能的核心数据源,用于存储和管理文档、向量、检索配置等。支持多种向量数据库,提供完整的文档管理、向量化、检索能力。

核心实体

AigcKnowledge(知识库)

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(文档)

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(文本分段)

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 核心方法:
public interface AigcKnowledgeService {
    
    /**
     * 创建知识库
     */
    String createKnowledge(AigcKnowledge knowledge);
    
    /**
     * 删除知识库
     */
    void deleteKnowledge(String id);
    
    /**
     * 获取用户的知识库列表
     */
    List<AigcKnowledge> listByUserId(String userId);
    
    /**
     * 批量创建知识库
     */
    List<String> batchCreate(List<AigcKnowledge> knowledges);
}

AigcDocsService

核心方法:
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

核心方法:
public interface AigcSegmentService {
    
    /**
     * 批量创建分段
     */
    void batchCreate(List<AigcSegment> segments);
    
    /**
     * 删除文档的所有分段
     */
    void deleteByDocsId(String docsId);
    
    /**
     * 检索相关分段
     */
    List<AigcSegment> search(String knowledgeId, String query, Integer topK);
}

文档处理流程

完整流程

文档上传

保存文件到OSS

创建AigcDocs记录

异步处理任务

┌─────────────────┐
│ 文档解析        │
│ - PDF/Word等  │
└────────┬────────┘

┌─────────────────┐
│ 文本提取        │
│ - 纯文本内容    │
└────────┬────────┘

┌─────────────────┐
│ 文本分块        │
│ - 分段策略      │
└────────┬────────┘

┌─────────────────┐
│ 向量化          │
│ - Embedding    │
└────────┬────────┘

┌─────────────────┐
│ 存储向量库      │
│ - PGVector等  │
└─────────────────┘

更新文档状态:completed

代码实现

@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

职责: 解析各种格式的文档 支持的格式:
  • PDF
  • Word (docx, doc)
  • Excel (xlsx, xls)
  • PowerPoint (pptx, ppt)
  • Text (txt, md)
  • HTML
实现示例:
@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. 固定大小分块

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. 按段落分块

public List<String> splitByParagraph(String text) {
    return Arrays.stream(text.split("\\n\\s*\\n"))
        .filter(StrUtil::isNotBlank)
        .map(String::trim)
        .collect(Collectors.toList());
}

3. 滑动窗口分块(推荐)

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
核心方法:
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实现示例

@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(",")) + "]";
    }
}

配置说明

知识库配置

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-
小说/长文windowwindow=5, overlap=2
短文章sizesize=1000

3. 向量化优化

  • 批量处理: 每批50-100个分块
  • 模型选择: 根据语言和场景选择
  • 维度考虑: 兼顾精度和性能
  • 缓存策略: 缓存重复文档的向量

4. 检索配置

  • Top-K: 通常5-10
  • 相似度阈值: 0.7-0.8
  • Rerank: 提高准确性
  • 混合检索: 结合全文检索

性能优化

1. 异步处理

@Async("docsExecutor")
public void process(String docsId) {
    // 异步处理文档
}

2. 批量操作

@Override
public void batchInsert(String vectorStoreId, List<AigcSegment> segments) {
    jdbcTemplate.batchUpdate(sql, params);
}

3. 向量索引

-- PGVector索引
CREATE INDEX ON langchat_segment USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

4. 缓存策略

@Cacheable(value = "docs", key = "#docsId")
public AigcDocs getById(String docsId) {
    // 查询逻辑
}

参考文档