简介:本文围绕大模型会话上下文管理的核心问题展开,拆解计量、压缩、换页、剪枝四类机制的协作方式。读者将理解请求前缀缓存对成本的影响、启发式计量为何只能用于触发决策,以及如何通过分级策略在有限窗口内维持长会话可用性,获得可落地的工程思路。
在大模型驱动的智能体系统中,上下文窗口是一项有限且昂贵的资源。以当前常见的长上下文模型为例,单次请求可承载的 token 规模已经达到百万级别,但智能体的会话并不是一次性问答:它包含系统提示词、工具清单、历史消息、工具调用结果、中间推理记录等多个组成部分,且随任务推进不断累积。一次包含多步工具调用的任务,往往在几十轮交互后就逼近窗口上限。
因此,一个成熟的运行时框架必须回答两个问题:第一,当前上下文到底占用了多少窗口容量;第二,当占用接近上限时,应该用什么方式腾出空间,同时尽量不破坏模型的推理连贯性。围绕这两个问题,一套典型的工程实现通常由四个互补的机制构成:计量、压缩、换页与剪枝。本文以一套开发者预览版运行时框架的公开设计为参照,结合实际 token 数据,把这四个机制拆开讨论。
理解上下文管理,最好从一次真实任务的 token 消耗入手。假设一个只有三步的示例任务,每一步调用模型时报告的输入 token 数如下:
第二步的输入为什么突然变小?原因在于模型请求的前缀缓存特性:只要本次请求的 token 序列前缀与上一次请求逐字节相同,服务提供方就可以复用上一次已经计算好的键值缓存(KV Cache),只需处理新增的增量部分。第一、二步之间新增的内容只有一条工具返回结果,因此真正需要重新计算的只有那几百个 token;而系统提示词、工具清单和已有历史这四千多个 token 全部命中缓存,不再重复计费。
这条规律带来一个重要结论:保持请求前缀稳定,是长会话成本控制的第一原则。任何会打乱前缀顺序的操作,例如在历史消息中间插入内容、动态重排工具清单、频繁改写系统提示词,都会让缓存失效,使原本可以复用的部分重新计费。因此在设计上下文管理策略时,应当优先采用”追加式”修改,把会破坏前缀的操作推迟到必要时才执行。
但前缀稳定只能延缓问题,不能解决问题。会话终将填满窗口,此时就必须主动收缩上下文。
上下文管理的第一步是量化。运行时框架中的计量组件通常以服务形式对外提供估算能力,用于展示当前窗口占用率,并在超过阈值时触发后续动作。它的实现方式一般是启发式的:按平均每 token 约四个字符的经验比例折算,再叠加角色标记、消息块边界和请求结构本身的开销。
需要特别强调的是,这类估算不是计费依据。公开文档通常会明确提示:中日韩文本与 JSON Schema 这类结构化内容容易被显著低估,因为字符数与 token 数的比例在这些场景下会明显偏离平均值。因此计量组件的定位应当是”决策触发器”而非”账单计算器”:它只需要保证在接近阈值时足够敏感,而不需要做到逐 token 精确。
在实践中,计量结果至少有三个用途:
一个常见的误区是把计量精度当作目标。实际上,只要估算误差在可控范围内,并且偏向”高估”而非”低估”,触发逻辑就是安全的。高估会让压缩略微提前发生,代价很小;低估则可能导致请求超限失败,代价很大。
当计量显示占用率达到阈值,最直接的腾挪手段是压缩。压缩的基本思路是:保留最近若干轮交互的原始内容,把更早的历史消息交给模型或规则化流程生成摘要,再用摘要替换原始消息。
压缩看似简单,工程上有几个必须权衡的点:
这里存在一个天然的张力:缓存要求前缀稳定,压缩要求改写历史。合理的做法是把压缩设计成”低频、大批量”的操作——与其每次释放 5% 的空间,不如在达到 80% 时一次性压缩到 50% 以下,让后续多轮交互都能继续享受缓存命中。这与内存管理中的分代回收思路类似:把昂贵的整理操作集中在少数几个时刻完成。
压缩处理的是历史消息的整体规模,但还有一类内容会以单点方式撑爆窗口:工具返回结果。某些工具(例如读取大文件、查询大结果集、抓取网页)可能一次返回数十万字符,远超单条消息的合理预算。
换页机制针对的正是这种场景:当工具结果超过预设大小阈值时,运行时把完整结果写入磁盘或对象存储,只把首尾两段预览注入上下文,并在预览中标注完整内容的存放位置。模型如果需要中间部分,可以通过后续的工具调用按需读取。
这种设计的好处很直接:
代价是需要额外的存储与读取路径,且模型必须理解”预览 + 按需读取”这一约定。因此在工具描述中清晰说明换页行为,是让该机制真正生效的前提。
剪枝与换页目标相似但手段更轻。对于超出预算但并不需要完整保留的工具结果,剪枝直接对其做结构化截断:保留开头若干 token、结尾若干 token,中间用省略标记替代。与换页不同的是,剪枝不落盘,被截断的中间内容不再可恢复。
因此两者的适用边界应当区分清楚:
| 机制 | 是否落盘 | 中间内容可否恢复 | 适用场景 |
|---|---|---|---|
| 换页 | 是 | 可恢复 | 大结果且后续可能需检索 |
| 剪枝 | 否 | 不可恢复 | 大结果但中间部分价值低 |
一个务实的策略是:先剪枝,剪枝仍不满足预算时再换页;或者按工具类型预设策略,对日志类、清单类结果优先剪枝,对文档类、数据集类结果优先换页。
把四个机制串起来,可以得到一个清晰的运行时决策流程:
每次请求前:1. 计量组件估算当前占用率2. 若占用率 < 阈值:直接发送,保持前缀稳定3. 若占用率 >= 阈值:a. 先对新增工具结果做预算检查b. 超预算结果按策略剪枝或换页c. 若仍超阈值,触发历史压缩d. 压缩后重新计量,确认回落到安全区间4. 记录本次请求的缓存命中情况,用于后续调优
这个流程体现了分层思想:优先做局部、廉价的修改(剪枝、换页),把全局、昂贵的修改(压缩)留到必要时。同时,每一层都应尽量保持”追加而非重写”,以维护前缀缓存的命中率。
上下文窗口管理不是一个单点功能,而是一组机制的协同:计量回答”用了多少”,压缩回答”历史太长怎么办”,换页回答”单条结果太大怎么办”,剪枝回答”如何在最小代价下截断”。四者共同服务于两个目标——在窗口上限内维持会话可用,以及尽可能保持请求前缀稳定以复用缓存、降低成本。理解这套分工,比记住任何单个参数都更有价值,因为它可以直接迁移到其他长会话系统的设计中。