agent-stocktake — 代理定义质量审计
评估 <harness>/agents/*.md 下的每个代理定义,并给出评价:
保留 / 改进 / 更新 / 合并 / 降级为技能 / 解散 / 淘汰。审计单位是文件,但成本单位跨越两个层级——这种分割正是此技能作为 skill-stocktake 和 rules-stocktake 之间的第三个兄弟技能存在的原因。
设计说明——混合成本模型。技能的成本是触发污染(概率性触发会降低选择精度);规则的成本是驻留(始终加载)。代理则同时具备两者:其
description通过“可用代理类型”列表注入到每个会话中(驻留,类似规则),而其主体仅在代理被调用时加载(调用,类似技能——但由 Claude 的委托判断触发,而非通过描述匹配用户的话语)。因此,描述根据驻留密度审计,主体根据调用质量审计,每个层面都有不同的问题。
设计说明 2——批准的编辑由该技能本身在 apply 分支上应用。与 rules-stocktake 同理:不存在针对代理定义的改进引擎,并且语料库足够小,可以在一个上下文中读取——委托将会是过度工程。唯一的例外是“降级为技能”(创建技能是 skill-writer 的工作)。
根目录。 下面的 <harness> 是该审计覆盖的工作目录:默认为 ~/.claude,或者当技能 harness-stocktake 在项目模式下运行时为仓库的 .claude/(其 Roots 部分映射路径,命名那里为 n/a 的证据,并说明哪些约定检查适用)。分类账 <ledger> 属于该工作目录:当该技能的目录位于 <harness> 内时(工作目录携带其自身的技能副本),则为 ${CLAUDE_SKILL_DIR}/results.json,否则为 <harness>/stocktake/agent-stocktake.json(已安装的插件,或项目模式)。无论哪种情况,它都会在工作目录的其他更改一起在 apply 分支上提交。
模式($ARGUMENTS)
| 参数 | 行为 |
|---|---|
无 / full | 读取并评估每个代理定义(默认) |
changed | 仅重新评估 mtime 早于 <ledger> 的 evaluated_at 的文件;其余从分类账中沿用 |
changed 检测内联变更(无脚本):
以下路径以
${CLAUDE_SKILL_DIR}开头,即此 SKILL.md 所在的目录;不替换该变量的代理将其读取为该目录。
find <harness>/agents -name "*.md" -newermt "$(jq -r .evaluated_at "<ledger>")"
与 rules-stocktake 一样,阶段 1 的完整性检查始终在整个集合上运行——静默地淘汰技能或钩子会破坏未修改的代理主体内部的引用,而 mtime 无法察觉这一点。任何引用检查(下面的步骤 3)新失败的代理都会加入重新评估集。该脚本也没有 changed 模式,原因不同:近重复对和驻留总量是集合属性,因此部分语料库会给出错误数字,而不是更少的数字。
阶段 1——证据、清单、使用情况
步骤 1——运行证据脚本(不要手动计数)
uv run --frozen --project "${CLAUDE_SKILL_DIR}" \
--directory "${CLAUDE_SKILL_DIR}" \
python scripts/agent_evidence.py --root <harness>
证据模式:JSON 输出到 stdout,无论找到多少发现都退出码为 0(仅在语料库不可读时退出码为 2)。它测量和枚举;它从不分配评价。
将 JSON 转录为发现——切勿通过眼动重新测量。 它提供的内容:
| JSON 字段 | 替代内容 |
|---|---|
desc_words / body_lines / total_desc_words | wc -w / wc -l 流程 |
tools.items[].status + registry.known_tools | builtin / unverified / mcp。unverified 表示“不在脚本的日期列表中”,而非“不存在”——你拥有实时工具注册表,而脚本没有,因此在将其称为 Update 证据之前,请对照你自己的工具列表确认 unverified。registry.known_tools.as_of 是该列表的日期(当 --known-tools 替换它时为 null) |
tools.items[].server_in_config + registry.mcp_config_files | 不是评价。 它仅说明服务器是否出现在列出的本地配置文件中——由连接器提供的服务器永远不会出现在那里,因此 false 不是“已淘汰”。null 表示未解析配置源。在使用此字段之前,请检查每个源的 status;非 ok 源旁边的 false 不是证据 |
unreadable | 扫描无法打开的文件,附有原因。非空表示扫描不完整——命名它们并在声明任何总数之前修复,因为未读文件内部的抑制指令现在被认定为不存在 |
tools_unparsable | tools: 行未解析的代理(例如 YAML 流样式)。它们的 items 是 [],看起来与没有 tools: 键的代理相同——即无限制。但实际上并非如此;只是列表未被读取 |
description_near_duplicates | 近相同描述检查;孪生对分散了委托流量,因此每对都输入阶段 1 的重叠问题 |
suppression_candidates | 阶段 1 抑制问题的行号候选(目录是双语的,因为语料库是双语的) |
always_never_candidates | 过度约束问题的行号候选,仅限主体——描述是委托触发器,而非对代理的指令 |
最后两个是候选,而非发现——在写入之前,请阅读每个引用的行。目录也是下限,而非普查:它包含从一个语料库中提取的五种措辞,因此以其他方式编写的抑制指令(“跳过你不确定的内容”、“避免仅仅成为噪音的发现”)不会出现在任何 JSON 字段中。继续阅读这些内容;空列表并非清白。
测量的假阳性比例存储在此文件之外,因此此步骤不携带会过时的数字。
步骤 2——读取已经拥有其余部分的门禁
三项检查保留在脚本之外,因为在工作目录具有 lint 门禁(<harness>/scripts/hooks/harness_lint.py)时,该门禁拥有它们。当它存在时读取其输出;没有它时,通过读取/搜索进行这三项检查:
- 前页解析并携带
name、description、origin、model,并且model是别名→lint 脚本的lint_agents(在作者的工作目录中,它在.claude/verify.sh和预提交钩子中运行)。在工作目录声明了来源约定的情况下,缺少来源仍然是一个完整性发现,会输入 Improve。 -
name等于文件名主干→同一个lint_agents。它不容许判断,并且委托注册表以它作为键,因此它属于阻止提交的门禁——而不是供读者可能转录或不转录的证据。这里的发现是阻止的提交,而不是 stocktake 评价。 - 代理主体内部的 Markdown 链接可解析→同一个脚本的
lint_markdown_links(其范围包括agents)。
步骤 3——解析裸路径引用(其他人都没有的检查)
lint_markdown_links 仅看到链接语法,而 skill-health 的 scan_refs.py 扫描技能树。以散文形式写在代理主体内部的路径(<harness>/hooks/foo.sh、skills/foo/SKILL.md)两者都不覆盖。使用 Grep 搜索语料库中的它们,并使用 ls 检查每个路径。
步骤 4——清单
使用 Glob 枚举:<harness>/agents/*.md。将每个文件读取到一个上下文中(语料库很小)。驻留和主体列来自步骤 1 的 JSON——切勿信任文档(包括此文档)中写入的数字。
步骤 5——使用次数
证据输入:低使用次数提名一个候选,内容决定评价。当设置保留代理使用日志(<harness>/metrics/agent-usage.jsonl;缺失 = 使用次数 n/a)时,内联读取它(使用日志记录钩子,例如 log-agent-usage.sh,每次 Agent 工具启动时追加一个 invoke 事件,以其 agent 字段为键,其值为 subagent_type)并按代理计算 7/30/90 天内的每个代理事件。使用一次性的 python3/jq 单行代码聚合。
- 如果日志缺失或其第一个事件早于 90 天,将使用次数渲染为
—(未测量)。切勿将其渲染为 0——未测量和未使用是不同的情况。 - 计数是下限:仅捕获 Agent(Task)工具启动。Workflow
agent()工作器、插件内部分发以及绕过工具调用的内置机制不会到达钩子。低使用次数的代理进入阶段 1 作为 Retire/Dissolve 候选;其价值可能是偶发性的(例如论文审阅者仅在存款附近触发),因此内容决定评价。 - 日志自 2026-07-27 起存在;在该日期之前没有任何测量。
首先说明扫描结果,从步骤 1 的 JSON 中获取计数:agents_total 减去 unreadable 中的任何内容,必须命名,total_desc_words(列表的每次会话驻留税),完整性失败,以及使用次数是否可测量。将失败结果作为预先计算的证据带入阶段 1。
阶段 2——评估(完全内联,整体)
阅读每个主体同时查看整个集合。
阶段 1——二进制筛选(每个代理)。 每项明确的是/否;仅输出“否”的答案。前两个问题审计描述层(驻留),其余问题审计主体层(调用):
- 描述值得其驻留?——在始终加载的列表中密集、独特且支持选择;说明何时委托以及何时不委托
- 描述与主体一致?——描述承诺的内容正是主体所做的(过时的描述会在每个会话中错误地引导委托,即使主体本身没问题)
- 主体中是否包含抑制指令?——从此代理在阶段 1 JSON 中的
suppression_candidates开始,按步骤 1 处理。哪些算抑制指令:置信度阈值(“仅报告你 ≥ N% 确定的发现”、“为每个发现分配置信度并丢弃低置信度的”)、严重性下限(“仅高严重性”)、“保守”框架。当前的指导是:报告所有内容,在单独的步骤中过滤——抑制指令会被字面遵循并静默地丢弃发现。这里的“否”是一个通过反转改进的候选:以相反方向重写指令,切勿仅删除该部分(删除会留下抑制框架;反转会替换它) - 外部来源副本是最新的?——对于
origin是外部仓库且主体是逐字复制(例如内置覆盖,如Explore)的代理,主体开头注释中命名的源版本与已安装的工具(claude --version)匹配;不匹配则是 Update(与捆绑包进行差异比较并重新复制)。对于此类副本,跳过抑制和过度约束问题——主体是供应商的,不是我们的 - 主体中是否包含上一代过度约束?——当前模型原生持有的详尽分步判断过程、重复强调、应由周围上下文判断拥有的 ALWAYS/NEVER 对。阶段 1 的
always_never_candidates定位了这些标记;判断命中是直接指令还是仅仅引用这些词是这个问题的工作 - 是否已被基底吸收?——工作目录现在是否原生覆盖了该代理的工作(原生审查机制、计划模式、内置斜杠命令)?吸收→Dissolve 候选;声明必须具体命名其吸收器。使用新鲜/丰富上下文轴进行判断:受益于新鲜上下文的角色(审查、对抗性验证、本质评估——与提议者的沉没成本去相关)合法地存在于子代理中;受益于丰富上下文的角色(规划、生成、实现——用户意图、会话内约束)是主循环工作,因此对于它们,主循环本身算作吸收器。两个辅助理由合法地覆盖丰富上下文拉力:冻结输入渲染契约——调用者在调用前冻结一个自包含包,因此会话上下文按设计不需要(prompt-forager,它特意获得一行)——以及批量上下文隔离——工作读取或产生的量会污染主上下文(researcher,每次运行一个网页搜索角度)
- 技术引用是否最新?——无条件验证主体命名的每个工件:对每个引用路径执行
ls,对命名的命令、标志、模型名称和工具列表执行--help/版本检查。“如果看起来过时则验证”是被禁止的措辞——条件才是稀释的东西;可确定性检查的声明每次都得到确定性检查 - 集合内是否唯一?——没有其他代理(或技能)拥有相同的工作;有文档记录的编排器→子代理拆分不是重叠
恰好八个问题,不再增加——进一步分解会降低整体判断(见参考文献)。
阶段 2——评价压力测试(仅限非 Keep 候选)。 生成 1-3 个针对特定代理的原子化是/否问题,旨在反驳草案评价,每个问题用一行证据(文件读取、路径检查、--help、WebSearch、工作目录文档检查)回答。对于 Dissolve 候选,一个问题必须存在:“吸收性工作目录功能是否可以具体命名——是/否”——无法命名其吸收器的吸收声明即被反驳。并且当 Dissolve 即将被基底对应物所缺乏的能力(工具、已连接的子代理)反驳时,在接受反驳之前必须回答一个反问题:“子代理是使用该能力的正确位置吗——还是主循环无论如何都持有它?” 能力存在是必要但不充分的;新鲜/丰富上下文轴决定了能力属于哪里(先例:规划器反驳失败,因为主循环持有完整的 Agent 工具)。Keep 绑定代理不会获得动态问题。
评估是完整且内联的。
极少数情况的进一步阐述。 当阶段 2 问题未能引出一个干净的评价时,以下阐述适用于罕见的子案例。
“降级为技能”的路径
当代理的核心逻辑很简单(一个提示模板,没有工具路由或子代理)并且其描述与技能的 description 字段没有显著不同时,Demote to skill 是候选。此路径要求技能作者的注意:创建该技能是单独的改进。
通信层
最大引用深度为 1(直接的,非传递的)。完整性检查跟随所有已知协议:hook、技能、技能的选择文档、子技能、规则、子规则、提示文件。外部参考仅在说“来源 X 现在做 Y”时被检查——即,直接链接到工具的发布说明或已知的 API 变更。
输出结构
每个工作目录一个有效的 JSON 对象。写入 <ledger>。