SentenceSplitter 说 metadata 比 chunk 还长,缩短正文为什么没用
SentenceSplitter 的 chunk_size 还要容纳元数据 token。排除进入 embedding 和 LLM 的大字段,使用实际 tokenizer 检查剩余正文预算;只缩短正文可能无效。
SentenceSplitter 说 metadata 比 chunk 还长,缩短正文为什么没用
一份只有几行正文的文档,在切块时出现:
Metadata length (1527) is longer than chunk size (1024)
先看 metadata。SentenceSplitter 会为进入模型的元数据预留 token,再把剩余预算分给正文。上面的数字对应有效正文预算 1024 - 1527,已经小于零;正文缩成一句话也过不了检查。调整 chunk_overlap 同样补不回这部分预算。
官方 issue #12200 报告过这个问题。本文按 llama-index-core 源码快照 6dd2f3cd 核对:split_text_metadata_aware 使用 splitter 的 tokenizer 计算元数据长度;当有效预算不大于零时抛出 ValueError。这里的长度不是 Python 的字符数。
先找出混进元数据的大字段
抓取页面时,经常把完整 HTML、接口原始响应或一大段摘要放进 metadata。存下来未必有错,但未必应该让每一个切块都重复携带这些内容。
排查时只记录字段名及长度,不打印整份抓取内容。把 title、source 等短字段和 raw_html、raw_response 这类大字段分开看。需要保留的大字段,可以留在存储中,并从实际供 embedding 和 LLM 使用的元数据里排除。
下面示例只切块,不调用模型。需要 llama-index-core。显式 tokenizer 让测试不依赖模型 tokenizer 的额外下载;生产项目应换回项目实际使用的 tokenizer,再检查真实 token 预算。
from llama_index.core import Document
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.schema import MetadataMode
# 按字符切分只是本例的可重复预算测试,不是生产 token 估算器。
splitter = SentenceSplitter(chunk_size=96, chunk_overlap=8, tokenizer=list)
doc = Document(
text="订单记录保存三十天。超过期限请联系支持。",
metadata={"source": "order-policy", "raw_html": "x" * 200},
)
try:
splitter.get_nodes_from_documents([doc])
except ValueError as exc:
assert "Metadata length" in str(exc)
else:
raise AssertionError("oversized metadata should fail")
doc.excluded_embed_metadata_keys = ["raw_html"]
doc.excluded_llm_metadata_keys = ["raw_html"]
nodes = splitter.get_nodes_from_documents([doc])
assert nodes
assert all(node.text.strip() for node in nodes)
assert "raw_html" in doc.metadata
for mode in (MetadataMode.EMBED, MetadataMode.LLM):
assert "raw_html" not in doc.get_metadata_str(mode=mode)
print([node.text for node in nodes])
为什么示例排除了两次?当前节点解析路径会比较 EMBED 和 LLM 两种元数据字符串,选择字符长度较长的一份用于切块预算。只排除 embedding 那边,LLM 那边如果仍带着大字段,切块仍可能失败。不要把这段内部选择逻辑误写成“两边分别算 token,再取最大值”。
能切开以后,再看切得是否合理
把生产中 metadata 最大的几份文档放进回归测试。检查非空节点数、每个节点正文,以及最终送入 embedding/LLM 的内容。异常从报错变成“每块只有十几个 token”,检索质量仍可能变差;源码也会对剩余预算过小给出提示。
确实需要完整元数据时可以增大 chunk_size,但应同时核对 embedding 输入上限、后续提示词预算和检索粒度。这个改动还可能改变索引内容,已有索引需要按项目的数据更新策略处理。


