Hermes集成Jev引擎:删掉75%上下文但一句人话都没丢

Jev 把 Hermes 的上下文砍掉 75%,但每条对话原文一个字都没动!

你正在用的 AI 编程助手,可能每聊几轮就悄悄丢掉一部分记忆,而你还以为它全都记得!

本文拆解 Jev 上下文引擎与 Hermes Agent 的集成方式、实测数据、配置细节,以及一个多数人没注意到的代价:压缩率越激进,Agent 回头找东西的次数就越多。

人人都说摘要安全,Jev 却说删掉更稳

几乎所有 AI 编程助手处理长会话的方式都一样:上下文快满了,就叫一个大模型把旧内容总结成一段话,用总结替换原文。这个做法听起来天经地义——总结至少保留了“发生了什么”,总比直接删掉强。

但 TypeSafe AI 发布的 Jev 给出的答案完全相反:总结本身就是风险来源。一段总结是模型“写”出来的新文字,而写的动作就意味着可能编造。一个文件路径、一条报错信息、一个约束条件,在总结过程中随时可能被“概括”掉,哪怕它在后续步骤里至关重要。你得到的是一段读起来通顺的叙述,但它已经不是你真实发生过的历史了。

更隐蔽的问题是缓存。Hermes Agent 使用 Anthropic 的 prompt cache(提示缓存)来降低重复上下文的费用,缓存的前提是上下文前缀保持不变。摘要器每压缩一次,就把上下文中间一整段换掉,缓存前缀随之失效,后面每一轮对话都要重新按全价计算那些本该被缓存的 token。你省下的是上下文长度,花掉的是缓存折扣。

Jev 的做法是把“写”这个动作彻底去掉。它接收当前完整的对话状态,对每一条工具调用和它的返回结果分别回答两个是/否问题,答案以概率形式返回,代码根据概率做删除、截断或保留的决定。Jev 从头到尾不生成任何一个字的自由文本。

这就怪了——一个不会写字的模型,凭什么比会写字的模型更懂该留什么?

Hermes 留了一道口子,Jev 顺着它钻了进来

Hermes Agent 是 Nous Research 开发的开源 AI Agent 框架,它的上下文管理建立在一个叫 ContextEngine 的抽象基类之上。内置的 ContextCompressor 是默认实现,但 Hermes 允许第三方插件注册自己的 context engine 来替换它。

选择由配置驱动:在 config.yaml 里把 context.engine 设成插件名称,Hermes 就会把压缩工作交给这个插件。插件引擎永远不会自动激活,用户必须显式设置。这个设计堵住了一个隐患:多个引擎同时接管上下文会导致结果不可预测。

Jev 正是通过这道口子进入 Hermes 的。一个叫 hermes-jev-compaction 的第三方插件,实现了 Hermes 的 ContextEngine 接口,注册为名为 jev 的上下文引擎。插件的日志显示:Plugin 'jev-context-engine' registered context engine: jev,长会话开始通过 Jev 进行压缩。

但是,这个集成方式有一个需要留意的细节。外部引擎在 Hermes 里是“黑箱”:宿主只调用 should_compress() 和 compress() 两个方法,不会主动告诉引擎压缩发生了什么、丢掉了什么、统计数字是多少。插件开发者为此提交了一个 issue,请求 Hermes 核心团队传递 max_tokens 参数以及增加一个压缩生命周期事件,让插件能构建审计追踪,而不是靠日志抓取。

这就引出了下一个问题:Jev 在 Hermes 里具体怎么判断哪条工具调用该删?

两个问题一次问完,Jev 的评分机制在算什么

要理解 Jev 的判断力从哪来,得先看它的输入输出契约。普通大模型的接口是“上下文进,token 序列出”,Jev 的接口是“状态进,类型化答案和概率出”。它提供三种基本问题类型:Noul 返回一个 0 到 1 之间的是/否概率;Choice 从预设选项中选一个并给出分布;Score 在 2 到 10 级的刻度上打分并返回数学期望。

在 Hermes 的上下文压缩场景里,hermes-jev-compaction 用的是 Noul。它对每一条非“钉住”的工具调用问两个问题:第一个问题关于调用本身,知道这条调用被做过、并且知道它的输入参数,对当前任务还重要吗?第二个问题关于结果,结果的原文内容现在还必需吗,重新跑一次工具能不能替代它?

这两个问题的区分是关键。一条工具调用可能重要,但它的结果不一定重要。Agent 执行了一次单元测试,工具调用的参数告诉系统“它跑过哪些测试”,这个信息可能对理解当前状态有用;但测试输出的那几百行日志,如果测试全绿,具体每一行是什么就不重要了。Jev 可以给出“保留调用、截断结果”的判断,而不是二选一地全留或全删。

默认的保留阈值是 0.5。如果两个问题的概率都低于这个线,这条调用连同结果一起被移除;如果结果概率低于线但调用概率高于线,调用保留,结果被截断到前 300 个字符加一行说明。最近三次工具调用以及结果长度不超过 1500 字符的,被直接钉住,不经过评分。

那么问题来了:Jev 凭什么能准确判断哪条调用重要?

它能看到完整的对话。整个对话历史按时间顺序排列,每条工具结果被替换成一个极短的摘要说明——比如“ok, 4213 chars (omitted)”——但工具调用的输入参数、所有用户消息和助手文本都原样呈现。Jev 在做一个判断时,手里握着的是整个会话的全貌,而不是压缩后的残片。

这就引出了下一个问题:75% 的压缩率是怎么在不丢对话的情况下做到的?

砍掉的东西不消失,Hermes 还能把它们捞回来

真实编码会话里的上下文,绝大部分不是“人说的话”。它是工具产生的数据:文件读取返回的内容、终端命令的输出、搜索结果的文本。一个读了二十个文件的 Agent,它的上下文里可能有十几万字符是文件内容,但真正推动任务前进的,可能只是其中某个文件里的三行代码。

Jev 压缩的就是这部分。实测数据显示,278,924 字符的上下文被砍到 52,215 字符,削减 81%;另一个案例从 277,078 砍到 50,695,削减 82%。所有用户消息和助手消息都原样保留,被移除的只有旧工具调用的结果块。助手消息如果所有工具调用都被删了,文本本身还在,只是不再附带那些调用记录。

但是,删除是有代价的。如果 Jev 判断某条工具结果“不再需要”,而 Agent 后来恰恰需要它呢?

awesome-jev-compaction 这个项目给出了一个补救方案。它把上下文分成“冻结区”和“工作区”两部分:冻结区只增不减,工作区里低分的工具输出不直接消失,而是被移到一个外部存储里,在原来的位置留下一行指针——类似 [[elided id=r:8506e122 lines=1-21 tokens=364]]——并且给 Agent 一个 expand 工具。Agent 如果发现需要被移除的内容,调用 expand,取回的是原始文本的逐字节副本。

这个机制把“判断错误”的代价从“信息永久丢失”降级为“多跑一个工具调用”。而且因为 Jev 从不生成文字,被保留的内容永远是原始输入,不存在“模型在记忆里编造了一个不存在的细节”这种情况。

但是,expand 本身也是工具调用,也会占用上下文。如果一个任务里 Agent 反复 expand 被移除的内容,压缩就变成了来回搬运。那么真正的测试标准就不该是压缩率,而是压缩之后 Hermes 的任务完成质量。

基准数字很好看,但它在测什么

现有数据给出了几个关键数字。平均上下文减少 75%,对比标准摘要器的 55%。平均压缩耗时 5.6 秒,对比摘要器的 44.8 秒。选择模型的成本是每次压缩 $0.002。粗略估算,长任务中 token 用量减少 30% 到 40%,缓存 token 成本相应下降。

但是,这些数字衡量的是压缩本身的效率和效果,不是压缩之后 Hermes Agent 的表现。PolarUgle 提出了一个关键质疑:工具调用往往需要结合更早的决策和结果才能理解其意义,单独删除某条结果可能导致 Agent 丢失关键状态,重复已经做过的工作。这个担忧不是空穴来风——一个文件读取的结果,可能在十轮对话之后才体现出它的价值。

另一个角度来自 Hermes 自身的演进。一个叫 Lossless Context Management(无损上下文管理)的引擎已经作为 Pull Request 提交到 Hermes 核心仓库,它用基于 SQLite 的 DAG(有向无环图)来替代一次性压缩,同样承诺不丢失任何消息。这说明“不丢对话原文”正在成为 Hermes 社区的一条共识,而不只是 Jev 一个插件的选择。

但 Jev 路线的独特之处在于判断的粒度。摘要器是粗粒度的——一整段历史被一段文字替换,你无法选择只保留其中的某条调用。Jev 是逐条判断的,每条工具调用和它的结果独立评分,独立决定去留。这意味着压缩不是“一刀切在某个时间点”,而是“沿着时间线逐条筛选”。

Jev 自己的上下文窗口是 32k token。超过这个窗口的会话,它看到的就不是完整的上下文,而是经过适配压缩后的版本:工具输入先截断到 1000 字符,不够截到 200,再不够截到 60;长文本压缩成头部加尾部;最老的非钉住消息折叠成一行省略标记。Jev 的判断质量,很大程度上取决于这层适配的质量。

在 Hermes 身上,选择式压缩到底值不值

回到最实际的问题。一个跑长编码任务的 Hermes Agent,每天要经历多少次上下文压缩?假设每次会话压缩两到三次,每次压缩节省 13000 个 token 的输入,而 Jev 的判断成本是 0.002 美元——这个账算下来,Jev 在成本上的优势是压倒性的。

真正的代价不在钱上。代价在延迟和回合数上。tanjiro2401 在讨论中承认,如果 Agent 后来需要某条被移除的工具结果,它可能需要“做一次会话搜索”来找回上下文,这等于多花了一到两个回合。在编码任务里,一个回合可能意味着一次完整的模型调用,包含思考、工具选择、结果处理。如果压缩误判频繁发生,多出来的回合数可能抵消掉压缩节省的时间。

所以核心的判断标准不是压缩率,而是误判率。一条工具结果被正确移除,省下的 token 是纯收益。一条被错误移除、后来被 expand 找回的工具结果,多花了一个回合。一条被错误移除、而且没有被找回来的工具结果,可能导致 Hermes 基于不完整的信息做出错误决策,这个代价就无法用 token 来衡量了。

awesome-jev-compaction 记录了一个调优实验:在阈值 0.10 时,节省 1092 个 token,Agent 回头找被移除内容的次数是零;在阈值 0.35 时,多节省 168 个 token,但 Agent 回头找了三次。阈值提高意味着更激进的删除,节省的 token 增加了 15%,但误判次数从零变成三。这个微小实验暴露了核心权衡:压缩率和 Agent 连续性之间的平衡点,不在 Jev 的默认参数里,而在每个具体任务的实际行为模式里。

这也是为什么“记录每一次决策”比“一次性设定最优阈值”更重要的原因。每次 Agent 调用 expand 去取回被删除的内容,都是一次明确的信号:这个删除做错了。日志把这些信号收集起来,让阈值可以在离线状态下被重新评估和调整,不需要重新跑 Agent。

被删掉的那条路,还回得去吗

所以这件事的真相是:Jev 不是在教 Hermes “如何更聪明地总结”,它是在改变 Hermes 记忆的物理结构——从“一段会随时间变模糊的叙述”,变成“一份逐条留档、可以按需检索的账本”。摘要器给你的是压缩后的故事,Jev 给你的是删减后的原始档案。

但这份档案有一个绕不开的缺口。Jev 最多能看到 32k token 的状态,超过这个窗口的会话,它看到的就不是完整的上下文,而是经过适配压缩后的版本。一个跑了三个小时的 Hermes 编码会话,可能早就超出了 Jev 能一次性审视的范围。Jev 在判断“这条工具结果还重要吗”的时候,手里握着的对话已经是被适配步骤筛选过的版本,而不是全部。

那么,一个自己都看不全上下文的模型,它在判断“什么该删”的时候,置信度到底有多少?这个问题的答案,目前没有任何公开数据能回答。