Token 是什么为什么长材料要分段处理。这个标题看起来基础但我敢说真正把这两件事彻底搞清楚的人并不多。我做 AI 应用开发这几年几乎每天都要跟 token 打交道——模型报错要看 token、API 计费要看 token、调优提示词还是要看 token。更别提那些“sign-in could not be completed token exchange failed”“your access token could not be refreshed”之类的报错几乎每个接入大模型服务的团队都遇到过。这篇文章我把 token 的前因后果、长材料为什么要分段处理、以及实际项目中怎么落地分段方案一次讲透。先说清楚这篇文章适合谁如果你是刚接触大模型开发的工程师、在做 RAG检索增强生成相关项目的同学、或者只是好奇“为什么 AI 对话框不能一次性塞一本小说进去”的普通用户都能从中拿到有用的东西。1. Token 不是字数先把这个概念彻底掰开1.1 我用“切菜”这个比喻讲清楚 Token很多教程习惯把 token 翻译成“词元”或者“令牌”翻译是没错但初学者听完还是一头雾水。我换个说法token 是模型处理文本时的最小单位你可以把它理解为“模型眼中的一块积木”。我们人类读中文天然按“字”“词”“句”来理解。但大语言模型本质上不是按“字”处理的——它处理的是数字。模型需要先把输入的文字切成一小块一小块每一块再映射成一个编号ID然后才能丢进神经网络里做计算。这一小块一小块的东西就是 token。我常用“切菜”来类比你给我一根黄瓜我炒菜之前得先切片。切片的方式可以厚可以薄厚片少切几刀薄片多切几刀但无论如何最终下锅的形态就是片状。Token 就是模型处理文本时的“片”。模型怎么切它有一套自己的规则——基于词表vocabulary做子词切分最常见的是 Byte Pair EncodingBPE算法。核心逻辑是高频出现的词直接作为一个完整 token低频或生僻的内容拆成更小的片段甚至单字符。举个例子“人工智能”这四个字如果模型词表里有“人工智能”这个完整词条它可能只算 1 个 token如果没有就可能拆成“人工”“智能”甚至逐字拆开。这解释了为什么同一段文字在不同模型里 token 数可能不一样——因为各家词表不同、切分规则不同。1.2 不同模型的 Token 算法差异主流模型的 token 切分规则差异其实很明显我整理了一个实际对比表方便你直观感受文本内容GPT-4o 系列cl100k_base 词表Claude 系列DeepSeek 系列“hello world”约 2 个 token约 2~3 个 token约 2 个 token“你好世界”约 4~6 个 token约 4~8 个 token约 4~5 个 token一整页中文文档约 1000 字约 800~1200 token约 1000~1500 token约 900~1300 token注意上面这些数字是估值实际以各家 tokenizer 工具的计算结果为准。但从这个表里能看到几个规律英文场景下一个 token 大约对应 3~4 个字符因为英文单词由字母组成组合模式更规整中文场景下一个字经常对应 1~2 个 token所以中文文本的 token 密度明显更高——同样是 1000 字的中文token 数可能比英文还多但传递的信息量却很大。这就是为什么很多做国际化的团队感慨中文内容接入 API 时token 消耗往往比同体量英文内容高。这直接关系到成本后面我详细说。1.3 Token 用量怎么算一个实际例子的计算光说概念容易飘我拿一个实际例子算给你看。假设你有一段话“我今天早上九点出发乘坐高铁从上海到北京全程约四个半小时。”如果模型词表完整这段话大概会被切成这样的 token 序列示意我 今天 早上 九点 出发 乘坐 高铁 从 上海 到 北京 全程 约 四个 半小时 。也就是说这句话大概消耗 15~20 个 token。如果这是一篇 5000 字的技术文档换算下来就是 4000~7000 个 token。那为什么“token 用量”这个概念这么重要两个原因第一模型有上下文窗口限制token 数量直接决定你能不能把材料完整塞进模型第二商用 API 按 token 计费你的每一次请求、每一轮对话都在烧钱。2. 上下文窗口为什么长材料一次塞不进去2.1 上下文窗口的本质上下文窗口Context Window是模型一次能够“看到”的 token 总量上限。它的单位不是字数而是 token。比如某模型的上下文窗口是 128K意思是最多能接收 128K 个 token 的输入——这包含了用户发的消息、系统提示词、历史对话以及模型生成的回复。我打个比方上下文窗口就像模型的工作台。工作台就那么大你把材料、工具、半成品都堆上去一旦超出面积要么后面的材料没地方放要么前面的东西被挤下去。2.2 超窗之后会发生什么很多人以为“超出窗口就报错”其实不完全是。要分场景看普通对话场景超出窗口后系统通常会报 context length exceeded 之类的错误请求直接失败。长文本处理场景有些 API 支持自动截断但截断往往是“从头截”或“从尾截”你不能保证留下的正好是重要内容。检索增强生成场景如果你把一大段材料塞进向量库让模型基于检索结果回答这时每段检索结果都要占用上下文窗口。检索回来的片段多了照样超窗。我踩过一个真实坑有一次做客户文档问答系统用户上传了一份 200 页的 PDF。我一开始图省事直接把整份 PDF 解析后拼成一个超长字符串丢给模型结果接口直接报错——上下文窗口不够请求被拒绝。后来我才意识到这不是模型的问题是我给材料的方式错了。2.3 长材料分段处理的核心原因长材料必须分段处理根本原因有三个拆开说第一个原因物理限制。模型架构决定了它内部的自注意力Self-Attention计算复杂度与序列长度呈平方级增长。窗口越长计算开销和内存占用涨得越夸张。所以各家模型的窗口不是“想要多大就有多大”而是工程取舍的结果。第二个原因精度衰减。即使模型支持很长的上下文窗口实测中往往会发现当输入特别长时模型对中间部分内容的注意力会减弱。学界对这种“Lost in the Middle”现象研究不少——模型对开头和结尾的内容记得更牢中间的内容容易“被忽略”。所以就算技术上能塞下 128K token你最好也别真的把它塞满。第三个原因成本控制。所有商用 API 都是按 token 计费的。同样一份 10 万字的材料你一次性全量塞进去请求一次的费用可能是几块钱到几十块钱不等具体取决于模型定价但如果你分段处理只把必要的片段发送给模型成本可以省下好几倍。我列个对比表直观感受一下处理方式每次请求占用 token假设材料为 10 万字约 8 万 token成本感受整段塞入约 8 万 token1 次请求高且可能超窗分段处理每段 2000 token约 2 千 token40 次请求低得多但需考虑合并逻辑一次性塞入可能在第一步就报错分段处理虽然请求次数多了但单次成本低、可控性高而且可以在每一段上做精细化处理。所以“分段处理”不是锦上添花而是绕不开的必经之路。3. 分段处理不是简单切一刀Chunking 的实操方法分段处理在专业领域叫 Chunking分块这是 RAG 和长文本处理里最基础也最容易被忽视的环节。很多人觉得这有什么难的——拿一个文本切割函数按长度切就完事了。但真正落地时你会发现“怎么切”直接决定了后续检索和生成的质量。3.1 分段粒度怎么选分段的粒度Chunk Size没有绝对标准取决于你的下游任务。我做了几年项目总结出这样的经验做问答 / 检索场景chunk 长度在 200~500 token 之间比较合适。因为检索到的片段最终会被拼进模型的上下文里片段太长你能拼给模型的候选数量就少片段太短语义又容易不完整。做摘要 / 分析长文档chunk 可以放到 1000~3000 token。因为摘要任务需要保留相对完整的语义段落切太碎会导致摘要碎片化。做翻译 / 改写要按语义段落切尽量保证一个 chunk 内是完整表达一个意思的一组句子。注意我说的 token 数不是字数。不同模型 token 换算不同实操中最好直接用各家 tokenizer 库计算长度而不是凭感觉按字符数切。3.2 常见分段策略对比实际工程中我试过四种主流的分段策略各有优劣固定长度切分按预设的 token 数或字符数均匀切开代码最简单。缺点是容易切断句子导致语义断裂。比如一段话讲到一半被切走后半句跑到下一个 chunk 里。按段落切分把文本先按换行符或段落标记分成块。优点是多以自然语义为单位切出来的 chunk 往往有相对完整的含义。缺点是段落长度不稳定有些段落太长仍需要二次切分。按句子边界切分用分句工具比如按句号、感叹号、问号切分再按长度合并。效果比前两种好但中文分句也不完全可靠引号内部的分句、缩写点之类会带来干扰。语义切分 / 递归切分先用段落做粗切再对超长段落递归细切。这是目前我在生产环境里最推荐的做法。LangChain 里的 RecursiveCharacterTextSplitter 就是这个思路——按一组分隔符依次尝试优先保留完整语义单位。我建议你直接采用“段落粗分 超长段递归细分 短段落向后合并”的组合策略。原因很简单它兼顾了语义完整性和长度可控性而且实现起来并不复杂。3.3 分段时的重叠设计分段处理里一个非常关键的细节是重叠Overlap。简单说就是相邻两个 chunk 之间保留一部分重复内容。为什么要重叠还是用切菜来类比。你把黄瓜切成片如果两片之间没有任何重叠那砧板上总会有一小段黄瓜纤维卡在刀缝里。文本也一样如果两个 chunk 刚好把一个完整的句子或概念从中间劈开那么检索时无论落在左边还是右边都可能拿到残缺信息。重叠的作用就是给“切断处”一个缓冲区保证关键信息至少完整出现在某个 chunk 里。实操中重叠量通常设为 chunk 长度的 10%~20%。比如 chunk 长度是 1000 token重叠 100~200 token 比较合理。重叠太少没意义重叠太多又浪费空间。3.4 元信息补充与召回优化分段处理不只是“切”还包括给每个 chunk 附上元数据。这是很多人忽略但价值极大的点。举个例子。你有一本小说切成 500 个 chunk。如果不做任何标记检索“男主角在第几章表白”时模型可能拿到一个没头没尾的片段根本不知道是哪一章、哪个角色说的。如果你在切分时保留了章节号、段落序号、文档名、页码等元信息检索到该 chunk 后就能告诉模型“这个片段来自第三章角色 A 和角色 B 的对话”生成结果会准确得多。我做过一个合同审查项目把每份合同切成多个 chunk并给每个 chunk 打上“条款编号”“所属章节”“合同类型”三个标签。效果提升非常明显——回答“设备采购合同里关于验收的条款是什么”时模型能直接定位到对应条款而不是在模糊的上下文里猜。所以分段的正确姿势永远是切分不是目的组织可检索的信息单元才是目的。4. Token 计费、失效与续签最容易翻车的三个细节4.1 Token 计费规则输入输出不对称所有主流大模型 API 的计费都按 token 数计算但有一个容易被新手忽略的规则输入 token 和输出 token 的单价往往不一样。通常输出 token 更贵因为生成过程的计算成本更高。举个例子价格以实际官方为准我这里只讲比例逻辑某模型输入是 $3 / 百万 token输出是 $15 / 百万 token。如果你做一个对话机器人每轮用户问得短但模型回复很长那你的成本大头其实在输出而不是输入。所以优化成本时不仅要减少输入内容还要控制生成长度——比如设置合理的 max_tokens 上限或者在提示词里要求“简短回答”。4.2 Token 失效的常见原因与排查热搜词里有大量关于 token 失效的报错“sign-in could not be completed token exchange failed: token endpoint returned status 403 forbidden: country”“your access token could not be refreshed. please log out and sign in again.”“failed to refresh token: 400 bad request: invalid refresh_token: empty string”。这些看着复杂归归类其实就三种情况第一类Access Token 过期。很多服务采用短时 access token 长时 refresh token 的机制。Access token 一般十几分钟到几小时有效过期后客户端需要用 refresh token 去换新的。如果你的 refresh 流程没实现或者实现有问题就会出现“your access token could not be refreshed”这类报错。第二类Refresh Token 无效。“invalid refresh_token: empty string”其实是开发时的典型错误——代码里根本没有正确传入 refresh_token或者把它写死在配置里没更新。这种情况在测试环境最常见不用太慌查一下代码里 token 的存储和读取逻辑就好。第三类服务端策略限制。比如 token endpoint 返回 403 forbidden: country这通常意味着服务方对请求来源区域做了限制。注意这不是你的代码 bug而是服务策略问题。这种场景下除了遵循服务方官方支持的区域接入方式没有正规的旁路手段别浪费时间瞎折腾。4.3 JWT 实现 Token 续签一个值得动手的设计在自研系统里JSON Web TokenJWT是最常见的 token 方案但 JWT 本身是无状态的——签发之后服务端不保存这份 token到期就失效。所以很多团队会自己做续签机制常见做法是 access token短期 refresh token长期双令牌。我在生产环境里做过一版设计核心就三步用户登录后服务端签发 access token有效期 30 分钟和 refresh token有效期 7 天。客户端请求接口时带 access token服务端验证失败且返回 401 后客户端自动调用 refresh 接口用 refresh token 换新的 access token。refresh 接口里校验 refresh token 的签名和有效期再检查是否在黑名单里比如用户主动登出后要将 refresh token 列入黑名单。这里有个易踩的坑如果直接在前端本地存储 refresh token一旦被脚本捞走攻击者就能长期保持会话。比较稳妥的做法是把 refresh token 放在 httpOnly cookie 里并设置合理的有效期和 SameSite 属性。别嫌麻烦token 安全问题在真实项目里占据的排障时间远超你想象。4.4 实操中的 Token 管理建议基于过往项目的教训我给你几条能直接拿去用的建议所有 token 相关报错先在代码里打日志把 token 是否存在、过期时间、报错体完整记录下来。很多“token 失效”根本原因都是代码逻辑问题不是服务商问题。不要手动在代码里拼 token用官方 SDK 或封装好的 client 库。它们通常已经处理好了自动刷新逻辑。定时检查 token 的过期时间离过期时间不足 5 分钟时提前刷新。很多偶发请求失败就是因为 token 恰好在请求发出前几秒过期了。遇到 403 country 类报错先核对请求出口区域是否符合服务商要求而不是试图绕过限制。安全红线不能碰合规最重要。5. 一套可复用的长材料处理流程前几节把概念和细节讲清楚了这一节我直接给你一套我平时在项目里反复使用的处理长材料流程。以“用大模型读一份 50 页 PDF 并回答相关问题”为例。5.1 预估与规划第一步不是动手切是先算账。你要评估三件事这份 PDF 总共有多少 token用解析工具提取文本后调用 tokenizer 库算一下总量。示例代码from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(gpt2) # 这里仅为估算示例 text open(document.txt, encodingutf-8).read() tokens tokenizer.encode(text) print(f总 token 数: {len(tokens)})注意估算 token 数最好使用目标模型官方提供的 tokenizer如果拿不到用同语系的近似 tokenizer 估算也能接受误差通常不超过 20%。目标模型上下文窗口是多少要留多少余量给系统提示词、历史对话和模型回复我一般会把模型窗口的 70% 作为材料区上限剩下 30% 给其他内容。你的预算里这次任务最多能烧多少 token这决定了 chunk 能不能切粗一点以及检索策略要多激进。5.2 分段执行规划完了按我前面讲的三段式策略切分先按段落粗切再递归细分超长段落最后做相邻 chunk 的合并控制长度在目标范围内并保留 10%~20% 的重叠。具体参数我举个例子假设目标 chunk 是 800 token、重叠 120 token那么每一步都是在保证语义完整的前提下往 800 上下靠。不要为了追求“刚好 800”而切碎句子略超或略短完全可以接受。切分完给每个 chunk 打标。至少要包含来源文档 ID、章节名、页码如有、段落序号。如果要走向量检索这些元数据会一并存入向量库。5.3 结果合并与生成分段处理后下游任务怎么把结果拼回来我以最典型的 RAG 问答流程为例把用户问题转成向量在向量库里做相似度检索取 top 5 个 chunk。把这 5 个 chunk 按原始位置排序后拼接到提示词里同时附上各自的元信息。最后加上系统提示词“请基于以下材料回答用户问题如果材料中没有相关信息请直接说明不要臆测。”这里有三个实操要点检索回的 chunk 不是越多越好5~8 个已经够大多数场景用了。chunk 太多会挤占上下文窗口甚至把用户问题淹没。拼接顺序按原文顺序不要按相似度排序。否则模型看到的材料逻辑是乱的。如果用户在一次会话里连续提问历史对话也要计入上下文窗口。这种情况下材料区更要克制必要时可以丢弃早期历史。5.4 成本控制与质量验证流程跑通之后我最关心的两件事就是成本和生成质量。成本这关我每次上线前都会做一轮 token 消耗审计把测试环境完整回放一遍统计每一轮请求的输入输出 token 数算清单次会话的平均成本再乘上预期用户量确认在预算范围内。质量这关我会建立小规模评测集——比如 50 个有标准答案的问答对每次修改分段策略后跑一遍对比准确率变化。这里抛一个让我印象深刻的实际案例。之前做一个合同问答机器人初版用固定 500 字符切分准确率只有 68%。后来换成“按条款边界切分 段内保持完整句子”准确率涨到 82%。再后来我加上了重叠 15%准确率又提升了 3 个百分点。表面上看只是切分方式变了本质上是让每个 chunk 的信息完整性大大增强。这也印证了我前面说的分段的质量直接决定了下游应用的天花板。6. 一个真实项目的完整踩坑记录这一节我分享一个让我印象很深的项目排障过程里面几乎踩遍了上面提到的所有坑你看一遍等于提前避雷。那是一个内部知识库问答系统用户上传了大量技术文档要求基于文档内容做回答。上线第三天客服那边陆续反馈三个问题回答经常是“我不知道”偶尔报“context length exceeded”有用户反映登录状态一小时后失效需要反复重登。我按当时的排障顺序复盘一下。第一个“我不知道”排查后发现问题出在分段粒度上。当时用的是固定 2000 字符切分很多段落被腰斩检索时经常只命中半截内容模型自然无法回答。换成按段落边界递归切分后这个现象明显缓解。第二个“context length exceeded”是另一个问题。当时为了追求召回率把检索到的 15 个 chunk 全部拼进去每个 chunk 将近 2000 字符再加上系统提示词和对话历史轻松超窗。后来我把检索数降到 6 个并取消过长的历史对话保留问题解决。第三个登录失效问题最典型。代码里 access token 有效期设了 2 小时但 refresh 令牌的逻辑只在“刷新接口被调用”时触发而前端压根没有在 401 后自动调用刷新接口所以用户 2 小时后必然被踢出。修复方式是前端 axios 拦截器统一捕获 401自动调 refresh 接口重试原请求后端把 refresh_token 放进 httpOnly cookie并实现黑名单功能。这三个问题花了差不多一周才全部解决。回头想如果一开始就按我前面说的“预估 token 数 → 合理切分 → 控制检索量 → 规范 token 管理”来设计至少能少走一半弯路。7. Token 与分段还有哪些进阶玩法基础的东西讲完了最后说几个我最近在试验的进阶思路算是抛砖引玉。一是自适应分段。传统分段策略是人预先定好 chunk 大小但不同文档的语义密度差异很大。现在有一些做法是用模型对文档做主题分割Topic Segmentation检测语义变化点在主题转折处分段。这样分出来的 chunk 每个都围绕一个核心主题检索质量会进一步提升。二是压缩式分段。对超长文档先做一次“伪摘要”——把每一段的核心信息提炼成短句形成一份紧凑版文档再在这个压缩版上进行检索定位命中后回到原文取详细内容。这种“粗检索 精读取”的两级结构在超长文档场景下效果很好。三是窗口式问答。有些场景不适合把文档切碎入库比如合同、论文这种强引用关系的文档。这时候可以按“当前段落 前后各一段”的窗口方式组织上下文保证模型看到的是相对完整的局部语境。这些方案各有适用场景谈不上谁绝对最优。核心思路都是一样的在 token 预算内让模型看到最有效的信息。我自己在实际项目里的体会是token 和分段这件事说到底是“信息组织”的问题。你的目标永远是让模型的注意力集中在和任务最相关的内容上而不是把材料粗暴地堆过去。理解了这个底层逻辑无论模型怎么迭代、窗口怎么加长你的方案都不会过时。最后再提醒一句开发阶段多预留一点时间做 token 估算和 chunk 效果评测这比上线后反复调参要划算得多。