一个模型支持 32,000 Token 的上下文窗口,并不表示用户可以输入 32,000 Token 后,再让它生成任意长度的回答。对于输入和输出共用窗口的模型,两部分需要一起放进这段空间。

上下文窗口限制的是模型一次推理可以处理的序列长度,以 Token 计量。Token 是文本经过分词器编码后的处理单位,与字符数没有固定比例。模型生成回答时,输入和已经生成的内容共同构成后续生成所依据的上下文。

应用传入的内容也不只有用户刚发的那条消息。系统指令、保留的历史、参考资料和工具信息,都会占用输入空间。因此,判断请求是否超限,需要看程序最终发送了什么,以及准备为回答留出多少空间。

完整输入与窗口容量

以一个产品文档问答应用为例。它从文档库检索相关片段来回答问题,必要时调用工具查询资料版本。

假设本次对话讨论接口兼容性。用户已经要求只参考 v2 版文档,部署环境是 Linux;前几轮还讨论过接口行为和报错。现在用户又补充了一个问题,程序准备结合历史、检索到的接口说明和已经取得的版本查询结果生成回答。

这次请求可以按内容分成下面几部分。数字是假定的 Token 数,用于容量演算,不是对本段示例文字的分词结果:

输入内容Token 数包含什么
系统指令1,200回答职责、资料使用规则和引用格式
历史对话12,800已讨论的接口行为、报错和问答记录
当前问题200用户这一轮提出的兼容性问题
工具定义1,800查询工具的名称、说明和参数 Schema
检索资料10,800从文档库取出的接口说明及来源信息
工具结果1,600版本查询工具返回的文档版本和状态
消息格式等开销600角色、消息边界和生成起始标记等
输入合计29,000

用户这一轮只输入了 200 Token,但完整输入已经有 29,000 Token。

表中按用途分组,不代表请求中有七个独立字段。历史、资料和工具结果都可能放在消息列表里,统计时要避免把同一份内容算两遍。例如,检索片段如果已经计入工具结果,就不应再额外加一次检索资料的长度。

消息格式也需要空间。聊天接口收到的 role、content 等内容,会按模型要求转换成 Token 序列,其中可能包含角色标记和消息分隔符。仅把各条消息正文的长度相加,未必等于最终输入长度。Hugging Face 的聊天模板文档展示了消息列表转换成模型输入的过程。

这 29,000 个 Token 会先占用窗口。假设模型的共享窗口为 32,000 Token,输入放进去之后,就只剩下 3,000 个 Token 可供继续生成。

如果希望为回答保留 4,000 Token,就需要比较:

29,000 输入 + 4,000 输出预算 = 33,000

如果回答要用满这 4,000 Token,就会超出窗口 1,000 Token。问题不在当前消息的 200 Token,而在完整输入已经占用了大部分空间。Claude 的上下文窗口说明介绍了历史、工具信息与新输出怎样共同占用窗口。

输出预算与安全余量

为避免回答写到一半耗尽窗口,应用可以先确定输出预算,再计算允许传入多少内容。这次文档问答计划为回答预留 4,000 Token,输入最多就只能占 28,000。

实际组装时,本地估算和服务计量之间还可能有差异。假设应用再预留 1,000 Token 的安全余量,可用输入预算就变成:

可用输入预算 = 上下文窗口 - 输出预算 - 安全余量
             = 32,000 - 4,000 - 1,000
             = 27,000 Token

当前输入有 29,000 Token,至少需要减少 2,000,才能满足这项预算。

这里的安全余量是应用主动留下的空间,不是模型统一要求的参数,也不代表固定留 1,000 就能覆盖所有误差。计数方式越不完整,余量越难弥补;它需要结合目标模型、接口和实际请求调整。

模型还可能有独立的输出上限。比如,这个虚构模型的窗口是 32,000 Token,但单次最多只能输出 8,000。即使输入只有 12,000,也不能因为窗口里还剩 20,000,就把输出预算设为 10,000。

因此,应用规划时同时检查两项:

输入 + 输出预算 + 安全余量 ≤ 上下文窗口
输出预算 ≤ 模型输出上限

预留 4,000 Token,不表示这次一定会生成 4,000。模型可以提前结束;这只是我们希望完整保留下来的输出空间。接口中的长度字段按各自协议设置,例如 DeepSeek 的 max_tokens 限制单次 completion 的生成量,同时输入和输出总长度仍受上下文窗口约束。字段定义见 DeepSeek 接口文档。

思考类模型还需要确认推理 Token 怎样计入预算。如果推理与最终答案共用输出额度,4,000 Token 就不能全部当作可见正文。前面轮次的推理内容是否保留在上下文,也由具体模型和接口决定;窗口和输出预算都应按对应规则计算。

超限后的两种结果

前面的公式是应用侧的预算检查。超过了计划预算,意味着需要调整请求,并不代表所有接口都会立即返回同一种错误。

一种情况是请求在生成前就被拒绝。比如完整输入本身已经超出窗口,或者接口会校验输入长度与请求的最大输出长度之和。此时要查看错误信息中说明的限制,以及服务给出的输入、输出和窗口数值,再决定减少哪一部分。

另一种情况是请求被接受,但生成过程中用完了窗口。上例有 29,000 个输入 Token,若接口允许开始生成,剩余空间也只有 3,000,回答可能还没有完成就停止。Claude 的窗口溢出说明区分了接受请求后停止生成与提前校验报错的行为。

所以,收到成功响应后,也需要检查停止原因。以 DeepSeek 为例,finish_reason="length" 可能表示达到了 max_tokens,也可能表示达到了上下文长度限制。它本身不能区分究竟是哪一种,需要结合输入用量、实际输出和请求参数判断,定义见 接口文档。

聊天产品或 Agent 框架也可能在发送之前截取历史、生成摘要。它们处理后的请求可以符合长度限制,但已经不再包含完整原文。排查时,需要查看实际发送了什么,不能只根据界面上仍能翻到的聊天记录判断模型看到了什么。

按任务缩减输入

这次文档问答需要从输入中至少减少 2,000 Token。当前问题只有 200 Token,较大的两部分是 12,800 Token 的历史和 10,800 Token 的检索资料,可以先检查其中是否有重复或已经无关的内容。

假设检索资料中的几段内容重复,去重后减少 2,400 Token,输入就从 29,000 降到 26,600:

26,600 输入 + 4,000 输出预算 + 1,000 安全余量 = 31,600
32,000 - 31,600 = 400 Token

这次调整满足预算,同时保留了原计划的输出空间。不过,需要确认去掉的是重复内容,没有把解释兼容性问题所需的限制条件一起删掉。

处理历史时,可以把早期讨论整理成摘要,但要保留仍然有效的条件。这个例子里,“只参考 v2 文档、部署在 Linux 上”会直接影响回答,摘要中就应保留。若只取最近几轮,最早提出的版本和环境要求可能被删掉,模型随后就可能引用不适用的资料。

摘要还需要区分用户要求和助手建议。即使助手曾建议升级到 v3,也不能在用户尚未确认时,把摘要写成“用户将升级到 v3”。压缩减少的是表述长度,不应顺便改变已经确认的事实。

处理检索资料时,可以去掉重复片段,只保留当前问题需要的内容,并保留对应的来源标识。工具定义则只传入这项任务会使用的工具;工具结果可以提取必要字段,避免把很长的查询结果原样塞进对话。

工具相关消息需要按目标接口的协议一起整理。保留一个工具结果时,也要保留它需要对应的调用记录;截取历史应以完整交互为单位,避免截到半次调用。有未完成的调用,就先完成或按协议处理,再继续组装。工具调用示例展示了调用记录和结果怎样一起进入后续请求。

如果当前问题、必须保留的指令和必要资料本身就放不下,需要把任务拆成多次处理,或使用容量满足需求的模型。反复删除同一批已经压缩过的历史,不会解决必需内容过长的问题。窗口截取、摘要和历史检索等方案的比较,可进一步参考 LLM实现记忆功能的常见模式。

另一个办法是降低输出预算。仍然保留 29,000 Token 输入,把输出预算从 4,000 改成 2,000,再加 1,000 Token 余量,合计刚好是 32,000。

这样在数值上满足预算,但允许生成的空间也减半了。如果兼容性问题需要完整的条件分析和说明,回答可能因输出长度不足而中断。只有任务本身可以缩小,或者更短的答案已经够用时,这项调整才合适。

发送前的计数与后续增长

分项表适合规划和查找大头,最终仍要按实际组装后的请求计数。分词器可能跨拼接位置合并片段,所以分别编码几段字符串再相加,结果不一定等于完整文本的 Token 数;消息格式、工具描述也不能漏算。分词细节与可运行示例可参考 Token 是什么:为什么字数不能直接换算模型用量。

如果服务提供请求计数接口,可以把准备发送的模型、消息和工具等内容交给它估算。比如 Claude 的 Token 计数接口支持系统指令、工具、图片和 PDF 等输入。自行使用 tokenizer 时,则需要匹配目标模型及其聊天格式;有图片、音频或文档输入时,还要按对应接口的处理规则计量,不能用上传文件的字节数代替 Token 数。

在程序中,可以先确定输出预算和余量,组装请求后计数;超出预算就按任务规则缩减,再对调整后的完整请求计数。满足预算后再发送。调用结束时记录实际用量与停止原因,后续用这些结果检查估算是否经常偏小。

命中缓存也不减少窗口里的逻辑长度。假设上例 29,000 个输入 Token 中有 20,000 命中缓存,输入仍然占 29,000,不能减成 9,000。缓存复用计算,命中部分仍是本次输入。DeepSeek 的缓存说明将输入分为命中与未命中两部分;计费分类的变化不等于上下文被删除。

预算检查还需要放在每次模型调用之前。上一轮的回答会成为下一轮的历史,工具执行完又会带回新结果;同一次用户提问中的第二次模型调用,也可能比第一次更长。第一步能放下,并不能保证整个工具调用过程都能沿用原来的余量。

请求长度符合限制后,还要检查回答是否满足保留下来的条件。在这份文档问答中,就是看它是否使用了 v2 文档、是否考虑 Linux 环境,以及结论能否由提供的接口说明支持。窗口容量与信息是否被正确使用,需要分别验证。消息结构与工具交互的更多例子可参考 一次模型调用的输入究竟包含什么。