caveman-compress 压缩机制解析:以 Taskflow 项目笔记为样本,看懂 caveman 记忆文件压缩的完整管线与安全边界
在 caveman 项目中,Claude 每次会话启动都会加载 CLAUDE.md 等记忆文件,大文件会反复消耗输入 token。caveman-compress 技能将这类自然语言记忆文件压缩为"穴居人风格"(caveman-speak),而 tests/caveman-compress/project-notes.original.md 正是仓库内置的官方基准样本之一:一份虚构项目"Taskflow"的完整项目笔记(架构决策、性能调查、安全评审、组件库选型、技术债清单),与它的压缩结果 tests/caveman-compress/project-notes.md 共同构成了一组"原始版 vs 压缩版"对照 fixture。读完本文,你将掌握 caveman-compress 的压缩规则与保留边界、端到端管线(检测 → 掩码 → 压缩 → 校验 → 重试 → 原子写入)、备份与回滚机制,并能在逐段对照真实 fixture 的情况下,判断一份自己的笔记文件压缩后哪些内容会被保住、哪些会被改写。
1. 样本文件是什么:一份典型的"会重复计费"的项目记忆
project-notes.original.md 是一份结构完整的 Markdown 记忆文件,包含 5 个带日期的章节:
- Architecture Decision: Background Job Processing (March 2026):团队选择 BullMQ 而非自研或 AWS SQS,理由是已有 Redis、无新基础设施依赖;BullMQ 自带指数退避重试、优先级队列、限流、调度;初始作业类型为邮件通知、文件上传(缩略图、病毒扫描)、第三方同步(Slack、GitHub)、过期会话清理;并新增了管理员路由
/admin/jobs的 BullMQ dashboard。 - Performance Investigation: Dashboard Slowness (March 2026):任务数超过 500 时仪表盘不可用;主瓶颈是 N+1 查询(500 条任务 = 501 次数据库查询),次因是前端未虚拟化一次性渲染 500+ 任务卡片;给出 5 条候选方案(JOIN 加载 assignee、
tasks(project_id, status, updated_at)复合索引、游标分页每页 50 条、TanStack Virtual 虚拟滚动、Redis 30 秒 TTL 缓存),决定先做前 3 条治本方案。 - Meeting Notes: Security Review (February 2026):外部审计发现 SQL 注入(字符串拼接查询,已改为 Knex.js 参数化查询并加 ESLint 规则)、JWT 有效期过长(access token 降至 15 分钟、refresh token 7 天存 HttpOnly cookie、access token 只存内存)、缺少 CSP(已加入 Next.js 中间件、先 report-only 模式运行 2 周)、公共 API 缺限流(
express-rate-limit+ Redis store 跨实例共享状态)。 - Design Decision: Component Library (January 2026):对比 shadcn/ui + Radix、MUI、Chakra UI 三个选项,最终选 shadcn/ui,理由是代码完全自主、Radix 提供可访问性、Tailwind 契合现有策略、包体积小。
- Technical Debt Inventory (January 2026):认证系统重构待测试覆盖补齐;测试套件统一为 Testing Library + MSW 并迁移 Enzyme;迁移脚本规范为"schema-only",数据转换独立成脚本;Webpack → Vite 迁移后清理残留配置。
从结构上看,这份文件恰好是 caveman-compress 最有代表性的输入类型:大量冗长叙述 + 若干不可触碰的技术锚点(库名 BullMQ/Knex.js/TanStack Virtual、路由 /admin/jobs、索引定义 `tasks(project_id, status, updated_at)`、数值 500/501/30 秒/15 分钟/7 天/2 周/50 条、人名 Sam/Alex/Maya、日期章节标题)。这些锚点在压缩后必须逐字保留,叙述部分则可以大刀阔斧地缩短——这正是该 fixture 入选基准集的原因。
2. 逐段对照:压缩到底改了什么、保住了什么
先看"背景作业处理"一节的原始段落:
After extensive discussion, the team decided to adopt BullMQ for background job processing instead of building a custom solution or using AWS SQS. The primary reasons for this decision were:
The team is already familiar with Redis, which is a requirement for BullMQ, and we're already running Redis for caching and session storage. Adding BullMQ doesn't introduce any new infrastructure dependencies. ...
对应压缩版(来自 project-notes.md):
Team pick BullMQ for background jobs. No custom build, no AWS SQS. Why:
Already run Redis for cache+sessions. BullMQ need Redis. No new infra. SQS break local dev, hurt contributor setup.
以及"性能调查"一节:
The primary bottleneck is the database query that loads the task list. The current implementation fetches all tasks for a project in a single query, then for each task, makes a separate query to load the assignee's profile. This classic N+1 problem means that loading 500 tasks results in 501 database queries.
Main bottleneck: N+1 query. Load all tasks, then per-task query for assignee profile. 500 tasks = 501 queries. Slow.
再比如技术债清单里"认证系统"段落,原文是完整的复合长句("was originally implemented in a rush ... leading to the reconnection race condition we're currently experiencing"),压缩后变成电报体:"Auth system: rushed at launch, messy. Token refresh split across 3 files, inconsistent error handling. WebSocket auth separate from HTTP auth — causing reconnect race condition now. Needs refactor, but team wait for better test coverage first."
把整份 fixture 的原始/压缩两版并排读完,可以归纳出 caveman 压缩的四个稳定特征,它们全部有源码级的规则依据(见 SKILL.md 的 Compression Rules 与 compress.py 的 build_compress_prompt):
| 内容 | 原始版 | 压缩版 | 处理规则 |
|---|---|---|---|
| 冠词/填充词 | "After extensive discussion", "It is important to" | 全部删除 | Remove: a/an/the、just、really、basically、actually 等 |
| 从句与被动语态 | "which is a requirement for BullMQ" | "BullMQ need Redis" | 改为短语/断句,允许残句(fragments OK) |
| 冗余连接词 | "The primary reasons for this decision were:" | "Why:" | Remove: however、furthermore、additionally 等连接性废话 |
| 章节标题 | ## Architecture Decision: Background Job Processing (March 2026) |
逐字不变 | Preserve Structure:所有 heading 原文保留 |
| 内联代码 | `tasks(project_id, status, updated_at)` |
逐字不变 | 反引号内容 EXACT 保留 |
| 技术术语与库名 | BullMQ、Knex.js、TanStack Virtual、MSW | 逐字不变 | 技术词/库名/API 名不压缩 |
| 专有名词 | Sam、Alex、Maya、Taskflow | 逐字不变 | 项目名、人名、公司名保留 |
| 数值与日期 | 500 tasks、501 queries、15 分钟、30 秒 TTL、March 2026 | 逐字不变 | 日期、版本号、数值保留 |
| 有序列表 | 1.–5. 方案编号 | 1.–5. 编号保留,条目文字压缩 | 保留编号结构,压缩单元格/条目文本 |
| 加粗标记 | **shadcn/ui with Radix primitives** |
保留 | Markdown 结构保留,正文压缩 |
值得强调的是:这份 fixture 里没有一个围栏代码块(fenced code block),但内联代码、路径与数值锚点无处不在——比如安全评审中的 localStorage、/admin/jobs、express-rate-limit。压缩版把这些全部原样搬走,而叙述部分从完整的"审计叙事"塌缩成"电报体结论"。整份文件从 1145 个计 token 降到 535 个,节省 53.3%(数据见 skills/caveman-compress/README.md 的 Benchmarks 表),是五个官方 fixture 中压缩率第二高的——第一是纯偏好的 claude-md-preferences.md(59.6%),最低是结构更杂的 mixed-with-code.md(36.9%)。这也印证了规律:叙述越密、代码锚点占比越低的文件,压缩收益越大。
3. 端到端管线:从 /caveman-compress 到落盘的安全链
fixture 不是手工写的,它是管线跑出来的产物。下面按 skills/caveman-compress/scripts/ 里的真实实现,走一遍这份 project-notes 被压缩时经历的每一步。
3.1 入口与预检(cli.py)
技能入口是 /caveman-compress <filepath>,底层运行 python3 -m scripts <absolute_filepath>(见 SKILL.md 的 Process 一节)。cli.py 先做存在性/文件检查、用 detect_file_type 打印检测结论、should_compress 判定是否继续;.md 命中可压缩扩展名,直接放行。
3.2 类型检测与敏感文件拒收(detect.py / compress.py)
detect.py 维护 COMPRESSIBLE_EXTENSIONS = {".md", ".txt", ".markdown", ".rst", ".typ", ".typst", ".tex"} 与 SKIP_EXTENSIONS(.py、.js、.json、.yaml、.sql 等),对无扩展名文件还会做 shebang / JSON / YAML / 代码行占比(>0.4 判为代码)内容启发;should_compress 额外硬跳过一切 *.original.md 备份文件,保证"压缩产物不会成为下一轮压缩输入"。
进入 compress.py 的 compress_file 后还有三道硬闸门(均先于加锁执行,避免被拒输入留下锁文件残留):
- 500KB 上限:
MAX_FILE_SIZE = 500_000,超限直接报错; - 敏感文件名拒收:
is_sensitive_path用正则与路径组件黑名单(.ssh、.aws、credentials、secret、*.pem等)拒绝任何"压缩会把原始字节送到第三方 API"的文件——这是数据边界保护; - 跨会话独占锁:
file_lock用 POSIXfcntl.flock/ Windowsmsvcrt.locking在共享状态目录上的哈希锁文件上加 OS 级独占锁,等待上限LOCK_WAIT_SECONDS = 900(15 分钟),进程崩溃会自动释放,无需陈旧锁簿记。
3.3 代码掩码:让模型"看不见"代码
_compress_file_locked 在读入源文件后,先 split_frontmatter 把 YAML frontmatter 整体摘出(frontmatter 从不交给模型,输出时原样拼回),再调用 mask_code_blocks:把围栏代码块(``` / ~~~)与 4 空格缩进代码块逐块替换为形如 @@CAVEMAN_PRESERVED_CODE_0_<sha256前16位>@@ 的不透明标记,再把这个"代码已打码"的正文交给 Claude。模型返回后经 restore_code_blocks 还原;若任一标记被模型删除、复制或缺改(restored.count(marker) != 1),直接抛 ValueError 拒绝写入——代码保真是 fail-closed 的。本 fixture 无围栏代码块,但内联代码、路径等锚点则靠压缩提示词中的 STRICT RULES("Do NOT modify anything inside inline backticks / Preserve ALL URLs exactly / Preserve ALL headings exactly")约束。
模型调用优先走 Anthropic SDK(设置 ANTHROPIC_API_KEY 时,默认模型可用 CAVEMAN_MODEL 覆盖,max_tokens=8192),否则回退到 claude --print CLI;每次调用有超时上限(LOCK_WAIT_SECONDS // (MAX_RETRIES + 1) = 300 秒)。
3.4 校验、定点修复与回滚(validate.py)
写入新文件后进入"校验 + 重试"循环(MAX_RETRIES = 2)。validate.py 对比备份原件与压缩产物:
- 标题:
HEADING_REGEX提取所有#~######标题,级别与文本必须逐一匹配——这就是 fixture 里 5 个章节标题(连同日期)一字不差的原因; - 代码块:逐行解析围栏块(支持不同长度围栏与嵌套),并额外识别缩进代码块(注释里明确解释:漏掉缩进代码块曾让
kubectl delete pod --all -n prod被悄悄改写而校验"干净通过"——"对一个破坏性命令给出干净通过是此工具最坏的失败"); - URL、内联代码、文件路径:
URL_REGEX、反引号配对、PATH_REGEX/DEFINITE_PATH_REGEX(路径误报宽容、明确路径硬失败)。
校验失败时不做重新压缩,而是把错误清单 + 原文(仅参考)+ 压缩稿喂给 build_fix_prompt,要求模型"只修列出的错误,其余逐字不动";修复产物还要过"首行锚点"检查(原文以 --- 或 # 开头时,修复稿首行必须一致,防模型夹带前言文本)。若 2 次重试后仍失败:把原始字节原子写回目标文件、删除备份,报告错误——原始文件最终保持原状。
3.5 备份、原子写入与"备份先验后写"
两个工程细节决定这份 fixture 生成过程零数据丢失风险:
- 备份放在树外:
backup_dir_for把原件字节级(write_bytes_atomic写原始 raw bytes,非重渲染)保存到$XDG_DATA_HOME/caveman-compress/backups/<父目录名>/(Windows 为%LOCALAPPDATA%\caveman-compress\backups\<父目录名>\),而非源文件旁边——目的是让技能自动加载器不会把.original.md当活文件二次摄入。本仓库 tests/caveman-compress/ 下的*.original.md正是按此命名约定留存的五组基准原件。 - 原子写 + 备份回读验证:
write_text_atomic先编码、写同目录临时文件、fsync、os.replace换入,并保持原文件权限位;写目标前会先backup_path.read_bytes() != original_raw逐字节核对备份,不一致就删掉坏备份并中止,绝不让用户拿到"损坏备份 + 已压缩主文件"的组合。行尾符也被保留(CRLF 文档保持 CRLF)。
4. 官方基准中的位置与适用边界
skills/caveman-compress/README.md 给出的五个 fixture 官方基准:
| 文件 | 原始 tokens | 压缩后 tokens | 节省 |
|---|---|---|---|
claude-md-preferences.md |
706 | 285 | 59.6% |
project-notes.md |
1145 | 535 | 53.3% |
claude-md-project.md |
1122 | 636 | 43.3% |
todo-list.md |
627 | 388 | 38.1% |
mixed-with-code.md |
888 | 560 | 36.9% |
| 平均 | 898 | 481 | 46% |
README 同时给出诚实边界声明:结构校验(标题、代码块、URL、文件路径逐字保留)在全部 fixture 上通过,但"结果并不证明语义等价于其他文件或其他模型"。对 project-notes 这类多章节长文档,收益主要来自叙述句的塌缩(冠词、从句、连接词、重复表述),而章节骨架、编号方案列表(1–5 条方案及其取舍顺序)、内联技术锚点全部存活——这正是该技能"输入 token 减半、结构零漂移"的核心承诺。
适用前提与限制(以当前仓库实现为准):
- 仅压缩自然语言文件(
.md/.txt/.rst/.typ/.typst/.tex及无扩展名文本文件),代码/配置文件一律跳过; - 单次输入上限 500KB,敏感命名文件硬性拒收;
- 依赖 Claude(SDK 需
ANTHROPIC_API_KEY,或本机已安装claudeCLI),Python 3.10+; - 只有"首次压缩"和"校验失败后的定点修复"消耗 token,检测、掩码、校验、写入全部是本地 Python。
如果你想亲手复现本文的对照,可直接查看 tests/caveman-compress/project-notes.original.md 与 tests/caveman-compress/project-notes.md 的完整逐行 diff,再对照 skills/caveman-compress/scripts/compress.py、skills/caveman-compress/scripts/validate.py 与 skills/caveman-compress/scripts/detect.py 三个文件,即可完整还原从"叙述型项目笔记"到"53.3% token 节省的 caveman 版"的全过程。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00