【LangChain】langchain.text_splitter.CharacterTextSplitter:将长文本分割成较小块(chunks)的工具类

在 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_contentmetadata)。
  • 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 常用于以下步骤:

  1. 文档预处理:将长文档分割为小块。
  2. 嵌入生成:使用嵌入模型(如 OpenAIEmbeddings)为每个块生成向量。
  3. 向量存储:将块和向量存储到向量数据库(如 Chroma)。
  4. 检索与回答:基于用户问题检索相关块,结合 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_llmgpt-3.5-turbo)将问题和历史浓缩为独立问题。
  • 问题ConversationalRetrievalChain 已废弃,触发 LangChainDeprecationWarning

迁移到 LCEL 链

由于 ConversationalRetrievalChain 已废弃,推荐迁移到 LCEL,使用 RunnableWithMessageHistoryCharacterTextSplitter 重现 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)是计算机科学的一个分支,专注于创建能够执行需要人类智能的任务的系统,例如学习、推理和决策。
人工智能包含子领域,如机器学习,涉及从数据中学习模型。
代码说明
  1. 文本分割
    • CharacterTextSplitter 将文档分割为约 100 字符的块,chunk_overlap=20 保留上下文。
    • 每个块转换为 Document 对象,存储 page_content
  2. 向量存储
    • Chroma.from_documents 为每个块生成嵌入向量。
    • 检索器返回 2 个相关块(k=2)。
  3. 浓缩链
    • condense_chain 使用 gpt-3.5-turbo 生成独立问题。
    • 输入:{"question": "...", "history": [...]}
  4. 回答链
    • answer_prompt 结合检索到的上下文、历史和原始问题。
    • main_llmgpt-4)生成最终答案。
  5. 输入处理
    • itemgetter("question")itemgetter("history") 确保正确提取字段。
    • 输入格式符合 RunnableWithMessageHistory
  6. 历史管理
    • ChatMessageHistory 存储对话历史,session_id 区分会话。

注意事项

  1. API 密钥
    • 使用 .env 文件:
      from dotenv import load_dotenv
      load_dotenv()
      
    • 确保密钥支持 text-embedding-3-smallgpt-3.5-turbogpt-4
  2. 依赖
    • 安装:
      pip install --upgrade langchain langchain-community langchain-openai langchain-core chromadb
      
  3. 分割优化
    • chunk_size=100 适合小规模测试,生产环境可调整为 500-1000。
    • 若需语义分割,考虑 RecursiveCharacterTextSplitter
      from langchain.text_splitter import RecursiveCharacterTextSplitter
      splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
      
  4. 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")
      
  5. 迁移
    • 使用 langchain migrate CLI 更新废弃代码:
      pip install langchain-cli
      langchain migrate
      

常见问题

Q1:CharacterTextSplitterRecursiveCharacterTextSplitter 的区别?
A:CharacterTextSplitter 按固定字符数或分隔符分割,简单但可能截断语义;RecursiveCharacterTextSplitter 按分层分隔符(如段落、句子)分割,保留语义边界,推荐用于复杂文档。

Q2:如何选择 chunk_sizechunk_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 修复输入处理,确保可运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彬彬侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值