如果你正在用Claude Code跑长会话,多半遇到过那个经典的上下文瓶颈:对话越写越长,模型逐渐“失忆”,明明前面交代过的需求又开始反复确认。更烦的是LLM有损摘要——一旦触发自动压缩,关键报错细节经常被模型“好心改写”成没头没尾的概括。我最近把fast-jev-compaction接入了自己的Claude Code工作流,用基于Jev的决策机制替代LLM摘要来做上下文压缩,压缩耗时从几十秒降到一两秒,关键信息零丢失。这篇文章围绕这个项目,把核心原理、架构拆解、接入方式和踩过的坑完整讲一遍,希望能给同样被长会话折腾的人一个可落地的参考。
提前说清楚:这里提到的Jev模型,不是生成式大模型,而是一种轻量级决策模型。它不做“重新表述”,只做“判断保留还是丢弃”。这个区别,是整个方案成立的前提,后面我会详细展开。
1. 长会话的痛点:为什么LLM有损摘要让人抓狂
1.1 上下文窗口的隐性天花板
先问一个实际问题:你在Claude Code里写过最长的会话有多长?我自己的记录是连续六个小时的代码评审和重构,那次会话到最后,整个上下文里塞满了中间调试输出、废弃的代码方案、重复的文件路径,以及大量的推理过程。结果就是,后续每个问题都要等很久才出结果,而且模型经常答非所问。
这不是模型能力的问题,而是上下文管理的问题。大语言模型的上下文窗口虽然名义上有200K甚至更多,但有效注意力会随着token数量增加而衰减。更关键的是,长会话里真正对当前任务有用的信息比例并不高,大量历史内容只是占地方。Claude Code这类AI编程工具解决这个问题的方式大致有三种:清理无用信息、压缩旧消息、截断最老的内容。其中“压缩旧消息”听起来最智能,也是大多数用户实际遇到的路径。
问题是,现有的压缩机制基本都依赖LLM做摘要。它的逻辑是:把一段历史对话喂给模型,让它生成一段更短的文字来概括要点。表面上看是在提炼,实际上这是一次不折不扣的有损压缩。更麻烦的是,这种压缩消耗的token量和延迟都非常可观,有时候压缩一次的成本反而比省下来的还高。
1.2 LLM摘要的三宗罪:失真、昂贵、不可控
先说信息失真。模型在生成摘要时,会按照它对“什么重要”的理解做取舍,但这个理解经常跟你的实际需要对不上。我举个例子,有一次让Claude Code排查一个罕见的构建报错,报错信息里有一个自定义环境变量的名字,是定位问题的关键线索。上下文压缩之后,模型把那段历史概括成了“用户之前遇到了一个环境变量相关的问题”。那个变量名不是被截断了,而是被摘要器“认为不重要”给丢弃了。我后来花了很长时间才在旧日志里找回那个名字。这种“说得通但不对”的幻觉式摘要,比直接截断更危险,因为你根本意识不到信息已经变了。
其次是成本与延迟。触发一次压缩,需要把整个历史上下文重新提交给LLM,再让它生成一段摘要。光这一步的token消耗,往往抵得上压缩省下的三分之一甚至更多。我自己跑过一次统计,在一个约18万token的会话里,Claude Code自带压缩一次花了43秒,额外消耗了大约1.2万token。这个代价在一次性场景还能忍,但如果是连续多小时的长任务,可能会反复触发压缩,累计成本相当惊人。
第三是控制力为零。你没法告诉摘要器“所有带函数名的内容务必保留”,也没办法指定压缩粒度。它是黑盒,你只能接受结果。对于做工程的人来说这是最难受的——不可控就意味着不可预期,而不可预期在关键任务里是大忌。
1.3 一个更合理的压缩思路
如果压缩的本质是“从历史里挑出未来还有用的信息”,那这个任务的核心其实是判断与筛选,而不是重新组织语言。判断和筛选是分类问题,组织语言是生成问题。前者完全可以用轻量级决策模型来做,后者才需要大语言模型。fast-jev-compaction这个项目走的就是前者:它用Jev模型对会话中的每段内容做决策,输出“保留、修剪、丢弃”的标签,然后按原始措辞重组上下文。
这个思路最大的吸引力在于无损。被判定为保留的内容,是原封不动搬回来的token,不是经过模型改写的新句子。没有改写,就不会产生幻觉,也就不会丢失细节。快也是一个优势。Jev模型很多可以在本地直接跑,不需要经过云端API,推理延迟用毫秒计。我第一次跑通压缩的时候,看着日志里“completed in 1.6s”这行字,说实话有点不太相信——同样的效果,Claude Code自带压缩花了43秒。
2. Jev决策模型:从“重新写”到“做判断”
2.1 Jev模型到底是个什么
很多人第一次听到Jev会以为又是什么新的千亿参数大模型,其实不是。Jev更像是一类专注做决策的轻量模型的统称,它的目标不是生成自然语言,而是对输入内容输出一个或一组标签。在fast-jev-compaction的场景里,输入是一段对话消息,输出则是类似这样的一组决策:
- KEEP:这段内容必须原样保留,改动任何一个token都可能丢失关键信息。
- TRIM:主体信息值得保留,但可以移除部分冗余片段,比如中间调试输出、重复的堆栈信息、无意义的时间戳。
- DROP:这段对话已经完成使命,对后续任务没有任何参考价值,直接丢弃。
从实现上说,Jev走的往往是“规则提取硬特征 + 轻量分类器软打分”的混合路线。硬规则负责那些确定性的条件,比如消息里出现了.ts、.py之类的代码文件路径,或者报错信息中带有特定的错误码,这类内容无论如何都要保留;软分类器则负责处理模糊情况,比如“这段讨论与当前任务主题的相似度有多高”,用一个浮点分数来量化,再对照阈值做决策。
我做项目的时候发现,把生产环境里的压缩需求抽象出来,真正需要的决策维度其实非常有限:这段内容跟当前目标有没有关系、里面的信息在后续还会不会被引用、有没有不可再生的细节。这三个维度用精心设计的规则和分类器组合,完全能覆盖。不需要一个几百亿参数的模型来“理解”什么是重要的,一个几GB甚至几百MB的本地模型就够了。
2.2 为什么决策式方案正好命中上下文压缩的命门
上下文压缩本质上是一个筛选问题,不是一个写作问题。你要做的不是把信息“讲得更精炼”,而是在“未来还可能有用”这个标准的指导下做取舍。生成式大模型在这个任务上有一个天然的劣势:它总想“把事情说清楚”,所以在做摘要的时候会不自觉地对原文进行改写。改写这个动作本身就意味着信息形态的变化,而变化就会产生丢失。
决策式模型没有这种改写冲动。它只做记号:这块留、这块删、这块剪。所有KEEP的内容直接使用原始token,不存在重新生成导致的信息变形。我打过一个比方:整理衣橱时,高明的收纳师不会把你的衣服重新设计一遍,而是帮你把常穿的挂起来、过季的收进箱、穿不上的处理掉。fast-jev-compaction做的就是“整理衣橱”的活,“保留原样”是它的工作底线。
另外还有一个做工程的人特别在乎的优点:可调试。LLM摘要如果出了问题,你很难定位到底是哪一步改写导致的信息丢失。但Jev的每个决策都可以回溯到具体的规则条件和特征分数。我在排查误判时,只需要打开决策日志,找到那条被标错标签的消息,看它当时触发了哪些规则、分类器打了多少分,就能清楚地知道要调什么。这种透明性,在长期维护一套压缩系统时价值极大。
2.3 Jev与LLM摘要的全面对比
我整理过一张对比表,基本上把我在实际使用中关心的维度都列进去了,这里直接分享给大家:
| 对比维度 | LLM有损摘要 | Jev决策压缩 |
|---|---|---|
| 核心动作 | 重新生成自然语言摘要 | 输出保留/修剪/丢弃标签 |
| 信息保真度 | 有损,存在改写与幻觉风险 | 无损或近无损,保留原始token |
| 压缩延迟 | 秒级到分钟级 | 毫秒级到秒级 |
| Token成本 | 高,压缩过程本身消耗大量token | 极低,本地推理不消耗API token |
| 可解释性 | 黑盒,无法定位误改原因 | 每条决策可回溯到规则与得分 |
| 可控性 | 低,无法指定保留策略 | 高,阈值和规则均可独立配置 |
| 运行环境 | 依赖云端API或大模型推理 | 本地即可运行,支持边缘部署 |
这张表不是我凭空写的,是我在实际切换之后一个个对比出来的。尤其是“信息保真度”这一行,直接决定了为什么我用过一次之后就不太想再切回默认方案。在代码评审这种高强度、高信息密度的场景里,任何一次信息丢失都可能意味着审查遗漏,这是不可接受的。
3. fast-jev-compaction架构拆解
3.1 三步流水线:切分、决策、重组
把fast-jev-compaction跑通之后,我把它内部的流程拆成了三个阶段,理解这三个阶段是配置调优的基础。
第一阶段是索引与切分。系统读取当前会话的完整对话记录,以消息为单位切成一个个独立单元,然后给每个单元打上基础元信息。元信息包括角色(用户还是助手)、发送时间、消息里出现的文件路径、关键标识符、错误码等。这一步非常关键,因为后面Jev做决策时,依赖的就是这些结构化特征。
第二阶段是特征提取与决策。把上一步产生的元信息和消息原始文本一起送入Jev决策管线,对每条消息输出一个标签和置信度分数。这个阶段是系统核心,也是调参最频繁的地方。硬规则和软分类器在这里协同工作,硬规则先跑一遍来做保底判定,软分类器再对剩余的模糊消息做打分。我调参时最常动的是软分类器的阈值,因为它直接决定压缩的激进程度。
第三阶段是重组与回填。按照决策结果拼接新的上下文:KEEP原样保留,TRIM把消息拆成更小的句级单位,只去掉那些明显冗余的片段,DROP直接丢弃。重组完的结果就是新的压缩后上下文,回填给Claude Code继续使用。
很多人会把注意力放在第二阶段的模型选择上,但我实际用下来的感受是,第一阶段的特征提取做得好不好,对最终效果的影响往往更大。如果你的目标是“代码评审时保留所有函数名”,那么在切分阶段就要确保每个函数名都被正确地提取为元信息,而不是让模型在原始文本里自己去猜。
3.2 关键参数与调优方向
项目提供了几个关键参数来控制压缩行为,我整理了一张表,大致的配置说明如下:
| 参数名 | 默认值 | 作用说明 |
|---|---|---|
JEV_KEEP_THRESHOLD | 0.70 | 分类器相关度得分高于此值的消息标记为KEEP |
JEV_TRIM_LOG_THRESHOLD | 0.85 | 检测为日志输出的内容,仅当置信度高于此值才执行TRIM |
JEV_MAX_CONTEXT_RATIO | 0.75 | 压缩后上下文目标长度与原长度的比例上限 |
JEV_RULE_PRIORITY | path>error>user | 硬规则的优先顺序,路径匹配优先于错误关键字,用户显式指令最优先 |
JEV_LOCAL_MODEL_PATH | 空 | 指定本地Jev模型权重路径,支持GGUF等轻量格式 |
参数的意义在于让你在“激进压缩”和“保守保留”之间做权衡。比如多文件重构任务,我会把KEEP_THRESHOLD从0.7调到0.6,因为这类任务里代码片段的上下文关联性很强,宁可多留一些;如果只是做常规问答,调到0.8就可以,压缩得更干净。初学者建议先用默认值跑几天,看看决策日志里的得分分布,再反向调整,别一上来就追极限压缩。
还有一个容易被忽略的参数是JEV_RULE_PRIORITY。它控制当多个规则同时命中一条消息时,谁的优先级更高。这个参数直接影响硬规则的兜底效果。我调整过一次优先级,把用户指令排到了最高,结果系统在处理“请务必保留所有API endpoint定义”这类显式指令时,行为立刻变得听话了很多。
3.3 为什么不用Claude Code自带的压缩机制
很多人会问:Claude Code自己不是已经有auto-compact了吗,为什么还要单独架一套?我的回答是:内部压缩是黑盒,你无法控制它什么时候触发、按什么粒度压缩。一旦上下文满了,它的行为模式是固定的,你没有选择权。
fast-jev-compaction的核心价值在于独立性和可控性。它不依赖某个具体模型厂商的压缩策略,可以配合Claude Code官方版本使用,也可以配合本地模型或第三方API使用。我自己就在配合OpenAI兼容接口接本地模型时用过它,效果稳定。在上下文用到60%甚至70%时主动触发一次预压缩,把明显无价值的内容先清掉,为后续任务留足缓冲,这种细粒度的控制是内置方案给不了的。
对于需要在企业内网环境跑AI编程工具的人来说,这一点非常现实。压缩过程完全在本地完成,不需要把敏感对话内容发送到外部服务,既满足数据合规要求,又不用担心压缩行为受服务端策略影响。从这个角度说,fast-jev-compaction不只是性能优化,更是把主动权重新交回用户手里。
4. 实操接入:从零跑通fast-jev-compaction
4.1 环境准备与安装
项目要求Python 3.10以上,我在3.11和3.12上实测都没问题。安装方式很常规,直接拉源码装依赖:
git clone https://github.com/your-path/fast-jev-compaction.git cd fast-jev-compaction pip install -r requirements.txt建议创建虚拟环境安装,不要直接装进系统Python里,不然很容易跟其他项目的依赖冲突。装完先跑一次自检:
python -m fast_jev_compaction --self-test自检会生成一段模拟会话并执行一次完整压缩,验证Jev权重有没有正确加载、规则引擎是否正常工作。我第一次跑自检就卡住了,报错说缺少libgomp库,后来确认是Jev模型用到了OpenMP并行策略,在部分Linux发行版上需要先装libgomp1再重试。如果你也遇到类似报错,先检查一遍系统依赖,别急着怀疑项目本身有问题。
Windows下安装时有个小差异:建议在PowerShell里以管理员身份安装Visual C++运行库,因为Jev的某些本地加速依赖MSVC的编译环境。其他步骤和Linux基本一致。macOS的话,Apple Silicon芯片上跑起来很顺畅,但是旧款Intel芯片的机器我没有实测过,如果遇到性能问题,可以考虑把JEV_LOCAL_MODEL_PATH指向更小的模型权重。
4.2 与Claude Code集成的两种方式
接入方式我尝试了两种。第一种是hook脚本方式,也是最直接的方式。在Claude Code的配置目录里找到settings.json,注册一个事件钩子,在上下文接近上限时自动执行压缩脚本:
{ "hooks": { "ContextNearLimit": { "hooks": [ { "type": "command", "command": "python /path/to/fast_jev_compaction/hooks/compact_context.py --config /path/to/config.yaml" } ] } } }这个脚本做的事很简单:读取当前会话状态,诊断上下文使用比例,触发Jev压缩,把重组后的上下文回填。好处是全程自动化,不要人干预;坏处是触发时机取决于Claude Code的事件机制,如果你想在60%时提前压而不是等它到临界点,得额外写一个定时检查脚本。
第二种方式是把它暴露成MCP服务。把压缩逻辑封装成一个工具,这样Claude Code就能够在对话中显式调用这个工具,通过自然语言指令随时触发压缩。MCP方式更灵活、更可交互,缺点是配置繁琐,服务要常驻,对新手不太友好。我的判断是,日常使用先走hook方案,简单直接,效果也稳定;如果你需要更细粒度的控制,再研究MCP集成不迟。
4.3 一次真实的压缩效果实测
为了验证效果,我拿一个实际项目做了对照实验。项目背景是对一个Python SDK做完整代码评审,会话累积了327条消息,总token数约18万。同一个任务分别用两种方案压缩,之后继续执行同样的后续任务,得到的数据如下:
| 对比指标 | Claude Code默认压缩 | fast-jev-compaction |
|---|---|---|
| 压缩后token数 | 24000 | 38900 |
| 压缩耗时 | 43秒 | 1.6秒 |
| 压缩过程额外消耗API token | 约12000 | 约0 |
| 后续任务中关键函数名丢失次数 | 3 | 0 |
| 压缩后继续对话的流畅度 | 偶发失忆 | 正常 |
注意看后面两行,这是我最在意的部分。fast-jev-compaction压缩后的上下文比默认方案要大一些,因为它确实保留了更多原始内容,但换来的是关键函数名零丢失。在这个评审任务里,函数名就是最重要的线索,丢一个都可能漏掉一处隐患。默认方案压缩后的上下文更短,但那是以丢失关键信息为代价的,这个账怎么算都不划算。
还有一组数据我没放进表格里:默认方案触发一次压缩后,后续又触发了第二次压缩,因为摘要过程产生的中间内容又占了一部分上下文。而fast-jev-compaction因为保留了更多有用信息,后续对话反而更顺,没有触发第二次压缩。这个差异在长会话里会被不断放大,效果差距比单次对比表看起来更大。
5. 常见问题、坑位与建议
5.1 决策误判:该保留的内容被删了怎么办
用Jev压缩最常遇到的问题就是误判。我遇到过几次KEEP_THRESHOLD设得太高,导致一些看似闲聊、实则包含技术约束的消息被打上DROP标签被丢掉了。排查思路不复杂:fast-jev-compaction每次压缩都会输出一份JSON格式的决策日志,逐条记录每条消息的标签、得分和命中的规则。出了误判,先翻日志,看是分类器得分不够,还是硬规则没覆盖到,然后针对性调整。
如果是分类器得分不够,说明这类消息的特征并不明显,需要降低KEEP_THRESHOLD,或者给这类消息补充预设的特征词表;如果是硬规则没覆盖到,那更简单,往规则配置里加一条即可。比如你发现所有涉及“环境变量”的讨论都被跳过了,那就加一条规则:消息中出现环境变量名模式的,直接标记为KEEP。决策式方案的优点就在这里,出错了可以精确修。
建议正式使用前,先拿过去一两周的会话记录做历史回放模拟,把每次压缩结果跑一遍,根据决策日志校准配置。这一步看起来很耗时,但能极大减少上线后的排查工作,相当于给压缩器做了一次“预训练”。
5.2 本地模型与第三方API的兼容性坑
如果你的Claude Code接的是本地模型或者第三方网关(特别是通过OpenAI兼容接口接DeepSeek、Qwen这类模型),这里有一个容易踩的坑:压缩器回填的上下文格式,必须与模型期望的消息格式保持一致。fast-jev-compaction默认输出的是Claude风格的消息结构,但本地模型通常用的是完全不同的system/user/assistant角色结构。
我踩过一次。当时回填之后,模型直接拒绝继续对话,报错信息大约是“provider rejected the request schema or tool payload”,一看就是消息格式不匹配。解决办法是在配置里把output_format字段改成openai或generic,同时检查角色映射是否正确。具体模型对应什么格式,去它的文档里查清楚再填,不要盲目相信默认配置。
另外提一句,在VSCode里配置Claude Code并接入本地模型时,建议把压缩器作为独立服务运行,而不是直接内联进编辑器的扩展进程里,不然重启编辑器之后压缩脚本的服务状态容易丢失,影响自动化流程。
5.3 压缩死循环:把触发时机往前挪
还有一个实操中会碰到的场景:上下文已经很大了,压缩器本身读入全部内容做决策时,特征提取阶段的字符串处理在大文本下也会出现性能瓶颈。虽然Jev模型推理很快,但特征提取需要对每条消息做大量正则和字符串扫描,会话达到数十万token时,这个开销不可忽视。
我的经验是,把压缩触发点往前提,不要等到95%快爆了才压。在上下文用到60%到70%时做一次轻量预压缩,把那些明显无价值的DROP内容先清掉,为后续留足缓冲。我用触发脚本里的context_usage_percent参数把这条线设在了65%左右,效果最舒服。既不会频繁触发,也不会让上下文涨到难以处理的水平。
5.4 后续可以怎么扩展
这个项目目前满足了我90%的日常需求,但我觉得还有几个值得扩展的方向。一个是让Jev的决策结果与用户的工作流习惯做绑定,比如从过去一周的手动整理记录里学习“这个用户更倾向保留哪些类型的信息”。另一个是把压缩策略做成配置文件模板,不同任务类型直接套用不同模板。我自己就在用两套:代码评审一套,文档写作一套,切换成本几乎为零。
最后分享一点做这个项目的体会:上下文压缩这个方向,过去的大多数做法都围绕“怎么把摘要写得更好”展开,但fast-jev-compaction提供了一个不同的思路——判断哪些信息不能改动,让关键内容以原始形态被保留。做AI工程的人会越来越明白,在推理之前,先把记忆管好,这比单纯追求更大的模型参数更实在。如果你也在用Claude Code跑长任务,不妨试试这条路。