知识库-向量化功能-文本文件向量化

知识库-向量化功能-文本文件向量化

一、核心逻辑

基于句子结束符的智能分片策略

  1. 按句子结束符(。!?;\n\.!?;)分割文本,保证分片语义完整性;
  2. 对每个文本分片独立向量化
  3. 分片后批量存储至Elasticsearch,同时保留原文档元数据与分片标识,便于后续检索溯源。

二、关键实现代码

2.1 文本向量化主方法

java 复制代码
/**
 * 处理文本分片与向量化,并批量存储至ES
 * @param largeText 待处理的文本内容
 * @param docMetadata 原文档元数据(如文件名称、上传时间、文件ID等)
 * @return 分片ID列表(用于后续关联查询)
 */
public List<String> processLargeText(String largeText, Map<String,Object> docMetadata) {
    // 1. 智能分片:按句子结束符分割,避免大文本一次性处理
    List<String> chunks = textSplitter.split(largeText);
    
    // 2. 构建分片Document,关联原文档元数据+分片信息+向量数据
    List<Document> chunkDocs = chunks.stream()
            .map(chunk -> {
                // 生成分片唯一ID
                String chunkId = UUID.randomUUID().toString().replace("-", "");
                // 文本分片向量化(调用lrs33/bce-embedding-base_v1模型)
                float[] embs = embeddingModel.embed(chunk);
                
                // 分片元数据:继承原文档元数据 + 分片专属信息
                Map<String, Object> chunkMetadata = new HashMap<>(docMetadata);
                chunkMetadata.put("chunkId", chunkId);          // 分片唯一标识
                chunkMetadata.put("chunkIndex", chunks.indexOf(chunk)); // 分片序号
                chunkMetadata.put("totalChunks", chunks.size());       // 总分片数
                chunkMetadata.put("overlapLength", TextSplitter.CHUNK_OVERLAP); // 分片重叠长度
                chunkMetadata.put("vector", embs);                // 向量数据
                
                return new Document(chunkId, chunk, chunkMetadata);
            }).collect(Collectors.toList());
    
    // 3. 批量存储分片至ES向量库(自动适配配置的bulk-size)
    compatibleVectorStoreService.batchStore(chunkDocs);
    
    // 返回所有分片ID,便于后续溯源/删除操作
    return chunkDocs.stream().map(Document::getId).collect(Collectors.toList());
}

2.2 智能文本分片工具类

java 复制代码
/**
 * 文本分片工具类:按句子结束符分割,保证语义完整性,避免大文本卡死
 */
public class TextSplitter {
    // 分片最大长度(可根据业务调整,建议500-1000字符)
    public static final int MAX_CHUNK_LENGTH = 1000;
    // 分片重叠长度(避免句子被截断导致语义丢失,建议50-100字符)
    public static final int CHUNK_OVERLAP = 50;
    
    /**
     * 智能分割大文本
     * @param text 待分割的大文本
     * @return 语义完整的文本分片列表
     */
    public List<String> split(String text) {
        List<String> chunks = new ArrayList<>();
        int textLength = text.length();
        int start = 0;
        
        // 循环分片,直到处理完所有文本
        while (start < textLength) {
            // 1. 计算分片初始结束位置(不超过最大长度)
            int end = Math.min(start + MAX_CHUNK_LENGTH, textLength);
            
            // 2. 若未到文本末尾,回溯至最近的句子结束符,保证分片语义完整
            if (end < textLength) {
                String subText = text.substring(start, end);
                int lastEndPos = findLastSentenceEnd(subText);
                
                // 找到句子结束符:调整结束位置至结束符后
                if (lastEndPos != -1) {
                    end = start + lastEndPos + 1;
                } else {
                    // 未找到结束符:兜底按最大长度分割(避免无限循环)
                    end = start + MAX_CHUNK_LENGTH;
                }
            }
            
            // 3. 截取分片并去除首尾空白,非空则加入列表
            String chunk = text.substring(start, end).trim();
            if (!chunk.isEmpty()) {
                chunks.add(chunk);
            }
            
            // 4. 计算下一个分片起始位置(重叠处理,避免语义断裂)
            start = end - CHUNK_OVERLAP;
            // 防止重叠后起始位置为负数(仅首次分片可能触发)
            if (start < 0) {
                start = 0;
            }
        }
        return chunks;
    }
    
    /**
     * 查找子文本中最后一个句子结束符的位置
     * 结束符:。!?;\n\.!?;
     * @param subText 子文本
     * @return 最后一个结束符的索引(无则返回-1)
     */
    private int findLastSentenceEnd(String subText) {
        // 定义句子结束符正则
        String endChars = "。!?;\n\\.!?;";
        for (int i = subText.length() - 1; i >= 0; i--) {
            if (endChars.indexOf(subText.charAt(i)) != -1) {
                return i;
            }
        }
        return -1;
    }
}

三、核心设计说明

3.1 分片策略优势

设计点 解决的问题
按句子结束符分割 避免生硬截断文本导致的语义破碎,保证分片语义完整性
分片重叠处理(CHUNK_OVERLAP) 解决跨分片句子语义断裂问题(如分片1末尾与分片2开头重叠50字符)
兜底按最大长度分割 避免无结束符的超长文本(如纯数字/英文)导致循环无法终止
非空分片过滤 避免空字符串分片占用ES存储空间

3.2 关键参数说明

参数名 默认值 调整建议
MAX_CHUNK_LENGTH 1000 可根据模型上下文窗口(num_ctx=1024)调整,建议不超过1000字符,避免模型处理超时
CHUNK_OVERLAP 50 建议为MAX_CHUNK_LENGTH的5%-10%,过小易丢失跨分片语义,过大则增加冗余存储

3.3 元数据设计

分片元数据同时包含原文档属性分片专属属性,便于:

  1. 检索时溯源至原文件(如通过文件ID关联);
  2. 查看分片在原文本中的位置(chunkIndex);
  3. 统计原文本的总分片数(totalChunks);
  4. 快速定位向量数据(vector字段)。

四、性能优化说明

  1. 避免大文本一次性处理:通过循环分片+逐片向量化,降低单次内存占用,解决split方法卡死问题;
  2. 批量写入ES :结合配置的bulk-size,减少ES网络请求次数,提升写入效率;
  3. 分片去空:过滤空白分片,减少无效数据存储和向量计算开销;
  4. 索引刷新间隔 :配合ES配置的refresh-interval,避免高频刷盘导致的性能损耗。

五、注意事项

  1. 句子结束符正则需覆盖中英文场景(。!?;\n\.!?;),避免遗漏分割点;
  2. 向量化时需保证embeddingModel与配置的lrs33/bce-embedding-base_v1模型一致,否则向量维度不匹配;
  3. 分片ID采用UUID生成,避免重复;若需关联原文件,可在元数据中增加fileId等标识;
  4. 处理长篇文本时,建议增加异步处理逻辑,避免阻塞主线程。
  5. 一次性传入超长文本容易使split方法卡死,建议提前将文本分割后再使用
相关推荐
Java水解2 小时前
MySQL索引分析以及相关面试题
后端·mysql·面试
总是学不会.2 小时前
[特殊字符] 自动分区管理系统实践:让大型表维护更轻松
java·后端·数据库开发·开发
阿杰AJie3 小时前
通用 Token 管理工具(详细注释 + 完整使用示例 + 设计说明)
java·后端·程序员
用户47949283569153 小时前
并发编程里的"堵车"与"红绿灯":死锁、活锁与两种锁策略(乐观锁、悲观锁)
前端·后端
一 乐3 小时前
智慧医药|基于springboot + vue智慧医药系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端
Tony Bai3 小时前
Goroutine “气泡”宇宙——Go 并发模型的新维度
开发语言·后端·golang
王中阳Go3 小时前
攻克制造业知识检索难题:我们如何用Go+AI打造高可用RAG系统,将查询效率提升600%
人工智能·后端·go
游浪踏3 小时前
006_prompt
后端·openai
悟空码字3 小时前
SpringBoot接口防抖大作战,拒绝“手抖”重复提交!
java·spring boot·后端