context-mode 拆解:98% 是怎么算出来的,装进 Claude Code 实际省多少

context-mode 这周在 GitHub 涨了 4,400 多星,README 和 npm 包描述都写着能省掉 98% 的上下文。本文读了它 1.0.169 版本的源码,并绕开模型直接调用它的 MCP server 做了复现。结论分三点:它省 token 靠三层机制,分别是 hooks 引导模型改用沙箱工具、子进程只回传 stdout、大输出切块存进 SQLite FTS5 按需检索;98% 是作者预先写好的汇总脚本输出与原始文件的字节比,对照基线是「原始数据全部进入上下文」;在 Claude Code 里,一份 56KB 的网页快照走检索路径实测省 89%,代价是多一次工具调用。

这个差距值得单独讨论。98% 与 89% 本身差得不多,问题在于基线。Claude Code 自带的机制已经把超过 3 万字符的 Bash 输出存成文件、只给模型 2,000 字符预览,WebFetch 抓到的页面也先经过一次独立的模型调用再进入主上下文。拿「全量原文」当分母,等于把 Claude Code 已经替你省掉的部分也算进了插件的功劳。

所以判断要不要装,看的是你的会话里有多少输出落在 Claude Code 自带闸门之外。下面先讲闸门在哪,再拆插件的三层机制,最后给出适用边界。

Claude Code 自带的四道输出上限

按官方文档(tools-reference、env-vars 两页),工具输出进入上下文前已经有四个上限:

工具 自带上限 超出后模型收到什么
Bash 有效结果内联约 30,000 字符 存成会话目录下的文件,只给路径 + 前 2,000 字符预览
Read 单次 25,000 token,整文件超过 256KB 拒绝 第一页 + PARTIAL view 提示,需要 offset/limit 分页
WebFetch 不设长度上限,但先跑一次独立模型调用 按 prompt 抽取后的结果,不是原页面
MCP 工具 MAX_MCP_OUTPUT_TOKENS 默认 25,000 token 超过 10,000 token 时警告

真正会整块涌进上下文的,是落在这些上限以内的中等输出:5,000 到 30,000 字符的测试日志和构建输出、25,000 token 以内的文件 Read、Playwright 和 GitHub 这类 MCP 工具的返回。一份 57KB 的 Playwright 页面快照约一万四千 token,低于 MCP 上限,会原样进入上下文。context-mode 能拿到的收益集中在这一段。

context-mode 的三层机制:hooks、沙箱、FTS5

第一层是 hooks 路由,大部分是建议而不是拦截。 PreToolUse hook 的逻辑在 hooks/core/routing.mjs。真正被改写或拒绝的只有几类:输出到 stdout 的 curl/wget、命令里内联的 HTTP 请求(fetch(、requests.get()、gradle/mvn/sbt 构建命令,以及 WebFetch。普通 Bash 命令、Read 和 Grep 全部放行,只在本会话第一次调用时附一段提示,建议改用沙箱工具。直接给 hook 喂 cat access.log 可以验证:第一次返回提示,第二次返回空,命令照常执行。这意味着 Read 和 Bash 这两条最常用的路径能省多少,取决于模型是否听从提示改用 ctx_execute。README 自己的表格也给出了这个区别:有 hooks 约 98%,只有说明文件约 60%。

第二层是子进程沙箱,只回传 stdout。 ctx_execute 在独立子进程里运行模型写的代码,ctx_execute_file 把文件内容作为 FILE_CONTENT 变量注入子进程。原始数据留在子进程里,进入上下文的只有代码打印出来的内容,外加一段代码回显。这就是 README 说的「Think in Code」:模型不读 50 个文件,而是写一段统计脚本,只读脚本打印的结果。

stdout 本身也有分支,阈值写在 src/server.ts:

  • 小于 5KB:原样返回。
  • 大于 5KB 且调用时传了 intent:全文切块入库,只返回匹配段落的标题、首行预览和一张可搜索词表。
  • 大于 100KB 且没传 intent:全文入库,只返回一句「Indexed N sections,用 ctx_search 查询」。
  • 5KB 到 100KB 之间且没传 intent:原样返回,没有任何压缩。

最后一条容易被忽略。模型如果写了一个打印太多的脚本又没带 intent,几十 KB 照样进上下文。

第三层是 FTS5 检索,用来按需取回原文。 入库内容存在 ~/.claude/context-mode/content/<项目hash>.db,按 markdown 标题切块、代码块保持完整,单块上限 4KB。检索同时跑两路:porter 分词的 BM25 和 trigram 子串匹配,再用 RRF 合并排名。ctx_search 返回的是原文片段,不是摘要,所以需要精确代码或数字时,这条路径不会丢细节。

98% 的分子和分母

98% 出自 BENCHMARK.md 第一部分:14 个真实工具输出的 fixture,合计 315KB,经 ctx_execute_file 处理后剩 5.5KB。计算公式在 tests/ecosystem-benchmark.ts 第 466 到 467 行:

ini 复制代码
savings = 1 − 脚本 stdout 字节数 / fixture 原始字节数

有三处需要读者自己折算:

  • 汇总脚本是作者预先写好的。 Playwright 场景的脚本专门用正则抽 Hacker News 的新闻标题,nginx 场景专门抽状态码和延迟。真实会话里这段脚本由模型现场写,第一次写偏了要重跑。
  • 分子不含脚本本身。 模型写脚本要消耗输出 token,脚本还会作为工具调用参数留在上下文里,ctx_execute 返回时也会回显代码。这些都没有计入。
  • 分母是全量原文。 前面已经说过,Claude Code 对 Bash 长输出和 WebFetch 本来就不会全量放进上下文。

同一份文档的第二部分换成 ctx_index + ctx_search 返回原文片段,节省率是 82%。在需要精确内容的场景,这个数字比 98% 更接近实际。

一份 56KB 页面快照的实测:89%,两次调用

复现方法:不经过模型,直接通过 JSON-RPC 调用 context-mode 的 MCP server,CLAUDE_CONFIG_DIR 指向隔离目录。素材用仓库自带的 playwright-snapshot.txt,即 Hacker News 首页的无障碍树快照,57,521 字节。

第一步按 README 推荐的方式调用 ctx_execute,执行 cat 并带上 intent: "top stories titles points"。返回 1,189 字节,号称匹配了 5 个段落,但段落标题是 - text: )、- generic [ref=e251]: 这类无意义的行。原因是无障碍树不是 markdown,没有标题可切,切块时只能拿块的第一行当标题。只看这次返回,模型拿不到任何新闻标题。

第二步按提示用 ctx_search 查一条具体新闻的分数和评论数。返回 5,088 字节,第一条结果正确,给出了标题、50 points 和 38 comments;第二条是另一条新闻的分数行,属于噪声。

两步合计 6,277 字节,比原文少 89%。BENCHMARK 对同一份 fixture 写的是 299 字节、99%,差别全部来自那段预先写好、专门针对 Hacker News 页面结构的正则脚本。

这次没有用 claude -p 跑装前装后的完整会话对比,所以没有端到端的 token 账单,89% 只覆盖「一份大输出进上下文」这一个环节。

会话记忆:compact 之后用 SQLite 快照接上

上下文省下来,是为了让会话少 compact、晚 compact。context-mode 的第二个功能处理 compact 之后的事。PostToolUse 和 UserPromptSubmit 两个 hook 把文件读写、git 操作、报错、用户的纠正(「用 X 不要用 Y」)写进每个项目一份的 SQLite。PreCompact 触发时生成一份快照,SessionStart 在 compact 或 resume 之后把它注入上下文,内容包括最后一次请求、任务清单、改过的文件和未解决的错误,详细记录仍留在库里供 ctx_search 查。

这里文档和代码不一致。README 说快照有 2KB 预算、超出时先丢低优先级事件;src/session/snapshot.ts 的注释写的是所有非空分类全部输出,没有字节预算,maxBytes 参数保留只为兼容、实际忽略。另外,不带 --continue 开新会话时,上一个会话的记录会被直接清掉。

装之前要知道的固定开销和副作用

  • 每个会话固定多出约 4.6KB。 SessionStart hook 注入的路由说明实测 4,631 字节,不管这个会话用不用大输出工具都要付。
  • 11 个工具定义合计 30KB。 Claude Code 默认开启 tool search,MCP 工具定义延迟加载,首轮只占工具名;但模型第一次调用前要先走一次 ToolSearch 把定义拉进来。
  • 会改全局配置。 插件的启动脚本 start.mjs 每次启动都会在 ~/.claude/hooks/ 写一个自愈脚本,并在 ~/.claude/settings.json 的 SessionStart 里注册它。
  • ctx_execute_file 只能读项目根目录以内的文件,越界会被拒绝,需要在权限设置里单独放行。
  • 许可证是 Elastic License 2.0,不是 MIT 或 Apache,公司内二次分发前要看条款。

对照:billion-context 压缩的是对话历史

同一周起量的 billion-context 走的是另一条路。它是一个本地代理,Claude Code 的 base URL 指向它,它在请求里注入 compress、decompress、search_context 等工具,由模型自己决定把哪一段历史消息折叠成摘要,需要细节时再展开。原文存在本机 ~/.local/share/billion-context/。

两者处理的是上下文的两个不同部分:context-mode 让工具输出一开始就不进来,billion-context 把已经进来的历史压掉。前者不碰模型请求,后者要接管所有模型流量,同一台机器上各实例共享同一份存储,没有按会话鉴权。README 标语里的「省 5 倍 token」没有给出测法,论文给的是 17 万次模型调用的生产统计,而不是对照实验。

什么时候值得装

会话特征 判断
大量使用 Playwright、GitHub 等 MCP 工具 值得。这类输出在 25,000 token 以内会整块进上下文,是收益最大的一段
反复跑测试或构建,单次输出在 5,000 到 30,000 字符 值得。前提是模型确实改用 ctx_execute 并只打印失败项
同一份文档要查很多次 值得。索引一次,之后每次只取几个 4KB 片段
主要在读文件、改文件 收益有限。Edit 需要原文在上下文里,Read 也只是被建议而不是被拦截
需要精确查找细节(某一行、某个数字) 要多一次检索往返;非 markdown 输出的预览标题可能全是噪声
短任务、少量工具调用 不值得。每个会话固定的 4.6KB 加 hook 开销可能比省下的多

context-mode 的工程判断是对的:大输出的处理应该交给代码,而不是交给上下文窗口。它的营销数字则需要打折。98% 是用预写脚本对着全量原文算出来的,装进 Claude Code 之后,前一部分要模型自己写,后一部分 Claude Code 已经替你挡掉了一截。值不值得装,取决于你的会话里有多少 MCP 和中等长度的命令输出,而不是那个百分比。


相关推荐
tz_zs2 小时前
【AI编程】 防止AI编程中的膨胀
ai编程
用户842558130872 小时前
同一篇稿子发到 6 个平台,为什么只有这里的图片全裂了?
ai编程
用户842558130872 小时前
把「一文多发」做成脚本流水线:CDP、滑块、二维码、WAF、限流,这些坑我替你踩完了
ai编程
猛犸象限2 小时前
【Grok Bot 仓颉实践】 编译通过了,我没让它合并——验证和证据我怎么卡
ai编程
小鱼儿亮亮2 小时前
大模型调用的演进与架构全景解析:从 cURL 原语、多维技术栈、API 规范到 LangChain 生产级实战
ai编程
用户842558130872 小时前
Chrome 自动化「点了没反应」:5 个真实坑与一套排查法
ai编程
tz_zs3 小时前
【AI编程】使用笔记:Rule/Skill/Prompt
笔记·prompt·ai编程
tz_zs4 小时前
【AI编程】使用笔记:AI编程项目文档体系与框架
笔记·ai编程
Shockang13 小时前
OpenCode 版 OmO:职责分工、编辑校验与技能边界
ai编程