langchain.text_splitter:用RecursiveCharacterTextSplitter而不是CharacterTextSplitter,解决chunk过长的问题

诸神缄默不语-个人技术博文与视频目录

这是我在想尝试实践RAG功能的时候出现的问题。
我一开始用的代码类似这样:

from langchain.text_splitter import CharacterTextSplitter

text_splitter = CharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
)

但是在使用chunks = text_splitter.split_documents(documents=documents)之后会出现类似这样的警告信息:

Created a chunk of size 593, which is longer than the specified 500

那我肯定会很好奇,为什么chunk的长度会超过我规定的数值呢?
在看源码的时候我们很容易发现CharacterTextSplitter有一个入参是separator: str = "\n\n",很容易发现这个入参就是用来切分文档的分隔符。在分隔符之间的文本无论多长都不会被切开。
但是我们有时其实希望它通过多个分隔符来分割文本,大致逻辑是在这些分隔符之间也能分割文本,这个分隔符还有优先级,就是如果能用"\n\n"分的话就先不用分。这个功能如果要手写的话可以参考苏神的代码,我整理在了这篇博文中,见第一节:中文分句的解决方案
如果要在langchain中直接实现现成的TextSplitter对象,则可以用RecursiveCharacterTextSplitter类来实现,代码类似这样:

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    separators=["\n\n", "\n", "。", "."],
    chunk_size=500,
    chunk_overlap=50,
)

大致逻辑就是在separators里面按顺序一个一个进行切分,如果前面的separator切分后chunk还是过长,就用后面的separator切,所以separator的顺序应该是分章 - 分节 - 分段 - 分句 - 分词这种从大到小的。按照你所想要的粒度确定最小到多少。

当然另一种情况是你并不想按照标点符号进行切分,你就是纯纯想按字数进行切分,那建议用TokenTextSplitter类。

本文撰写过程中参考的网络资料:

  1. Splitting/Chunking · langchain-ai/langchain · Discussion #3786
  2. RecursiveCharacterTextSplitter和CharacterTextSplitter代码随读 - dmesg - 博客园:这一篇有介绍了一下源码实现算法,虽然我没仔细看但是我觉得我理解的是对的
<think>好的,我现在要帮助用户解决他们在构建RAG系统时遇到的错误。用户提供了一个错误信息,指出在调用FAISS.from_documents()时出现了TypeError,因为传入了FAISS对象而不是可迭代的文档列表。用户现在给出了build_rag_system函数的完整代码,我需要仔细分析这段代码,找出问题所在。 首先,我需要回顾之前的对话。用户第一次询问错误时,我分析了可能的原因,比如参数传递错误、变量覆盖或依赖版本问题。现在用户提供了具体的函数代码,我需要根据这段代码进一步排查。 用户提供的函数定义是def build_rag_system(embeddings, llm, splits):,其中splits作为参数传入FAISS.from_documents()的documents参数。根据错误信息,splits可能是一个FAISS对象而不是文档列表。那为什么splits会被误传为FAISS对象呢? 可能的错误点在于调用build_rag_system时传递的参数是否正确。例如,用户在调用该函数时,可能错误地将vectorstore(FAISS实例)作为splits参数传入,而不是传入正确的文档分割后的列表。例如,可能在函数外部进行了文档分割,但分割后的结果没有被正确传递,或者中间步骤中splits被错误地覆盖成了FAISS对象。 接下来,我需要检查函数内部的逻辑。在提供的代码中,函数接收splits参数,并直接将其传递给FAISS.from_documents()。如果splits本身不是文档列表,而是其他类型(比如FAISS对象),就会导致错误。因此,问题可能出在调用函数时传递的splits参数是否正确。 可能的解决方案包括: 1. 确保调用build_rag_system时,splits参数是文档列表,例如通过文档加载器(如TextLoader、DirectoryLoader)加载并分割后的结果。 2. 检查生成splits的代码是否正确,确保没有在此前的步骤中将splits变量错误地赋值为FAISS对象。 3. 添加类型检查或调试语句,以验证splits的类型是否正确。 另外,依赖版本问题也不能完全排除,但根据当前提供的代码,更可能的是参数传递错误。因此,建议用户检查调用build_rag_system的地方,确保传入的splits是文档列表。例如,在view.py中调用该函数时是否正确处理了splits参数。 总结,用户需要检查splits参数的来源,确保它是文档分割后的列表,而不是其他对象。如果splits生成过程中有误操作,比如错误地将其赋值为FAISS实例,就会导致这个错误。此外,在函数内部添加类型检查和打印语句可以帮助调试,确认splits的实际类型。</think>根据你提供的 `build_rag_system` 函数代码,结合之前的错误信息 `TypeError: 'FAISS' object is not iterable`,问题可能出现在 **调用该函数时传入的 `splits` 参数**。以下是详细分析: --- ### **错误原因分析** 1. **函数定义**: ```python def build_rag_system(embeddings, llm, splits): vectorstore = FAISS.from_documents( documents=splits, # 关键点:splits 必须是文档列表 embedding=embeddings ) ``` - `FAISS.from_documents()` 要求 `splits` 是一个 **文档列表**(如通过 `TextLoader`、`DirectoryLoader` 加载的 `Document` 对象列表)。 - 错误提示 `'FAISS' object is not iterable` 表明 **实际传入的 `splits` 是一个 `FAISS` 对象**,而非文档列表。 --- ### **排查步骤** #### 1. **检查函数调用时的 `splits` 参数** - 在 `view.py` 中调用 `build_rag_system()` 的位置,确认 `splits` 的来源。 - **错误示例**: ```python # view.py 中可能存在的错误代码 splits = FAISS(...) # 错误:将 FAISS 对象赋值给 splits build_rag_system(embeddings, llm, splits) ``` - **正确示例**: ```python # 正确:splits 应是文档分割后的列表 from langchain.text_splitter import CharacterTextSplitter text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0) splits = text_splitter.split_documents(documents) # documents 是已加载的 Document 列表 build_rag_system(embeddings, llm, splits) ``` --- #### 2. **验证 `splits` 的类型** 在调用 `build_rag_system()` 前,添加类型检查: ```python # 在 view.py 中调用 build_rag_system() 前插入 print(f"[Debug] Type of 'splits': {type(splits)}") if not isinstance(splits, list): raise ValueError("'splits' 必须是文档列表(list of Documents)!") ``` --- #### 3. **检查 `splits` 的生成过程** - 确保 `splits` 是通过 **文本分割器(Text Splitter)** 生成的列表。 - **正确流程示例**: ```python from langchain.document_loaders import TextLoader from langchain.text_splitter import CharacterTextSplitter # 1. 加载文档 loader = TextLoader("data.txt") documents = loader.load() # documents 是 Document 列表 # 2. 分割文档 text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0) splits = text_splitter.split_documents(documents) # splits 是分割后的 Document 列表 # 3. 调用函数 build_rag_system(embeddings, llm, splits) ``` --- ### **修复方案** 1. **确保 `splits` 是文档列表**: - 如果 `splits` 是通过 `split_documents()` 生成的,无需修改。 - 如果 `splits` 被错误覆盖为 `FAISS` 对象,需修正其生成逻辑。 2. **避免变量名冲突**: - 检查代码中是否有类似 `splits = vectorstore` 的误操作(如将 `FAISS` 对象赋值给 `splits`)。 --- ### **完整代码验证** 假设你的数据加载和分割逻辑正确,最终代码应类似: ```python # load_data.py 或 view.py 中的数据准备部分 from langchain.document_loaders import DirectoryLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 加载文档 loader = DirectoryLoader("data/", glob="*.txt") documents = loader.load() # 2. 分割文档 text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200 ) splits = text_splitter.split_documents(documents) # 确保 splits 是列表 # 3. 调用 RAG 构建函数 build_rag_system(embeddings, llm, splits) ``` --- ### **总结** - **核心问题**:`splits` 参数被错误地传入了一个 `FAISS` 对象。 - **解决方法**:确保 `splits` 是文档分割后的列表,并在调用函数前验证其类型。 - **调试技巧**:添加 `print(type(splits))` 或断言检查关键变量类型。 如果仍有问题,请提供生成 `splits` 的代码片段,以便进一步定位问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

诸神缄默不语

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

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

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

打赏作者

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

抵扣说明:

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

余额充值