首页
/ Caveman 的 caveman-learn 编辑技能:以逐编辑同意为核心的 Token 成本修复闭环

Caveman 的 caveman-learn 编辑技能:以逐编辑同意为核心的 Token 成本修复闭环

2026-09-06 12:31:49作者:戚魁泉Nursing

本篇技术指南以 skills/caveman-learn/CLAUDE.md 为核心,讲解 Caveman 项目中 caveman learn 工作流的两段式分工:Go 代理(analyzer)只负责测量 token 流向并输出排序后的修复计划,而 caveman-learn 技能才是 agent 加载后执行修复的那一半——每个改动都需用户逐条确认,且必须通过"净 token 下降"与"不许让 agent 变笨"两道门槛。读完本文,你将理解该技能的目录结构、CLI 安装路径与字节级一致性保障、写权限边界,以及从读取报告到记账验证的完整同意循环。

1. 定位:测量与执行的分工

caveman learn 是 Caveman 的 token 成本分析器。它扫描 agent 会话历史,识别出 token 消耗去向(官方称为 token sinks),并写出一份带排序的修复计划(plan)。但分析器本身只读:它物化候选修复方案,却不修改任何用户配置。

caveman-learn 技能承担另一半职责:agent 加载它之后,逐条向用户提出修复建议,仅在用户对该条编辑明确回答 yes 之后才动手,并在改完后重新测量,确认修复确实降低了 tokens/turn。按 skills/caveman-learn/CLAUDE.md 的表述,该技能是 learn 规范第 10 节所描述的"闭环者"(loop-closer),并额外引入了 cavemem_offload 这一类修复动作。

技能本体 skills/caveman-learn/SKILL.md 在 frontmatter 中声明了触发语义:description 指明它在"被要求降低 agent token 成本、查询 caveman 已省了多少、修剪臃肿的 CLAUDE.md、或把反复粘贴的上下文卸载到 cavemem"时被激活;name: caveman-learn 是规范文件名。这一设计让 Claude Code 等宿主能够按描述匹配自动加载技能。

2. 目录结构与文件职责

skills/caveman-learn/ 目录布局在 CLAUDE.md 中有明确约定:

文件 职责
SKILL.md 技能规范本体(frontmatter + "读计划 → 按类同意循环"正文),是唯一的 source of truth
tests/skill-file.test.mjs 断言规范文件"格式良好且诚实":frontmatter 存在、净 token 下降门槛、不许变笨护栏、逐编辑同意、可逆性均已声明;行为类发现不含命令式措辞;无占位符
README.md 面向用户的安装说明与行为概述
package.json 包元数据

测试文件 skills/caveman-learn/tests/skill-file.test.mjs 值得细看,它是用可执行断言固化的"诚实契约":

  • frontmatter 检查:assert.match(skill, /^---\nname: caveman-learn\n/) 要求声明名称;description 必须非空;
  • 绑定规则检查:逐条正则断言正文必须出现 net-token-negative gatenever make the agent dumberconsent per editreversibleinferred only 等关键词;
  • offload 动作检查:必须描述 cavemem_offload 修复类型,必须校验 content_sha256,必须给出字节级恢复路径 caveman mem recover;
  • 纵向闭环检查:必须声明 caveman learn applied <sink_id> 只在该编辑通过复测后才记录,必须列全四种纵向裁定(improved, unchanged, regressed, or insufficient_data),且回归必须如实呈现并给出精确回滚路径;
  • 负面清单:禁止出现 you don't needyou over-use 等把行为发现写成命令的措辞,也禁止出现任何 $ 符号(货币符号);
  • 占位符扫描:通过字符串拼接构造 TODOFIXME 等标记,确保正文没有未完成的草稿残留(拼接写法是为了避免测试文件本身触发仓库的全局占位符扫描)。

这种"用测试锁定 prompt 行为"的做法意味着:任何人改动 SKILL.md 而删掉某条诚实规则,CI 会直接失败。

3. 安装路径:CLI 内嵌字节级一致的副本

CLAUDE.md 给出的安装命令是:

caveman tools skills install caveman-learn

它把规范文件写入目标仓库的 .claude/skills/caveman-learn/SKILL.md(Claude Code)或 ~/.codex/skills/caveman-learn/SKILL.md(Codex)。skills/caveman-learn/README.md 还列出了用户级安装变体:

caveman skills install caveman-learn            # 本仓库的 .claude/skills
caveman skills install caveman-learn --user      # 所有仓库(~/.claude/skills)
caveman skills install caveman-learn --agent codex

关键在于发布出去的 CLI 是单文件产物,不携带任何同目录资产,因此它必须内嵌一份与规范文件字节级一致的副本。CLAUDE.md 中该常量名为 CAVEMAN_LEARN_SKILL_MD;从当前源码结构看,这份内嵌副本落在生成文件 packages/cli/src/agent-skills.generated.tsAGENT_SKILLS 记录中("caveman-learn" 键对应完整 SKILL.md 正文,含 frontmatter)。漂移守卫 packages/cli/tests/skills.runtime.mjs 断言内嵌副本与规范文件逐字节相等,因此文档特别警告:改这个文件,必须同时改那个常量

安装逻辑可在 packages/cli/src/index.tsskills() 函数中得到印证:

  • --agent 参数仅接受 claudecodex,否则报错退出;
  • 安装时从内嵌的 SKILLS 表读取正文,mkdirSync 建目录后 writeFileSync 落盘,即"生成的字节级一致副本"注释所指的流程;
  • 默认还会把技能做 pixel 化转换(可用 --no-pixel 关闭),并提示宿主如何自动加载:Claude Code 按 description 匹配自动加载,Codex 从 ~/.codex/skills 自动读取技能目录。

4. 写权限边界:技能是唯一的写者

CLAUDE.md 用 "Boundary (binding)" 一节划定了强制性的写权限边界,这是整个 learn 工作流的权限模型:

  1. 技能(借助 agent 自带的文件工具)是唯一能编辑用户配置的东西。 所有对 CLAUDE.md/AGENTS.md 的落盘修改都发生在同意循环内、用户 yes 之后。
  2. caveman learn apply 保持只读。 它只物化候选修复(materializes candidates),例如把候选 JSON 写到 ~/.caveman/candidates/ 下,本身不动用户文件。
  3. caveman mem * 是机械的存储操作。 remember/forget/recall/recover 只操作 cavemem 存储,不做决策。
  4. offload 动作在修剪前强制执行两道门槛:净 token 下降与"不许让 agent 变笨"。

这个边界的意义在于把"建议"和"执行"解耦:分析器与 CLI 永远不越权写盘,写盘责任与同意责任完全集中在技能一侧,从而任何一次配置变更都可追溯到一次用户确认。

5. 核心工作流:读计划 → 按类同意循环

SKILL.md 正文定义了技能加载后的完整行为。以下按其脉络完整展开。

5.1 新出现的 sink 类别及其语义

技能首先向使用者解释报告中可能出现的新 sink,并规定各自"能说什么、不能说什么":

  • cache_efficiency —— 缓存复用后每百万输入 token 的实际成本。它是一个比率(其他 sink 按它计价),不是量,严禁把它加进任何总量;
  • tool_output_portfolio —— 按排序主导上下文的工具调用形态;
  • session_outcomes —— 无 commit 窗口内的会话占 token 的比例。它是相关性数据,必须当作观察呈现并读出处限说明——"没有 commit 的会话不等于浪费的会话";
  • subagent_spend —— 在子 agent 中运行的上下文占比。仅供可见性,不得引申成"少开子 agent"的建议;
  • procedure_repeat:* —— 蒸馏候选,走 5.4 节的技能蒸馏流程。

5.2 读取计划与展示 spend

第一步执行 caveman learn report --json,解析 caveman.learn.v1 JSON,展示 Cave Score 及其四个分量与排序后的 token sinks,并逐条说明每个 sink 的类别与依据(basis)。行为类 sink 的数字按事实呈现,但建议部分必须软化,不得变成命令式。

如果计划携带 spend 块,必须以它开头:扫描窗口的花费,以及缓存复用后的有效输入比率(effective_input_multiplier)。展示金额的规则是硬性约束:

  • spend 是窗口花了多少,绝不是"修复能拿回多少";
  • 必须说明覆盖的窗口,严禁外乘成月/年/run rate;
  • unpriced 非空,必须声明总额只是下限并点名被排除的模型;
  • 订阅套餐(Max/Plus/Advanced)下边际成本为零,该数字是 token 的 API 等价价值,不是实际支出;
  • 任何时候都不得称之为"已验证"。

在正式提案前,可运行 caveman learn simulate <sink_id> 预览,但只能作为"对已扫描历史的规模汇总"呈现——它对历史求和,从不向前预测

5.3 REDUCIBLE 与 RECURRING_CONTEXT 两类同意循环

REDUCIBLE(臃肿的 CLAUDE.md、从未被调用的技能):

  1. caveman learn apply <sink_id> --dry-run —— 只物化候选,不编辑任何东西;
  2. 提案给出具体 diff,展示 before -> after 的 tokens/turn;
  3. 请求用户 yes/no;同意后用 agent 自己的文件工具落盘;
  4. 重跑 caveman learn report --json(或重新统计被改文件)确认下降。这就是净 token 下降门槛:after 不低于 before 就回滚并报告,永远不保留一个没有降低 tokens/turn 的编辑

RECURRING_CONTEXT(跨会话反复重建的重块,修复类型 cavemem_offload): 把该块移入 cavemem,使其被紧凑召回而不是每轮重贴。候选里只携带定位器(locator)——绝不携带块体,流程为:

  1. caveman learn apply <sink_id>,读取写入 ~/.caveman/candidates/ 的候选 JSON;只取定位器、数字与提案的指针文本,不信任候选中的任何"正文"(它不存在);
  2. 自行本地重读真实块:打开定位器的 rel_path,定位 jsonl_line,用同样方式重新切分该 turn(按空行、按顺序切分),取 block_index,校验原始块的 sha256 是否等于定位器的 content_sha256——不等说明文件在扫描后已变化,放弃该项;
  3. caveman mem remember -- "<真实块>" 存库并捕获返回的 id。末尾的 -- 结束选项解析,使以 --- 规则开头的块被逐字存储而不是被当成 flag;
  4. 诚实测量门槛:before = 块的 tokens/turn(它每轮都加载);after = 指针的 tokens/turn 加上召回成本(运行 caveman mem recall "<topic>",读取命中上的 tokens_added)。after 不低于 before 就 caveman mem forget <id>、不动源文件、停止;
  5. 修剪源文件并写入指针文本:从 CLAUDE.md/AGENTS.md 对应小节删除该块(若是用户手工粘贴的内容,则告知用户别再粘贴),在原位置写入候选的指针文本。指针指明召回路径:紧凑形式用 caveman mem recall "<topic>",字节级原文用 caveman mem recover <handle>;
  6. 不许让 agent 变笨:收工前必须确认 caveman mem recall "<topic>" 返回命中指针已就位。召回不到或指针没写,就回滚(caveman mem forget <id> 并恢复源文件)。"移除上下文却没有可用的召回路径"正是这道护栏唯一要拦截的失败。

5.4 SKILL_DISTILLATION 与 LOAD_BEARING

SKILL_DISTILLATION(procedure_repeat sink,修复类型 skill_distillation): 用户跨会话重复的一串工具步骤,写成 skill 可以避免 agent 反复重新推导——但 skill 每个会话都加载进前缀,只在命中该模式的会话才回本。这与报告所惩罚的 dead_load sink 同形,因此必须单独分级、不得走捷径:

  • 永远不把它过净 token 下降门槛——那道门槛只是重数一个文件,看不见成本与收益落在不同地方的情况;
  • 先展示候选:步骤、重复出现的会话数、这些片段消耗的 token,并明说回本尚未证明;
  • 用户同意后写 skill,并在同一步启动 holdout:caveman learn experiment start <label> --sink <sink_id> --fix-kind skill_distillation;随后 caveman learn experiment arm <label> off 关闭它工作一段可比时长。每个 arm 至少 5 个会话才存在裁定;
  • caveman learn experiment report <label> 读结果:insufficient_data 意味着继续跑,绝不能包装成小胜;regressed 意味着删掉 skill,并直说;
  • 实验框架比较的是每会话中位 token。如果它标记开启臂每轮工具错误更多,必须把它放在最前:更便宜但更容易失败的会话不是节省。

LOAD_BEARING:永不触碰。 它出现在报告里只是为了让评分保持诚实。

5.5 节省记账:按测量方式分组

caveman learn savings 账本展示已应用修复的回本,按如何测量分组——分组不是装饰,而是论断的强度:

测量级别 含义
deterministic_remeasure 被编辑的文件被重新统计,本地最强的一级
controlled_holdout 本机开/关对照测量
counterfactual_replay 真实历史在改动应用后重放
interrupted_time_series 改动前会话 vs 改动后会话,无对照臂

三条绑定规则:

  • 永不跨级求和,永不给出单一混合的"节省"头条——重数过的文件与前后中位数不是同一类证据;
  • 把某行呈现为收益时,必须读出声其 confounders。它们是常设警告而非小字,正是为"好消息"场景而存在;
  • attribution.provenance:intact 表示文件仍带着我们提案的编辑;changed_since 表示有人在它之上又改过,部分 delta 不属于我们——必须明说;target_missing 表示 delta 根本挂不到该修复上。changed_sincetarget_missing 的行永远不得作为 caveman 的结果呈现。

回归按设计不携带美元数字,必须连同裁定一起呈现并给出回滚路径,不得淡化或省略。

6. 绑定规则汇总与相邻模块

SKILL.md 结尾的 Binding rules 是整篇行为契约的浓缩,与 CLAUDE.md 的 Boundary 一节互为表里:

  • 逐编辑同意。 不存在隐藏单个 diff 的 "apply all";
  • 编辑落盘且复测门槛通过后,运行 caveman learn applied <sink_id>,后续 learn 运行据此报告纵向裁定(improved / unchanged / regressed / insufficient_data);回归如实呈现,并给出该编辑的精确回滚路径;
  • 每个编辑可逆:精确报告改了什么;offload 的反操作是 caveman mem forget <id> 加恢复被修剪的源文件;
  • inferred only:本地数字永远不得呈现为已验证;货币仅在报告自带 spend 块或带价节省行时允许出现,且必须保留该块自身的框架——窗口限定、从不外推、从不宣称已验证;
  • 分析器只读。 技能是唯一写者,且只在 yes 之后写。

相邻资源可继续深入:cavemem 存储侧的说明见 mem/CLAUDE.md;技能打包的先在范例见 skills/caveman-explore/SKILL.md

7. 小结

caveman-learn 是 Caveman 项目把"token 成本优化"做成可验证闭环的关键一环:CLI 内嵌字节一致的规范副本并以运行时测试守卫漂移,安装只复制、不修改;分析器与 CLI 全程只读,唯一写者是加载了该技能的 agent,且每一次写都以一次用户 yes 为前提、以一次复测(或召回验证)为收尾。净 token 下降门槛、不许变笨护栏、按测量强度分组的节省账本与四种纵向裁定,共同保证了"省下的每一个 token"都必须可测量、可归因、可回滚——这正是 CLAUDE.md 所定义的 binding 边界的完整落地。

登录后查看全文
热门项目推荐
相关项目推荐