RAG知识库核心优化|基于语义的智能文本切片方案(对比字符串长度分割)

RAG知识库核心优化|基于语义的智能文本切片方案(对比字符串长度分割)

一、改造核心价值

传统的「按字符串长度硬分割」会破坏文本语义完整性,导致切片内容碎片化、检索答非所问;而基于语义的智能切片以完整语义单元(句子、段落)为边界拆分文本,核心价值如下: 切片内容完整:每个切片都是独立的语义单元(如完整句子/段落),避免「一句话被拆成两个切片」的问题; 检索精度提升:语义完整的切片向量化后,与用户提问的匹配度更高,大幅减少检索误差; 上下文连贯性:新增语义重叠(Overlap)机制,相邻切片保留部分重叠内容,解决跨切片语义断裂问题; 灵活适配:支持长切片二次拆分、短切片合并,兼顾长度限制与语义完整性; 去重优化:可选相似切片合并,减少冗余向量数据,降低存储/检索成本。

二、语义切片 vs 字符串长度切片(核心区别)

对比维度 字符串长度切片(传统方案) 基于语义的智能切片(优化方案)
分割依据 固定字符数(如每500字切一片),无语义考量 语义边界(段落、句子、标点),优先保证语义完整
内容完整性 易拆分完整句子/概念(如"张三是工程师"拆成"张三是"+"工程师") 每个切片都是完整语义单元,无碎片化内容
检索匹配度 向量特征碎片化,易匹配到不相关切片 向量特征完整,精准匹配用户提问的语义
上下文连贯性 相邻切片无关联,跨切片内容无法衔接 新增15%语义重叠,相邻切片保留重叠内容,衔接自然
灵活性 固定长度,无法适配长短句混合的文本 长切片二次拆分、短切片合并,动态适配文本结构
冗余度 无去重机制,相似内容重复切片 可选相似切片合并,减少冗余向量数据
适用场景 纯代码/无语义的纯字符文本 知识库文档(PDF/Word)、自然语言文本

三、完整集成方案

1. Maven依赖

xml 复制代码
<!-- HanLP:中文分词、词性标注、语义边界识别(核心依赖) -->
<dependency>
    <groupId>com.hankcs</groupId>
    <artifactId>hanlp</artifactId>
    <version>portable-1.8.4</version>
</dependency>

<!-- 文本相似度计算(相似切片合并用,可选) -->
<dependency>
    <groupId>info.debatty</groupId>
    <artifactId>java-string-similarity</artifactId>
    <version>2.0.0</version>
</dependency>

2. 核心模型:SemanticChunk

java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 语义切片实体【生产级】
 * 封装切片核心信息,支撑知识库溯源+向量检索
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SemanticChunk {
    /** 切片唯一ID(UUID) */
    private String chunkId;
    /** 切片文本内容(完整语义单元) */
    private String content;
    /** 来源文件名称(如:技术手册v1.0.pdf) */
    private String fileName;
    /** 来源文件在MinIO的存储路径(用于溯源) */
    private String objectName;
    /** 切片在文件中的段落序号(切片顺序) */
    private Integer paraNum;
    /** 切片长度(字符数) */
    private Integer length;
    /** 新增:切片类型(段落/句子,便于检索过滤) */
    private String chunkType;
    /** 新增:创建时间戳(用于排序/排查) */
    private Long createTime;

    // 快捷构建方法(简化调用)
    public static SemanticChunk build(String content, String fileName, String objectName, Integer paraNum) {
        SemanticChunk chunk = new SemanticChunk();
        chunk.setChunkId(java.util.UUID.randomUUID().toString());
        chunk.setContent(content);
        chunk.setFileName(fileName);
        chunk.setObjectName(objectName);
        chunk.setParaNum(paraNum);
        chunk.setLength(content.length());
        chunk.setChunkType(content.contains("\n") ? "paragraph" : "sentence");
        chunk.setCreateTime(System.currentTimeMillis());
        return chunk;
    }
}

3. 核心服务:SemanticChunkService

java 复制代码
import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
import info.debatty.java.stringsimilarity.Cosine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 语义切片核心服务【生产级】
 * 职责:基于语义边界拆分文本,生成完整、连贯、无冗余的切片,支撑RAG知识库向量化
 */
@Service
@Slf4j
public class SemanticChunkService {
    @Resource
    private EmbeddingModel embeddingModel;
    @Resource
    private ElasticsearchVectorService elasticsearchVectorService;

    // ========== 核心配置参数(可抽离到yaml) ==========
    /** 最大切片长度(超过则二次拆分,避免向量超限) */
    private static final int MAX_CHUNK_LENGTH = 1000;
    /** 最小切片长度(低于则合并,避免碎片化) */
    private static final int MIN_CHUNK_LENGTH = 50;
    /** 中文语义分割符(优先级从高到低:句子结束符→分句符→分隔符) */
    private static final String[] SPLIT_DELIMITERS = {"。", "!", "?", ";", ","};
    /** 去噪正则:匹配多余空格、换行、制表符、全角空格 */
    private static final Pattern NOISE_PATTERN = Pattern.compile("\\s+| ");
    /** 相似度阈值:0.8以上视为高度相似,可合并 */
    private static final double SIMILARITY_THRESHOLD = 0.8;
    /** 切片重叠比例(15%,平衡连贯性与冗余度) */
    private static final double OVERLAP_RATIO = 0.15;
    /** 最小重叠字符数(避免短切片重叠过少) */
    private static final int MIN_OVERLAP_LENGTH = 20;
    /** 最大重叠字符数(避免长切片重叠过多) */
    private static final int MAX_OVERLAP_LENGTH = 100;
    /** 批量写入ES的批次大小(根据ES性能调整) */
    private static final int BATCH_SIZE = 20;

    // 相似度计算工具(全局单例)
    private final Cosine cosine = new Cosine();

    // ========== 1. 文本预处理:清洗与标准化(核心前置步骤) ==========
    /**
     * 清洗文本:去除冗余空格/换行、标准化格式,为语义分割做准备
     */
    public String cleanText(String rawText) {
        if (StringUtils.isBlank(rawText)) {
            log.warn("待清洗文本为空,返回空字符串");
            return "";
        }
        // 1. 去除所有冗余空白字符(空格、换行、制表符、全角空格)
        String cleanText = NOISE_PATTERN.matcher(rawText).replaceAll(" ");
        // 2. 去除首尾空白,标准化格式
        cleanText = cleanText.trim();
        // 3. 替换连续标点(如"。。。"→"。"),避免分割异常
        cleanText = cleanText.replaceAll("[。!?;,]{2,}", "$1");
        log.info("文本清洗完成,原长度:{},清洗后长度:{}", rawText.length(), cleanText.length());
        return cleanText;
    }

    // ========== 2. 粗分割:基于语义边界拆分基础块(核心第一步) ==========
    /**
     * 粗分割:优先按段落拆分,再按语义标点拆分,生成基础语义块
     */
    public List<String> roughSplit(String cleanText) {
        List<String> chunks = new ArrayList<>();
        if (StringUtils.isBlank(cleanText)) {
            return chunks;
        }

        // 第一步:按段落拆分(段落是天然的大语义边界)
        String[] paragraphs = cleanText.split("\n");
        for (String para : paragraphs) {
            para = para.trim();
            if (para.length() == 0) {
                continue;
            }
            // 第二步:对每个段落,按语义标点拆分(保留标点,保证语义完整)
            List<String> paraChunks = splitByDelimiters(para);
            chunks.addAll(paraChunks);
        }

        log.info("粗分割完成,生成基础语义块数量:{}", chunks.size());
        return chunks;
    }

    /**
     * 按语义分割符优先级拆分文本(核心工具方法)
     * 保留分割符,确保每个基础块都是完整的语义单元
     */
    private List<String> splitByDelimiters(String text) {
        List<String> result = new ArrayList<>();
        result.add(text);

        // 按分割符优先级依次拆分(先拆句子结束符,再拆分句符)
        for (String delimiter : SPLIT_DELIMITERS) {
            List<String> temp = new ArrayList<>();
            for (String chunk : result) {
                String[] parts = chunk.split(Pattern.quote(delimiter)); // 转义特殊字符
                for (int i = 0; i < parts.length; i++) {
                    String part = parts[i].trim();
                    if (part.length() == 0) {
                        continue;
                    }
                    // 保留分割符(否则语义不完整,如"我是张三"拆成"我是"+"张三")
                    if (i < parts.length - 1) {
                        part += delimiter;
                    }
                    temp.add(part);
                }
            }
            result = temp;
        }
        return result;
    }

    // ========== 3. 细调整:长块拆分+短块合并+语义重叠(核心第二步) ==========
    /**
     * 细调整:
     * 1. 长块→基于HanLP分词二次拆分(保证句子完整)
     * 2. 短块→合并(避免碎片化)
     * 3. 所有块→添加语义重叠(保证上下文连贯)
     */
    public List<String> fineAdjust(List<String> roughChunks) {
        List<String> finalChunks = new ArrayList<>();
        StringBuilder shortChunkBuffer = new StringBuilder();
        String prevChunk = ""; // 记录前一个切片,用于生成重叠

        for (String chunk : roughChunks) {
            int length = chunk.length();

            // 情况1:切片过长(超过MAX)→ 基于HanLP分词拆分(按句子边界)
            if (length > MAX_CHUNK_LENGTH) {
                List<String> subChunks = splitLongChunkByHanLP(chunk);
                subChunks = addOverlapToSubChunks(subChunks); // 子切片添加重叠
                finalChunks.addAll(subChunks);
                prevChunk = subChunks.isEmpty() ? prevChunk : subChunks.get(subChunks.size() - 1);
                continue;
            }

            // 情况2:切片过短(低于MIN)→ 暂存合并,凑够长度再加入
            if (length < MIN_CHUNK_LENGTH) {
                shortChunkBuffer.append(chunk);
                if (shortChunkBuffer.length() >= MIN_CHUNK_LENGTH) {
                    String mergedChunk = shortChunkBuffer.toString();
                    mergedChunk = addOverlapWithPrevChunk(mergedChunk, prevChunk); // 添加重叠
                    finalChunks.add(mergedChunk);
                    prevChunk = mergedChunk;
                    shortChunkBuffer.setLength(0); // 清空缓冲区
                }
                continue;
            }

            // 情况3:长度适中→直接添加重叠后加入
            String chunkWithOverlap = addOverlapWithPrevChunk(chunk, prevChunk);
            finalChunks.add(chunkWithOverlap);
            prevChunk = chunk;
        }

        // 处理缓冲区剩余的短切片
        if (shortChunkBuffer.length() > 0) {
            String remainingChunk = shortChunkBuffer.toString();
            remainingChunk = addOverlapWithPrevChunk(remainingChunk, prevChunk);
            finalChunks.add(remainingChunk);
        }

        log.info("细调整完成,最终切片数量:{}", finalChunks.size());
        return finalChunks;
    }

    /**
     * 基于HanLP分词拆分长切片
     * 避免硬分割破坏语义,保证拆分后的子块仍是完整句子
     */
    private List<String> splitLongChunkByHanLP(String longChunk) {
        List<String> subChunks = new ArrayList<>();
        List<Term> terms = StandardTokenizer.segment(longChunk); // HanLP标准分词
        StringBuilder currentSubChunk = new StringBuilder();

        for (Term term : terms) {
            currentSubChunk.append(term.word);
            String word = term.word;

            // 遇到句子结束符→拆分(保证子块是完整句子)
            if (word.equals("。") || word.equals("!") || word.equals("?")) {
                String subChunk = currentSubChunk.toString().trim();
                if (subChunk.length() > 0) {
                    subChunks.add(subChunk);
                }
                currentSubChunk.setLength(0);
            }

            // 防止子块过长→强制拆分(兜底策略)
            if (currentSubChunk.length() > MAX_CHUNK_LENGTH / 2) {
                String subChunk = currentSubChunk.toString().trim();
                if (subChunk.length() > 0) {
                    subChunks.add(subChunk);
                }
                currentSubChunk.setLength(0);
            }
        }

        // 处理剩余内容
        if (currentSubChunk.length() > 0) {
            subChunks.add(currentSubChunk.toString().trim());
        }

        log.info("长切片拆分完成,原长度:{},拆分子块数量:{}", longChunk.length(), subChunks.size());
        return subChunks;
    }

    // ========== 语义重叠核心方法(保证上下文连贯) ==========
    /**
     * 给拆分后的子切片添加语义重叠(相邻子切片保留15%重叠内容)
     */
    private List<String> addOverlapToSubChunks(List<String> subChunks) {
        if (subChunks.size() <= 1) {
            return subChunks;
        }

        List<String> subChunksWithOverlap = new ArrayList<>();
        for (int i = 0; i < subChunks.size(); i++) {
            String current = subChunks.get(i);
            if (i == 0) {
                subChunksWithOverlap.add(current);
                continue;
            }

            // 非第一个切片:添加与前一个切片的重叠内容
            String prev = subChunks.get(i - 1);
            String overlapContent = getOverlapContent(prev);
            String currentWithOverlap = overlapContent + current;

            // 兜底:超过最大长度则按语义截断
            if (currentWithOverlap.length() > MAX_CHUNK_LENGTH) {
                currentWithOverlap = truncateByDelimiter(currentWithOverlap, MAX_CHUNK_LENGTH);
            }
            subChunksWithOverlap.add(currentWithOverlap);
        }
        return subChunksWithOverlap;
    }

    /**
     * 给当前切片添加与前一个切片的语义重叠(核心:保留上下文)
     */
    private String addOverlapWithPrevChunk(String currentChunk, String prevChunk) {
        if (StringUtils.isBlank(prevChunk) || StringUtils.isBlank(currentChunk)) {
            return currentChunk;
        }

        String overlapContent = getOverlapContent(prevChunk);
        String currentWithOverlap = overlapContent + currentChunk;

        // 防止拼接后超过最大长度,按语义截断
        if (currentWithOverlap.length() > MAX_CHUNK_LENGTH) {
            currentWithOverlap = truncateByDelimiter(currentWithOverlap, MAX_CHUNK_LENGTH);
        }
        return currentWithOverlap;
    }

    /**
     * 计算重叠内容:取前一个切片末尾的N个字符(15%,且在[20,100]之间)
     * 优先按语义分割符调整,保证重叠内容是完整语义单元
     */
    private String getOverlapContent(String prevChunk) {
        if (StringUtils.isBlank(prevChunk)) {
            return "";
        }

        // 计算目标重叠长度(15%,限制上下限)
        int targetOverlapLength = (int) (prevChunk.length() * OVERLAP_RATIO);
        targetOverlapLength = Math.max(MIN_OVERLAP_LENGTH, Math.min(targetOverlapLength, MAX_OVERLAP_LENGTH));

        // 前一个切片过短→直接返回整个切片
        if (prevChunk.length() <= targetOverlapLength) {
            return prevChunk;
        }

        // 取前一个切片末尾的目标长度字符
        String overlapContent = prevChunk.substring(prevChunk.length() - targetOverlapLength);

        // 优化:按语义分割符调整,保证重叠内容完整
        for (String delimiter : SPLIT_DELIMITERS) {
            int delimiterIndex = overlapContent.indexOf(delimiter);
            if (delimiterIndex != -1 && delimiterIndex < overlapContent.length() - 1) {
                overlapContent = overlapContent.substring(delimiterIndex + 1).trim();
                break;
            }
        }

        return overlapContent;
    }

    /**
     * 按语义分割符截断文本(兜底:保证截断后仍是完整语义单元)
     */
    private String truncateByDelimiter(String text, int maxLength) {
        if (text.length() <= maxLength) {
            return text;
        }

        // 先截断到最大长度,再往前找最近的语义分割符
        String truncated = text.substring(0, maxLength);
        for (int i = truncated.length() - 1; i >= 0; i--) {
            char c = truncated.charAt(i);
            if (Arrays.asList(SPLIT_DELIMITERS).contains(String.valueOf(c))) {
                return truncated.substring(0, i + 1);
            }
        }

        // 无分割符→直接返回截断内容(兜底)
        return truncated;
    }

    // ========== 4. 相似切片合并(可选:减少冗余) ==========
    /**
     * 合并高度相似的切片(相似度≥0.8),减少冗余向量数据
     * 注意:合并后需重新检查长度和重叠,避免超限
     */
    public List<String> mergeSimilarChunks(List<String> chunks) {
        if (chunks.size() <= 1) {
            return chunks;
        }

        List<String> mergedChunks = new ArrayList<>();
        for (String chunk : chunks) {
            boolean isSimilar = false;
            for (int i = 0; i < mergedChunks.size(); i++) {
                String existing = mergedChunks.get(i);
                // 计算余弦相似度(文本向量化后对比)
                double similarity = cosine.similarity(chunk, existing);
                if (similarity >= SIMILARITY_THRESHOLD) {
                    // 合并相似切片,保留完整内容
                    String merged = existing + " " + chunk;
                    // 合并后过长→拆分
                    if (merged.length() > MAX_CHUNK_LENGTH) {
                        mergedChunks.addAll(splitLongChunkByHanLP(merged));
                    } else {
                        mergedChunks.set(i, merged.trim());
                    }
                    isSimilar = true;
                    break;
                }
            }
            if (!isSimilar) {
                mergedChunks.add(chunk);
            }
        }

        log.info("相似切片合并完成,原数量:{},合并后数量:{}", chunks.size(), mergedChunks.size());
        return mergedChunks;
    }

    // ========== 5. 构建切片对象(补充元信息) ==========
    /**
     * 构建语义切片对象,补充文件名、存储路径等元信息,支撑溯源
     */
    public List<SemanticChunk> buildSemanticChunks(List<String> textChunks, String fileName, String objectName) {
        List<SemanticChunk> chunks = new ArrayList<>();
        for (int i = 0; i < textChunks.size(); i++) {
            String content = textChunks.get(i);
            chunks.add(SemanticChunk.build(content, fileName, objectName, i + 1));
        }
        log.info("语义切片对象构建完成,数量:{}", chunks.size());
        return chunks;
    }

    // ========== 6. 对外暴露的核心方法:完整语义切片+向量化流程 ==========
    /**
     * 完整流程:文本清洗→粗分割→细调整→构建切片→向量化→写入ES
     * @param rawText 原始文本(如PDF/Word解析后的内容)
     * @param fileName 文件名(如:技术手册v1.0.pdf)
     * @param objectName MinIO存储路径(如:tech-manual/xxx.pdf)
     * @param docMetadata 文档元数据(如:文件类型、上传人、创建时间)
     */
    public void sliceAndVector(String rawText, String fileName, String objectName, Map<String, Object> docMetadata) {
        try {
            log.info("开始处理文件【{}】的语义切片,原文本长度:{}", fileName, rawText == null ? 0 : rawText.length());

            // 1. 文本清洗
            String cleanText = cleanText(rawText);
            if (StringUtils.isBlank(cleanText)) {
                log.warn("文件【{}】清洗后文本为空,跳过切片", fileName);
                return;
            }

            // 2. 粗分割(基于语义边界)
            List<String> roughChunks = roughSplit(cleanText);

            // 3. 细调整(长拆短合+语义重叠)
            List<String> finalTextChunks = fineAdjust(roughChunks);

            // 可选:合并相似切片(减少冗余,根据业务需求开启)
            // finalTextChunks = mergeSimilarChunks(finalTextChunks);

            // 4. 构建语义切片对象
            List<SemanticChunk> semanticChunks = buildSemanticChunks(finalTextChunks, fileName, objectName);

            // 5. 向量化并批量写入ES
            vectorAndWriteToEs(semanticChunks, docMetadata);

            log.info("文件【{}】语义切片+向量化完成,最终切片数量:{}", fileName, semanticChunks.size());
        } catch (Exception e) {
            log.error("文件【{}】语义切片处理失败", fileName, e);
            throw new RuntimeException("语义切片处理失败:" + e.getMessage());
        }
    }

    /**
     * 切片向量化+批量写入ES(生产级:分批写入,避免ES压力过大)
     */
    private void vectorAndWriteToEs(List<SemanticChunk> chunks, Map<String, Object> docMetadata) {
        if (chunks.isEmpty()) {
            log.warn("无切片数据,跳过向量化");
            return;
        }

        List<Document> batchDocs = new ArrayList<>();
        int batchCount = 0;

        for (SemanticChunk chunk : chunks) {
            try {
                // 1. 文本向量化(生成浮点型向量)
                float[] embeddings = embeddingModel.embed(chunk.getContent());

                // 2. 构建ES文档(补充切片元数据)
                String chunkId = UUID.randomUUID().toString().replace("-", "");
                Map<String, Object> chunkMetadata = new HashMap<>(docMetadata);
                chunkMetadata.put("chunkId", chunkId);
                chunkMetadata.put("vector", embeddings);
                chunkMetadata.put("chunkIndex", chunk.getParaNum());
                chunkMetadata.put("chunkLength", chunk.getLength());
                chunkMetadata.put("chunkType", chunk.getChunkType());
                chunkMetadata.put("objectName", chunk.getObjectName());

                // 3. 添加到批次
                Document document = new Document(chunk.getChunkId(), chunk.getContent(), chunkMetadata);
                batchDocs.add(document);

                // 4. 达到批次大小→批量写入ES
                if (batchDocs.size() >= BATCH_SIZE) {
                    elasticsearchVectorService.writeBatchToEs(batchDocs);
                    log.info("批量写入ES完成,批次:{},文档数:{}", batchCount, batchDocs.size());
                    batchDocs.clear();
                    batchCount++;
                }
            } catch (Exception e) {
                log.error("切片【{}】向量化失败", chunk.getChunkId(), e);
                // 单个切片失败不影响整体流程,继续处理下一个
                continue;
            }
        }

        // 处理最后一批剩余文档
        if (!batchDocs.isEmpty()) {
            elasticsearchVectorService.writeBatchToEs(batchDocs);
            log.info("最后一批写入ES完成,文档数:{}", batchDocs.size());
        }
    }
}

四、语义切片完整链路(RAG知识库集成)

markdown 复制代码
1. 前端上传知识库文件→MinIO存储→获取文件流/文本内容
2. 调用SemanticChunkService.sliceAndVector()方法:
   → 文本清洗(去噪、标准化)
   → 粗分割(按段落→按语义标点拆分)
   → 细调整(长拆短合+15%语义重叠)
   → 构建切片对象(补充元信息)
   → 切片向量化→批量写入ES向量库
3. 用户检索时→ES向量检索→返回完整语义切片→大模型生成答案

五、生产级进阶扩展(可选)

扩展1:配置参数外置(灵活调整)

将核心参数(如最大/最小切片长度、重叠比例)抽离到application.yml,无需修改代码即可调整:

yaml 复制代码
# 语义切片配置
semantic-chunk:
  max-length: 1000
  min-length: 50
  overlap-ratio: 0.15
  min-overlap-length: 20
  max-overlap-length: 100
  similarity-threshold: 0.8
  batch-size: 20

扩展2:支持多语言语义切片

集成多语言分词工具(如Jieba、NLTK),适配英文/多语言知识库文件:

java 复制代码
// 示例:英文语义分割(按句号/问号拆分)
private static final String[] ENGLISH_DELIMITERS = {". ", "! ", "? ", "; ", ", "};

扩展3:切片质量校验

新增切片质量评分(如语义完整性、长度合规性),过滤低质量切片:

java 复制代码
/**
 * 切片质量校验:评分≥80视为高质量切片
 */
public int scoreChunkQuality(SemanticChunk chunk) {
    int score = 100;
    // 长度超标扣20分
    if (chunk.getLength() > MAX_CHUNK_LENGTH) score -= 20;
    // 碎片化扣10分
    if (chunk.getLength() < MIN_CHUNK_LENGTH) score -= 10;
    // 无有效语义扣30分(如纯数字/符号)
    if (chunk.getContent().matches("^[0-9\\p{Punct}]+$")) score -= 30;
    return Math.max(0, score);
}

扩展4:切片缓存

对高频访问的切片(如热门知识库文档)进行缓存,减少重复切片/向量化:

java 复制代码
@Cacheable(value = "semanticChunk", key = "#fileName + #objectName")
public List<SemanticChunk> getCachedChunks(String fileName, String objectName) {
    // 生成切片的逻辑
}

六、关键注意事项(生产落地必看)

HanLP配置 :生产环境建议下载HanLP数据包,避免首次调用分词时加载缓慢; 向量长度适配 :根据嵌入模型的向量维度(如384/768维)调整最大切片长度,避免向量特征丢失; 重叠比例调整 :根据文档类型调整重叠比例(技术文档建议15%-20%,通用文档建议10%); 相似合并慎用 :对于高精准度要求的知识库(如技术手册),建议关闭相似切片合并,避免语义丢失; 监控告警:监控切片数量、向量化成功率、ES写入成功率,异常时及时告警。

相关推荐
G31135422731 分钟前
如何用 QClaw 龙虾做一个规律作息健康助理 Agent
大数据·人工智能·ai·云计算
幂律智能3 分钟前
零售行业合同管理数智化转型解决方案
大数据·人工智能·零售
旺财矿工4 分钟前
零基础搭建 OpenClaw 2.6.6 Win11 本地化运行环境
人工智能·openclaw·小龙虾·龙虾·openclaw安装包
九成宫5 分钟前
动手学深度学习PyTorch版初步安装过程
人工智能·pytorch·深度学习
Traving Yu6 分钟前
Prompt提示词工程
人工智能·prompt
NOCSAH6 分钟前
统好AI CRM功能解析:智能录入与跟进
人工智能
He少年8 分钟前
【AI 辅助编程做设备数据采集:一个真实项目的迭代复盘(OpenSpec 驱动)】
人工智能
华万通信king12 分钟前
WorkBuddy知识库企业级搭建实战:从零到生产级别的完整路径
大数据·人工智能
等风来_shy18 分钟前
如何写好一个 Skill
后端
测试员周周19 分钟前
【AI测试系统】第3篇:AI生成的测试用例太“水”?14年老兵:规则引擎+AI才是王炸组合
人工智能·python·测试