为什么需要切割?
回忆一下 RAG 的流程:
- 用户提问
- 从知识库检索相关内容
- 将检索到的内容和用户问题一起交给模型推理
如果文档不切割,检索阶段就只能以整篇为单位,长文会超出模型的 Token 限制,无法一次性送进模型。
快速上手#
如何切割?
最通用的是使用 RecursiveCharacterTextSplitter 来进行切割。
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 64, chunkOverlap: 0,})- chunkSize:每块的最大长度
- chunkOverlap:块之间的重叠长度
在线的 可视化工具,可以快速看到不同 chunkSize 和 chunkOverlap 的切分效果。初学推荐设置:chunkSize = 1000,chunkOverlap = 200,再通过工具观察效果慢慢调。
快速上手示例:
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'import { TextLoader } from 'langchain/document_loaders/fs/text'
const loader = new TextLoader('data/kong.txt')
const docs = await loader.load()
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 64, chunkOverlap: 10,})
const result = await splitter.splitDocuments(docs)
console.log(result)其它类型切割器#
现实场景里,处理的文档类型五花八门,比如:
- 📄 Markdown 教案
- 🌐 HTML 页面
- 🧑💻 JS/Python 代码片段
- 🧾 LaTeX 报告
- 📚 跨语言 API 文档……
LangChain 中提供了不同的切割器
| Splitter 名称 | 适用文档类型 | 切分策略说明 |
|---|---|---|
RecursiveCharacterTextSplitter | 普通文本(小说、说明) | 默认最常用,按字符层级递进切分 |
MarkdownTextSplitter | Markdown 文件 | 根据标题层级(#、## 等)结构来切 |
TokenTextSplitter | 精确控制 token 情况 | 按 token 数切分,不考虑语义 |
CharacterTextSplitter | 非结构文本 | 纯字符切,简单粗暴 |
切割Markdown示例
import { TextLoader } from 'langchain/document_loaders/fs/text'import { MarkdownTextSplitter } from 'langchain/text_splitter'
const loader = new TextLoader('data/markdown语法.md')
const docs = await loader.load()
const splitter = new MarkdownTextSplitter({ chunkSize: 300, chunkOverlap: 0,})
const result = await splitter.splitDocuments(docs)
console.log(result)切割代码
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'import { TextLoader } from 'langchain/document_loaders/fs/text'
const loader = new TextLoader('data/test.js')
const docs = await loader.load()
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 320, chunkOverlap: 0,})
const result = await splitter.splitDocuments(docs)
console.log(result)RecursiveCharacterTextSplitter.fromLanguage("js", {...})
-
切割逻辑:在
chunkSize和chunkOverlap基础上,会根据指定语言(这里是"js")注入专门的分隔符表。 -
JS 例子:
-
优先按函数或类定义等结构分割(
function/class等关键字周围) -
再按语句结束符(
;)分割 -
再按换行符、空格等兜底分割
-
这样会尽量避免把一行代码切成两半或破坏语法结构,利于后续大模型理解。
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'import { TextLoader } from 'langchain/document_loaders/fs/text'
const loader = new TextLoader('data/test.js')
const docs = await loader.load()
const splitter = RecursiveCharacterTextSplitter.fromLanguage('js', { chunkSize: 320, chunkOverlap: 0,})
const result = await splitter.splitDocuments(docs)
console.log(result)支持的编程语言:
import { SupportedTextSplitterLanguages } from 'langchain/text_splitter'console.log(SupportedTextSplitterLanguages)也可以在 这里 看到。
按照Token切割
适合用在 token 预算敏感的场景。
import { TokenTextSplitter } from 'langchain/text_splitter'
const text = 'I stand before you today the representative of a family in grief, in a country in mourning before a world in shock.'
const splitter = new TokenTextSplitter({ chunkSize: 10, // 每块最多 10 个 token chunkOverlap: 0, // 不需要重叠})
const docs = await splitter.createDocuments([text])
console.log(docs)splitDocuments(docs) —— 传 Document 对象
- 输入:必须是 LangChain 的
Document实例数组(形如{ pageContent, metadata })。 - 作用:
- 会保留原来的
metadata - 切割完的每个 chunk 也会带上对应的
metadata
- 会保留原来的
- 常见用法:用 loader 加载了文档(
TextLoader,PDFLoader等),直接把docs交给它切。
createDocuments(texts, metadatas?) —— 传纯字符串
- 输入:纯文本数组(
string[]),可选metadata[] - 作用:
- 先把字符串封装成
Document对象 - 再按规则切割成多个小的
Document
- 先把字符串封装成
- 常见用法:你手里没有现成的
Document对象,只有一段文本(比如从接口返回的字符串),需要直接切成 LangChain 能用的Document[]。
注意事项:
- TokenTextSplitter 不会“考虑语言结构”!它只按 token 数硬切。
- 切出来的块有可能把一个句子、段落、甚至一个词给切断。
- 它默认使用的 tokenizer 是 Tiktoken(适配 OpenAI 模型),如果是其他模型,可能 token 数会略有差异。