2026 年 4 月 4 日,一个叫 Julius Brussee 的开发者在 GitHub 上建了个仓库,取名 caveman(穴居人)。三个月后,这个仓库的 star 数冲到了 87972,fork 数 5054, contributor 30 位。
说真的,这个增长速度放在整个 GitHub 历史上都属于第一梯队。
更离谱的是,这个项目的核心,就一个 5KB 的 Markdown 文件。没有后端,没有数据库,没有云服务,安装完之后零网络调用。它做的事只有一件,让你的 AI 编程助手说话像个穴居人。
你想想看,正常情况下你问 Claude「我的 React 组件为什么一直重渲染」,它会给你一段 1180 token 的解释,从头到尾讲得明明白白。装上 caveman 之后,同样的回答变成 159 token。
同样的技术结论,七分之一的字数。
这不是什么模型压缩或者知识蒸馏。caveman 不动模型的脑子,只动模型的嘴。它是一个注入到 system prompt 里的 Skill 文件,用一套精密的语言压缩规则,让模型把废话砍掉,只留干货。
我花了一个下午把它的源码翻了一遍,从 Skill 规则到 hook 架构到安全设计,说实话这个项目比我预想的有意思得多。表面上它是个 meme 项目,实际上它是一堂关于 token 经济学和 prompt engineering 的实战课。
穴居人不蠢,穴居人只是话少
先说清楚 caveman 到底在压缩什么。
很多人第一反应是,这不就是个 prompt,让模型「简洁回答」吗。我自己一开始也这么想。但读了 docs/HONEST-NUMBERS.md 之后才发现,作者把这件事想得很清楚。
caveman 是一个 system prompt Skill,它只压缩模型的输出。你的输入不变,你的上下文不变,模型的推理 token(thinking tokens)也不变。它压缩的是模型嘴巴里吐出来的那部分。
这句话听着简单,但它定义了一个清晰的能力边界。HONEST-NUMBERS 里有一句原话我觉得特别好,「Caveman no make brain smaller. Caveman make mouth smaller.」(穴居人不把脑子变小,穴居人把嘴巴变小。)
具体怎么压缩的呢。skills/caveman/SKILL.md 里有一套很精细的规则。
第一层,砍废话。去掉冠词(a/an/the),去掉填充词(just/really/basically/actually),去掉客套话(sure/certainly/of course/happy to help),去掉模糊修饰。允许碎片化句式。用短同义词替代长表达,big 代替 extensive,fix 代替 implement a solution for。
第二层,保真。代码块、命令行、错误信息、API 名称、技术术语,一字不改。这个边界划得非常硬。SKILL.md 里反复强调,code blocks unchanged, errors quoted exact。你不会因为穴居人模式而漏掉任何技术信息。
你想想看,这个设计其实很聪明。模型回答里真正有信息密度的部分(代码、命令、报错)本来就是高度压缩的,caveman 碰都不碰。它只压缩那些「Sure! I'd be happy to help you with that」之类的社交废话。
一个反直觉的 tokenizer 发现
深入读 SKILL.md 的时候,我发现了一条特别有意思的规则。
caveman 明确禁止发明新的缩写词。具体来说,ultra 模式下有一条硬规定,NO prose abbreviations (cfg/impl/req/res/fn/auth)。理由写得非常清楚,「measured zero token saving under tokenizer, cost decode clarity」,翻译过来就是,在 tokenizer 下面测过了,这些缩写零 token 节省,还损失可读性。
这条规则我一开始觉得有点奇怪。cfg 不是比 config 短吗,为什么省不了 token。
后来想想就明白了。现代 tokenizer(不管是 BPE 还是 SentencePiece)是按子词切分的。config 和 cfg 在词表里大概率都是独立的 token,你把它写成 cfg 不会比 config 少占 token。但读者需要额外解码一次。
坦白讲,这个发现挺有价值的。很多人做 prompt 优化的时候本能地想用缩写来省 token,但 tokenizer 的实际行为和人类直觉是相反的。caveman 把这个反直觉的发现固化成了一条规则,而不是靠每个用户自己去踩坑。
这也是我说的「一堂 prompt engineering 实战课」的具体体现。一个好的 Skill 文件不只是写一堆指令,它还把踩过的坑固化成规则。
六个压缩等级,文言文是最狠的那个
caveman 提供了六个压缩等级,你可以用 /caveman <level> 随时切换。
lite 是最温和的,只去废话和模糊修饰,保留完整句子和冠词,适合需要专业语气的场景。full 是默认等级,去掉冠词,允许碎片化,经典的穴居人风格。ultra 是最激进的英文模式,连词都砍,一个词够用的地方绝不写两个。
但真正让我眼前一亮的是后面三个。
wenyan-lite、wenyan-full、wenyan-ultra,这三个等级让模型用文言文回答。
SKILL.md 里有实际的例子。同样是「解释数据库连接池」,wenyan-full 模式的回答是「池蓄已開之連,不逐請而新開,省握手之費。」wenyan-ultra 更狠,「池蓄連,免逐請新開,省握手。」
说真的,我第一次看到这个的时候愣了好几秒。然后反应过来,文言文确实是目前人类语言中信息密度最高的表达方式之一。古人在竹简上刻字,每个字都是成本,所以演化出了极度压缩的语法结构。
caveman 把文言文作为终极压缩模式,不是一个段子。它是把「语言的信息密度」这件事推到了极致。README 里说了一句,「classical Chinese packs the most meaning per token」,古典中文在每个 token 里塞进最多的含义。
这是有数据支撑的。wenyan-ultra 模式号称能实现 80-90% 的字符缩减率。
三件套 Hook 架构,把 Skill 变成自动挡
如果 caveman 只是一个 Skill 文件,那它也就是个不错的 prompt 模板。真正让它从「有趣」变成「好用」的,是 Claude Code 的 hook 系统。
在 Claude Code 上安装 caveman,会落地三个 hook,构成一套完整的自动激活机制。这套架构在 CLAUDE.md 里有清晰的文档,我在源码里一一验证过。
第一个是 caveman-activate.js,SessionStart hook。每次 Claude Code 会话启动时跑一次,做三件事。第一,把当前激活的模式写入一个 flag 文件 $CLAUDE_CONFIG_DIR/.caveman-active。第二,把 caveman 的规则集作为隐藏的 stdout 输出,Claude Code 会把 SessionStart hook 的 stdout 注入为 system context,用户看不到。第三,检查 settings.json 里有没有配 statusline,没配就轻轻提醒一下。
关键在第二步。这意味着你在 Claude Code 上装完 caveman 之后,从第一条消息开始就是穴居人模式,不需要每次打 /caveman。
第二个是 caveman-mode-tracker.js,UserPromptSubmit hook。每次你发消息都会触发,负责解析你的输入。如果你打 /caveman ultra,它就把 ultra 写进 flag 文件。如果你用自然语言说「talk like caveman」或「be brief」,它也能识别。说「normal mode」或「stop caveman」就删除 flag 文件,恢复正常。
这个 hook 里有一个细节我很欣赏。wantsOff(想关掉)的判断逻辑被放在最前面,优先于所有激活逻辑。源码注释解释了原因,旧版本的连续匹配正则会漏掉「turn caveman mode off」这种语序,然后激活正则又重新武装了 caveman。修这个 bug 的是 issue #598。
你想想看,这种 bug 如果出现在安全相关的场景里是很危险的。用户说「关掉」,系统理解为「打开」,这种语义反转的 bug 在 prompt 系统里极其隐蔽。caveman 团队花精力修了它,说明他们对边界情况的处理是认真的。
第三个是 caveman-statusline.sh,读取 flag 文件,在 Claude Code 的状态栏显示一个橙色徽章 [CAVEMAN] 或 [CAVEMAN:ULTRA]。如果你跑过 /caveman-stats,它还会在后面追加你累计省下的 token 数,比如 ⛏ 12.4k。
三个 hook 通过一个 flag 文件通信,SessionStart 写、UserPromptSubmit 写、statusline 读。架构干净利落。
安全设计比你想的认真
读到 hook 架构的时候,我以为就是个简单的文件读写。但深入 caveman-config.js 之后,发现了一套完整的 symlink 防护机制。
核心函数叫 safeWriteFlag(flagPath, content)。它做的事比你想象的多。
首先,它检查 flag 目标路径和它的直接父目录是不是符号链接。如果是,拒绝写入。其次,在支持的系统上用 O_NOFOLLOW 标志打开文件,防止操作系统层面跟随符号链接。然后通过「写临时文件 + rename」的方式做原子写入,文件权限设为 0600。
为什么要这么谨慎。因为这个 flag 文件的路径是可预测的,就在 $CLAUDE_CONFIG_DIR/.caveman-active。如果本机有攻击者把这个路径替换成一个符号链接,指向用户可写的某个重要文件(比如 .bashrc 或 SSH 配置),那 caveman 每次写入 flag 就会覆盖那个文件。
说真的,在一个「让 AI 说话简短点」的工具里看到这种安全意识,我是有点意外的。大部分 prompt 工具根本不会考虑 symlink 攻击。但 caveman 不但考虑了,还在 CLAUDE.md 里写了一条硬规定,「Any new flag file write must go through safeWriteFlag()」,以后新增的 flag 写入都必须走这个安全函数。
另外,caveman-config.js 里的 getDefaultMode() 函数有一套优先级解析链,从环境变量到 repo 级配置到用户级配置再到默认值 full。findRepoConfigPath() 会从当前目录往上走,最多查 64 层祖先目录,找 .caveman/config.json 或 .caveman.json。这让一个团队可以在项目里 check in 一个共享的默认模式,而不需要每个开发者去改自己的环境变量。
caveman-compress,把省 token 变成持久化收益
前面说的所有机制,省的都是输出 token,一次性的,下一条消息就没了。caveman 还有一个更狠的功能,caveman-compress,它压缩的是你的记忆文件。
你在用 Claude Code 的时候,可能会维护一个 CLAUDE.md,里面写了项目约定、代码风格、注意事项。这个文件每次会话都会作为 context 加载。caveman-compress 做的事就是把这个文件压缩成穴居人风格,以后每次会话加载的都是压缩后的版本。
README 里有实测数据。claude-md-preferences.md 从 706 token 压到 285 token,省了 59.6%。五个文件平均省 46%。
这不是省一次。是每次会话、每条消息都省,永久性的。
我读了 plugins/caveman/skills/caveman-compress/scripts/compress.py 的完整源码,这个压缩管线设计得相当扎实。
首先是安全闸门。is_sensitive_path() 函数在读取文件之前先做硬检查。它维护了一个正则 SENSITIVE_BASENAME_REGEX,匹配 .env、credentials、secrets、id_rsa、.pem、.key 等敏感文件名。还有一组路径组件检查,.ssh、.aws、.gnupg、.kube、.docker 目录下的文件一律不碰。以及一组自然语言敏感词,secret、credential、password、apikey、token、privatekey。
为什么要这么做。因为压缩文件需要把原始内容发给 Anthropic API。源码注释里写得很直接,「Compressing ships raw bytes to the Anthropic API, a third-party data boundary that developers on sensitive codebases cannot cross」,压缩会把原始字节发给 Anthropic API,对于敏感代码库的开发者来说,这是一个第三方数据边界,不能越。
然后是 frontmatter 保护。split_frontmatter() 函数在压缩之前把 YAML frontmatter 整块拆出来,压缩完再原样拼回去。源码注释解释了原因,「The compression LLM has a habit of stripping or rewriting these despite preserve-structure rules」,LLM 有个坏习惯,即便你在 prompt 里告诉它别动 frontmatter,它还是会改。所以干脆在代码层面隔离。
接着是验证重试管线。压缩完之后跑 validate(),检查代码块、URL、标题、文件路径有没有被破坏。如果验证失败,最多重试 2 次。重试用的不是重新压缩,而是 build_fix_prompt(),只修复报错的具体问题,其余部分不动。如果两次重试都失败,自动恢复原始文件,删除备份。
备份文件存在一个独立的目录(~/.local/share/caveman-compress/backups/),不在源目录里。源码注释说这是为了防止 Skill 自动加载器把 .original.md 当成活文件重新读取。
还有一个小细节我很喜欢。备份写入后会做 readback 验证,如果磁盘上的备份内容和内存里的原始内容不一致(可能是编码问题、杀毒软件拦截、磁盘满了),立刻删掉坏备份,中止操作,绝不在备份不可靠的情况下覆盖原始文件。
整个 compress.py 有 342 行,每一行都在处理「LLM 可能搞砸的边界情况」。这个文件几乎是一份「如何安全地让 LLM 改你的文件」的工程指南。
那个 65% 的数字,水分到底有多大
说了这么多好的,现在来泼冷水。
caveman 标榜「65% output token reduction」,这个数字是真实的。benchmarks/ 目录里有完整的测试脚本和结果 JSON,10 个 prompt 通过真实的 Claude API 跑出来的,范围从 22% 到 87%。但这个 65% 只算了输出 token。
实际的 token 账本远比这复杂。
第一笔账,Skill 本身要占 input token。SKILL.md 规则集大约 5KB,加上 skill-list 条目,每轮对话增加约 1 到 1.5k input token。这是固定成本,不管你这轮省了多少输出 token,这个开销都在。
第二笔账,agentic coding 场景下 input token 远大于 output token。你的 prompt、你的上下文、你的文件内容、注入的规则,这些加起来轻松是输出 token 的好几倍。所以输出 token 省 65%,整个 session 的总 token 可能只省 14 到 21%。
HONEST-NUMBERS.md 里有一段话,我觉得是整个项目最值得尊敬的地方。「the skill costs ~1--1.5k input tokens every turn. If it saves less output than that, you are paying to use it.」这个 Skill 每轮花费约 1-1.5k input token,如果它省的输出 token 还没这个多,你就是在花钱用它。
issue 区有几个真实的翻车案例。#145 的用户测了,如果正常回答只有 150 output token,caveman 可能只省 70 到 100 个,但每轮要多花 1k+ input token overhead,净亏损。#506 的用户用 GitHub Copilot,Copilot 按请求计费而不是按 token 计费,回答再短也是一个请求,caveman 完全无效。最狠的是 #550,一个 Cursor 用户做了 A/B 测试,装了 caveman 之后 4.3M token,不装 1M token,而且执行时间翻倍。
caveman 团队没有删这些 issue。他们把 #550 的结论写进了 HONEST-NUMBERS.md,「Wanting the rock to work does not make the rock work」,想让石头起作用,不代表石头真的起作用。
这种诚实程度在开源项目里是罕见的。大部分项目会把自己的 benchmark 做得好看,把不利的测试藏起来。caveman 把不利数据贴在首页,还专门写了一篇 HONEST-NUMBERS.md 告诉你什么时候不该用它。
HONEST-NUMBERS 最后给了一个经验法则。正常回答超过 1.5 到 2k output token 的时候,caveman 可能省钱。低于这个数,或者你按请求计费,caveman 可能反而费钱。但不管哪种情况,caveman 的回答读起来更快,这一点是白送的。
35 个 Agent 的安装矩阵
caveman 另一个让人印象深刻的地方是它的跨平台覆盖率。
bin/install.js 里有一个 PROVIDERS 数组,我数了一下,35 个条目。从 Claude Code、Codex、Gemini CLI、Cursor、Windsurf、Cline、Copilot,到 Junie、Trae、Warp、Tabnine、Mistral、Qwen、Devin、Droid、ForgeCode、Antigravity 等等。
每个 Agent 的安装机制不一样,有的是 plugin,有的是 extension,有的是 rule file,有的是 npx skills add。caveman 用一个统一的 Node 安装器处理所有这些差异。install.sh 和 install.ps1 只是两个 30 行的 shim,委托给 bin/install.js。
CLAUDE.md 里有一条注释解释了为什么要这么做。之前 install.sh 和 install.ps1 各自有独立的逻辑,bash 和 PowerShell 的引号处理差异导致 JSON 合并步骤出 bug(issue #249)。统一成 Node 脚本后,单一源,跨平台,没有引号地狱。
安装器还有一个细节值得说。它用 PINNED_REF 锁定到一个不可变的 release tag(当前是 v1.9.1),而不是跟踪 main 分支。源码注释说这是为了 issue #261,一次 push 到 main 不应该静默改变 curl|bash 下载和执行的内容。这是供应链安全的基本操作,但很多开源安装脚本并不做。
从一个 Skill 到一整个洞穴
caveman 仓库本身只是 Julius Brussee 构建的「cave 生态」的一部分。
| 仓库 | 压缩什么 |
|---|---|
| caveman | Agent 说了什么(输出层) |
| caveman-code | 整个 Agent(端到端编码 Agent) |
| cavemem | Agent 记住了什么(跨会话记忆) |
| cavekit | 构建循环(spec 驱动开发) |
| cavegemma | 压缩烤进模型权重(Gemma 微调) |
五个工具,一个思路,让 Agent 用更少的 token 做更多的事。
最值得注意的是 cavegemma。它把 caveman 的压缩策略通过微调烤进了 Gemma 模型的权重里。也就是说,不需要 system prompt,不需要 Skill 文件,模型本身就学会了用穴居人风格回答。这是从 prompt engineering 到 model engineering 的跨越。
还有一个 cavecrew 子 Agent 系统,包含三个角色。investigator 是只读定位 Agent(用 haiku 模型),负责找到代码在哪。builder 是精准编辑 Agent,限制在 1 到 2 个文件范围内,拒绝 3 个以上的改动。reviewer 是 diff 审查 Agent,输出格式是 L42: 🔴 bug: user null. Add guard. 这种一行式报告。整个 cavecrew 比标准子 Agent 省约 60% token。
caveman 还在做 Caveman 2,目标是把 token 节省从「本地估算」变成「团队级可验证」,真实的 dashboard,真实的证据。官网 caveman.so 已经开放了 waitlist。
30 秒装上,立刻体验
说了这么多,你大概率想自己试试。caveman 的安装设计得非常丝滑,一行命令搞定。
一键安装(macOS / Linux / WSL / Git Bash):
bash
curl -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.sh | bash
Windows(PowerShell 5.1+):
powershell
irm https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.ps1 | iex
安装器会自动扫描你机器上装了哪些 AI 编程工具,逐个注入,不存在的跳过,已装的跳过,可以安全重复执行。整个过程大约 30 秒,需要 Node ≥18。
如果你不放心 curl | bash 这种方式,可以先下载再看源码:
bash
curl -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.sh -o install.sh
# 自己审一遍 install.sh
bash install.sh
也可以用 --dry-run 预览安装器会执行哪些操作,不动任何文件:
bash
curl -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.sh | bash -s -- --dry-run
如果你只想给某一个 Agent 装,比如只用 Claude Code:
bash
claude plugin marketplace add JuliusBrussee/caveman && claude plugin install caveman@caveman
用 Cursor、Windsurf、Cline 这类通过 skills registry 安装的:
bash
npx skills add JuliusBrussee/caveman -a cursor
装完之后怎么用。在 Claude Code、Codex、Gemini CLI 上,caveman 从第一条消息就自动生效,你什么都不用做。其他 Agent 需要在会话里打一次 /caveman,或者说一句「talk like caveman」来激活。
日常使用就几个命令,我整理了一张速查表:
| 命令 | 作用 |
|---|---|
/caveman |
开启穴居人模式(默认 full 等级) |
/caveman ultra |
切到最激进的压缩等级 |
/caveman wenyan |
切到文言文模式,信息密度拉满 |
/caveman-commit |
生成 Conventional Commit 格式的提交信息,subject 不超过 50 字符 |
/caveman-review |
一行式 PR 评论,比如 L42: 🔴 bug: user null. Add guard. |
/caveman-stats |
查看本次会话的 token 使用和累计节省量,加 --share 可以生成可分享的数据 |
/caveman-compress <file> |
把 CLAUDE.md 等记忆文件压缩成穴居人风格,以后每次会话永久省 token |
| 说「normal mode」或「stop caveman」 | 关闭穴居人模式,恢复正常 |
在 Claude Code 上,状态栏会显示一个橙色徽章 [CAVEMAN],如果你跑过 /caveman-stats,后面还会跟着累计省下的 token 数,比如 ⛏ 12.4k。觉得碍眼可以用环境变量 CAVEMAN_STATUSLINE_SAVINGS=0 关掉数字显示。
我的建议是先装上,用 lite 或 full 等级跑两天日常开发,体感最直接。觉得太激进就降回 lite,觉得不够狠就上 ultra 或者试试 wenyan,文言文的回复真的会让人会心一笑。
一个可复用的判断
读完整个项目,我想提炼的不是「caveman 很好用」这种结论。
caveman 给我最大的启发是一个判断框架。当你在优化 LLM 的成本时,有三个层面可以动手。
输入层,压缩你的 prompt、context、记忆文件。caveman-compress 和 caveman-shrink 做的就是这个,收益是持久化的,但需要一次性改写。
输出层,压缩模型的回答。caveman Skill 本体做的就是这个,收益是即时的,但只对 verbose 的回答有效。
推理层,压缩模型的 thinking。目前 caveman 没碰这个,cavegemma 的微调方向算是间接涉及。
大部分人的直觉是先优化输入层,因为「输入 token 更多」。但 caveman 的实践证明,输出层优化的 ROI 可能更高。原因很简单,输出层不需要改你的任何东西,装一个 Skill 就生效,而且输出 token 的单价通常是输入 token 的 3 到 5 倍。
caveman 还回答了另一个问题,一个 prompt engineering 的最佳实践,能不能被固化成一个可分发的产品。答案是能。caveman 的 SKILL.md 不只是一堆指令,它包含了 tokenizer 行为的知识(禁用缩写)、安全边界(auto-clarity 规则)、跨语言处理策略、压缩等级体系。这些是一个资深 prompt engineer 踩过无数坑之后积累的隐性知识,caveman 把它们编码成了一个 5KB 的文件,任何人一行命令就能装上。
这种「Prompt as Code」的模式,把 prompt 从一次性的一次性工程变成了可版本化、可分发、可迭代的软件制品。
当然 caveman 也不是万能的。如果你的工作流是简洁问答,如果你按请求计费,如果你的 Agent 本身就很安静,caveman 对你来说可能是负收益。但如果你每天和 AI Agent 聊几百条消息,如果那些「Sure! I'd be happy to help you with that」让你心烦,那花 30 秒装一下 caveman,可能是你今年 ROI 最高的技术决策之一。