豢缎狼汲什么是切块?为什么要切块?

简单来说,切块就是把“长篇大论”切成“语义胶囊”。

它把成千上万字的文章,按照一定的逻辑(比如每 500 字一段,或者按段落)切分成一个个独立的小块。这样,AI 在处理信息时,就不需要每次都“读全集”,而是“精准点餐”。

那为什么要切块的?有以下几点原因:

大模型的上下文有 token 的长度限制

切块可以突出表示某个向量的特征,如果说某个大文本的向量能表示10个特征,那么这个向量表达就会很模糊,会影响检索精度

嵌入模型有限制,大多数主流的 Embedding 模型(如 text-embedding-3-small)通常只能处理 512 或 8192 个 Token

省钱,大模型 api 是按 toekn 收费的,省时间,上下文长度越长,大模型处理的时间越长

怎么切块?

目前常用的切块方法有 5 种,下面我们逐一介绍:

1. 固定切块(CharacterTextSplitter)

最简单的切块方式,按固定字符数分割文本。这种切块方式很简单,但是缺点也很明显:很容易切断语义完整性,比如:一个句子被切分成了两个块,那么这个句子的语义就会丢失。

文件名: 01-固定切块.py

from langchain_community.document_loaders import TextLoader

from langchain_text_splitters import CharacterTextSplitter

loader = TextLoader("../99-doc-data/黑悟空/黑悟空wiki.txt")

data = loader.load()

text_splitter = CharacterTextSplitter(

chunk_size=100, # 每个块的大小

chunk_overlap=5 # 块之间的重叠大小

)

chunks = text_splitter.split_documents(data)

for chunk in chunks:

print("====== 切块分页 ======")

print(chunk.page_content)

参数说明:

chunk_size=100:每个块最多 100 个字符

chunk_overlap=5:相邻块之间重叠 5 个字符(避免信息丢失)

优点:

实现简单,速度快

块大小可控

缺点:

容易在句子中间断开

破坏语义完整性

适用场景: 结构简单的文本、需要快速处理的场景

2. 递归切块(RecursiveCharacterTextSplitter)

按优先级递归尝试不同的分隔符,保持文本结构。

文件名: 02-递归切块.py

from langchain_community.document_loaders import TextLoader

from langchain_text_splitters import RecursiveCharacterTextSplitter

loader = TextLoader("../99-doc-data/黑悟空/黑悟空wiki.txt")

data = loader.load()

# 定义分割符列表,按优先级依次使用

separators = [

"\n\n", # 双换行符(段落分隔)

"\n", # 单换行符(行分隔)

"。", # 中文句号

".", # 英文句号

"!", # 中文感叹号

"!", # 英文感叹号

"?", # 中文问号

"?", # 英文问号

";", # 中文分号

";", # 英文分号

",", # 中文逗号

",", # 英文逗号

" ", # 空格

"" # 最后按字符分割

]

recursive_text_splitter = RecursiveCharacterTextSplitter(

chunk_size=100,

chunk_overlap=5,

separators=separators,

length_function=len

)

r_chunks = recursive_text_splitter.split_documents(data)

for chunk in r_chunks:

print("====== 递归切块分页 ======")

print(chunk.page_content)

工作原理:

先尝试用 \n\n(段落)分割

如果块还是太大,用 \n(行)分割

如果还是太大,用 。(句号)分割

依此类推,直到满足 chunk_size

优点:

保持文本结构(段落、句子)

比固定切块更智能

适用范围广

缺点:

仍可能在不合适的地方断开

适用场景: 普通文章、博客、没有明确标题的文本

3. 代码切块(Language-specific Splitter)

专门为代码设计的切块器,保持代码结构完整。

文件名: 03-代码切块.py

from langchain_text_splitters import Language, RecursiveCharacterTextSplitter

GAME_CODE = """

class CombatSystem:

def __init__(self):

self.health = 100

self.stamina = 100

def update(self, delta_time):

self._update_stats(delta_time)

self._handle_combat()

class InventorySystem:

def __init__(self):

self.items = {}

self.capacity = 20

def add_item(self, item_id, quantity):

if item_id in self.items:

self.items[item_id] += quantity

else:

self.items[item_id] = quantity

"""

python_splitter = RecursiveCharacterTextSplitter.from_language(

language=Language.PYTHON,

chunk_size=1000,

chunk_overlap=0

)

py_docs = python_splitter.create_documents([GAME_CODE])

for i, chunk in enumerate(py_docs, 1):

print(f"\n--- 第 {i} 个代码块 ---")

print(chunk.page_content)

支持的主流的20多种语言,工作原理: 按类、函数、方法等代码结构分割,保持代码的完整性和可读性

优点:

保持代码结构完整

不会在函数中间断开

支持多种编程语言

缺点:

仅适用于代码

适用场景: 代码文档、技术教程、API 文档

4. 语义切块(SemanticChunker)

最智能的切块方式,根据语义相似度分割文本。

文件名: 04-LangChain-语义分块-DeepSeek.py

from langchain_community.document_loaders import TextLoader

from langchain_experimental.text_splitter import SemanticChunker

from langchain_huggingface import HuggingFaceEmbeddings

# 1. 加载文档

loader = TextLoader("../99-doc-data/黑悟空/黑悟空wiki.txt", encoding="utf-8")

docs = loader.load()

# 2. 设置嵌入模型(用于计算语义相似度)

embeddings = HuggingFaceEmbeddings(

model_name="BAAI/bge-small-zh-v1.5",

model_kwargs={'device': 'cpu'},

encode_kwargs={'normalize_embeddings': True}

)

# 3. 创建语义分块器

semantic_splitter = SemanticChunker(

embeddings=embeddings,

breakpoint_threshold_type="percentile", # 使用百分位数阈值

breakpoint_threshold_amount=90 # 90% 不相似度时分割

)

# 4. 执行语义分块

semantic_chunks = semantic_splitter.split_documents(docs)

for i, chunk in enumerate(semantic_chunks, 1):

print(f"\n--- 第 {i} 个语义块 ---")

print(f"长度: {len(chunk.page_content)} 字符")

print(f"内容: {chunk.page_content[:200]}...")

工作原理:首先将文本按句子分割,然后计算相邻句子的语义相似度(使用 embedding),如果相似度低于阈值时,创建新的分块

示例对比:

传统分块(可能在句子中间断开):

块1: "黑神话悟空是一款动作游戏。游戏基于西游"

块2: "记改编。主角是孙悟空的转世。"

语义分块(保持语义完整):

块1: "黑神话悟空是一款动作游戏。游戏基于西游记改编。" # 游戏介绍

块2: "主角是孙悟空的转世。" # 角色介绍

参数说明:

breakpoint_threshold_type="percentile":使用百分位数阈值(推荐)

breakpoint_threshold_amount=90:90% 不相似度时分割

85:块很多(细粒度)

90:块适中(推荐)

95:块较少(粗粒度)

优点:

保持语义连贯性

自动识别主题边界

检索质量最高

缺点:

速度慢(比传统分块慢 50 倍)

需要 embedding 模型

适用场景: 问答系统、长文档分析、高质量检索

5. 按段落和标题切块(最推荐)

最实用的切块方式,按文档的自然结构(标题、段落)分割。

文件名: 05-按段落标题切块.py

2.1 Markdown 标题切块

from langchain_text_splitters import MarkdownHeaderTextSplitter

markdown_document = """

# 黑神话:悟空

## 游戏简介

《黑神话:悟空》是一款动作角色扮演游戏。

## 游戏玩法

### 战斗系统

游戏的战斗系统流畅爽快。

### 技能系统

玩家可以学习72变等经典技能。

"""

# 定义要按哪些标题级别切块

headers_to_split_on = [

("#", "一级标题"), # H1

("##", "二级标题"), # H2

("###", "三级标题"), # H3

]

# 创建 Markdown 标题切块器

markdown_splitter = MarkdownHeaderTextSplitter(

headers_to_split_on=headers_to_split_on,

strip_headers=False # 保留标题在内容中

)

# 执行切块

md_chunks = markdown_splitter.split_text(markdown_document)

for chunk in md_chunks:

print(f"内容: {chunk.page_content}")

print(f"元数据: {chunk.metadata}") # 包含标题层级信息

输出示例:

内容: # 黑神话:悟空

元数据: {'一级标题': '黑神话:悟空'}

内容: ## 游戏简介

《黑神话:悟空》是一款动作角色扮演游戏。

元数据: {'一级标题': '黑神话:悟空', '二级标题': '游戏简介'}

内容: ### 战斗系统

游戏的战斗系统流畅爽快。

元数据: {'一级标题': '黑神话:悟空', '二级标题': '游戏玩法', '三级标题': '战斗系统'}

2.2 段落切块

from langchain_text_splitters import RecursiveCharacterTextSplitter

# 段落之间用双换行符分隔的文本

paragraph_document = """

《黑神话:悟空》是一款动作角色扮演游戏。

游戏采用虚幻引擎5开发,画面表现力极强。

玩家在游戏中扮演孙悟空的转世。

"""

# 优先按段落(双换行符)分割

paragraph_splitter = RecursiveCharacterTextSplitter(

separators=["\n\n", "\n", "。", " ", ""],

chunk_size=500,

chunk_overlap=50

)

chunks = paragraph_splitter.split_text(paragraph_document)

2.3 混合切块(推荐)

# 先按标题切块

md_chunks = markdown_splitter.split_text(markdown_document)

# 再对大块进行段落切分

final_chunks = []

for header_chunk in md_chunks:

if len(header_chunk.page_content) > 200:

# 块太大,再按段落切分

sub_splitter = RecursiveCharacterTextSplitter(

separators=["\n\n", "\n", "。", " ", ""],

chunk_size=200,

chunk_overlap=20

)

sub_chunks = sub_splitter.split_text(header_chunk.page_content)

# 保留标题元数据

for sub_chunk in sub_chunks:

from langchain_core.documents import Document

final_chunks.append(Document(

page_content=sub_chunk,

metadata=header_chunk.metadata

))

else:

final_chunks.append(header_chunk)

优点:

保持逻辑完整性

每个块都有明确的主题

元数据包含标题信息,便于溯源

缺点:

需要文档有明确的结构

适用场景: 说明书、论文、技术文档、Markdown 文档

切块方法对比

以下各种切块策略对比,可以根据使用场景来选择,当然了,也可以多种组合使用,比如技术文档,先基于 markdown 分段,然后再使用递归或者代码切块。

切块策略 速度 质量 适用场景 推荐度

固定切块 ??? ?? 简单文本、快速处理、要求较低的需求 ??

递归切块 ??? ???? 普通文章、博客、无明显分段的文章 ????

代码切块 ??? ???? 代码文档、技术教程 ????

语义切块 ? ????? 问答系统、长文档 ????

段落标题切块 ??? ????? 说明书、论文、Markdown、HTML 格式文档 ?????

学习路径

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐