$ cat ~/articles/35 _

测试

作者:jaifire 2016-09-23 13:34 173 阅读

一、如何实现「语义切分」(而不是死板按字数截断)

1. 主流实现方案

1)基于标点/段落分割(最简单实用,RAG项目首选) 先按换行、句号、分号、段落分隔把文档切成自然句子,再把连续句子拼接起来,直到接近token上限再截断。 不会把一句话从中间劈断,保证每一块语义完整。

工具:LangChain 的 RecursiveCharacterTextSplitter 分割顺序:段落→换行→句号→逗号,逐级切分,优先保证句子完整。

2)基于大模型语义分割(精度更高,成本更高) 调用LLM把整篇文档拆成逻辑章节,保证每一块是完整知识点(一条佣金规则、一条违约条款)。适合合同、规章这类结构化文档。

3)基于句向量聚类(进阶方案) 先把每句话向量化,再把语义相近的连续句子自动合并成一块,自动识别章节边界。


2. 生产落地首选方案(你这个租赁知识库直接用)

用递归字符分割器:

  • 优先在段落、句号处断开;
  • 块之间设置 overlap 重叠文本;
  • 达到token上限才切,不会腰斩完整条款。
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,        # 单块token上限
    chunk_overlap=60,       # 块之间保留重叠,防止句子被切断
    separators=["\n\n", "\n", "。", ";", ","]  # 优先分割顺序
)

二、拆分片段的原始数据如何保存?

结论:不要把整块原文塞进payload,只存索引+文件路径是最优方案。

方案对比

方案A:payload存放完整文本块

  • 缺点:向量库体积膨胀;重复占用存储空间;更新原文时很难同步清理旧向量。
  • 适用:极小知识库。

方案B(推荐,生产标准做法)

payload只保存三类信息:

  1. file_path:文档本地/对象存储路径
  2. chunk_idx:当前是这篇文档的第几块
  3. 业务标签:分类、创建时间、房源类型等用于过滤

向量库只存:向量 + 精简元数据,不存大块原文。

完整工作流

  1. 文档本地/MinIO存原文;
  2. 文档语义分块,给每一块打上 文件路径+分片序号
  3. 把向量 + {path, chunk_idx, category, time} 写入Qdrant;
  4. 检索命中后,根据 path + chunk_idx 去原文文件读取对应片段;
  5. 再把真实文本送入Rerank+LLM。

好处

  1. 向量库轻量化,读写更快;
  2. 原文单独维护,修改文档后可以全量重建向量,互不干扰;
  3. 避免Qdrant存储大量长文本造成存储冗余。

三、补充一条面试踩坑点

如果把完整文本都塞进向量库payload: 当知识库达到几十万分片,Qdrant的索引体积会成倍变大,查询延迟上升。 行业最佳实践都是:向量库只存索引与元数据,原文独立存放,检索命中后再二次加载文本内容。

如果你需要,我可以把「文件分块→记录路径+分片号→命中后读取对应片段」写成一整套连贯代码。