LLM 本地知识库构建:文档分割与向量化方案探讨
最近在研究如何将大语言模型结合本地知识库进行问答。虽然网上有很多基于 LangChain 的教程,通过文本分割调用模型向量化的 API,这种方式简单但存在前提限制:
- 若不使用 ChatGPT 等强效模型的 Embedding API,自建模型效果可能较差。
- 尽管有多种切分方式,仍容易撕裂文档中的语义连贯性。
由于使用外部 Embedding API 的限制,搭建本地知识库时往往需要自行处理文档预处理步骤。本文总结了一些关于文档预处理、分词及向量化的技术方案,供参考。
构建本地知识库的前提
在构建本地知识库问答系统时,第一步是对本地知识文档进行处理。为了降低使用门槛,通常不希望人工参与分段或摘要。但直接使用全文喂给大模型会超出 Token 限制,因此需要将文档知识转化为向量存储到向量数据库中。问答时,先在向量数据库匹配问题,将结果提供给 LLM 整理回答。
将文档转为向量数据通常分为两个步骤:Tokenizer 和 Embedding。
- Tokenizer:负责将文本拆分成词元(Token)。它将字符序列转换为词元序列。常见的有基于空格、标点的简单 Tokenizer,以及基于字典的复杂 Tokenizer。
- Embedding:将词元转换成稠密向量表示。它为每个词元映射到向量空间,使语义相关的词元向量更相近。
我们最终通过 Embedding 得到词汇或语句的向量。由于文档长度通常超过模型 Token 限制,且搜索目标是相关联的知识而非整篇文档,因此第一步是将文档拆分成合适的片段。
LangChain 文档拆分示例
使用 LangChain 进行文档拆分较为简单。以下是一个基础 Demo:
from langchain.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 导入文本
loader = UnstructuredFileLoader(file_path)
document = loader.load()
# 初始化文本分割器
# chunk_size: 每个块的最大长度
# chunk_overlap: 块之间的重叠部分,有助于保持上下文
text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=20)
# 切分文本
split_documents = text_splitter.split_documents(document)
LangChain 提供了多种分割器,如字符文本分割器、Markdown 文本分割器等。切分后的文本送入 Tokenizer 处理,再进行 Embedding,即可构建简单的本地知识库。
现有方式的局限性
- 上下文撕裂:分割器难以理解文档整体上下文,若切分点破坏了段落关系,会导致向量搜索效果差,进而影响 LLM 回答质量。
- 垂直领域表现:通用分割器在专业文档中容易拆分专业术语或完整语句。
文本分割对知识库至关重要。LLM 无法直接理解自然语言,有效的分词确保细微差别被准确捕捉。
进阶处理方案
目前主要考虑纯文本类型文档(Word, PDF, TXT, Markdown),暂不考虑图表穿插场景。
1. 层级摘要
大部分文本文档具有段落层级关系。最简单的方式是按段落分割,但长段落可能超过 Token 限制。针对此情况,可使用摘要模型对长段落生成包含主要信息的短版本,再使用支持长序列的模型(如 Longformer 或 BigBird)进行向量化。


