Skip to content

文本切分 (Text Splitting)

将长文档加载进内存后,下一步是切分 (Chunking)

为什么需要切分?

  1. 模型限制:Embedding 模型和 LLM 都有最大 Token 限制(如 8192 tokens)。
  2. 检索精度:切分得越细致,语义越集中。如果一个 Chunk 包含太多无关主题,检索的准确率会下降("大海捞针"效应)。

Golang 中的切分策略

1. 递归字符切分 (Recursive Character Splitter)

这是最常用的策略。它尝试按顺序使用分隔符列表(如 \n\n, \n, , "")进行切分,直到块的大小符合要求。这能最大程度保持段落的完整性。

Golang 实现注意事项: Go 语言中 len(string) 返回的是字节数,而不是字符数。对于中文环境,必须按 Rune(Unicode 码点)计算长度,否则会出现乱码或截断。

使用 langchaingotextsplitter 包:

go
package main

import (
	"fmt"
	"github.com/tmc/langchaingo/textsplitter"
)

func main() {
	text := "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。\n\n它不同于Java和Python..."

	// 创建 Splitter
	splitter := textsplitter.NewRecursiveCharacter()
	splitter.ChunkSize = 100    // 每个块大概 100 字符
	splitter.ChunkOverlap = 20  // 重叠 20 字符,防止上下文丢失
	
	// 执行切分
	chunks, _ := splitter.SplitText(text)
	
	for i, chunk := range chunks {
		fmt.Printf("Chunk %d: %s\n---\n", i, chunk)
	}
}

2. Markdown 标题切分 (Markdown Header Splitter)

对于 Markdown 文档,按照标题层级(# H1, ## H2)切分效果最好,因为标题天然定义了语义边界。

langchaingo 目前对 Markdown Splitter 的支持正在完善中。我们可以实现一个简单的逻辑:

go
// 伪代码思路
func SplitMarkdown(text string) []string {
    // 1. 正则匹配 # 标题
    // 2. 将标题下的内容聚合为一个 Chunk
    // 3. 将标题作为 Metadata 附加到 Chunk 中(非常重要!增强检索上下文)
}

3. Token 切分

更精准的做法是按 Token 数切分(因为模型限制是按 Token 算的)。Go 中可以使用 tiktoken-go 库。

go
import "github.com/pkoukk/tiktoken-go"

func CountTokens(text string) int {
    tkm, _ := tiktoken.GetEncoding("cl100k_base") // GPT-4 编码
    token := tkm.Encode(text, nil, nil)
    return len(token)
}

切分参数最佳实践

  • Chunk Size:
    • 512 - 1024 tokens: 适合大多数问答场景。
    • 256 tokens: 适合细粒度的事实检索。
  • Chunk Overlap:
    • 建议设置为 Chunk Size 的 10% - 20%
    • 例如:Size=500, Overlap=50。
    • 作用:确保跨 Chunk 的句子不会被切断,保持语义连贯性。

总结

切分是 RAG 效果优化的低垂果实

  • 太小:缺乏上下文,模型看不懂。
  • 太大:包含噪音多,检索不准。
  • Golang 重点:时刻记住处理 UTF-8 字符问题,使用 []rune 而不是 byte 切片。

🚀 学习遇到瓶颈?想进大厂?

看完这篇技术文章,如果还是觉得不够系统,或者想在实战中快速提升?
王中阳的就业陪跑训练营,提供定制化学习路线 + 企业级实战项目 + 简历优化 + 模拟面试。

了解训练营详情