1. 从"人写压缩规则"到"模型自己写压缩函数"的范式转移
第一次看到"模型自己写了个压缩函数,工程师两年的工程白做了"这个说法,我的反应是:这标题党也太狠了。但仔细琢磨上下文管理这条技术线的发展脉络,我发现它戳中的是一个真实存在的尴尬——过去几年里,围绕长上下文做的工程优化,绝大多数是"人来定规则,模型来遵守"。而CLM(Context Lifecycle Management,上下文生命周期管理)这类思路的核心主张,是把"怎么压缩、什么时候压缩、压缩成什么"这个动作空间,直接交给模型自己去决策。
这两者的差别,不是优化程度的差别,是范式的差别。
打个比方。以前的上下文管理像是一个公司里,老板提前写好一本《会议纪要规范手册》:超过多少字必须摘要、哪些字段必须保留、哪些内容可以丢弃。秘书(模型)只能照着手册执行。而CLM的思路是:不给手册,只给秘书一个目标(比如"在有限预算内保持任务可完成"),让秘书自己决定这次会议该怎么记、记多少、哪些细节值得留。
为什么这件事让不少做工程的同行感到"两年白做"?因为过去两年大量精力花在手工设计压缩启发式规则上:滑动窗口留多少token、关键实体怎么抽取、摘要用什么prompt模板、检索召回怎么和压缩配合。这些工作有一个共同前提——压缩策略是外生的、静态的、由人写死的。一旦模型能自己生成压缩函数,这些手工规则的价值就被大幅稀释了。
但我要先把话说清楚:这不是"工程师没用了",而是工程的重心从"写规则"迁移到了"设计动作空间和约束"。这个迁移本身,才是CLM最值得聊的地方。下面我会把这条线拆开,讲清楚动作空间到底指什么、模型怎么"自己写压缩函数"、工程侧真正该做什么、以及我在实际搭建类似机制时踩过的坑。
2. 上下文管理的动作空间到底包含哪些维度
要理解"把动作空间交给模型",先得把动作空间本身讲明白。很多人一提到上下文管理,脑子里只有"压缩"一个动作,其实远不止。我把一个上下文管理系统里模型可以做的决策,拆成下面几个维度。
2.1 保留、压缩、丢弃、外置:四类基础动作
任何一次上下文处理,本质上是在对每一段历史内容做一个四选一的决策:
- 保留(Keep):原样留在上下文窗口里,不动。
- 压缩(Compress):用更少的token表达同样的信息,比如摘要、抽取关键句、结构化改写。
- 丢弃(Drop):直接扔掉,认为它对后续任务不再有价值。
- 外置(Externalize):从上下文窗口移出,但存到外部存储(向量库、键值存储、文件),需要时再召回。
传统工程做法是:人写一套规则,比如"最近N轮保留,更早的做摘要,实体表外置"。规则是固定的,模型只是被动接受处理后的上下文。
CLM的关键转变在于:这四个动作的选择权,从规则引擎转移到了模型本身。模型在每一步可以自己判断"这段内容该保留还是该压缩",而不是被一条if-else规则决定。
2.2 压缩粒度:从整段到token级
压缩不是只有"整段摘要"这一种粒度。动作空间里,压缩的粒度本身就是一个可决策维度:
| 粒度层级 | 典型做法 | 信息损失 | 计算成本 |
|---|---|---|---|
| 整段级 | 对整段对话做摘要 | 高 | 低 |
| 句子级 | 抽取关键句、删冗余句 | 中 | 中 |
| 实体级 | 只保留实体和关系三元组 | 中高 | 中 |
| token级 | 学习式token合并/剪枝 | 低 | 高 |
人写规则时,通常只能固定在某一两个粒度上,因为要覆盖所有情况太复杂。而模型自己决策时,可以针对不同内容动态选择粒度——一段闲聊可以整段丢弃,一段关键推理可以token级保留。
2.3 时机:什么时候触发压缩
"什么时候压缩"和"压缩什么"同样重要。常见触发策略有三种:
- 阈值触发:上下文占用超过某个比例就压缩。简单但粗暴,可能在关键推理中途打断。
- 任务边界触发:在一个子任务完成、进入下一个子任务时压缩。符合语义,但需要识别任务边界。
- 模型自主触发:模型自己判断"现在信息够了,可以整理一下了"。
CLM倾向第三种。这就像人写笔记,不是每写满一页就整理,而是"我感觉这段想清楚了,先归纳一下再继续"。模型自主触发压缩,能避开在推理链中途做破坏性压缩。
2.4 目标函数:压缩到底为了什么
这是最容易被忽略、却最决定成败的维度。压缩的目标可以是:
- 省token:纯粹为了塞进更小的窗口。
- 保任务成功率:压缩后任务还能完成。
- 保可追溯性:压缩后还能回溯到原始信息。
- 保一致性:多轮之间不出现自相矛盾。
人写规则时,目标通常是单一的(多数是省token)。而把动作空间交给模型后,模型可以在不同情境下权衡不同目标。这也是为什么"模型自己写压缩函数"能work——它把目标函数也内化了。
3. 模型"自己写压缩函数"的三种实现路径
标题里"模型自己写了个压缩函数"这句话,字面上容易让人以为是模型输出了几行代码。实际落地时,它有三种不同深度的实现路径,复杂度依次递增。
3.1 路径一:模型生成压缩指令(Prompt级)
最轻量的做法:让模型输出一段"压缩指令",再由执行器去执行。比如模型说"把第3到第8轮压缩成三条要点,保留所有数字和时间",执行器就按这个指令调用摘要模块。
这条路径的优点是可控、可审计——模型输出的指令是自然语言,人能看懂,出问题能追责。缺点是模型并没有真正"写函数",它只是写了一段配置。压缩的实际逻辑还是人写的摘要器在执行。
我在早期原型里用过这条路径,实测下来它适合规则相对稳定、但需要动态选择的场景。比如客服对话里,模型判断"这轮是投诉,要保留情绪词;那轮是查询,只留订单号"。指令级已经能覆盖大部分需求。
3.2 路径二:模型生成可执行代码(Code级)
进阶做法:让模型直接输出一段可执行代码,比如一个Python函数,输入是历史消息列表,输出是压缩后的列表。执行器直接跑这段代码。
def compress_context(messages, budget): # 模型生成的压缩逻辑示例 kept = [] for msg in messages: if msg.role == "system": kept.append(msg) # 系统提示永远保留 elif msg.role == "tool" and msg.age > 5: kept.append(summarize_tool_result(msg)) # 旧工具结果摘要 elif msg.role == "user" and is_key_entity(msg): kept.append(msg) # 含关键实体的用户消息保留 else: continue # 其余丢弃 return fit_budget(kept, budget)这条路径的威力在于表达力:模型可以写出人没想到的压缩逻辑,比如"如果连续三轮都在讨论同一个实体,只保留最后一轮"。这就是标题里"工程师两年白做"的来源——模型写出的压缩函数可能比人手工设计的规则更贴合实际数据分布。
但代价也很明显:代码级输出有安全风险和执行不确定性。模型可能写出死循环、可能引用不存在的函数、可能在某些输入下崩溃。所以这条路径必须配沙箱和超时。
3.3 路径三:模型内化压缩策略(权重级)
最深度的做法:不生成任何显式函数,压缩策略直接内化在模型权重里。模型在生成下一个token时,隐式地决定了对历史信息的利用方式。这通常通过训练阶段的课程学习或强化学习实现——用"任务成功率"作为奖励,让模型自己学会什么时候该"忘记"。
这条路径是研究前沿,工程上还很难直接落地,因为不可解释、不可干预。你没法在线上告诉模型"这次别压缩这段"。所以目前生产环境里,路径一和路径二是主流,路径三是方向。
3.4 三条路径的选型对照
| 维度 | 指令级 | 代码级 | 权重级 |
|---|---|---|---|
| 可控性 | 高 | 中 | 低 |
| 表达力 | 中 | 高 | 最高 |
| 安全风险 | 低 | 高 | 中 |
| 可审计 | 高 | 中 | 低 |
| 落地难度 | 低 | 中 | 高 |
| 适合场景 | 规则稳定需动态选择 | 复杂多变的数据分布 | 端到端训练 |
我的建议是:从指令级起步,验证"模型自主决策"确实比固定规则好,再考虑代码级。直接上代码级,很容易在调试上耗掉大量时间,而收益未必比指令级高多少。
4. 工程侧真正该做的事:从写规则到设计约束
既然压缩策略交给模型了,工程师是不是就没事干了?恰恰相反。工程的重心从"写压缩规则"变成了"设计模型能安全、有效做决策的环境"。这部分才是真正体现工程价值的地方,也是我认为"两年白做"这个说法过于夸张的原因。
4.1 定义动作空间的边界
模型自主决策不等于放任。你必须定义清楚:模型能做哪些动作、不能做哪些。比如:
- 系统提示(system prompt)永远不可压缩、不可丢弃。
- 涉及安全、合规的内容必须保留。
- 压缩后的上下文长度必须落在预算区间内。
- 某些工具调用的原始结果必须可追溯。
这些边界就是新的"工程规则"。它们不再是"怎么压缩",而是"压缩时不能碰什么"。这个转变很关键——从过程约束变成了结果约束。
4.2 给模型提供决策所需的信号
模型要自己决定压缩什么,前提是它得知道"什么重要"。工程侧要做的是把信号喂给它:
- 任务目标:当前在做什么任务,成功标准是什么。
- 预算信息:还剩多少token空间。
- 历史使用情况:哪些内容之前被召回过、被引用过。
- 时间戳与轮次:帮助模型判断新旧。
我踩过的一个坑是:早期只给模型"当前上下文",没给"预算剩余",结果模型要么压缩过度(把有用的也压了),要么压缩不足(超预算)。加上预算信号后,压缩决策的合理性明显提升。
4.3 建立压缩质量的评估闭环
模型自己写的压缩函数好不好,不能靠感觉。必须有一套评估机制:
- 任务成功率:压缩前后,任务完成率变化多少。
- 信息保真度:压缩后能否回答原本能回答的问题。
- 压缩率:实际省了多少token。
- 稳定性:同样的输入,多次压缩结果是否一致。
这套评估要自动化、要能回归。因为模型生成的压缩策略可能这次好、下次差,没有回归测试根本发现不了。
4.4 沙箱与回滚机制
如果走代码级路径,沙箱是必须的。我见过模型生成的压缩函数在特定输入下进入死循环,把整个请求拖垮。所以:
- 执行超时(比如200ms)必须硬性设置。
- 内存和调用次数要有上限。
- 压缩失败要能回滚到"不压缩"的兜底策略。
注意:兜底策略不是可选项。任何模型自主决策的系统,都必须有"模型决策失败时怎么办"的答案。我的做法是永远保留一个"原样透传"的降级路径。
5. 实测中的意外:模型压缩策略比人想的更"激进"也更"保守"
讲几个我在实际搭建这类机制时观察到的、和直觉相反的现象。这些是文档里不会写、只有跑过才知道的东西。
5.1 模型倾向于丢弃"看起来冗余"但实际关键的重复信息
人写规则时,通常会保留重复出现的实体,因为"重复说明重要"。但模型自主压缩时,往往把重复信息当作冗余直接合并。问题在于,有些重复是跨轮次的一致性锚点——比如用户三次确认同一个地址,模型合并成一次后,后续如果出现地址变更,就失去了"之前确认过三次"这个上下文。
我的应对是:在动作空间里加一条软约束——"同一实体出现超过两次时,保留出现次数标记"。这条约束不是硬规则,而是作为信号提示模型。
5.2 模型对"工具调用结果"的压缩比人更狠
工具调用(比如查数据库、调API)返回的结果通常很长很结构化。人写规则时倾向于保留完整结构,只删冗余字段。但模型自主压缩时,经常把整个工具结果压成一句话结论。
这在多数场景下没问题,但当后续任务需要回溯原始数据时就会出问题。比如模型把"查询返回了20条记录,其中3条符合条件"压成"找到3条符合条件的记录",后续想再筛选就没数据了。
解决办法是把工具结果走"外置"而非"压缩"——移出上下文但存到外部,需要时召回。这也是为什么动作空间里"外置"这个动作不能省。
5.3 压缩时机上,模型比人更愿意"提前压缩"
人设计的阈值触发是"快满了才压"。但模型自主决策时,经常在上下文只用了50%时就开始整理。一开始我觉得这是浪费,后来发现提前压缩反而降低了后续推理的负担——模型在信息还新鲜时整理,比在快溢出时仓促压缩质量更高。
这个现象让我调整了预算策略:不再把预算当作"硬上限",而是当作"软目标",允许模型在预算充裕时主动整理。
5.4 不同任务类型下,模型压缩策略差异巨大
同一个模型,在代码任务和闲聊任务下的压缩行为完全不同。代码任务里,模型倾向于保留所有函数签名和变量名,压缩注释和空行;闲聊任务里,模型倾向于保留情绪和意图,压缩具体措辞。
这意味着不能用一套评估标准衡量所有场景。我后来按任务类型分别建了评估集,否则整体指标会被平均掉,看不出问题。
6. 这套机制不适合什么场景:边界与反例
任何技术方案都有边界。CLM这套"把动作空间交给模型"的思路,在下面几类场景里我建议慎用,甚至不用。
6.1 强合规、强审计场景
金融、医疗这类场景,上下文里的每一条信息都可能需要审计追溯。模型自主压缩意味着"决策过程不可完全解释",这在合规上很难过关。这类场景更适合规则明确、可解释的压缩策略,哪怕效率低一点。
6.2 极低延迟场景
模型自主决策压缩,本身要消耗一次推理。如果整个请求的延迟预算只有几十毫秒,这次额外推理就是不可接受的。这种情况下,预定义的静态压缩规则反而更合适。
6.3 上下文本身就很短
如果上下文从来没超过窗口的30%,那压缩本身就是伪需求。我见过一些团队在短上下文场景硬上CLM,结果增加了复杂度却没带来收益。先测量,再优化——确认上下文真的是瓶颈,再考虑这套机制。
6.4 任务目标频繁变化的场景
模型自主压缩依赖"任务目标"这个信号。如果任务目标本身在快速变化,模型很难判断什么该留。这种情况下,要么先把任务目标稳定下来,要么退回规则式压缩。
7. 从"两年工程白做"到"工程重心迁移"的再思考
回到标题那句话。我的真实看法是:"白做"是情绪化的表达,但"重心迁移"是真实的。
过去两年围绕上下文管理做的工程,大量集中在"手工设计压缩启发式"上。这部分工作的价值,在模型能自主决策压缩后,确实被稀释了。但同一批工程里积累的东西——对数据分布的理解、对任务成功率的评估方法、对边界情况的处理经验——这些并没有白费,它们恰恰是设计"动作空间和约束"的原材料。
真正需要转变的是心态:从"我要写一套规则让模型遵守",变成"我要设计一个环境让模型能好好决策"。前者是控制,后者是赋能。这个转变,比任何具体技术点都重要。
我在实际项目里的体会是,最难的不是让模型学会压缩,而是忍住不去替模型做决定。看到模型压缩得"不够好",第一反应总是想加一条规则。但每加一条规则,就把动作空间收窄一分,模型自主决策的价值就少一分。这个平衡点,得靠评估数据来找,不能靠直觉。
最后一个实用建议:如果你打算尝试这套机制,先别动生产环境。拿一个历史日志,离线跑一遍"模型自主压缩 vs 现有规则压缩",对比任务成功率和token消耗。如果模型自主压缩在两个指标上都不差,再考虑上线。如果只是token省了但成功率掉了,那说明动作空间设计有问题,回去调约束,而不是急着替换规则。