最近因为项目上的需要,加上 Deepseek-R1 的能力也很出众,打算重新尝试和研究一下企业级完整的知识库搭建的流程,以及在搜集和尝试的过程中遇到的问题

整体流程

无论是企业级还是本地知识库的部署,原理都是类似的:

  • 文档分块 (Chunking)
  • 文本向量化 (Embedding)
  • 向量检索 (Vector Search)
  • 大模型生成 (LLM)
  • **将检索到的内容作为上下文 (Context)**,让模型基于上下文回答问题
    区别最大的是可拓展性,并发性以及安全的考虑。

接下来先介绍分块:

分块

因为有如下的几个限制,所以目前所有的输入文本最好还是先进行分解:

  • 文本太长:Embedding 模型通常有最大长度限制(例如 512 或 1024 tokens)。
  • 细粒度检索:如果索引整个文档,当用户提问时,很难精确检索到相关内容。
  • 存储和检索效率:向量数据库存储的单位通常是“段落”或“句子级别”的小块,而不是整个文档。

结合上面的场景,简单的的分块方法有如下几种:

  1. 固定长度分块
  2. 滑动窗口
  3. 按照标点或者自然语言逻辑切分

knowledge-chunk

固定长度切分

比较简单粗暴。直接按照字符或者 token 长度来进行切分

车辆启动后,驾驶员发现发动机声音异常,有较大噪音。经检查,发现排气系统存在泄漏。 维修人员建议更换排气管。更换后,噪音消失,问题解决。

- Chunk 1: 车辆启动后,驾驶员发现发动机声音异常,有较大噪音。
- Chunk 2: 经检查,发现排气系统存在泄漏。维修人员建议更换排气管。
- Chunk 3: 更换后,噪音消失,问题解决。

优点

  • 计算简单,易于实现。
  • 适用于结构化文本,如 JSON、数据库数据、API 文档。
    缺点
  • 可能把相关内容截断,导致语义理解丢失。
  • 可能会把一个完整的概念切开,影响后续检索。

滑动窗口

在固定长度切分的基础上,每个块可能会与部份重复的语义和内容,相比上一种可以减少信息的丢失

- Chunk 1: 车辆启动后,驾驶员发现发动机声音异常,有较大噪音。
- Chunk 2: 驾驶员发现发动机声音异常,有较大噪音。经检查,发现排气系统存在泄漏。
- Chunk 3: 经检查,发现排气系统存在泄漏。维修人员建议更换排气管。

优点

  • 解决了固定长度切分导致的“上下文丢失”问题。
  • 避免问答时检索不到完整上下文信息。
    缺点
  • 存储量增加(重复存储部分内容),索引占用空间变大。
  • 需要优化重叠大小,避免重复度过高导致信息冗余。

自然语言切分

基于句子和段落的内容来进行切分。比如可以根据句号或者换行来实现,再叠加上 Token 的长度限制等条件。如果还是超长可以叠加使用一些 NLP 工具来拆分

- Chunk 1: 车辆启动后,驾驶员发现发动机声音异常,有较大噪音。
- Chunk 2: 经检查,发现排气系统存在泄漏。
- Chunk 3: 维修人员建议更换排气管。更换后,噪音消失,问题解决。

优点

  • 语义完整,检索时不容易丢失关键信息。
  • 适合 PDF、Markdown、文章、法规等结构化文本。
    缺点
  • 需要 NLP 解析库,处理较复杂。
  • 对不同文档格式可能要调整切分规则。

knowledge-chunk-1

现有分块方法

Langchain

Langchain 的 Langchain.text_splitter.RecursiveCharacterTextSplitter,可以实现递归分割文本,先按照换行符,然后再按照字符数切分

from langchain.text_splitter import RecursiveCharacterTextSplitter

text = "车辆启动后,驾驶员发现发动机声音异常,有较大噪音。\n经检查,发现排气系统存在泄漏。\n维修人员建议更换排气管。更换后,噪音消失,问题解决。"
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=50, # 每个 Chunk 最大 50 字符
chunk_overlap=10 # 每个 Chunk 之间重叠 10 字符
)
chunks = text_splitter.split_text(text)

Hugging Face Tokenizer

可以用 BERT/GPT 的分词器 来计算 Token 数,然后按 最大 Token 限制切分

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
text = "车辆启动后,驾驶员发现发动机声音异常,有较大噪音。经检查,发现排气系统存在泄漏。"

tokens = tokenizer.tokenize(text)
print(len(tokens)) # 计算 Token 数量

基于 NLP(Spacy/NLTK)

import spacy
nlp = spacy.load("zh_core_web_sm") # 中文 NLP 模型

text = "车辆启动后,驾驶员发现发动机声音异常,有较大噪音。经检查,发现排气系统存在泄漏。"
doc = nlp(text)
sentences = [sent.text for sent in doc.sents] # 按句子切分
print(sentences)