Skill 使用审计(Skill Usage Audit)
何时用
用户想看本机已装 skill 清单 + 使用频率 + 上次使用时间,或想据此判断哪些 skill 该清理、哪些在吃灰。
也用于给 skill 分类、排价值、给处置结论。
典型原话:「列出我当前已安装的所有 skill」「哪些 skill 我最常用」「哪些装了一直没用」「skill 使用统计」
「按类型分一下」「哪些是项目型」「哪个 skill 最值得留」「帮我关掉没用的」。
一、四维梳理模型(这是本 skill 的主干)
| # | 维度 | 取值范围 | 回答什么问题 |
|---|---|---|---|
| 1 | 类型 | 项目型 / 专用领域型 / 通用领域型(+ 待确认兜底) | 这东西能不能跟着我换项目?迁移性多高? |
| 2 | 业务域 | 可自定义域表(默认含电商、金融、设计文档、软件开发、内容创作、Agent 协同等) | 这东西属于哪条业务线? |
| 3 | 使用价值 | 0~100 分,分 S/A/B/C 四档 | 哪几个真的在扛活,哪几个在吃灰? |
| 4 | 处置建议 | 保留 / 保留观察 / 观察(新装未验证)/ 不可关闭:先改专家声明 / 可关闭:与插件版重复 / 可关闭候选 / 随包安装不可单独关闭 / 无需处置 | 下一步该对它做什么? |
四维的关系:类型看迁移性,业务域看归属,价值看投入产出,处置给结论。
只看「使用次数」会得出错结论(新建的基建技能常只有 1 次),必须四维交叉。
维度 1 · 类型三分类(判据与纪律)
| 类型 | 定义 | 迁移性 |
|---|---|---|
| 项目型 | 绑定某个具体项目 / 系统 / 表单 / 作品,脱离该项目即失效 | 换项目即废 |
| 专用领域型 | 绑定某一行业 / 平台 / 业务域(跨境、电商、金融、影视),域内可跨项目复用 | 换项目仍可用 |
| 通用领域型 | 与具体业务无关的通用能力(技能治理、Agent 编排、文档、设计、云开发) | 任何场景可用 |
判定顺序(禁止跳步、禁止静默猜测):
taxonomy.TYPE_OVERRIDE人工确认表 —— 优先级最高,每条附判定依据taxonomy.TYPE_RULES关键词规则:name 命中 ×2 + desc 命中 ×1,命中多者胜- 载体兜底(仅非用户级载体):规则零命中 + 制品来自插件/内置/连接器 ⇒ 通用领域型,
置信度标低(载体兜底)、理由写明、报告里单独计数 —— 这是明示规则,不是猜测 - 仍无结论 ⇒
待确认(只在用户级技能上会落到这里)
并列消解:同分时取更「窄」的类型(项目型 > 专用领域型 > 通用领域型),置信度标 低(并列,需复核)。
为什么 name 权重是 desc 的两倍(实测逼出来的,别改回去):
- 图片生成类技能的 desc 举例提到「电商详情图」⇒ 被判成专用领域型(错,它是多模态工具)
- 海报类技能的 desc 提到「文案」⇒ 被判成专用领域型(错,它是设计工具)
- 企微转发类技能被 ecom(merce)? 正则命中 WeCom 的 "eCom" ⇒ 判成电商(错)
→ 规则:名字是技能的自我声明,desc 里的领域词往往只是举例,不能等权。
置信度纪律:产出里 低(…) 的条目必须能在报告里一眼看到。
同一技能连续两次落低置信度 ⇒ 补进 TYPE_OVERRIDE 并写明依据,别让它一直悬着。
维度 2 · 业务域
判定顺序:name 关键词 → desc 关键词 → 载体兜底(插件/连接器/内置 且无域可归 → 平台能力)→ 未归类。
name 与 desc 的关键词必须分开维护(DOMAIN_TABLE 是三元组 (域, name_keys, desc_keys)):
- discount 若放进 desc 关键词 ⇒ DCF 模型的 "discount rate" 会把 dcf-model 判成「电商运营与营销」
- ASCII 关键词一律走词首边界匹配(_key_in)⇒ ecom- 才不会命中 wecom-
载体兜底放最后:插件件若 desc 已说清业务域(如 equity-research → 金融与投资)就该拿真实域,
只有确实无域可归的平台件才标「平台能力」。
目标:「未归类」必须为 0。不为 0 说明关键词表该补了(跑一次 --enumerate-only 看名单)。
维度 3 · 综合价值 v2(0~100,使用频率权重最高)
综合分 = U 使用频率 35 + R 新近度 25 + T 类型迁移 15 + D 依赖地位 15 + S 稀缺性 10
| 分项 | 权重 | 构成 | 口径要点 |
|---|---|---|---|
| U 使用频率 | 35(最高) | 次数 25(封顶 10 次)+ 活跃天数 10(封顶 5 天) | 「活跃天数」兜住刷次数:一天连调 10 次 ≠ 跨 5 天持续使用 |
| R 新近度 | 25 | 3天25 / 7天22 / 14天18 / 30天14 / 60天9 / 90天5 / 180天2 / 更早0.5 | 只看「上次使用距今天数」,不看累计次数——累计已在 U 里。「三个月前狂用、之后再没碰」R 归零,频率再高也救不回来 |
| T 类型迁移 | 15 | 通用 15 / 专用 10 / 项目 7;项目型 >60 天未用 → 衰减为 4 | 类型直接进评分;项目型保底 7 是为了不误杀当红项目技能(U35+R25+T7+S10=77 仍 S)。>60 天未用说明项目可能收尾,迁移性风险兑现 |
| D 依赖地位 | 15 | 硬声明 15 / 软引用 6 / 无 0 | 处置建议的前置闸门(硬声明 >0 ⇒ 不可关闭) |
| S 稀缺性 | 10 | 用户级独有 10 / 其他载体 6 / 与插件版重复 3 | 重复 = 有现成替代 |
档位:S ≥70(核心资产)· A 50–69(在岗可用)· B 25–49(低频观察)· C <25(低价值)
校验锚点(taxonomy.py --selftest 断言,改权重必跑):权重守恒 =100;
满血路径(10 次/5 天/1 天前用/硬声明/用户级/通用)= 100;
满频率但从未再用 = 75(R=0,仍 S——证明 R 独立于总频率);
当红项目技能 = 77 仍 S(证明类型维度不误杀项目型)。
「从未启用」的技能 U 与 R 必然为 0,天然落 C 级 —— 设计如此。
所以处置规则里 C 级还必须叠加「无依赖 + 装载 ≥30 天」才给「可关闭候选」。
已知局限(想清楚再扩展):①「同族冗余」没进评分(20 个同域文案技能互相重复,
理论上该互相稀释 S 分,但聚类口径太主观,先不做);②类型置信度低不扣分——
置信度影响的是「这个判定可不可信」,不是「这个技能值不值钱」,两者混在一起会说不清。
维度 4 · 处置建议(按序命中,先命中者生效)
if origin == "无载体(历史调用名)": return ADV_ORPHAN # 本机已无该技能,无事可做
if origin != "用户级": return ADV_PLATFORM # 随包件,只能整包走 enabledPlugins
if 硬声明 > 0: return ADV_LOCKED # 先改专家声明,顺序不能反
if 与插件版重复 且 价值<50: return ADV_DUP
if 档位 in (S, A): return ADV_KEEP
if 启用==0 且 装载<30天: return ADV_NEW # 新装未验证,下月复评
if 档位==C 且 无软引用 且 装载≥30天: return ADV_CAND
return ADV_WATCH
二、数据口径(踩过坑定下来的,别改)
| 指标 | 定义 | 可靠性 |
|---|---|---|
| 启用次数 | 同一会话同一天去重后的「技能被启用」事件 = 显式调用 ∪ 仅路径引用 | 排序主键,偏保守 |
| 显式调用 | 会话记录里 Skill 工具的调用次数 |
最硬 |
| 仅路径引用 | 未走 Skill 工具,但工具调用里出现该技能目录路径(读 SKILL.md / 跑 scripts) | 硬,但需排除安装场景 |
| 会话数 / 活跃天数 | 使用广度与持续性 | 辅助 |
三、必须避开的坑(全部实际中过)
坑 1 · 自我污染
分析脚本本身用 Write 落盘、以及推理文本,都会进当前会话记录。脚本里若出现技能名或
Base directory for this skill 标记,下次扫描会把它们当成使用证据,读数虚高。
→ 硬规则:扫描时必须排除当前工作区当天的会话文件(EXCLUDE_MARK 按工作区目录名过滤)。
坑 2 · <available_skills> 清单重复注入
每个会话历史都重复带着整份技能清单,「某技能名出现 N 次」与使用毫无关系。
→ 禁止用原文出现次数当频次,必须定位到 function_call 记录。
坑 3 · 安装/批量校验被误判成使用
装技能包那一场会读一遍每个技能目录,产生大量路径引用。
→ 单场会话引用 ≥5 个技能目录 ⇒ 判为批量安装会话,其路径引用不算使用,
该类技能归「仅安装/校验时触碰」,计数归零。
坑 4 · 载体枚举:installPath 存在 ≠ 技能实体在
某些插件包的 cache 目录里只有业务子目录、没有 skills/,
真实体在应用安装目录的市场源路径下。
→ resolve_plugin_dirs() 必须按「有没有真技能实体」选候选,不能看路径是否存在就 break。
→ 候选顺序:installPath → manifest.plugins[].source → installLocation/<插件名> →
installLocation/skills → installLocation/builtin-plugins;选中后靠 seen 去重,内置市场只 walk 一次。
→ 绝不把 installLocation 整个加进来兜底:市场根目录里躺着全部「可安装」插件,
实测会把官方插件类条目从 2 条灌到 433 条(全是噪声)。
坑 5 · 插件包根的清单 SKILL.md 会被当成技能
插件包根有一份清单 SKILL.md(frontmatter 含 metadata: 键),
它跟真技能长得一样。直接扫会 ① 把整包当成版本号名字的「技能」;
② 把清单里的 name: 错当成别名来源,写出反向垃圾别名。
→ 用 is_plugin_manifest()(frontmatter 顶层有 metadata:)区分。
坑 6 · 单技能插件的 SKILL.md 直接放在版本目录根
<插件名>/<版本>/SKILL.md 直接放在版本目录根——父目录是版本号,但它就是技能本体
(frontmatter 只有 name/version/description,无 metadata:)。
→ 版本目录根一律再判一次 is_plugin_manifest:是清单才跳过,否则用 frontmatter name 命名后收录。
→ has_skill_entity() 也要同步这条,否则单技能插件的 cache 被误判「无实体」,回落到错目录、技能消失。
坑 7 · 容器目录 …/skills/SKILL.md 产生垃圾别名
有些结构里 SKILL.md 直接放在 skills/ 下,按父目录名取名字会得到容器名 skills,
产生「技能名 → skills」这类垃圾别名——并入时找不到目标行,
凭空多出一条「插件/内置」重复行。
→ CONTAINER_NAMES = {skills, references, plugins, connectors, assets, scripts, src, lib, …},
命中容器名时技能名改取 frontmatter name,且不建别名。
→ _sane_alias_target() 二次拦截:目标落在容器名/版本号里一律丢弃。
坑 8 · 展示名与目录名不同 ⇒ 同技能分裂成两条
会话里用中文展示名(如「资料库」)、磁盘上叫 library;技能改过名(旧名 task-planner、新名 output-spec-judge)。
→ DISPLAY_ALIAS_CONFIRMED 显式表,每条必须附证据,三类来源:
① 会话展示名(同期命令指向同一载体目录)
② 用户明确告知的改名
③ 同插件内唯一近名候选
→ 报告里保留 展示名/旧名 列 + 搜索索引,用户按旧名能搜到。
→ 自检硬指标:产出清单里不允许出现重名行(len(names) != len(set(names)) 即为 bug)。
坑 9 · 历史调用名 ≠ 平台件
会话里调用过、但本机任何已安装载体都找不到的名字(已卸 / 只存在于未安装的市场),
不能含糊地混进「插件/内置」。→ 单列来源 无载体(历史调用名),处置建议 无需处置(本机无载体)。
坑 10 · python -c "…" 装长文本会被 shell 吃掉
-c 是双引号字符串:反引号会被命令替换吃掉、$ 会被展开、! 可能触发历史扩展
→ 写进文件的内容残缺,而且进程退出码仍是 0、看不出报错。
→ 含反引号/$/长文本的写入,一律先 Write 落 .py 脚本再执行;
必须用 -c 时改单引号包裹或走 heredoc 的 <<'EOF'(引号形式)。
四、执行步骤
# 全量审计(推荐带 --today 保证价值分可复现)
python scripts/audit_skills.py --workspace "<工作区绝对路径>" --today 2026-09-27
# 只枚举 + 分类,不扫会话(秒级,用于校准类型/业务域规则)
python scripts/audit_skills.py --workspace "<工作区>" --enumerate-only
# 单技能四维判定明细(调规则时用)
python scripts/taxonomy.py --explain <技能名>
python scripts/taxonomy.py --selftest # 改规则后必跑
输出到工作区:skill_usage_report.html + skill_usage_report.csv(UTF-8-BOM)。
交付前必查的 5 项自检
| 检查项 | 期望 | 不达标说明什么 |
|---|---|---|
| 重名行 | 0 | 别名表缺条目(坑 7/8) |
| 待确认(类型) | 0 | 规则覆盖不足,或用户级技能确实边界 → 补 TYPE_OVERRIDE |
| 未归类(业务域) | 0 | DOMAIN_TABLE 该补关键词 |
| 低置信度条目 | 尽量 0 | 有则列出名字让用户复核,别藏起来 |
events vs scanned |
events < scanned×6 |
超了 ⇒ 自污染,查排除标记 |
五、分类与状态
- 活跃使用:显式调用 ≥3 场会话,或启用次数高且跨 ≥3 场会话
- 偶发使用:有真实使用但场次少
- 仅安装/校验时触碰:只在批量安装会话被读过,不算用过
- 从未启用:无任何证据
六、载体口径
| 载体 | 路径 | 说明 |
|---|---|---|
| 用户级 | ~/.workbuddy/skills/ |
用户自装/自建,报告主体 |
| 内置 / 官方插件 / 市场技能 / 专家技能 / 本地专家技能 | plugins/cache/(按 installed_plugins.json 解析) |
随插件包落盘 |
| 连接器技能 | 同上,workbuddy-connector-plugins-official + ~/.workbuddy/connectors/ |
钉钉、云开发、腾讯文档等 |
| 无载体(历史调用名) | — | 会话调用过但本机找不到实体 |
| 市场目录缓存 | plugins/marketplaces/ 等 |
不算已安装,报告里排除 |
权威来源 = ~/.workbuddy/plugins/installed_plugins.json + known_marketplaces.json。
不要靠扫 cache/ 目录推断已安装(会漏、会被可安装目录污染)。
七、输出契约(可交互的单文件 HTML 报告,不是静态文档)
五个视图 + 全局筛选条 + 行详情抽屉(标签页导航,顶栏 sticky):
| 视图 | 内容 | 交互 |
|---|---|---|
| ① 总览 | KPI 卡(含 S 级数 / 低置信度数)+ 价值速览 Top 10 + 评分模型卡 + 类型/业务域/来源分布 | 全部实时跟随筛选;速览点行开抽屉;来源分布含合计行,各来源相加=总数 |
| ② 清单总表 | 全量条目,15 列含 U/R/T/D/S 五分项 | 列头点击排序、分组方式(来源/类型/业务域/处置/档位/状态)、点行开抽屉 |
| ③ 处置台 | 可关闭候选 / 不可关闭·先改专家声明 / 观察新装 / 无载体 四栏 | 标签页带候选数徽标 |
| ④ 口径说明 | 评分口径、数量口径、局限 | — |
视图合并纪律:价值排行不单独设页——它本是清单总表默认排序的前 20 行,
列集还是总表子集,纯属重复。速览(Top 10)进总览承担「一眼看头部」,总表是唯一明细视图。
新增板块前先问:「这和清单总表是不是同一份数据的另一种切法?」是,就并进总览做紧凑块。
(历史坑:排行独立成页时用的是「全量 Top20 里挑筛过的」逻辑,筛低分来源整块空掉而计数照旧——已随合并消失,但口径教训保留在此。)
展示策略:并非所有技能都值得呈现。
- 呈现(默认可见)=① 用户级自装(自己的资产,含零启用的新装——那是待验证,不能藏)
② 无载体·历史调用名(用户的调用历史)③ 平台侧中实际用过的(启用>0)。
- 折叠(默认不可见,切「展示范围:全部条目」可展开)=平台侧且从未启用——
随包自动安装 / 市场装的专家包细粒度技能,用户从没见过,列出来只有噪声。
- 呈现方式:总表默认按总分(重要性)降序,分组方式可选;速览 Top 10 承担头部速读。
CSV 永远导全量(数据完整性),页面只做「呈现策略」。
- 实现:JS inScope() + 筛选条「展示范围」下拉;下拉计数、分布表、KPI 全部跟随 scope 重建。
全局筛选条(sticky):搜索框 + 来源/类型/业务域/状态/价值档/处置 六个下拉(各带条数),
叠加生效,实时显示「筛出 N / 总数」。筛选实时作用于全部视图——
总览 KPI、三张分布表、处置台四栏的计数与列表都会重算;清空筛选即回全量。
行详情抽屉:点任意行 → 右侧抽屉展示全部字段(类型判定理由、五维分项、使用证据、
依赖方、装载天数、操作提示)。ESC / 遮罩 / 关闭按钮均可退出。
输出文件被占用时降级不静默:CSV/HTML 被 Excel/WPS 锁住时,自动改写
<名>.locked-<时分秒>.<ext> 并打到 stderr 显式提示——绝不静默失败,也绝不杀用户进程。
硬规矩:所有内容必须服务端渲染 —— 各视图在 HTML 里全部展开可见,JS 只负责
「切成标签页 / 筛选 / 排序 / 抽屉」。JS 挂掉时页面退化成一份从上到下可读的长报告,绝不白屏。
(实现:body.js 类由 JS 添加;无 JS 时 .view 全部 display:block。)
模板外置:页面模板在 scripts/template.html(与 audit_skills.py 同目录,运行时读入),
不在 .py 字符串里——改报告版式只动模板文件。不用 f-string:令牌替换 @TOKEN@ → .replace(),
交付前正则 @[A-Z_]+@ 扫描必须为空。
chip 类名映射单源化:type/grade/adv 的「值→CSS 类」映射只维护在 audit_skills.py
(TYPE_CHIP 等),经 @CHIP_JSON@ 注入 JS,JS 端禁止再写一份映射。内嵌 JSON 一律过 _safe_json()(</ → <\/,防 </script> 截断)。
CSV 33 列,含 展示名/旧名、类型判定理由、U/R/T/D/S 五分项、硬声明方/软引用方、处置建议。
报告页面改动必须过验证(双通道)
scripts/smoke_test.js(playwright):四视图切换 / 展示范围切换 / 六维筛选叠加与清除 /
下钻(分布行、KPI 卡)/ 列头排序 / 分组 / 抽屉 / 处置台 / 空态 / hash 持久化(reload 恢复)/
导出下载 / 无 JS 退化。scripts/dom_check.py(headless Chromium --dump-dom):playwright 被沙箱 SIGTERM 时的
备用通道——同一内核真实执行页面 JS 后对渲染 DOM 断言,不需要 node API。
两条都验:数量断言跟数据走(示例基线见脚本注释),换数据集先核对 CSV 再改断言,
不能拿旧预期硬套。沙箱杀 playwright 子进程时:换到工作区目录运行,仍不行就用 dom_check.py。
八、关闭闲置 skill(官方机制,已从程序源码核实)
用户说「把这些没用的 skill 关掉 / 禁用 / 停用」时走这里。不要删目录、不要 rm——
删除不可逆,且部分环境有安全删除钩子拦截。
机制
配置位置:~/.workbuddy/settings.json 顶层 skillOverrides 对象。
| 取值 | 效果 |
|---|---|
"on" |
默认,正常 |
"name-only" |
只把名字列给模型(省 token),仍可调用 |
"user-invocable-only" |
模型不能自动触发,用户可手动调(软关闭) |
"off" |
完全关闭:不列给模型、菜单隐藏、用户也调不动 |
"skillOverrides": { "<skill-name>": "off" }
关闭口径
- 所有技能(含插件/内置/连接器/市场包内技能)都可以关闭:单项走
skillOverrides,
整包走enabledPlugins("<plugin>@<market>": false);修改同步生效,随时可恢复。
本体保留在磁盘,应用更新不会冲掉关闭状态(关闭态在用户配置里)。 - 关闭被专家
skills:声明的技能时,必须同步修改专家包声明 —— 否则声明空索引。
顺序:先改声明 → 再关闭。这是唯一的硬前置,不是「不能关」。 - 取值非法会被静默忽略(只认
on/name-only/user-invocable-only/off四个字面量)。
→ 写完必须回读断言。 - 删除边界:用户级技能可删(走删除 SOP);市场安装的可卸载(整包卸载,可重装);
内置/连接器/官方件用关闭替代——删除会被应用更新复原,且破坏整包完整性。
关闭执行 SOP
备份 settings.json → enabledPlugins 整包停用(先核对包内无呈现区在用技能,PARTIAL 包改逐条)
→ skillOverrides 单项 → 回读断言(顶层键不变、新值生效、旧值保留)→ 重跑审计看「已关闭」标记。
回滚:用备份整体还原。
关闭之后的主动流程(AI 行为,不能止步于展示面板)
报告交付后,AI 必须:① 读「处置台」给出关闭建议(确认弃用域整包停用 / 重复项单项关闭);
② 等用户逐项确认;③ 执行关闭(按上面 SOP);④ 重跑审计验证「已关闭」标记;
⑤ 用户明确同意删除的,按删除边界执行(用户级删目录、市场包走卸载)。
建议口径分两档:保守(价值 C + 无依赖 + 装载 ≥30 天)、宽松(从未启用 + 无硬声明 + 装载 ≥30 天 + 弃用域)。
硬规矩:用户对「关闭口径 / 名单组成」的确认 ≠ 对「立即执行」的授权。
处置类动作(关闭/删除)必须排在「报告完善 + 用户看到逐项清单」之后(实测教训)。
处置流程(缺一不可)
- 先建专家依赖图。 这是第 0 步,不能跳。只看「使用次数 + 创建时间」会给出错方案——
实测踩过:5 个"建议关闭"里有 3 个其实是专家声明的依赖。
做法:扫专家包目录(plugins/cache/my-experts/**/agents/*.md及plugins/marketplaces/**),
把用户级技能名逐个匹配,命中即为被依赖。
同时提取 agent 正文里的绝对路径引用(指向用户级技能目录的硬编码路径),
这类是最脆的依赖,删了会在运行中期静默失效。
专家怎么"持有"技能有两种模式,判定完全不同:
- 按名引用(多数):plugin.json 无 skills 字段、包内无 skills/ 目录,
只在 agents/*.md 的 frontmatter skills: 列表里写技能名。
→ 技能本体仍在 ~/.workbuddy/skills/,删掉 = 专家清单指向空气。不可删。
- 自带副本(少数):plugin.json 里有 "skills": ["./skills/<name>"],包内确实有 skills/<name>/SKILL.md。
→ 表面上可删用户级那份,但必须再查 agent 正文有没有硬编码用户级路径;
有 → 实际执行的仍是用户级那份,删了照样断。
结论:「已封装进专家」几乎不等于「可安全删除」,必须逐一验证,不要推断。
判依赖必须分「硬声明」与「软引用」两级,混算会虚报:
- 硬声明 = agents/*.md frontmatter 的 skills: 字段。这才是真依赖,删了专家就断。
- 软引用 = 正文里提到的技能名(典型:调度路由表、脚本绝对路径)。
属路由建议/运行期路径,处置前要一并改正文,但不是声明依赖。
→ 只用 正文子串包含技能名 去判依赖 ⇒ 会把软引用全算成硬依赖。
实测中过:报告写「某专家声明 11 个技能」,按 frontmatter 实际只有 5 个,其余 6 个是路由表软引用。
frontmatter 的 skills: 有两种写法,解析器必须都支持:
- 块状列表:skills:\n - a\n - b
- inline 数组:skills: [a, b]
→ 只写块状解析会把 inline 形式的专家误判成「无声明」。实测中过。
专家改过名要按别名解析,别报成「悬空引用」。 expert_dependency.SKILL_ALIASES
维护「旧名 → 新名」映射。旧名本体不存在但新名存在时,
按别名计入并输出 [i] 提示,而不是 [!] 报错。
→ 同时提醒:语义可能已偏移,处置前确认新版职责是否仍覆盖原用途。
专家包数量要实测,别信记忆/旧笔记。 依赖图每次都要现扫,不缓存。
-
先出清单,不直接动手。 呈现分组表 + 风险提示,等用户确认范围。
必做风险拆分:把「第三方包/重复副本」与「用户自己搭建的基建/专家依赖」分栏。
后者的「启用次数=1」通常是建成时验证过一次,不是废弃——批量关掉会拆掉用户自己的体系。 -
备份
settings.json→settings.json.bak_<yyyyMMdd_HHmm>,成功后才改原文件。 -
只用增改、不做重排:解析 → 改指定键 → 原样写回,别动其他区块。
-
回读断言:目标键 ==
"off"、原有 override 仍在、顶层键数量不变。 -
给回滚路径:任一步失败用备份整体还原,并显式说明失败在哪一步。
删除文件必须复核(部分环境行为反直觉)
某些环境的安全删除钩子每次都会抛错——但文件/目录其实已经删掉了。这是假失败(钩子 fails-closed 报错,动作已完成)。
→ 绝不能只信返回值。删完必须重新列目录确认;否则会误判成"没删掉",
给用户一个错误结论,或重复操作。实测中过一次:报告写"37 个文件删除失败仍在磁盘",
实际早已清空。
→ 禁止用 rm / rm -rf 绕过钩子。
「创建时间」口径的坑
用目录 CreationTime 判「装了一年以上/以上月」时注意:
重装 / 覆盖安装会把 CreationTime 重置为当天,导致老技能被误判成新装的而漏掉。
→ 交叉校验 首次使用时间(取自会话记录,不受文件系统影响)。
典型现象:某技能首次使用在 2 个月前、CreationTime 却是今天 ⇒ 它被重装过,应重新评估入选项。
九、输出模式 B:单专家技能清单(用户问「XX 专家声明了哪些技能 / 它们有什么用」)
只聚焦一个专家,不要重复输出全量依赖图。步骤:
- 解析该专家
agents/*.md的 frontmatterskills:→ 硬声明列表
(两种写法都要支持:块状 list 与 inline[a, b])。 - 逐个到技能目录读 SKILL.md 的 frontmatter
description
—— 用处必须引官方 description,不要自己推断或改写。 - 再从专家正文(如「调度路由表」)抽出软引用技能,单独成段并显式标注「不是依赖断言」。
- 交叉比对
skill_usage_report.csv补上「启用次数 / 最近使用」。 - 交付 HTML(硬声明表 + 软引用表 + 风险块)+ UTF-8-BOM CSV。
必带的三个提醒(否则用户会误判):
- 「关掉 ≠ 没删」:
skillOverrides: off只对模型隐藏,专家照样找不到 → 后果等同删除。 - 顺序不能反:先改专家
skills:声明,再处置技能本体;反过来留下悬空引用,运行中期才静默失效。 - 启用次数低 ≠ 废弃:新建专家体系的技能常只有 1 次(建成时的端到端验证)。
十、脚本职责划分
| 文件 | 职责 | 备注 |
|---|---|---|
scripts/audit_skills.py |
主流程:枚举载体 → 扫会话证据 → 合并去重 → 出 HTML/CSV | 所有 IO 都在这里 |
scripts/taxonomy.py |
四维判定纯函数库(无 IO),含 TYPE_OVERRIDE / TYPE_RULES / DOMAIN_TABLE / 评分 / 处置规则 |
调规则改这里;--selftest 断言自检 |
scripts/expert_dependency.py |
专家依赖图(硬声明 + 软引用 + SKILL_ALIASES 更名别名) |
被 audit_skills.py import 复用 |
scripts/template.html |
报告页面模板(令牌替换) | 改报告版式只动这里 |
scripts/smoke_test.js / scripts/dom_check.py |
报告双通道验证 | 改模板/JS 后必跑其一 |
改规则的顺序:先 --enumerate-only 看名单 → 改 taxonomy.py → --selftest → 全量跑 → 查五项自检。
audit_skills.py 支持被 import(collect_records / summarize 等函数可复用),别复制粘贴实现。