在 LangChain 中,langchain.text_splitter.CharacterTextSplitter
是一个用于将长文本分割成较小块(chunks)的工具类,属于 langchain.text_splitter
模块。它通过基于字符数的简单规则进行文本分割,适用于需要将大段文本切分为适合嵌入模型或语言模型(LLM)处理的片段的场景,例如文档预处理、向量存储索引或 RAG(检索增强生成) pipeline。
本文基于 LangChain 0.3.x,详细解释 CharacterTextSplitter
的功能、参数、使用场景,提供代码示例,展示如何使用 CharacterTextSplitter
预处理文档并集成到 RAG 系统中。
CharacterTextSplitter
概述
CharacterTextSplitter
是一个基础的文本分割器,通过以下规则将文本分割为块:
- 基于字符计数:每个块的长度不超过指定大小(
chunk_size
)。 - 分隔符:可选地使用分隔符(如换行符
\n
)分割文本。 - 重叠:支持块之间的重叠(
chunk_overlap
),保留上下文。 - 简单逻辑:相比其他高级分割器(如
RecursiveCharacterTextSplitter
),它直接按字符数或分隔符切割,逻辑简单但不够灵活。
适用场景:
- 将长文档分割为适合嵌入模型(embedding model)或 LLM 的小块。
- 预处理文本以存储到向量数据库(如 Chroma、FAISS)。
- 简单的文本分割任务,不需要复杂的分层逻辑(如按句子或段落)。
局限性:
- 不考虑语义边界(如句子、段落),可能截断有意义的上下文。
- 对于复杂文档(如代码、Markdown),推荐使用
RecursiveCharacterTextSplitter
或特定分割器(如MarkdownTextSplitter
)。
CharacterTextSplitter
参数
CharacterTextSplitter
的主要参数包括:
chunk_size
(整数,默认 1000):每个块的最大字符数(包括重叠部分)。chunk_overlap
(整数,默认 200):相邻块之间的重叠字符数,防止上下文丢失。separator
(字符串,默认\n
):用于分割文本的分隔符(如换行符、空格)。strip_whitespace
(布尔值,默认True
):是否去除块首尾的空白字符。length_function
(函数,默认len
):计算文本长度的函数,可自定义(如按 token 计数)。add_start_index
(布尔值,默认False
):是否在每个块的元数据中添加原始文本的起始索引。
初始化示例:
from langchain.text_splitter import CharacterTextSplitter
splitter = CharacterTextSplitter(
chunk_size=500,
chunk_overlap=100,
separator="\n",
strip_whitespace=True
)
使用 CharacterTextSplitter
CharacterTextSplitter
提供以下方法:
split_text(text: str) -> List[str]
:将单个字符串分割为块列表。split_documents(documents: List[Document]) -> List[Document]
:将Document
对象列表分割为更小的Document
对象,每个块包含元数据(如page_content
和metadata
)。create_documents(texts: List[str], metadatas: Optional[List[dict]] = None) -> List[Document]
:从文本列表创建Document
对象并分割。
基本用法:
text = """人工智能(AI)是计算机科学的一个分支,专注于创建智能系统。
机器学习是 AI 的子领域,涉及从数据中学习模型。
深度学习是机器学习的一种,使用神经网络进行复杂模式识别。"""
splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=20, separator="\n")
chunks = splitter.split_text(text)
for i, chunk in enumerate(chunks):
print(f"Chunk {i+1}: {chunk}")
输出示例:
Chunk 1: 人工智能(AI)是计算机科学的一个分支,专注于创建智能系统。
Chunk 2: 机器学习是 AI 的子领域,涉及从数据中学习模型。
Chunk 3: 深度学习是机器学习的一种,使用神经网络进行复杂模式识别。
结合 RAG 系统的使用
在 RAG 系统中,CharacterTextSplitter
常用于以下步骤:
- 文档预处理:将长文档分割为小块。
- 嵌入生成:使用嵌入模型(如
OpenAIEmbeddings
)为每个块生成向量。 - 向量存储:将块和向量存储到向量数据库(如 Chroma)。
- 检索与回答:基于用户问题检索相关块,结合 LLM 生成答案。
构建一个多轮对话的 RAG 系统。以下是 CharacterTextSplitter
在 RAG 中的典型应用,以及从传统链到 LCEL 的迁移。
传统 ConversationalRetrievalChain
示例
以下展示如何使用 CharacterTextSplitter
预处理文档,并结合 ConversationalRetrievalChain
:
import os
os.environ["OPENAI_API_KEY"] = "Your OpenAI API Key"
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.documents import Document
# 原始文档
texts = [
"""人工智能(AI)是计算机科学的一个分支,专注于创建智能系统。
机器学习是 AI 的子领域,涉及从数据中学习模型。
深度学习是机器学习的一种,使用神经网络进行复杂模式识别。"""
]
# 使用 CharacterTextSplitter 分割文档
splitter = CharacterTextSplitter(
chunk_size=100,
chunk_overlap=20,
separator="\n"
)
documents = [Document(page_content=text) for text in splitter.split_text(texts[0])]
# 初始化嵌入模型和向量存储
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(documents, embeddings)
# 初始化对话历史
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# 创建 ConversationalRetrievalChain
qa = ConversationalRetrievalChain.from_llm(
ChatOpenAI(temperature=0, model="gpt-4"),
vectorstore.as_retriever(search_kwargs={"k": 2}),
memory=memory,
condense_question_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
verbose=True
)
# 调用链
result = qa({"question": "什么是人工智能?"})
print(result["answer"])
result = qa({"question": "它包含哪些子领域?"})
print(result["answer"])
输出示例:
人工智能(AI)是计算机科学的一个分支,专注于创建能够执行需要人类智能的任务的系统,例如学习、推理和决策。
人工智能包含子领域,如机器学习,涉及从数据中学习模型。
说明:
CharacterTextSplitter
将长文本分割为小块(每个约 100 字符),生成Document
对象。Chroma.from_documents
使用嵌入模型为每个块生成向量。condense_question_llm
(gpt-3.5-turbo
)将问题和历史浓缩为独立问题。- 问题:
ConversationalRetrievalChain
已废弃,触发LangChainDeprecationWarning
。
迁移到 LCEL 链
由于 ConversationalRetrievalChain
已废弃,推荐迁移到 LCEL,使用 RunnableWithMessageHistory
和 CharacterTextSplitter
重现 RAG 功能,包括问题浓缩(condense_question_llm
)。以下是修正后的代码。
import os
os.environ["OPENAI_API_KEY"] = "Your OpenAI API Key"
from operator import itemgetter
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.documents import Document
# 原始文档
texts = [
"""人工智能(AI)是计算机科学的一个分支,专注于创建智能系统。
机器学习是 AI 的子领域,涉及从数据中学习模型。
深度学习是机器学习的一种,使用神经网络进行复杂模式识别。"""
]
# 使用 CharacterTextSplitter 分割文档
splitter = CharacterTextSplitter(
chunk_size=100,
chunk_overlap=20,
separator="\n"
)
documents = [Document(page_content=text) for text in splitter.split_text(texts[0])]
# 初始化嵌入模型和向量存储
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(documents, embeddings)
# 初始化 LLM
main_llm = ChatOpenAI(temperature=0, model="gpt-4")
condense_llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
# 问题浓缩提示模板
condense_prompt = ChatPromptTemplate.from_messages([
("system", "给定以下对话历史和当前问题,重写当前问题为一个独立的、上下文无关的问题。"),
MessagesPlaceholder(variable_name="history"),
("human", "当前问题:{question}\n独立问题:")
])
# 回答提示模板
answer_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个助手,根据以下上下文和对话历史回答问题:\n上下文:{context}"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}")
])
# 创建浓缩链
condense_chain = (
{
"question": itemgetter("question"),
"history": itemgetter("history") | RunnablePassthrough()
}
| condense_prompt
| condense_llm
| StrOutputParser()
)
# 创建 LCEL 链
runnable = (
{
"context": condense_chain | vectorstore.as_retriever(search_kwargs={"k": 2}),
"question": itemgetter("question"),
"history": itemgetter("history") | RunnablePassthrough()
}
| answer_prompt
| main_llm
| StrOutputParser()
)
# 初始化消息历史存储
store = {}
def get_session_history(session_id: str) -> ChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 创建带历史的链
qa = RunnableWithMessageHistory(
runnable,
get_session_history,
input_messages_key="question",
history_messages_key="history"
)
# 调用链
session_id = "user1"
response = qa.invoke(
{"question": "什么是人工智能?", "history": []},
config={"configurable": {"session_id": session_id}}
)
print(response)
response = qa.invoke(
{"question": "它包含哪些子领域?", "history": []},
config={"configurable": {"session_id": session_id}}
)
print(response)
输出示例:
人工智能(AI)是计算机科学的一个分支,专注于创建能够执行需要人类智能的任务的系统,例如学习、推理和决策。
人工智能包含子领域,如机器学习,涉及从数据中学习模型。
代码说明
- 文本分割:
CharacterTextSplitter
将文档分割为约 100 字符的块,chunk_overlap=20
保留上下文。- 每个块转换为
Document
对象,存储page_content
。
- 向量存储:
Chroma.from_documents
为每个块生成嵌入向量。- 检索器返回 2 个相关块(
k=2
)。
- 浓缩链:
condense_chain
使用gpt-3.5-turbo
生成独立问题。- 输入:
{"question": "...", "history": [...]}
。
- 回答链:
answer_prompt
结合检索到的上下文、历史和原始问题。main_llm
(gpt-4
)生成最终答案。
- 输入处理:
itemgetter("question")
和itemgetter("history")
确保正确提取字段。- 输入格式符合
RunnableWithMessageHistory
。
- 历史管理:
ChatMessageHistory
存储对话历史,session_id
区分会话。
注意事项
- API 密钥:
- 使用
.env
文件:from dotenv import load_dotenv load_dotenv()
- 确保密钥支持
text-embedding-3-small
、gpt-3.5-turbo
和gpt-4
。
- 使用
- 依赖:
- 安装:
pip install --upgrade langchain langchain-community langchain-openai langchain-core chromadb
- 安装:
- 分割优化:
chunk_size=100
适合小规模测试,生产环境可调整为 500-1000。- 若需语义分割,考虑
RecursiveCharacterTextSplitter
:from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
- Ollama 替代:
- 使用
langchain_ollama
:from langchain_ollama import ChatOllama, OllamaEmbeddings main_llm = ChatOllama(model="qwen3:7b") condense_llm = ChatOllama(model="qwen3:1.7b") embeddings = OllamaEmbeddings(model="nomic-embed-text")
- 使用
- 迁移:
- 使用
langchain migrate
CLI 更新废弃代码:pip install langchain-cli langchain migrate
- 使用
常见问题
Q1:CharacterTextSplitter
和 RecursiveCharacterTextSplitter
的区别?
A:CharacterTextSplitter
按固定字符数或分隔符分割,简单但可能截断语义;RecursiveCharacterTextSplitter
按分层分隔符(如段落、句子)分割,保留语义边界,推荐用于复杂文档。
Q2:如何选择 chunk_size
和 chunk_overlap
?
A:chunk_size
取决于嵌入模型的输入限制(通常 500-2000 字符),chunk_overlap
设为 10-20% 的 chunk_size
(如 50-200),确保上下文连续性。
Q3:可以跳过 condense_question_llm
吗?
A:可以直接用主 LLM 或跳过浓缩,但可能降低多轮对话的检索准确性。
Q4:如何调试分割和 RAG?
A:打印 splitter.split_text()
的结果检查块;设置 langchain.debug = True
查看 LCEL 链日志。
总结
langchain.text_splitter.CharacterTextSplitter
是一个简单的文本分割器,通过字符数和分隔符将长文本切分为小块,适合 RAG 系统的文档预处理。结合 RAG 场景(ConversationalRetrievalChain
),示例代码展示了如何使用 CharacterTextSplitter
分割文档,并迁移到 LCEL 链,重现 condense_question_llm
的功能。LCEL 代码使用 itemgetter
修复输入处理,确保可运行。