测试环境:Windows 11,Claude Code(需 2.1.274+,函数钩子抢先体验功能),winnow 0.5.0、fast-jev-compaction 0.3.0,2026-09-27 ~ 10-10。
Claude Code 的上下文是消耗品:一次 Read 一个大文件、一次 grep 满屏结果,几万 token 就进去了,而其中大半跟当前任务无关。
TypeSafe 的 Jev 能回答”这个东西跟当前任务有关吗”这类判断题,而且一次只要一秒左右、成本几乎可以忽略。于是有人拿它做了两个 Claude Code 插件,我装了跑了 13 天:
- Winnow:东西进上下文之前先过一遍筛子
- fast-jev-compaction:压缩上下文时,用 Jev 删掉不再需要的工具调用,而不是让模型写一份摘要
先说清楚两者的分工
| Winnow | fast-jev-compaction | |
|---|---|---|
| 什么时候动手 | 每个大的 Read / Bash / Grep 结果进上下文之前 |
上下文用到 60% 触发压缩时(/compact 也走它) |
| 动什么 | 把结果里”不相关”的块换成一个占位 stub | 删掉历史里”不再需要”的工具调用和结果 |
| 不动什么 | 磁盘文件、命令本身、Claude Code 自己的会话记录 | 你和助手说的话,原文、原顺序全部保留 |
| 对谁提问 | Jev:这一块对当前任务需要吗? | Jev:这次调用还要留吗?它的完整输出还要留吗? |
一个在门口拦,一个在清库存。都依赖同一个开关 CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1,也都需要一把 TypeSafe 的 Key。
Winnow:给每个大输出过一遍筛子
它怎么工作
每个超过 1500 字的 Read / Bash / Grep 结果,会被切成约 25 行一块,每一块向 Jev 问一个问题:”对当前任务,这一块需要吗?”拿到概率后:
- 概率低于 0.1 → 整块隐藏,换成 stub
- 介于 0.1 ~ 0.5 之间(不确定)→ 保留
- 判断这份输出里有报错 → 一块都不隐藏
stub 长这样(官方示例):
1 | [winnow] Lines 41-188 (148 lines) hidden: judged unlikely to matter for the current task (relevance <= 0.22). |
隐藏的原文完整存在本地 ~/.winnow/cache/,助手需要时调 winnow_recall 取回,所以是”暂时不花 token”,不是”丢了”。
它还偷偷做了另一件事
Winnow 有第二个钩子,在你每次提问时触发:把项目的记忆文件拿去和你的问题比对,挑最相关的几份(默认最多 3 份、共 8000 字符)直接塞进这轮对话,省得助手再去 Read。
13 天的真实数据
winnow stats 的输出(2026-09-27 装上,到 10-10):
| 指标 | 数值 |
|---|---|
| 被判断的输出 | 2286 次 |
| 实际被改写(隐藏了内容) | 143 次(6.3%) |
| 原样放行 | 2143 次 |
| 估算省下的 token | 约 9.8 万(39 万字符) |
| 判断本身的耗费 | 约 885 万输入 token,折合约 $0.37(估算) |
| 单次判断平均耗时 | 1.14 秒 |
| 提问时注入记忆文件 | 234 次 |
| 被隐藏后又被召回 | 45 / 143(31%) |
放行的 2143 次,原因分布是:
| 为什么没动 | 次数 |
|---|---|
| Jev 认为每一块都需要 | 1761 |
| 值得隐藏的比例太低(不足 20%,不值得改写) | 163 |
| 输出里有报错,按规则不动 | 218 |
| 判断请求本身失败 | 1 |
怎么读这组数字
它很保守,这是设计出来的。
94% 的输出原样放行,真正动手的只有 6%。这就是上面那两条安全规则的结果:不确定就保留、有报错就不碰。
便宜,但收益不大。
13 天省下约 10 万 token,判断花了不到 4 毛美元。成本几乎可以忽略,但也别指望它让你的额度翻倍。
31% 的召回率不能忽略。
143 次隐藏里有 45 次后来被助手要了回去,约三分之一的判断被”撤销”了。召回不一定意味着判错(助手也可能只是谨慎),但每次召回都多一轮工具调用,说明筛子还不够准。官方的建议是先开 WINNOW_MODE=shadow 跑一周,只记录不改写,看决策合理了再切到 active。
每个大输出多等一秒。
按平均耗时粗算,13 天累计约 43 分钟,不显眼但确实存在。
几个细节
- 后台有个常驻小服务(
127.0.0.1:47311)负责调 Jev,空闲 45 分钟自动退出。每次调用的开销是 16 毫秒,而不是每次起一个 Python 进程的 381 毫秒。 - 我关掉了摘要(
WINNOW_SUMMARY=0)。因为摘要要用 Anthropic 的 API Key,订阅登录的 Claude Code 没有这个。关掉后一切照常,只是 stub 里没有那段话。 - 如果没有 Jev 的 Key,可以设
WINNOW_JUDGE=adapter,改用 Claude Haiku 4.5 来回答同样的问题,但概率没有校准过,且走 Anthropic 账户计费。 - 常驻服务会占住启动它那个会话的工作目录:要删那个目录,得先
winnow serve --stop。
fast-jev-compaction:用”删”代替”写摘要”
传统压缩的问题
Claude Code 自带的压缩,是让模型把前面的对话写成一份摘要。摘要是有损的:一个文件路径、一条确切的报错、一个约束、一条命令,都可能在摘要里消失,而它们恰恰是后面需要的。
这个插件换了个思路:不改写任何东西,只删。 助手和你的话原样保留、顺序不变,只有”工具调用 + 工具结果”才是候选,Jev 判断哪些已经不再需要。
它怎么决定删什么
- 每个
tool_use和它的tool_result按 id 配对。第一条消息和最新的 6 条消息被钉住,不动。 - 发给 Jev 的
state是到目前为止的整段对话,旧的在前;每个工具结果被替换成一行说明(如ok, 4213 chars (omitted)),工具的输入和所有文字原样保留。超过 25k token 的话分阶段裁剪。 - 对每一个没被钉住的调用,问两个是非题:这次调用还要留吗?它的完整结果还要留吗?
- 按阈值 0.5 决定:
| 判断 | 处理 |
|---|---|
| 结果要留 | 调用和结果都保留 |
| 结果不要留、但调用要留 | 保留调用,结果只留开头 300 个字符加一行说明 |
| 都不要留 | 调用连同结果整个删掉 |
什么时候触发、什么时候放弃
- 上下文用到 60% 时自动请求压缩,手动
/compact也走它。 - 这些情况会退回 Claude Code 自带的摘要:Jev 请求失败、返回格式不对、没有 Key、历史太长塞不进 25k、或者**预计删掉的比例不到 25%**。
- 结果会用一个提示告诉你:
kept N/M messages, no summary (…)表示这次用的是删减版,fallback to built-in summary (…)表示退回了。
它做不到的
- 只能删工具调用和结果。一个纯聊天、几乎没有工具调用的长对话,它帮不上忙,会直接退回内置摘要。
- 旧的工具结果删掉后,信息就真没了,助手要么靠剩下的 300 个字符,要么重新执行一遍工具。
- token 数量是估算出来的,不是真分词。
- 每次请求都会把完整的 state 重发一遍,历史越接近上限,每个批次的开销越高(但按 Jev 的价格,25k token 一次也就约 0.1 美分)。
我这边的实测
做法:准备 5 个互不相关的日志文件(各约 9KB),连续 5 轮让助手 Read + Grep 找出一个标记值,然后手动 /compact。
| 项目 | 结果 |
|---|---|
| 工具调用 | 10 次,其中最新 2 次被钉住不动,其余 8 次交给 Jev 判断 |
| Jev 的判断 | 8 次**全部判”可删”**(调用保留概率 0.09 |
| 压缩结果 | 消息 35 条 → 19 条,缩减 **91%**(插件自报),没有生成摘要 |
| 耗时 | 一次 Jev 请求 1.1 秒,整个压缩 1.1 秒;Claude Code 自带的摘要流程完全没有运行 |
| 发给 Jev 的内容 | 约 2043 token,一次请求搞定 |
留意这是最理想的场景:5 轮问题互不相关,每轮的答案都已经写在助手的回答里,被删掉的只是用完了的原始日志。更难的情况是后面的轮次还要回头用前面的输出,这种我没测,不能从这次结果外推。
装法和判断是否生效
两个都要先在 ~/.claude/settings.json 里打开函数钩子:
1 | { "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" } } |
然后各自 claude plugin marketplace add + claude plugin install。
判断有没有真正生效:看会话的调试日志(~/.claude/debug/<会话 id>.txt)里有没有插件的 loaded 记录,比如 fast-jev-compaction@fast-jev-compaction loaded … events: session.compact,turn.complete。命令行 claude -p --debug 的 stderr 是空的,没法判断。函数钩子类插件新开会话才加载。
Winnow 另外有两个自查入口:winnow doctor 检查环境,~/.winnow/decisions.jsonl 里每个决策是一行,可以看到每一块的概率。
在 Git Bash 里测试 claude -p "/compact" 要加 MSYS_NO_PATHCONV=1,否则 /compact 会被改写成 C:/Program Files/Git/compact。
小结
| 值不值得装 | |
|---|---|
| Winnow | 低成本、保守、收益有限;31% 的召回率说明还有打磨空间,建议先 shadow 模式观察 |
| fast-jev-compaction | 思路更干净(只删不写);实测一次,8 次工具调用全部删除、消息减少 91%,但那是理想场景,且只对工具调用多的会话有用 |
两个都建立在同一个前提上:Jev 一次能回答几十个”需要吗”,而且足够便宜。如果你的会话本来就不长,这两个插件的价值很有限。