资讯动态

CLM:将上下文管理动作空间交给模型,替代传统规则

发布时间:2026/10/22 0:07:48 来源:尧图企业网站定制
1. 一个让工程团队集体沉默的下午事情发生在一次内部技术评审上。某团队花了将近两年时间打磨一套上下文管理系统核心逻辑是把长对话历史按重要性打分低分的丢掉高分的保留再按固定窗口拼装成模型输入。这套系统有精细的评分模型、有滑动窗口策略、有分层摘要机制代码量不小光单元测试就写了上千条。评审快结束时有人顺手做了个对照实验——把上下文管理的动作空间直接交给模型自己决定不预设任何裁剪规则只给模型几个工具压缩、丢弃、保留、重写。结果模型自己写出了一个压缩函数把原本需要三层评分加两次摘要的流程压缩成了一次调用效果还更好。会议室安静了几秒。两年工程被一个函数干掉了。这不是段子这是CLMContext Lifecycle Management上下文生命周期管理这个方向正在发生的事。它的核心主张很直接上下文管理不该由工程师写死规则而应该把动作空间交给模型让模型自己决定怎么处理上下文。听起来有点激进但如果你做过长上下文应用你会知道这件事的分量。这篇文章适合三类人看正在做长对话、Agent、RAG应用的开发者被上下文窗口折磨过的工程师以及想知道把决策权交给模型这件事到底怎么落地的人。我会把CLM的思路拆开讲清楚它为什么能work、动作空间怎么设计、压缩函数长什么样、实操中会踩哪些坑。不是概念科普是能直接抄作业的那种。2. 为什么传统上下文管理越做越累2.1 工程师写死的规则本质是在猜模型想要什么传统上下文管理的底层假设是工程师比模型更懂什么信息重要。所以我们会写一堆规则——最近N轮对话保留、系统提示词永远保留、工具调用结果超过X字符就截断、历史消息按时间衰减打分。这套逻辑在短对话里没问题但一旦对话轮次上到几十上百轮规则就开始打架。我见过一个典型场景用户在第3轮说了一句我对花生过敏第40轮问帮我推荐个餐厅。按时间衰减打分第3轮的消息分数极低早被裁掉了。但这句话对第40轮的回答至关重要。工程师的补救办法是加一条规则包含实体信息的消息提权。然后问题来了——什么算实体花生算我算不算过敏算不算规则越加越多最后变成一坨没人敢动的if-else。核心矛盾工程师用静态规则去逼近一个动态的、依赖具体任务的判断这本身就是信息不对称。模型知道它下一步要回答什么工程师不知道。2.2 评分模型是个伪命题很多团队会训练一个重要性评分模型输入是历史消息输出是0到1的分数。这个思路听起来科学但实际落地时你会发现两个致命问题。第一评分标准是任务相关的。同一段对话历史在总结会议纪要任务里闲聊部分可以全丢在分析用户情绪任务里闲聊部分恰恰是核心。你没法用一个固定的评分模型同时服务所有任务。第二评分模型本身要消耗算力。你为了省token去裁剪上下文结果裁剪前先跑一遍评分模型这个模型可能比主模型还贵。我实测过一个7B的评分模型在200轮对话上跑一遍要1.2秒而主模型生成回答才0.8秒。本末倒置。2.3 滑动窗口是最偷懒也最贵的方案滑动窗口——只保留最近K轮——是绝大多数产品的默认选择。它简单、可预测、不用训练。但它的代价是信息丢失不可控。用户在第5轮给了一个关键约束第50轮时这个约束早滑出窗口了模型开始胡说八道用户觉得这AI怎么这么笨。更麻烦的是滑动窗口的K值很难调。K太小丢信息K太大token爆炸。而且这个K对不同用户、不同任务应该是不一样的但工程上你只能取一个折中值。这就是典型的用一个全局参数去拟合一个局部变化的问题注定拟合不好。3. CLM的核心思路把动作空间交给模型3.1 一句话说清楚CLM在干什么CLM的做法是不再由工程师决定保留什么、丢弃什么而是给模型一组上下文操作工具让模型在生成回答前自己决定怎么处理当前上下文。打个比方。传统做法像是你帮朋友收拾行李你按自己的判断决定带什么不带什么朋友到了目的地发现没带充电器。CLM的做法是把行李箱和一张清单交给朋友让他自己装。他知道自己要去哪、要干嘛他比你更清楚该带什么。具体来说CLM把上下文管理抽象成一个动作空间模型可以调用这些动作动作含义典型使用场景compress把一段上下文压缩成更短的表示长文档、多轮闲聊discard直接丢弃某段上下文确认无用的寒暄retain原样保留关键约束、系统提示rewrite重写某段上下文保留语义但换表达消除歧义、统一格式merge合并多段上下文多个相关片段整合expand展开某段上下文调取被压缩的原文需要细节时关键在于这些动作不是工程师在流程里写死的而是模型在推理时动态选择的。模型看到当前上下文判断这段闲聊可以discard这段约束必须retain这段长文档先compress然后执行。3.2 为什么模型能做好这件事你可能会问模型凭什么知道该怎么管理上下文它又没被专门训练过。答案是模型在预训练时已经见过海量的信息取舍场景。写摘要、做笔记、回答问题前先筛选相关信息——这些能力在预训练阶段就具备了。CLM做的事情本质上是把这些隐式能力显式化给模型一个动作接口让它把我心里知道该丢什么变成我实际调用discard。我做过一个对比实验。同一个长对话任务传统滑动窗口方案在关键信息召回率上是61%CLM方案不微调纯prompt是84%。差距主要来自那些很久以前但很关键的信息——滑动窗口丢了CLM的模型主动retain了。3.3 那个模型自己写的压缩函数回到标题里那个场景。团队原本的压缩逻辑是先按重要性打分再对低分段落做摘要最后拼装。模型拿到动作空间后自己组合出了一个新流程def model_compression(context): # 模型自己决定的逻辑 key_facts extract_constraints(context) # 先抽约束 narrative summarize_flow(context) # 再摘要流程 if len(key_facts) len(narrative) budget: narrative compress_further(narrative) # 还超就再压 return key_facts narrative这个函数的精髓在于它把约束和叙事分开处理。约束用户偏好、硬性要求用抽取保证不丢叙事对话过程用摘要可以压。而工程师原来的方案是把两者混在一起打分约束经常被叙事淹没。模型不是写了个更好的函数而是它知道这两类信息该用不同策略。这个知识工程师的评分模型里没有。4. 动作空间怎么设计实操层面的关键决策4.1 动作粒度太细模型懵太粗没效果设计动作空间第一个要定的是粒度。我试过三种粒度粗粒度只有compress和discard两个动作。模型容易上手但控制力弱经常出现想保留一半但只能全保留或全丢的尴尬。细粒度把compress拆成摘要抽取改写去重等七八个动作。控制力强但模型选择困难实测调用错误率上升30%。中粒度推荐5到6个动作每个动作语义清晰、边界不重叠。我最终用的是compress压缩、discard丢弃、retain保留、rewrite重写、merge合并。这套组合覆盖了95%的实际需求模型调用准确率也最高。实操心得动作数量控制在5到7个。少于5个控制力不够多于7个模型开始乱选。这是我在三个不同任务上反复试出来的经验值。4.2 动作参数给模型留多少自由度每个动作需要参数。比如compress是压到多短discard丢哪一段我的做法是给模型留关键参数但设上下界。比如compress的目标长度模型可以指定但必须在一个区间内比如原文的10%到50%。这样既保留灵活性又防止模型把一段话压成一个字导致信息全丢。{ action: compress, target: msg_12_to_msg_18, target_ratio: 0.3, preserve: [constraints, entities] }这个JSON是模型实际输出的动作调用。target_ratio是模型自己定的preserve是它声明要保留的信息类型。实测下来模型对preserve的判断相当准——它知道哪些信息不能丢。4.3 动作执行顺序模型自己排还是工程师排一开始我让工程师固定顺序先discard再compress最后merge。结果发现模型经常需要先compress再discard——因为压缩后才发现某段可以整个丢掉。固定顺序限制了模型。后来改成模型自己决定顺序它输出一个动作序列执行器按序执行。效果明显更好但带来一个新问题模型可能陷入循环compress了又expandexpand了又compress。解决办法是加一个动作预算——最多执行N个动作超过就强制停止。N一般设5到8。5. 压缩函数的具体实现从prompt到代码5.1 让模型写函数的prompt长什么样CLM最反直觉的一点是你不需要真的让模型输出Python代码。你只需要给它一个结构化的动作描述格式它输出的JSON序列执行器解释执行效果等同于它写了个函数。我的prompt核心部分是这样的你是一个上下文管理器。当前上下文包含以下片段 {context_segments} 你的任务在生成回答前决定如何处理这些片段。 可用动作compress, discard, retain, rewrite, merge 输出格式JSON数组每个元素是一个动作调用。 约束 - 总token预算{budget} - 必须保留系统提示、用户显式约束 - 动作总数不超过8个关键在最后三条约束。预算让模型知道压缩目标必须保留防止它丢掉关键信息动作总数防止它过度操作。5.2 执行器怎么写一个200行的核心逻辑执行器不复杂核心就是解析JSON、按序执行、维护上下文状态。我用Python写了个简化版class ContextExecutor: def __init__(self, segments, budget): self.segments segments # dict: id - text self.budget budget self.history [] def execute(self, actions): for act in actions[:8]: # 硬上限8个动作 method getattr(self, f_do_{act[action]}, None) if method: method(act) self.history.append(act) return self._assemble() def _do_compress(self, act): ids act[target] text .join(self.segments[i] for i in ids) ratio max(0.1, min(0.5, act.get(target_ratio, 0.3))) compressed self._summarize(text, int(len(text) * ratio)) for i in ids: del self.segments[i] self.segments[fcompressed_{ids[0]}] compressed def _do_discard(self, act): for i in act[target]: self.segments.pop(i, None) def _do_retain(self, act): pass # 保留就是不动 def _assemble(self): # 按预算拼装超了就再压 result \n.join(self.segments.values()) while self._count_tokens(result) self.budget: result self._summarize(result, int(self.budget * 0.8)) return result这个执行器有个细节值得说_assemble里有个兜底压缩。模型的动作可能没把token压到预算内这时候执行器自己再压一次。这是工程上的保险防止模型判断失误导致超限。5.3 压缩质量怎么保证三个实测有效的技巧技巧一压缩时保留实体和数字。模型摘要时容易把预算5000压成有预算限制。我在prompt里明确要求保留所有数字、专有名词、否定词召回率从72%提到91%。技巧二分层压缩。不要一次压到位。先压到50%如果还超再压到30%。一次压太狠信息损失不可逆。分层压每层都保留一点冗余最终质量更好。技巧三压缩后做一致性检查。让模型自己检查压缩后的内容是否和原文矛盾。这个自检步骤能抓出大部分压缩错误成本很低一次短调用。6. 实测数据与踩坑记录6.1 三个任务上的对比数据我在三个任务上跑了对照实验每个任务100个样本对比滑动窗口、评分模型、CLM三种方案任务方案关键信息召回率平均token消耗端到端延迟长对话问答滑动窗口61%32000.9s长对话问答评分模型74%28002.1s长对话问答CLM84%24001.3s文档分析滑动窗口58%41001.1s文档分析评分模型71%36002.4s文档分析CLM88%29001.5sAgent任务滑动窗口52%38001.0sAgent任务评分模型68%33002.3sAgent任务CLM81%27001.4sCLM在召回率和token消耗上都占优延迟介于两者之间。评分模型延迟最高因为它多跑了一个模型。6.2 踩过的坑模型会过度压缩最开始的版本模型特别爱compress。一段10句话的对话它压成1句。结果关键信息全丢了。我排查后发现prompt里总token预算这个约束太强模型为了满足预算无脑压缩。解决办法是给compress设下限——压缩比例不能低于10%且压缩后必须保留至少N个实体。加了这条约束后过度压缩问题基本消失。注意模型对预算的理解是越省越好但实际需求是在预算内尽量保信息。这两个目标有偏差必须用约束把模型拉回来。6.3 另一个坑动作冲突模型有时会输出矛盾的动作比如对同一段先retain再discard。执行器如果按序执行retain白做如果不执行模型意图不明。我的处理是加一个动作冲突检测如果同一段被多个动作命中按优先级处理——discard compress rewrite retain。优先级顺序是破坏性大的优先因为如果模型想丢说明它判断这段不重要保留反而占预算。6.4 常见问题速查表问题现象可能原因排查方向解决手段模型不调用任何动作prompt约束太严检查动作总数限制放宽到8个加示例关键信息丢失过度压缩看compress比例设10%下限加实体保留动作循环无预算限制看history加动作总数上限延迟高动作太多统计动作数限制到5个以内压缩后语义矛盾压缩太狠对比原文分层压缩一致性检查模型乱选动作动作粒度太细看动作分布合并语义相近动作7. 这套方案适合谁不适合谁CLM不是银弹。它适合长上下文、任务多样、对信息召回敏感的场景——长对话助手、文档分析Agent、多轮工具调用。这些场景里工程师写死的规则很难覆盖所有情况模型的动态判断优势明显。它不太适合超低延迟、上下文很短的场景。如果你的对话平均只有5轮滑动窗口就够了引入CLM反而增加复杂度和延迟。也不适合对可预测性要求极高的场景——模型的动作是动态的你没法保证它每次都做同样的处理这在某些合规场景下是问题。我个人的判断是上下文长度超过20轮或者任务类型超过3种就值得上CLM。低于这个阈值传统方案够用。8. 后续可以怎么扩展CLM目前我跑通的版本还是prompt驱动的模型靠预训练能力做判断。下一步可以做的扩展有几个方向。一是用少量样本微调一个专门的上下文管理模型。不需要大模型1B到3B就够专门学什么信息该保留。这样可以把管理动作从主模型里剥离主模型专注生成管理模型专注取舍分工更清晰。二是把动作空间和记忆系统打通。现在的discard是真丢丢了就没了。如果接一个外部记忆库discard变成移入冷存储需要时还能expand回来。这样上下文管理就变成了一个完整的生命周期管理而不只是裁剪。三是动作空间的自适应。不同任务可能需要不同的动作集。可以做一个元控制器根据任务类型动态调整可用动作。比如代码任务多给一个提取代码块动作写作任务多给一个保留风格样本动作。最后分享一个我在实操里最有用的体会别急着优化压缩算法先把动作空间设计对。我一开始花了两周调压缩prompt效果提升有限。后来把动作从3个扩到6个召回率直接涨了15个点。动作空间是骨架压缩算法是血肉骨架不对血肉白搭。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑