fast-jev-compaction
Claude Code 插件,用 Jev 决策取代压缩摘要:每个工具调用及其结果都在一次快速请求中被打分,过期的被删除或截断,所有保留的内容逐字原样。也可以作为 npm 库使用。
是什么与为什么
大多数上下文压缩会让 LLM 总结旧的对话轮次。摘要是无损的——不对,是有损的:文件路径、确切的报错、约束条件或命令可能在之后仍然重要时消失。这个库从不改写任何内容,只删除 Jev 认为不再需要的工具调用和工具结果,并且在向 Jev 提问时向其展示完整对话。用户和助手的文本保持逐字原样、顺序不变。
该仓库既是 npm 包(src/),也是 Claude Code 插件(hooks/、.claude-plugin/),插件利用这个包,把 Claude Code 内置的压缩摘要替换为原始消息。
工作原理
- 每个
tool_use通过tool_use_id与其tool_result配对。第一条消息中或最新的preserveRecentMessages条消息中的调用被固定,绝不改动。 - 发送给 Jev 的状态是到目前为止的完整对话,从旧到新排列,每个工具结果都被替换为一条简短说明(
ok, 4213 chars (omitted))。工具输入被包含在内,文本被包含在内,不做任何总结。 - 状态被分阶段适配到
maxStateTokens(默认 25k)以内,每个阶段仅在前一阶段不够时才应用:工具输入截断到 1000、然后 200、再 60 字符;长文本摘录为头部+尾部,最旧的未固定消息优先;旧的未固定消息折叠为一条[… N chars omitted …]说明;旧的工具调用各缩减为一行(t12 Read file_path=src/a.ts → ok 480ch);旧的没有调用的消息被略去;连续的旧"仅调用"消息折叠为一条。如果仍然放不下,压缩会抛出异常。Token 估算不使用分词器(每六个字母算一个词、每个数字半个 token、其他符号各约一个),并校准到略高于 Jev 报告的计数。 - 对于每个未固定的调用,Jev 会收到两个
noul问题:调用本身是否应保留(知道它被发起过、连同其输入,是否仍然重要),以及结果是否应逐字保留(其内容仍然需要,且重新运行工具无法替代)。 - 问题被拆分为多个请求,以确保状态加问题保持在
maxRequestTokens(默认 30k,低于 Jev 的 32k 请求上限)以内。每次请求都重新发送同样的完整状态;请求并发运行,答案随后合并。 - 针对每个调用的决策,依据
keepThreshold:keepResult ≥ threshold→ 保留调用和结果;- 否则
keepCall ≥ threshold→ 保留调用,结果截断为前truncateHeadChars个字符加一行说明; - 否则 → 连同结果一起删除该调用。
- 重建消息列表:失去全部内容的消息被移除,未改动的消息以原对象返回,任何结果都不会失去其对应的调用。
Jev 失败、答案格式错误、缺少密钥,或无法适配的历史都会抛出异常;由调用方(或 Claude Code hook)决定回退方案。
安装与使用
npm install fast-jev-compaction
export TYPESAFE_API_KEY=...
import { compactMessages, reductionRatio, type Message } from 'fast-jev-compaction';
const transcript: Message[] = [
{ role: 'user', text: 'Fix the failing test. Never edit src/generated.', toolUses: [] },
{
role: 'assistant',
text: '',
toolUses: [{ tool_use_id: 'toolu_1', tool: 'Read', input: { file_path: 'src/a.ts' } }],
},
{ role: 'user', text: '', toolUses: [], toolResults: [{ tool_use_id: 'toolu_1', text: '…file…' }] },
// …
];
const result = await compactMessages(transcript, { preserveRecentMessages: 4 });
console.log(result.messages, result.decisions, result.stats);
if (reductionRatio(result) < 0.25) {
// not worth it: keep the original transcript, or summarize instead
}
Message 是 Claude Code SessionMessage 的一个子集,因此会话记录可以直接传入。
要自带传输层,实现 JevAsker(一个 ask(state, questions) 方法)并调用 compact(messages, asker, options);buildJevRequest 和 parseJevResponse 为你提供 HTTP 请求体构造和响应校验。构建块(collectToolCalls、fitState、batchCalls、decideCall、applyDecisions)也都已导出。
apiKey 默认为 process.env.TYPESAFE_API_KEY。切勿提交密钥或将其放入源文件。
选项
| 选项 | 默认值 | 说明 |
|---|---|---|
apiKey | TYPESAFE_API_KEY | TypeSafe API 密钥(compactMessages/JevClient) |
model | jev-latest | Jev 模型名 |
baseUrl | https://api.typesafe.ai/v1/systemone | System One 端点 |
fetch | 原生 fetch | 可注入的 fetch 实现,用于测试 |
goal | 最近 3 条用户提示 | 包含在状态中的当前任务描述 |
keepThreshold | 0.5 | 调用或结果得以保留的最低保留概率 |
preserveRecentMessages | 6 | 永不改动的最新消息数(第一条总是保留) |
maxStateTokens | 25000 | 状态的估算 token 上限 |
maxRequestTokens | 30000 | 状态加一批问题的估算上限 |
truncateHeadChars | 300 | 被删结果在其说明之前保留的字符数 |
result.stats 报告压缩前后的消息数和字符数、按原因分类的决策计数、以估算 token 计的状态大小、所需的适配阶段以及请求数。
局限性
- 只有工具调用和结果是候选对象;文本消息在输出中绝不会被删除或缩短(它们只在 Jev 所见的状态中被摘录)。
- Token 大小基于字符数估算,而非分词器。
- 校准在请求层面进行;一个概率并不能证明删除某个结果是安全的。助手随时可以重新运行工具。
- 每次请求都重复完整状态,因此接近状态上限的历史记录,每几个问题就要花费一次请求。
Claude Code 插件
仓库根目录就是一个 Claude Code function-hook 插件:hooks/fast-jev.ts 是一个轻量适配器,将 session.compact 的记录送入 src/ 处理,在出错或压缩量不足时回退到 Claude Code 内置摘要。配置方法和 Claude Code 2.1.274 类型参考见 hooks/README.md。
在 Claude Code 中安装
Function hooks 是 Claude Code 的早期访问功能(2.1.274+),因此必须在 Claude Code 运行之处设置 opt-in 标志,例如在 ~/.claude/settings.json 中:
{ "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1", "TYPESAFE_API_KEY": "<your key>" } }
然后将本仓库添加为插件市场并安装插件,可以在 shell 中操作,也可以在会话内用斜杠命令:
claude plugin marketplace add tamaratran/fast-jev-compaction
claude plugin install fast-jev-compaction@fast-jev-compaction
安装时会提示输入插件选项(API 密钥、阈值、truncateHeadChars 等);保持默认即可使用环境中的 TYPESAFE_API_KEY。重启 Claude Code 或运行 /reload-plugins。从此 /compact(以及自动压缩)都会经过 Jev:当剪枝后的历史替换内置摘要时,提示显示 fast-jev-compaction: kept N/M messages, no summary (…);当 Jev 无法删除足够内容时(短会话或失败时),显示 fallback to built-in summary (…)。
要在 checkout 中直接运行而无需安装:从仓库根目录执行 CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir .。无需发布步骤;市场就是仓库的 .claude-plugin/marketplace.json。
开发
npm install
npm run typecheck # library + hook
npm test
npm run build
npm run validate:plugin # claude plugin validate
TYPESAFE_API_KEY="$(cat ~/.typesafe_key)" npm run demo
单元测试使用假 Jev,绝不连接 TypeSafe。demo 是真实的网络检查。
动画演示
demo/JevDemo 是一个原生 SwiftUI 小应用,在 Claude Code 风格的终端里播放脚本化、戏剧化的压缩流程:预设记录中的工具调用被打分,Jev 放弃的结果和调用变红并折叠消失,其余保持逐字原样。它从不调用 API;它的存在就是为了被屏幕录制。
demo/JevDemo/build.sh # builds demo/JevDemo/build/JevDemo.app and launches it
在应用中按空格键即可从头重播。