caveman 中的 cavecrew-investigator:一个只读代码定位子代理及其压缩输出契约
本文以 agents/cavecrew-investigator.md 这一子代理定义文件为核心,完整拆解 caveman 项目中 cavecrew-investigator 的定位职责、只读工具边界、path:line — symbol — note 压缩输出契约与拒绝策略,并结合 skills/cavecrew/SKILL.md 决策指南、src/hooks/cavecrew-model-overrides.js 模型覆盖实现及其测试,说明该子代理如何在 Claude Code 等宿主代理中落地、如何被调用与如何配置模型。读完后你可以直接复用这套"定位—汇报—停止"的子代理契约设计,理解压缩工具结果对主上下文预算的节省机制。
它是什么:cavecrew 三人组中的"侦察兵"
caveman 是一套主打 token 节省的 Claude Code skill 体系,其中 cavecrew 由三个输出经过"原始人式压缩"的子代理组成。仓库根目录 CLAUDE.md 的模块索引对它的定义是:
agents/cavecrew-investigator.md— Read-only locator subagent (haiku). Output contract:path:line — symbol — note.
它与另外两位成员的分工(见 skills/cavecrew/SKILL.md):
| 子代理 | 职责 | 典型场景 |
|---|---|---|
cavecrew-investigator |
只读代码定位 | "X 在哪里定义 / 谁调用了 Y / 列出 Z 的所有使用" |
| cavecrew-builder | 1–2 个文件的手术式编辑,3+ 文件直接拒绝 | 范围明确的 typo 修复、单函数重写 |
| cavecrew-reviewer | 单行式、带严重度标记的 diff/文件审查 | "review 这个 PR / 我的 diff" |
它与 Anthropic 的默认 Explore 代理做的是同一类工作,区别在于返回给主线程的工具结果是被压缩过的。SKILL.md 中给出的量化对比是:一次普通 Explore 可能返回 2k tokens 的散文,而同样结论从 cavecrew-investigator 返回约 700 tokens——子代理的工具结果会被原样注入主上下文,这个差距在单次会话中委派 20 次时就是"上下文耗尽"与"完成任务"的区别。
定义文件逐行解读:frontmatter 决定权限与模型
cavecrew-investigator 的完整定义见 agents/cavecrew-investigator.md(插件分发副本在 plugins/caveman/agents/cavecrew-investigator.md,两份内容一致)。frontmatter 四项各有明确含义:
name: cavecrew-investigator
description: >
Read-only code locator. Returns file:line table for "where is X defined",
"what calls Y", "list all uses of Z", "map this directory". Output is
caveman-compressed so the main thread eats ~60% fewer tokens than
vanilla Explore. Refuses to suggest fixes.
tools: [Read, Grep, Glob, Bash]
model: haiku
description:这段描述本身也是"原始人压缩风格"的范例——它同时声明了输入问题类型(where/what calls/list uses/map directory)、输出特性(caveman 压缩,主线程省 ~60% token)和边界(Refuses to suggest fixes)。宿主代理在做委派决策时,正是靠这段描述判断该任务是否适合派给它。tools: [Read, Grep, Glob, Bash]:注意这里有Bash,比 docs/technical/exploration-and-delegation.md 中描述的fastcontext探索器(仅 Read/Glob/Grep,"cannot edit files or run commands")多了一项。后文"工具策略"一节解释这项 Bash 被限定在哪些命令上。model: haiku:定位是纯检索任务,不需要最强的推理模型,默认钉在 haiku 上以进一步压低单次委派成本。这个默认值可被环境变量覆盖,见后文"模型覆盖"一节。
正文第一行是全局语体指令:"Caveman-ultra. Drop articles/filler/hedging. Code/symbols/paths exact, backticked. Lead with answer."——省掉冠词、填充词与含糊措辞,但代码、符号、路径必须精确且加反引号,答案先行。这是压缩输出契约能成立的前提:省的是自然语言外壳,不是事实精度。
职责边界:Locate. Report. Stop.
文档中 "Job" 一节只有一句话:Locate. Report. Stop. Never edit, never propose fix.(定位、汇报、停止。绝不编辑,绝不提出修复方案。)
这条边界有两层工程意义:
- 输出纯净性:子代理返回的内容直接进主上下文。如果定位结果里混入"我建议在 X 处添加防御性判空"之类的修复建议,主线程会把这些 prose 一并吞掉,压缩收益被稀释。只给证据,不给意见。
- 与 builder 的职责切分:需要动手时,investigator 不是"顺便修一下",而是触发拒绝语把任务交出去(见下节)。这与
SKILL.md的反模式提醒一致:不要对已知文件的编辑任务先派 investigator 再派 builder,也不要指望 investigator 对 5 文件重构给出可行方案——builder 会直接回too-big.,浪费一轮。
输出契约:主线程可以依赖的格式
这是整个定义文件的核心。"Output" 一节规定了四套规则,全部以可解析的结构化形式表达:
<path:line> — `<symbol>` — <≤6 word note>
<path:line> — `<symbol>` — <≤6 word note>
- 行格式:每行是
路径:行号 — 反引号包裹的符号 — 不超过 6 个词的注释。SKILL.md强调这个格式"Always file-path-first, line-number-attached, backticked symbols. Safe to grep withpath:\d+"——即主线程或自动化脚本可以直接用path:\d+这样的正则从结果中抽取引用,这是"压缩但不失机器可读"的关键设计。 - 分组头:结果行 ≥3 行时,用一个单词的分组头归类,限定在
Defs:/Refs:/Callers:/Tests:/Imports:/Sites:六个词内。命中仅 1 行时不加头。 - 零命中:统一返回
No match.,禁止猜测或编造位置(这与fastcontext探索器"无结果时必须回no relevant locations found而非虚构引用"的证据规则同源,见 exploration-and-delegation.md 的 Evidence rules 一节)。 - 总计行:末行给出计数,如
2 defs, 5 refs.;0 或 1 时省略。
文档内置示例:一次完整的定位汇报
定义文件末尾给了一个 Q/A 示例,问题用原始人体:"where symlink-safe flag write?"(符号链接安全的标志位写操作在哪?),返回:
Defs:
- hooks/caveman-config.js:81 — `safeWriteFlag` — atomic write w/ O_NOFOLLOW
- hooks/caveman-config.js:160 — `readFlag` — paired reader
Callers:
- hooks/caveman-mode-tracker.js:33,87
- hooks/caveman-activate.js:40
Tests:
- tests/test_symlink_flag.js — 12 cases
2 defs, 3 callers, 1 test file.
这个示例同时展示了契约的全部要素:多行时按 Defs/Callers/Tests 分组、同行可并多个行号(:33,87)、note 控制在 6 词内("atomic write w/ O_NOFOLLOW")、末行总计。值得注意的是示例中引用的 tests/test_symlink_flag.js 在当前仓库确实存在(tests/test_symlink_flag.js),说明示例取材自本仓库真实功能;但示例中的 hooks/ 前缀与行号是示意性路径(本仓库对应实现位于 src/hooks/ 下),引用时应以实际检索结果为准。
工具策略:Bash 只用于"更快的检索"
"Tools" 一节把四个工具的分工压成一句话:
Grepfor symbols/strings.Globfor paths.Readonly specific ranges.Bashforgit log -S/git grep/findwhen faster.
拆开看:
Grep负责符号与字符串匹配——"X 在哪定义"、"谁引用 Y"的第一手段;Glob负责路径模式匹配——"map this directory" 类问题;Read限定"only specific ranges":只读具体行区间,避免把整个文件灌进子代理上下文(子代理上下文同样有预算,虽然最终不占主线程);Bash白名单化:只允许git log -S(pickaxe 搜索符号引入/删除的提交)、git grep、find这类比逐文件 Grep 更快的检索命令。这与cavecrew-reviewer的同类约束(Bashonly forgit diff/git log -p/git show. No mutating commands.)风格一致:Bash 权限存在,但语义上被围栏在"只读检索"内。
从源码结构看,这类约束是靠 prompt 围栏而非系统权限实现的——tools frontmatter 中的 Bash 是宿主代理(Claude Code)侧的真实权限,围栏是否被遵守依赖模型遵循指令;这也是 SKILL.md 提醒"Isolation is mechanism, not outcome guarantee"(隔离是机制,不是结果保证)的原因。
拒绝策略与 Auto-clarity
"Refusals" 一节规定了两条固定拒绝语:
| 被问到 | 返回 |
|---|---|
| 让它修 bug | Read-only. Spawn cavecrew-builder. |
| 让它做设计 | Read-only. Spawn cavecrew-builder or use main thread. |
拒绝语本身就是可被主线程解析的结构化信号(builder 的终态行如 too-big. / regressed. 同理),主线程拿到后可以直接路由给 cavecrew-builder 或留在主线处理,不需要再花一轮澄清。
"Auto-clarity" 一节是三条 cavecrew 共享的安全阀:
Security warnings, destructive ops → write normal English. Resume after.
遇到安全警告或破坏性操作时,临时切回正常英文措辞(因为碎片化原始人文体在安全语境下可能被误读),说完再恢复压缩风格。SKILL.md 将其泛化为三个触发条件:安全警告、不可逆操作确认、以及"片段歧义可能被误读"的任何输出。
落地实现:CAVECREW_INVESTIGATOR_MODEL 覆盖机制
frontmatter 里 model: haiku 只是默认值。skills/cavecrew/README.md 说明:investigator 与 reviewer 默认钉在 haiku,builder 没有 model: 行(用 API 会话默认模型);启动宿主代理前设置环境变量即可逐代理覆盖,取值就是任何 Claude Code agent frontmatter 里能用的模型名(haiku、sonnet、opus):
| 环境变量 | 作用对象 |
|---|---|
CAVECREW_INVESTIGATOR_MODEL |
cavecrew-investigator |
CAVECREW_BUILDER_MODEL |
cavecrew-builder |
CAVECREW_REVIEWER_MODEL |
cavecrew-reviewer |
这个覆盖不是靠重发布 agent 文件,而是靠 SessionStart 钩子现场打补丁。实现见 src/hooks/cavecrew-model-overrides.js:
AGENT_ENV_MAP把CAVECREW_INVESTIGATOR_MODEL映射到agents/cavecrew-investigator.md(第 26 行),由caveman-activate.js在 SessionStart 早期调用;- 核心函数
patchFrontmatterModel(content, modelValue)只做 frontmatter 内的model:行操作:已有model:行则原位替换,没有则在tools:行后(或收尾---前)插入;同时保留文件原始行尾(CRLF/LF),避免在 Windows 上制造混合行尾; - 安全护栏:空值/含控制字符的值 → 静默 no-op;文件不存在或布局不符 → 静默 no-op;
insideGitWorkTree()会向上最多 64 层找.git,若插件根目录位于 git 工作树内(即这是源码检出而非安装副本)则整体跳过——否则每次开会话都会改写被跟踪的agents/*.md、弄脏工作区; - 所有文件系统错误静默失败,"never block session start"。补丁持久化到插件更新或重装为止,且只改
model:一行,prompt 正文保持原样,后续上游更新仍可正常生效。
测试覆盖见 tests/test_cavecrew_model_overrides.js,用 INVESTIGATOR_FM 构造真实 frontmatter 片段,验证如"investigator 的 model: haiku 被替换为 opus 时,旧行消失、正文不丢失"等用例。
安装与分发:它装到哪里
从源码结构看,这份 agent 文件有三个分发通道:
- Claude Code 插件:插件市场以仓库根为 source(见 CLAUDE.md 中"
skills/is auto-discovered wholesale"一节),agents/cavecrew-investigator.md随插件整体发现,安装后落在~/.claude配置目录下,模型覆盖钩子也只在这一布局中生效; - opencode:bin/install.js 的
OPENCODE_AGENT_FILES列表(第 699 行)包含cavecrew-investigator.md等三个文件,安装时会拷贝到 opencode 的 agents 目录,skills/cavecrew目录同列; - 插件包内副本:plugins/caveman/agents/cavecrew-investigator.md 与源文件内容一致,随
cavecrewskill 一起打包(OPENCODE_SKILL_DIRS/HERMES_SKILL_DIRS均含cavecrew)。
与其他代理的协作模式
investigator 很少单独工作。SKILL.md 的 Chaining patterns 给出三种编排:
- Locate → fix → verify(最常见):investigator 返回站点列表 → 主线程挑 1–2 个站点把精确
path:line交给 builder → reviewer 审计 diff; - Parallel scout(检索面很宽时):一条消息里并行 spawn 2–3 个 investigator,从不同角度(defs / callers / tests)分头检索,主线程聚合;
- Single-shot edit(位置已知时):跳过 investigator,直接给 builder 精确
path:line。
对应的反模式清单同样值得记住:不知道文件就别直接派 builder(否则主线程要耗 token 传递上下文);不要拿 investigator→builder 链路去做多文件重构;不要在 investigator 输出上期待散文——它是结构化、乃至"简短到近乎隐晦"的,人类要直接读时请自己转述。
小结
cavecrew-investigator 是一个以"输出契约"为产品形态的子代理:frontmatter 声明只读工具面与 haiku 默认模型,正文用 12 行左右的原始人文体锁定"定位—汇报—停止"的行为边界,四套输出规则(行格式、分组头、零命中、总计行)保证结果既能被 LLM 主线程低成本消费、也能被 path:\d+ 正则机器解析;拒绝语与 auto-clarity 则分别处理"越界请求"和"安全表达"两类边缘情形。配合 CAVECREW_INVESTIGATOR_MODEL 的 frontmatter 热补丁机制与插件/opencode 双通道分发,它构成了一套可复制的模式:当你想给任何 LLM 宿主代理加一个"低 token 侦察兵"时,可以照这份文件的骨架——权限面、语体指令、输出契约、拒绝语、安全阀——逐一落位。
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 StartedRust0623
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