资讯详情

告别LLM摘要幻觉:用Jev决策重构上下文压缩

📅 2026/10/29 8:16:37 | 华诺云谱 👁 阅读
告别LLM摘要幻觉:用Jev决策重构上下文压缩
1. 当上下文压缩开始说谎一个被忽视的工程问题用Claude Code写代码的人大概率都遇到过这样的场景一个稍大的重构任务聊到三四十轮模型突然开始失忆——前面明确说过的接口约定它记不住了刚改过的函数名它又用回了旧的甚至把已经删掉的依赖重新引入。你以为是模型能力不行其实很多时候问题出在上下文压缩这一环。主流做法是让LLM自己去做有损摘要把历史对话喂给模型让它总结一下重点。这个方案听起来优雅实际用起来问题一大堆。摘要是有损的模型在压缩时会丢掉它认为不重要的细节而这些细节往往正是后续任务的关键约束。更麻烦的是摘要过程本身会引入幻觉——模型会脑补一些没发生过的约定或者把两个不同阶段的决策混在一起。等你发现的时候代码已经跑偏了。fast-jev-compaction这个项目提出的思路很有意思抛弃LLM有损摘要改用Jev决策来重构上下文压缩。核心主张是上下文压缩不应该是一个生成任务而应该是一个决策任务——判断哪些信息该保留、哪些该丢弃、哪些该折叠这个判断过程可以用更轻量、更确定性的机制来完成而不是交给一个会自由发挥的LLM。这篇文章我会从工程实践的角度把这件事拆开讲清楚为什么LLM摘要会出问题、Jev决策到底在决策什么、fast-jev-compaction的压缩流程是怎么设计的、实际接入Claude Code时要注意哪些坑。如果你正在用Claude Code做长期项目或者自己在搭Agent的记忆系统这篇应该能帮你少走不少弯路。2. LLM有损摘要为什么在长任务里必然翻车2.1 摘要的本质是信息丢弃而丢弃标准不可控先想清楚一件事上下文压缩的本质是什么是把一个长度为N的token序列映射成一个长度为MM远小于N的序列同时尽量保留对后续任务有用的信息。这里的关键词是有用而有用是一个依赖未来的判断——你现在不知道后面会问什么怎么知道哪些信息该留LLM摘要的做法是让模型基于常识和当前语境去猜。它会把用户说要用TypeScript严格模式这种约束压缩成用户偏好TS把接口返回字段是user_id不是userId这种细节直接丢掉。前者还算能接受后者一旦丢了后面生成的代码就是错的。我实测过一个案例一个涉及数据库迁移的任务前面明确说了不要动production库的schema聊到第20轮让模型写迁移脚本它直接生成了ALTER TABLE语句。回头看摘要记录那句约束在第三次压缩时就被优化掉了。这不是模型不听话是摘要机制本身就在做有损丢弃。2.2 摘要幻觉模型会编造没发生的共识比丢信息更隐蔽的问题是摘要幻觉。LLM在生成摘要时会倾向于让摘要读起来连贯、合理于是它会自动补全一些逻辑上说得通但实际没发生的内容。举个真实的例子。一个前端项目里用户先说了用Vite后来说算了还是用Next.js吧再后来又改回其实Vite也行。三轮压缩之后摘要变成了用户最终确定使用Next.js。模型把中间那次反复当成了最终决策因为它无法判断哪次是最终态。后续所有代码都基于错误的框架假设生成。这类问题的根源在于LLM摘要是一个生成任务生成任务天然带有创造性。你让它总结它就会为了让总结好看而做平滑处理而工程约束恰恰是不能被平滑的。2.3 压缩成本被严重低估还有一个常被忽略的点LLM摘要本身要消耗token和时间。每次压缩都要把完整历史喂进去输出一份摘要这个过程的成本可能比你想的高。在一个高频交互的Agent场景里压缩调用可能占到总调用量的20%以上。而且压缩是串行的——你必须等摘要生成完才能继续这直接拖慢了响应。所以问题的本质是用LLM做压缩等于用一个不确定的、昂贵的、会幻觉的组件去处理一个需要确定性、低成本、零幻觉的工程问题。这个错配就是fast-jev-compaction想解决的。3. Jev决策机制把总结换成判断3.1 Jev在这里扮演什么角色先说清楚Jev在这个项目里不是一个更小的LLM而是一套决策框架。它的核心思想是上下文压缩不需要理解内容只需要对每个信息单元做出保留/丢弃/折叠的分类决策。打个比方。LLM摘要像是让一个人读完一整本书然后写读后感读后感里必然有他的主观取舍。而Jev决策像是给书里每一页贴标签这页是核心设定、这页是临时草稿、这页是已废弃方案。贴标签不需要理解全书只需要按规则判断每一页的属性。这个思路的工程价值在于决策是可枚举、可验证、可复现的。给定同样的输入和规则Jev会做出同样的决策不会今天保留明天丢弃。这对Agent的稳定性至关重要。3.2 决策的输入信息单元的结构化Jev决策的第一步是把原始对话拆成信息单元。不是按句子拆而是按语义角色拆。常见的单元类型包括单元类型说明默认处理策略约束Constraint用户明确的技术选型、禁止事项永久保留决策Decision已确认的方案选择保留最新态折叠历史态事实Fact项目结构、接口定义、字段名保留冲突时标记过程Process试错、调试、中间讨论可折叠为一行摘要噪音Noise寒暄、确认、重复表述直接丢弃这个分类不是让LLM去做的而是通过规则轻量分类器完成。规则部分处理格式明确的比如不要用X这种否定句分类器处理模糊的。关键是分类器只做分类不做生成所以不会幻觉。3.3 决策的输出压缩后的上下文骨架Jev决策的产出不是一段摘要文本而是一个结构化的上下文骨架。它长这样[约束区] - 语言: TypeScript strict - 禁止: 修改production schema - 框架: 最终确定 Vite [事实区] - 接口: GET /api/user 返回 {user_id, name} - 文件: src/utils/format.ts 已存在 [过程区] - 第3-8轮: 讨论框架选型最终Vite - 第12轮: 修复了一个日期格式化bug注意这里没有总结只有归类后的原始信息。约束就是约束原文事实就是事实原文过程才做折叠。这样做的结果是关键信息零损失只有过程性内容被压缩。3.4 为什么这比LLM摘要更可靠对比一下两种方案在几个维度上的表现确定性Jev决策给定输入必得同输出LLM摘要每次可能不同。幻觉风险Jev不生成新内容只归类LLM会脑补。成本Jev的分类器可以很小甚至纯规则LLM摘要要跑大模型。可调试Jev的决策可以打日志、可以回溯为什么这条被丢了LLM摘要是个黑盒。速度Jev可以做到毫秒级LLM摘要要秒级。这不是说Jev在所有场景都碾压LLM。对于把一段长文压缩成一段通顺的话这种任务LLM确实更强。但上下文压缩的目标不是通顺是保真。在保真这个目标下决策框架天然优于生成模型。4. fast-jev-compaction的压缩流水线拆解4.1 整体流程从原始对话到压缩上下文fast-jev-compaction的流水线大致分四步我按实际接入时的顺序讲切分把对话历史按轮次和语义边界切成信息单元。分类对每个单元打上类型标签约束/决策/事实/过程/噪音。决策根据类型和冲突规则决定保留、折叠还是丢弃。重组把保留的单元按区域重新拼装成压缩上下文。这四步里第一步和第四步是纯工程第二步是分类第三步是规则引擎。整个流程不涉及任何生成式模型所以延迟极低。4.2 切分阶段语义边界怎么找切分是最容易被低估的一步。切得太碎单元之间失去上下文切得太粗一个单元里混了约束和噪音分类就难做。实践中比较稳的做法是按意图转换切分。一次意图转换的标志包括用户提出新要求、模型给出方案、用户确认或否定、话题切换。这些边界通常有明显的语言信号比如好的那就这样、等等改成、另外、回到刚才那个问题。我自己的经验是切分规则不要追求完美宁可稍微碎一点。因为碎了的单元在重组阶段还能合并但一个混了多种意图的大单元分类阶段就会出错而且错了很难修。4.3 分类阶段规则和分类器怎么分工分类是整个流水线里最需要调优的部分。我的建议是规则优先分类器兜底。规则能覆盖的情况其实比想象的多。比如包含不要、禁止、必须、一定要的句子大概率是约束。包含就用、确定、最终的大概率是决策。包含具体字段名、路径、函数签名的大概率是事实。纯确认性的好的、收到、嗯直接判噪音。规则覆盖不到的模糊情况再交给一个轻量分类器。这个分类器可以是一个小模型也可以是一个基于关键词权重的打分器。关键是它只输出类别不输出内容所以不会引入幻觉。提示分类器的训练数据可以从你自己的历史对话里标注。标个几百条效果就比通用模型好很多因为你的项目有自己的语言习惯。4.4 决策阶段冲突消解是核心难点决策阶段真正难的不是保留还是丢弃而是冲突消解。同一个约束被用户改了三次保留哪个同一个字段名在两个阶段不一样信哪个fast-jev-compaction的处理原则是时间戳优先 显式覆盖标记。具体来说对于决策类永远保留最新的一次历史决策折叠成一行曾考虑X最终Y。对于事实类如果出现冲突两个都保留但标记冲突让后续模型自己判断而不是替它决定。对于约束类如果新约束和旧约束矛盾保留新的但把旧的移到已废弃约束区不直接删。这个设计很关键。不直接删意味着信息可追溯出问题时能查到这条约束是什么时候被覆盖的。很多压缩方案为了省token直接删结果调试时完全不知道模型为什么做了某个选择。4.5 重组阶段区域化拼装重组不是简单拼接而是按区域组织。推荐的区域顺序是约束区 → 事实区 → 决策区 → 过程区。这个顺序有讲究约束和事实是硬信息放前面让模型优先看到过程是软信息放后面模型需要时才参考。每个区域内部按时间倒序排列最新的在前。这样模型读到的第一眼就是最新状态减少它被历史信息误导的概率。5. 接入Claude Code时的实操细节与踩坑5.1 压缩触发时机别等爆了才压最常见的错误是等上下文快满了才触发压缩。这时候历史已经很长切分和分类的成本都上去了而且压缩本身要占token容易触发二次压缩。我的做法是按轮次触发而不是按token触发。比如每8轮做一次增量压缩把已经稳定的历史比如5轮之前的压掉最近5轮保持原样。这样每次压缩的量都不大延迟可控而且最近上下文永远是完整的。5.2 和Claude Code的上下文管理怎么配合Claude Code本身有自己的上下文管理机制fast-jev-compaction要做的是在它之上做一层预处理。具体接入方式有两种一种是前置拦截在把历史发给Claude Code之前先过一遍Jev压缩把压缩后的骨架作为上下文传入。这种方式对Claude Code透明改动最小。另一种是旁路注入保持Claude Code原有上下文额外注入一份Jev压缩的约束提醒。这种方式适合不想动原有流程、只想加一层保险的场景。实测下来前置拦截效果更好因为从源头就控制了上下文质量。但要注意压缩后的骨架要保留足够的格式信息别让Claude Code看不懂。5.3 分类器的冷启动问题刚接入时没有标注数据分类器效果差怎么办我的建议是先纯规则跑一周把规则判错的case记下来攒够一两百条再训分类器。这一周里规则可能只有70%准确率但比LLM摘要的不可控要好得多。另外规则要写成可配置的别硬编码。因为不同项目的语言习惯差别很大A项目里就这样是确认B项目里可能是犹豫。把规则外置成配置文件换项目时改配置就行。5.4 一个真实的翻车案例我踩过最坑的一次是分类器把一条否定约束判成了噪音。用户说这个字段不要加索引分类器看到不要以为是寒暄直接丢了。结果后面生成的迁移脚本给这个字段加了索引上线前才发现。复盘原因那条约束的表述太口语化规则没匹配上分类器又没见过类似样本。修复方式是加了一条规则任何包含不要、别、禁止且后面跟技术名词的句子一律判约束宁可误判也不放过。误判的代价是多留一条信息漏判的代价是线上事故这个权衡很清楚。6. 效果验证怎么知道压缩没压坏6.1 用约束召回率代替压缩率大多数人评估压缩方案看的是压缩率——压到原来的百分之多少。这个指标是错的。压缩率再高把关键约束压没了也是白搭。正确的指标是约束召回率压缩后的上下文里原始对话中出现过的所有约束有多少被保留了。这个指标应该追求100%一条都不能丢。事实召回率可以放宽到95%以上过程信息无所谓。我自己的验证方法是准备一组带标准答案的对话人工标出所有约束然后跑压缩看召回率。低于100%就回去查是哪条规则漏了。6.2 端到端验证让模型做记忆测试光看召回率还不够要看模型实际用起来怎么样。方法是设计一组依赖早期约束的任务先聊20轮建立约束然后问一个必须用到早期约束的问题看模型答不答得对。比如前面说所有API都要加rate limit聊20轮后让模型写一个新接口看它加不加。加了说明约束保住了没加说明压缩出了问题。这种测试比看指标直观得多。6.3 压缩日志出问题时能回溯一定要给压缩过程打日志。每条信息单元被分成了什么类、做了什么决策、为什么都要记下来。出问题时你能直接查到这条约束在第几次压缩时被折叠了。这个日志平时看着没用一旦出事故就是救命稻草。我现在的做法是日志保留最近30天的压缩记录按会话ID索引查起来很快。7. 这套思路还能往哪扩展fast-jev-compaction的核心洞察——压缩是决策不是生成——其实可以推广到很多场景。比如Agent的长期记忆管理。现在很多方案是把记忆丢给向量库检索时按相似度召回。但相似度召回同样有该记的没记住的问题。如果用Jev决策的思路把记忆按约束/事实/过程分类存储检索时按类型优先级召回效果会稳很多。再比如多Agent协作时的信息传递。Agent之间传消息如果每条都完整传上下文爆炸如果让LLM摘要又会失真。用决策框架做一层信息路由只传对方需要的类型是个值得试的方向。还有一个我觉得很有潜力的点把压缩决策和代码结构对齐。比如约束对应到项目的lint规则事实对应到类型定义过程对应到git历史。这样压缩就不是在压缩文本而是在维护一份和代码同步的项目状态。这个方向我还在试目前看思路是通的但工程量大适合有长期项目需求的团队做。回到最开始那个问题为什么Claude Code聊久了会失忆因为它在用生成模型做决策任务这个错配是结构性的。fast-jev-compaction给出的答案不一定是最优解但它指出的方向——把压缩从生成问题重新定义为决策问题——我认为是对的。至少在我自己的项目里换成决策式压缩之后那种模型突然忘了约定的情况基本没再出现过。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑