agent-stocktake — 代理定義品質審計
評估 <harness>/agents/*.md 下的每個代理定義,並為每個定義分配一個審核結果:
Keep / Improve / Update / Merge / Demote to skill / Dissolve / Retire。審計單位是檔案,但成本單位跨越兩個層面——正是這種劃分使此技能成為與 skill-stocktake 和 rules-stocktake 並列的第三個技能。
設計說明——混合成本模型。一個技能的成本是觸發污染(機率性觸發會降低選擇品質);一個規則的成本是常駐性(始終載入)。一個代理則同時具有兩者:其
description會透過「可用代理類型」列表注入到每個工作階段中(常駐性,類似規則),而其本體僅在代理被調用時才載入(調用,類似技能——但由 Claude 的委派判斷觸發,而非透過描述與用戶輸入的匹配)。因此,描述會根據常駐密度進行審計,而本體則根據調用品質進行審計,兩者使用不同的問題。
設計說明 2——已批准的編輯由此技能本身在套用分支上執行。與 rules-stocktake 的理由相同:目前沒有針對代理定義的改進引擎,且語料庫規模足夠小,可以在一個上下文中讀取完——委派會造成過度工程化。例外情況是「降級為技能」(Demote to skill),因為建立技能是 skill-writer 的職責。
根目錄。 下面的 <harness> 是此審計涵蓋的環境:預設為 ~/.claude,或當技能 harness-stocktake 以專案模式執行時的存放庫 .claude/ 目錄(其根目錄部分會映射路徑,命名該處為 n/a 的證據,並說明哪些約定檢查適用)。分類帳 <ledger> 屬於該環境:當此技能的目錄在 <harness> 內部時為 ${CLAUDE_SKILL_DIR}/results.json(環境攜帶自己的技能副本),否則為 <harness>/stocktake/agent-stocktake.json(已安裝的插件或專案模式)。無論哪種情況,它都會與環境的其他變更一起提交到套用分支上。
模式($ARGUMENTS)
| 引數 | 行為 |
|---|---|
無 / full | 讀取並評估每個代理定義(預設) |
changed | 僅重新評估修改時間晚於 <ledger> 中 evaluated_at 的檔案;其餘部分從分類帳中沿用 |
changed 模式會內聯檢測變更(無腳本):
以下路徑從
${CLAUDE_SKILL_DIR}開始,即此 SKILL.md 所在的目錄;未替換該變數的代理會將其讀取為該目錄。
find <harness>/agents -name "*.md" -newermt "$(jq -r .evaluated_at "<ledger>")"
與 rules-stocktake 相同,第一階段的完整性檢查始終對完整集合執行——停用一個技能或掛鉤會默默地破壞未修改代理主體內的引用,而 mtime 無法察覺到這一點。任何在引用檢查(下面的步驟 3)中新失敗的代理都會加入重新評估集合。該腳本也沒有 changed 模式,原因不同:近乎重複的配對和常駐總數是集合屬性,因此部分語料庫會給出錯誤的數字,而非較少的數字。
第一階段——證據、清單、使用情況
步驟 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 表示「不在腳本的標記日期列表中」,而非「不存在」——你擁有最新的工具註冊表而腳本沒有,因此在稱其為「更新」證據之前,請根據你自己的工具列表確認 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 | 近乎相同描述的檢查;分身會分散委派流量,因此每個配對都會提供第一階段的「重疊」問題 |
suppression_candidates | 具有行號的候選項,用於第一階段的「抑制」問題(目錄是雙語的,因為語料庫也是) |
always_never_candidates | 具有行號的候選項,用於「過度約束」問題,僅限本體——描述是委派觸發器,而非對代理的指令 |
最後兩項是候選項,而非發現——在將其寫入報告之前,請閱讀每個被引用的行。目錄也是一個下限,而非普查:它包含從一個語料庫中提取的五種措辭,因此以其他方式書寫的抑制(「跳過任何你不確定的內容」、「避免只會產生噪音的發現」)不會出現在任何 JSON 欄位中。請繼續閱讀以查找這些內容;一個空列表並不代表完全乾淨。
測量到的誤報比率存在於此檔案之外,因此此步驟不攜帶會過時的數字。
步驟 2——讀取已擁有其餘部分的閘門
有三項檢查保留在腳本之外,因為在具有 lint 閘門(<harness>/scripts/hooks/harness_lint.py)的環境中,該閘門擁有它們。當存在時讀取其輸出;若不存在,則使用「讀取」/「搜尋」進行這三項檢查:
- Frontmatter 可解析且包含
name、description、origin、model,且model是別名 → lint 腳本的lint_agents(在作者的環境中,它運行在.claude/verify.sh和 pre-commit hook 中)。當環境規定了 origin 約定時,缺少 origin 仍是一個完整性發現,屬於「改進」(Improve)。 -
name等於檔案名稱主體 → 同一個lint_agents。它不允許判斷,且委派註冊表以其為鍵,因此它屬於阻止提交的閘門——而非讀者可能轉錄或不轉錄的證據。這裡的發現是一個被阻止的提交,而非 stocktake 審核結果。 - 代理主體內部的 Markdown 連結可解析 → 同一腳本的
lint_markdown_links(其範圍包含agents)。
步驟 3——解析裸路徑引用(唯一一個不屬於其他任何地方的檢查)
lint_markdown_links 僅能看到連結語法,而 skill-health 的 scan_refs.py 掃描的是 skills 樹。以 prose 形式寫入代理主體內的路徑(<harness>/hooks/foo.sh、skills/foo/SKILL.md)兩者都無法涵蓋。搜尋語料庫中的這些路徑,並對每個路徑執行 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(任務)工具的啟動被捕獲。工作流程
agent()工作者、插件內部調度以及繞過工具呼叫的內建機制不會到達掛鉤。使用次數低的代理會作為「停用/解散」(Retire/Dissolve)候選項進入第一階段;其價值可能是偶發性的(例如,論文審查者僅在提交截止日期附近觸發),因此由內容決定。 - 日誌自 2026-07-27 起存在;在此之前沒有測量。
首先說明掃描結果,從步驟 1 的 JSON 中取得計數:agents_total 減去 unreadable 中的任何內容(必須命名)、total_desc_words(每次工作階段的常駐稅)、完整性失敗以及使用情況是否可測量。將失敗作為預先計算的證據帶入第一階段。
第二階段——評估(完全內聯,整體性)
在查看完整集合的同時閱讀每個主體。
第一階段——二元篩選(每個代理)。 每個項目明確回答是/否;僅回報「否」的答案。前兩個問題審計描述層(常駐性),其餘問題審計本體層(調用):
- 描述值得其常駐成本嗎?——在始終載入的列表中,描述應緊湊、有區別性且有助於選擇;說明何時委派 AND 何時不委派
- 描述對本體誠實嗎?——描述的承諾與本體的行為一致(漂移的描述會導致每次工作階段的委派錯誤,即使本體本身沒問題)
- 本體沒有抑制指示嗎?——從此代理的第一階段 JSON 中的
suppression_candidates開始,按照步驟 1 處理。 什麼算作抑制指示:信心閾值(「僅報告你 ≥N% 確定的發現」、「為每個發現分配信心並丟棄低的」)、嚴重性下限(「僅高嚴重性」)、「保守」框架。當前世代指引是:報告所有內容,在單獨的過程過濾——抑制指示會被字面遵循並默默地丟棄發現。這裡的「否」是一個透過反轉來改進的候選項:以相反方向重寫指示,絕不要僅僅刪除該部分(刪除會留下抑制框架;反轉則會替換它) - 外部來源的副本是最新的嗎?——對於一個
origin是外部儲存庫且其本體是逐字副本的代理(例如內建覆蓋,如Explore),本體開頭註釋中命名的來源版本與已安裝的工具(claude --version)相符;不符則為「更新」(與套件比對差異並重新複製)。對於此類副本,將跳過抑制和過度約束問題——本體是供應商的,而非我們的 - 本體沒有前一代的過度約束嗎?——當前模型原生具備的詳盡逐步判斷程序、重複強調、應由周圍上下文判斷擁有的「總是/從不」配對。第一階段的
always_never_candidates定位了這些標記;判斷一個命中是指令還是僅僅引用該詞語是此問題的工作 - 未被底層機制吸收嗎?——環境現在是否原生涵蓋了此代理的工作(原生審查機制、計劃模式、內建斜線命令)?吸收 → 「解散」候選項;該聲明必須具體命名其吸收者。使用新鮮/豐富上下文軸線進行判斷: 從新鮮上下文中獲益的角色(審查、對抗性驗證、本質評估——與提議者的沉沒成本去相關)合法地位於子代理中;從豐富上下文中獲益的角色(規劃、生成、實作——用戶意圖、對話中約束)是主循環工作,因此對於它們,主循環本身即被視為吸收者。兩個輔助理據合法地凌駕於豐富上下文拉力之上:凍結輸入渲染合約——調用者在調用之前凍結一個自包含的資料包,因此按設計不需要對話上下文(prompt-forager,它故意只獲得一行)——以及大量上下文隔離——工作會讀取或產生會污染主上下文的資料量(researcher,每次運行一個網路研究角度)
- 技術參考是最新的嗎?——無條件驗證本體命名的每個工件:對每個引用的路徑執行
ls,對命名的指令、標記、模型名稱和工具列表執行--help/ 版本檢查。禁止使用「如果看起來過時則驗證」的措辭——條件是削弱檢查的根源;可確定性驗證的主張每次都要進行確定性檢查 - 在集合中是唯一的嗎?——沒有其他代理(或技能)擁有相同的工作;有文件記錄的編排器→子代理拆分不是重疊
八個問題,沒有更多——進一步分解會降低整體判斷的品質(參見參考文獻)。
第二階段——審核結果壓力測試(僅限非「保留」候選項)。 生成 1–3 個特定於代理的原子化是/否問題,嘗試反駁草案審核結果,每個問題用一行證據回答(檔案讀取、路徑檢查、--help、網路搜尋、環境文件檢查)。對於解散候選項,一個問題是強制性的:「能否具體命名吸收該功能的環境特性——是/否」——一個無法命名其吸收者的吸收主張被反駁。當一個解散即將被底層機制缺失的能力(一個工具、一個有線子代理)反駁時,在接受反駁之前必須提出一個反問題:「使用該能力的合適位置是子代理嗎——還是主循環本身就持有它?」 能力存在是必要但不充分的條件;新鮮/豐富上下文軸線決定該能力屬於何處(先例:規劃器的反駁失敗,因為主循環持有完整的 Agent 工具)。保留的代理不會獲得動態問題。
評估是 *
…
[已截斷以進行翻譯]