一个几乎所有人都会犯的错
要把代码切成块(chunk)喂给向量模型,最\”专业\”的做法是什么?
大多数工程师的第一反应是:按 AST(抽象语法树)切,一个函数一个 chunk。理由很硬——函数是最自然的语义单元,AST 精确对齐了语义边界,不会把一个函数拦腰截断。相比之下,按固定行数硬切(比如每 20 行一块)显得又土又蠢,会把函数切得七零八落。
听起来无懈可击。可实验数据把这个直觉打了个响亮的耳光。
在同一份 266 行的 Python 代码上,我跑了三种 Chunking 策略。结果是:AST 函数级分割的得分最低。 那个\”又土又蠢\”的固定行分割,反而拿了满分。
这不是随机噪声,背后有一个非常值得挖的机制。
三种策略
数据集: 266 行 Python 代码,涵盖认证、数据库、缓存、支付、通知 5 个模块,共 27 个函数。Embedding 模型统一用 bge-large-zh-v1.5,评估用 12 个自然语言查询。
三种切法:
策略 1:固定行分割(每 20 行,overlap=3)
不管代码结构,每 20 行切一刀,相邻 chunk 重叠 3 行防止边界信息丢失。
Chunk 1: 第 1-20 行
Chunk 2: 第 18-37 行 ← 和上一块重叠 3 行
Chunk 3: 第 35-54 行
…
策略 2:文件级分割(整个文件一个 chunk)
简单粗暴,整份代码就是一个 chunk。
Chunk 1: 第 1-266 行 ← 全塞进去
策略 3:AST 函数级分割(一个函数一个 chunk)
用 Python 的 ast 模块解析语法树,按函数定义精确切割。
Chunk 1: def validate_jwt_token(…) 第 9-18 行
Chunk 2: def hash_password(…) 第 20-28 行
Chunk 3: def create_payment_intent(…) 第 …
…
先看一眼固定行分割\”土\”在哪里。以 validate_jwt_token 这个函数为例(第 9-18 行,共 10 行):
函数 \’validate_jwt_token\’(第 9-18 行,10 行)
被切进 2 个 chunk:
Chunk 第 1-20 行: 包含函数第 9-18 行(10/10 行)
Chunk 第 18-37 行: 包含函数第 18-18 行(1/10 行)
问题:没有任何一个 chunk 包含完整的函数。
一个关于 \’validate_jwt_token\’ 的查询会检索到一段残缺的实现。
看到没?函数的最后一行(第 18 行)被切到了下一个 chunk 里。这正是固定行分割最被诟病的地方——它不认识函数边界,说切就切。
按理说,这种残缺应该会拖累检索效果。AST 分割每个函数都完完整整,怎么看都该赢。
运行结果
Strategy Chunks AvgLen R@3 R@5 vs AST R@5
──────────────────────────── ─────── ─────── ─────── ─────── ──────────
1_fixed_lines_20 16 755 0.917 1.000 +0.042
2_file_level 1 10270 1.000 1.000 +0.042
3_ast_function 27



