Skip to content

文本切割

为什么需要切割?

回忆一下 RAG 的流程:

  1. 用户提问
  2. 从知识库检索相关内容
  3. 将检索到的内容和用户问题一起交给模型推理

如果文档不切割,检索阶段就只能以整篇为单位,长文会超出模型的 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普通文本(小说、说明)默认最常用,按字符层级递进切分
MarkdownTextSplitterMarkdown 文件根据标题层级(#、## 等)结构来切
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", {...})

  • 切割逻辑:在 chunkSizechunkOverlap 基础上,会根据指定语言(这里是 "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 数会略有差异。