这类问题我又碰上了一次。用 tokenizer 统计批量文本长度做切分,切到 8192 就截断,实际请求却报超出上限,逐条对比发现统计值比接口返回的输入 token 少了几个。

第一反应是「工具定义也占 token 没计入」,验证下来不成立。

这类报错背后常见的原因有几处,逐个排除就行。统计时加了特殊 token 但请求侧没算;聊天模板会额外插入角色标记;批量编码时 padding 被算进长度。

动手时先对比两种编码的长度差

python -c "from transformers import AutoTokenizer;t=AutoTokenizer.from_pretrained('Qwen/Qwen2.5-7B-Instruct');s='你好';print(len(t(s).input_ids),len(t(s,add_special_tokens=False).input_ids))"

再往下用聊天模板算真实输入长度

python -c "from transformers import AutoTokenizer;t=AutoTokenizer.from_pretrained('Qwen/Qwen2.5-7B-Instruct');print(len(t.apply_chat_template([{'role':'user','content':'你好'}],tokenize=True)))"

接着把工具定义一起编码计入

python count_tools.py --tools tools.json --tokenizer Qwen/Qwen2.5-7B-Instruct

剩下的就是切分阈值留出固定余量

python split_ctx.py --max 8128 --reserve 64 --in docs.txt

中间停过一次,把已经改的东西回滚干净,再重新来一遍。

改完先别撤,确认这几项。统计长度与接口 usage 一致,最长分片不再触发超限,切分后的条数变化在预期内。

聊天模板和工具定义都是隐式开销,按纯文本长度切分一定偏小,留固定余量最省事。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。

作者 9823189