dsh-skill-folder 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -0
- package/cordis.patch.yml +31 -0
- package/docs/class-diagram.mermaid +34 -0
- package/docs/refactoring-plan-v2.md +237 -0
- package/docs/refactoring-plan-v3-final.md +284 -0
- package/docs/sequence-diagram.mermaid +17 -0
- package/docs/skill-metadata-schema.md +49 -0
- package/docs/system_design.md +299 -0
- package/lib/bm25.js +115 -0
- package/lib/catalog.js +133 -0
- package/lib/index.js +215 -0
- package/lib/pattern.js +23 -0
- package/lib/quality-scorer.js +191 -0
- package/lib/render.js +102 -0
- package/lib/select.js +66 -0
- package/lib/semantic.js +239 -0
- package/lib/skill-search.js +155 -0
- package/lib/tool-skill-search.js +92 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# dsh-skill-folder
|
|
2
|
+
|
|
3
|
+
根治「DSH 每轮把全部技能 description 平铺进 prompt 吃 token」问题。
|
|
4
|
+
|
|
5
|
+
**v0.3.0(2026-08-30,语义混合 + 自动路由)**:
|
|
6
|
+
- **语义检索腿**:`skill_search` 升级为 BM25 + 本地 bge-m3(Ollama)RRF 混合检索——中文意图可直接命中英文技能(BM25 词面鸿沟补上)。语义索引按内容指纹懒构建 + 落盘缓存(`~/.dsh/state/semantic-cache.json`),Ollama 离线/超时自动降级纯 BM25,绝不比旧版慢或差。
|
|
7
|
+
- **自动路由提示**(`autoRoute`,默认开):用户消息明显指向某技能时(aliases 命中或技能名 token 命中),在**用户消息尾部**追加一行 `<skill-route>` 提示——用户区本就是动态区,catalog 前缀字节不变,KV 缓存零破坏。无关消息不路由,幂等,fail-safe。
|
|
8
|
+
- 新配置:`semanticEnabled` / `ollamaBase` / `embedModel` / `autoRoute`。
|
|
9
|
+
|
|
10
|
+
**v0.2.0(KV-cache-stable)**:静态稳定 catalog(前缀永不变化)+ `skill_search` 检索工具(按需精准发现)。这是 **Deferred loading 模式**(Anthropic Tool Search / SkillRouter 同款)——动态裁剪目录文本会破坏 prompt cache 前缀(每轮数万 token 重算,净收益为负),静态 catalog + 检索工具则两者兼得:**token 省 + 缓存命中 + 选择精准**。
|
|
11
|
+
|
|
12
|
+
> 架构评审版规格:`docs/system_design.md`(权威,含宿主契约行号 A-F 全章节)
|
|
13
|
+
|
|
14
|
+
## 核心机制(一句话)
|
|
15
|
+
|
|
16
|
+
**catalog 静态渲染(保缓存前缀)+ skill_search 工具(保选择质量)。**
|
|
17
|
+
|
|
18
|
+
- 宿主 `@deepseek-ai/dsh-tool-skill` 负责:`skill` 工具注册、`/name` 手势注入(L1)、catalog 发布/更新/digest/历史(L2)。
|
|
19
|
+
- 本插件以 **`ctx.on("agent/pre-step", fn, true)`(prepend → 最外层)** 挂在瀑布最外层:等 L1/L2 全部完成拿到含全量 catalog 的最终 decision 后,**只替换 `message.content[0].text`(模型看到的渲染),绝不动 `message.source.entries`(digest 输入)**。
|
|
20
|
+
- **静态渲染**:目录文本只依赖技能集合(core 全量 + 其余截断 + deny 剔除),**永不随 query 变** → 每轮字节相同 → DeepSeek 自动前缀缓存 100% 命中。
|
|
21
|
+
- **skill_search**:注册 `skill_search(intent)` 工具(静态前缀),按意图 BM25 检索(name+description+aliases,中文可命中英文技能),结果追加消息尾部 → 不碰前缀。
|
|
22
|
+
|
|
23
|
+
## 安装
|
|
24
|
+
|
|
25
|
+
1. 把本目录放进 DSH 插件搜索路径(或 bundle 依赖),`cordis.patch.yml` 会把 `skill-folder` 插入 profile 组合。
|
|
26
|
+
2. `package.json` 的 `dsh.bundle.patch` 指向 `./cordis.patch.yml`;也可在 profile 自己的 `cordis.patch.yml` 里按 `id: skill-folder` 覆盖配置。
|
|
27
|
+
3. (可选)语义检索腿需要本地 **Ollama**(`http://127.0.0.1:11434`)+ `bge-m3` 模型:`ollama pull bge-m3`。不装也能用——自动降级纯 BM25,功能与 v0.2.0 完全一致,只是少了中英跨语言语义命中。
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd dsh-skill-folder
|
|
31
|
+
npm install # 只需 @deepseek-ai/schemastery(宿主已内置 cordis/dsh-tools 作 peer)
|
|
32
|
+
npm test # node:test,49 条测试,零外部测试依赖
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> ⚠️ 不要禁用 `@deepseek-ai/dsh-tool-skill`:会导致 `skill` 工具消失、`/name` 注入消失,模型无法按名加载技能。本插件与其共存,最小干预 = 最小风险。
|
|
36
|
+
|
|
37
|
+
## 配置(schemastery,profile 可按 `id: skill-folder` 覆盖)
|
|
38
|
+
|
|
39
|
+
| 键 | 默认 | 说明 |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `enabled` | `true` | 总开关:关闭则完全不注册监听器 |
|
|
42
|
+
| `core` | `["dsh-injection-guard", "dsh-verifier"]` | P0 常驻:每轮全量描述可见(安全底线,不截断;**deny 不能删除 core**) |
|
|
43
|
+
| `deny` | `["autotelic-evolution", "dsh-team-orchestra"]` | P3 剔除:精确名或 `prefix*`;core 技能豁免 |
|
|
44
|
+
| `aliases` | 12 技能中文映射 | 意图词→技能:`skill_search` 检索索引(BM25 加分),中文意图可命中英文技能 |
|
|
45
|
+
| `maxDescLength` | `100` | 非 core 技能描述最大长度(core 不受限) |
|
|
46
|
+
| `maxFoldMs` | `5` | 裁剪耗时上限(ms),超时仅告警,结果仍应用 |
|
|
47
|
+
| `toolSearchEnabled` | `true` | 注册 `skill_search` 检索工具(静态前缀,结果追加尾部,不破坏缓存) |
|
|
48
|
+
| `maxDescLength` | `100` | 动态条目描述最大长度;core 不受限 |
|
|
49
|
+
| `maxFoldMs` | `5` | 裁剪耗时上限(ms),超时仅告警,结果仍放行 |
|
|
50
|
+
|
|
51
|
+
默认 `aliases`:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
{
|
|
55
|
+
"viking-memory-guide": ["记忆", "回忆", "记住", "memory", "remember"],
|
|
56
|
+
"dsh-grilling": ["访谈", "对齐", "先问我", "grilling", "问清楚", "开工前"],
|
|
57
|
+
"dsh-delegation-checklist": ["委派", "子智能体", "subagent", "delegate", "openhands"],
|
|
58
|
+
"dsh-context-language": ["术语", "词汇表", "领域语言", "context", "语言"],
|
|
59
|
+
"dsh-injection-guard": ["注入", "安全", "不可信", "injection", "外部内容"],
|
|
60
|
+
"dsh-verifier": ["验证", "检查完成", "防假完成", "verify", "验证器"],
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`core` / `deny` / `aliases` 的键(技能名)均支持**精确名**或 **`prefix*` 前缀**匹配。
|
|
65
|
+
|
|
66
|
+
## 文件结构
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
dsh-skill-folder/
|
|
70
|
+
├─ package.json # name: dsh-skill-folder, type: module, main: lib/index.js
|
|
71
|
+
├─ cordis.patch.yml # profile patch: insert {id: skill-folder, name: 'dsh-skill-folder'}
|
|
72
|
+
├─ README.md
|
|
73
|
+
├─ lib/
|
|
74
|
+
│ ├─ index.js # 插件入口:name/inject/Config/apply + pre-step 监听器(prepend 最外层)+ 注册 skill_search
|
|
75
|
+
│ ├─ bm25.js # 原样 vendor dsh-tool-folder/lib/bm25.js(零依赖,CJK bigram)
|
|
76
|
+
│ ├─ pattern.js # matchesAnyPattern(精确名或 prefix*,deny/core 共用)
|
|
77
|
+
│ ├─ select.js # selectEntries(entries, cfg) -> 静态有序选择(core 豁免 deny)
|
|
78
|
+
│ ├─ catalog.js # findCatalogMessage / trimDecision(静态渲染:只改 content,不动 entries)
|
|
79
|
+
│ ├─ render.js # renderCatalogText(selected, [], opts):保留宿主 framing
|
|
80
|
+
│ ├─ skill-search.js # 纯函数检索(BM25 over name+description+aliases)
|
|
81
|
+
│ └─ tool-skill-search.js # defineTool 包装 skill_search(依赖 ctx.skills snapshot)
|
|
82
|
+
├─ test/
|
|
83
|
+
│ ├─ fixtures.js # 10 技能 fixture(含 cordis 技能)+ 宿主渲染/digest 复刻
|
|
84
|
+
│ ├─ apply.test.js # 插件入口回归:prepend/disabled/fail-safe/next 传播/慢告警/disposer/tool 注册
|
|
85
|
+
│ ├─ catalog.test.js # T1-T6 裁剪 + T5b KV 稳定性 + T5c 全列 + T5d 安全底线 + digest 一致性 + 放行
|
|
86
|
+
│ ├─ skill-search.test.js # S1-S10 检索质量(中文→英文技能命中/deny/确定性/纯函数)
|
|
87
|
+
│ └─ waterfall.test.js # T13-T14 瀑布集成 + digest 一致性回归
|
|
88
|
+
├─ node_modules/@deepseek-ai/ # 测试专用轻量 stub(schemastery/dsh-tools,npm install 会被真实包覆盖)
|
|
89
|
+
└─ docs/ # system_design.md + class/sequence mermaid(架构评审版)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 红线(规格 Shared Knowledge)
|
|
93
|
+
|
|
94
|
+
1. **catalog 消息 `source.entries` 是全量快照,永不裁剪**——只替换 `content[0].text`(digest 一致性红线,否则每轮重发全量 token 更爆炸)。
|
|
95
|
+
2. **注册必须 `prepend:true`(最外层)**——普通 `ctx.on` 是最内层,在 L2 之前运行,改不到 catalog。
|
|
96
|
+
3. **所有改写返回新对象(spread),绝不 mutate 冻结消息**;失败一律原样放行,绝不 throw。
|
|
97
|
+
4. **catalog 渲染必须静态**(只依赖技能集合,不随 query 变)——否则 KV cache 前缀失效,净收益为负。
|
|
98
|
+
5. 渲染保留宿主 framing(`<system-reminder>`/`<available_skills>`/加载指引/直呼指引)。
|
|
99
|
+
6. 全零运行时依赖(除 schemastery + cordis/dsh-tools peer)。
|
|
100
|
+
|
|
101
|
+
## 测试
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
node --test "test/*.test.js"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
49 条测试(node:test + node:assert,零外部依赖;经 `node_modules/@deepseek-ai/` 下的轻量测试 stub 直接 import `lib/index.js`):
|
|
108
|
+
|
|
109
|
+
- **apply.test.js**:插件入口回归——exports 契约(name/inject/Config);disabled 不注册监听器;`agent/pre-step` 以 `prepend=true`(最外层)注册;`skill_search` 工具注册 / toolSearchEnabled=false 不注册;trim 抛错 → catch → 原样放行(fail-safe);`next()` 抛错 → 传播不吞错;maxFoldMs 超时 → 告警但结果仍放行;disposer 幂等。
|
|
110
|
+
- **catalog.test.js**:T1 全量裁剪显著变短 + entries deep-equal + kind/id 保留;T2 无 catalog 同一引用;T3 reject 放行;T4 entries 畸形放行;T5 全选中文本不变同一引用;T5b **KV 稳定性(不同 query 字节相同)**;T5c **全列(cordis 技能可见,绝不丢技能)**;T5d **安全底线(deny 不能删 core)**;T6 多条 catalog 全部裁剪。
|
|
111
|
+
- **skill-search.test.js**:S1 deny 剔除;S2-S7 中文意图命中英文技能(cordis 插件/composition/委派/审查/记忆/规划);S8 空意图空结果;S9 确定性;S10 纯函数无副作用。
|
|
112
|
+
- **hybrid-route.test.js**(v0.3.0):routeHint 中文/英文 alias 命中、无关消息不误路由、deny 技能不提示;searchSkillsHybrid 无语义降级 BM25、语义命中、双命中 RRF 排序、语义抛错降级;appendRouteHint 尾部追加 / catalog 引用不变 / 幂等 / fail-safe。
|
|
113
|
+
- **waterfall.test.js**:T13 模拟 L2 注入全量 catalog + 外层监听器裁剪,其它消息顺序/引用不变;T14 下轮 digest 判定「无变化」不追加(防 republish 死循环)。
|
|
114
|
+
|
|
115
|
+
## 已知边界(v1 接受)
|
|
116
|
+
|
|
117
|
+
- 技能集变更时宿主会追加一条裁剪版 catalog(~600-1000 字符),远小于全量;v2 可研究 session surface 重写。
|
|
118
|
+
- `maxDescLength=100` 为初始值,落地后按真实 token 收益调参。
|
|
119
|
+
- deny 只影响目录,不影响用户 `/name` 直呼(L1 绕过目录,仍能注入)。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# dsh-skill-folder bundle patch.
|
|
2
|
+
#
|
|
3
|
+
# Inserts the skill-folder plugin row into the profile composition. Nothing in
|
|
4
|
+
# the base bundle is disabled or replaced — @deepseek-ai/dsh-tool-skill keeps
|
|
5
|
+
# owning the `skill` tool, the /name L1 injection and the catalog L2 publish
|
|
6
|
+
# machinery. This plugin only post-processes the final pre-step decision:
|
|
7
|
+
# it trims the *rendered* catalog text (content[0].text) while leaving
|
|
8
|
+
# source.entries (the digest input) untouched, so the host's digest feedback
|
|
9
|
+
# loop stays stable and never re-publishes the full catalog.
|
|
10
|
+
#
|
|
11
|
+
# Harness patches replace the targeted row's WHOLE `config` rather than
|
|
12
|
+
# merging. Omitting `config` here means the code defaults (lib/index.js
|
|
13
|
+
# DEFAULTS) apply. To override, restate the complete config under the same
|
|
14
|
+
# `id: skill-folder` in your profile's own `cordis.patch.yml`.
|
|
15
|
+
#
|
|
16
|
+
# v0.3.0: semanticEnabled (default true) adds the bge-m3 hybrid leg to
|
|
17
|
+
# skill_search; autoRoute (default true) appends <skill-route> hints to the
|
|
18
|
+
# user message tail. Both degrade/disable safely — code defaults suffice.
|
|
19
|
+
- insert:
|
|
20
|
+
- id: skill-folder
|
|
21
|
+
name: 'dsh-skill-folder'
|
|
22
|
+
# config:
|
|
23
|
+
# enabled: true
|
|
24
|
+
# core: [dsh-injection-guard, dsh-verifier]
|
|
25
|
+
# topK: 3
|
|
26
|
+
# deny: [autotelic-evolution, dsh-team-orchestra]
|
|
27
|
+
# catalogEnabled: true
|
|
28
|
+
# maxDescLength: 100
|
|
29
|
+
# maxFoldMs: 5
|
|
30
|
+
# semanticEnabled: true
|
|
31
|
+
# autoRoute: true
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
classDiagram
|
|
2
|
+
class SkillFolderPlugin {
|
|
3
|
+
+name: string
|
|
4
|
+
+inject: string[]
|
|
5
|
+
+Config: SchemasteryObject
|
|
6
|
+
+apply(ctx, config) void
|
|
7
|
+
}
|
|
8
|
+
class CatalogTrimmer {
|
|
9
|
+
+findCatalogMessage(messages) Message
|
|
10
|
+
+trimDecision(decision, cfg, logger) Decision
|
|
11
|
+
+rewriteContent(cat, text) Message
|
|
12
|
+
}
|
|
13
|
+
class QueryExtractor {
|
|
14
|
+
+extractQuery(claimed) string
|
|
15
|
+
}
|
|
16
|
+
class EntrySelector {
|
|
17
|
+
+selectEntries(entries, query, cfg) Selection
|
|
18
|
+
+matchAlias(q, aliases) Set~string~
|
|
19
|
+
}
|
|
20
|
+
class Bm25Index {
|
|
21
|
+
+tokenize(text) string[]
|
|
22
|
+
+buildIndex(docs) Index
|
|
23
|
+
+search(index, query, topK, opts) number[]
|
|
24
|
+
}
|
|
25
|
+
class CatalogRenderer {
|
|
26
|
+
+renderCatalogText(selected, footer, opts) string
|
|
27
|
+
+desc(value, max) string
|
|
28
|
+
}
|
|
29
|
+
SkillFolderPlugin --> CatalogTrimmer : 注册 pre-step(prepend)
|
|
30
|
+
CatalogTrimmer --> QueryExtractor
|
|
31
|
+
CatalogTrimmer --> EntrySelector
|
|
32
|
+
CatalogTrimmer --> CatalogRenderer
|
|
33
|
+
EntrySelector --> Bm25Index
|
|
34
|
+
CatalogTrimmer ..> Decision : 返回
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# 技能系统灵活性根治方案(v2 实测驱动版)
|
|
2
|
+
|
|
3
|
+
**修订日期**: 2026-08-28
|
|
4
|
+
**修订依据**: 基线测试结果(BM25 命中率 85%、质量平均分 9.7/25、描述一致性需重新定义)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 实测结论与方案调整
|
|
9
|
+
|
|
10
|
+
| 原方案 | 原优先级 | 实测发现 | 调整后方案 | 新优先级 |
|
|
11
|
+
|---|---|---|---|---|
|
|
12
|
+
| 方案 1: 语义检索 | P0 | BM25 中文命中率 85%,问题在描述未本地化 | **1a: 描述本地化**(英文描述补中文关键词) | **P0** |
|
|
13
|
+
| | | | 1b: 语义检索(仅处理剩余 15%) | P3 |
|
|
14
|
+
| 方案 2: 技能路由器 | P3 | 冲突频率待测,但 metadata schema 缺失 | **2: 元数据增强**(加 scope/tested/version 字段) | **P1** |
|
|
15
|
+
| 方案 3: 质量评分 | P2 | 100% 无测试,60% 低于 10 分 | **3: 质量门控**(新增技能强制结构完整+测试) | **P1** |
|
|
16
|
+
| 方案 4: 一致性校验 | P1 | Jaccard 0.103 是设计特性,非问题 | **4: 触发条件验证**(描述中的 When 是否在内容中有对应章节) | **P2** |
|
|
17
|
+
| 方案 5: 懒加载 | P4 | 待实测 token 消耗 | **5: Token 实测**(先测再决定) | P4 |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 方案 1a: 描述本地化(P0,1 天)
|
|
22
|
+
|
|
23
|
+
### 问题根因
|
|
24
|
+
BM25 miss 的 3 个案例中,2 个是**英文描述不含中文关键词**:
|
|
25
|
+
- `dsh-sec-code-audit`: "Use for authorized source-code security review..."(不含"审计")
|
|
26
|
+
- `dsh-sec-diagram-generator`: "...mind maps..."(不含"思维导图")
|
|
27
|
+
|
|
28
|
+
### 实施方案
|
|
29
|
+
**不改 BM25,改技能描述**:
|
|
30
|
+
1. 写脚本扫描所有技能,识别英文描述
|
|
31
|
+
2. 对英文描述加中文别名(修改 SKILL.md 的 `description` 字段或加 `aliases` 字段)
|
|
32
|
+
3. 受益:BM25 直接命中,无需向量检索
|
|
33
|
+
|
|
34
|
+
### 实施步骤
|
|
35
|
+
```bash
|
|
36
|
+
# 1. 扫描英文描述技能
|
|
37
|
+
node test/scan-english-desc.js
|
|
38
|
+
|
|
39
|
+
# 2. 批量补中文关键词(半自动)
|
|
40
|
+
# 例:description: "Use for authorized source-code security review..."
|
|
41
|
+
# → "代码安全审计。Use for authorized source-code security review..."
|
|
42
|
+
|
|
43
|
+
# 3. 重跑 BM25 测试,验证命中率提升至>95%
|
|
44
|
+
node test/baseline-bm25.js
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 验收标准
|
|
48
|
+
- BM25 中文命中率 ≥ 95%
|
|
49
|
+
- 不新增运行时依赖
|
|
50
|
+
- 不改变检索逻辑
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 方案 2: 元数据增强(P1,2 天)
|
|
55
|
+
|
|
56
|
+
### 问题根因
|
|
57
|
+
技能无统一 metadata schema,导致:
|
|
58
|
+
- 无法自动路由(无 scope 字段)
|
|
59
|
+
- 无法质量评分(无 tested 字段)
|
|
60
|
+
- 无法版本管理(无 version 字段,虽有但无校验)
|
|
61
|
+
|
|
62
|
+
### 实施方案
|
|
63
|
+
**扩展现有 frontmatter**:
|
|
64
|
+
```yaml
|
|
65
|
+
---
|
|
66
|
+
name: dsh-debugging
|
|
67
|
+
version: 1.0.0
|
|
68
|
+
description: 系统化调试一体化技能...
|
|
69
|
+
# 新增字段
|
|
70
|
+
scope: [debugging, testing] # 用于路由
|
|
71
|
+
tested: true # 是否有测试
|
|
72
|
+
testDir: test/ # 测试目录路径
|
|
73
|
+
createdAt: 2026-08-01
|
|
74
|
+
updatedAt: 2026-08-28
|
|
75
|
+
author: DSH Team
|
|
76
|
+
---
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 实施步骤
|
|
80
|
+
1. 定义 schema(`lib/schema.js`)
|
|
81
|
+
2. 写验证脚本(`test/validate-metadata.js`)
|
|
82
|
+
3. 存量技能补 metadata(半自动)
|
|
83
|
+
4. 新增技能强制校验(CI 门控)
|
|
84
|
+
|
|
85
|
+
### 验收标准
|
|
86
|
+
- 100% 技能有完整 metadata
|
|
87
|
+
- 路由逻辑可基于 scope 字段工作
|
|
88
|
+
- 质量评分可基于 tested 字段
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 方案 3: 质量门控(P1,3 天)
|
|
93
|
+
|
|
94
|
+
### 问题根因
|
|
95
|
+
实测 60% 技能低于 10 分,100% 无测试
|
|
96
|
+
|
|
97
|
+
### 实施方案
|
|
98
|
+
**新增技能强制要求**:
|
|
99
|
+
1. 结构完整(When to Use + Core Pattern + Examples)
|
|
100
|
+
2. 至少 1 个测试用例(验证技能加载后行为)
|
|
101
|
+
3. 描述含中文关键词(方案 1a 复用)
|
|
102
|
+
|
|
103
|
+
**存量技能**:
|
|
104
|
+
- 标"未验证"标签,不阻断使用
|
|
105
|
+
- 高频技能优先补全(Top 20)
|
|
106
|
+
|
|
107
|
+
### 测试设计
|
|
108
|
+
技能测试 ≠ 代码测试,而是**行为验证**:
|
|
109
|
+
```javascript
|
|
110
|
+
// test/dsh-debugging.test.js
|
|
111
|
+
import { loadSkill } from '../lib/test-helpers.js';
|
|
112
|
+
|
|
113
|
+
describe('dsh-debugging', () => {
|
|
114
|
+
it('应包含 When to Use 章节', async () => {
|
|
115
|
+
const skill = await loadSkill('dsh-debugging');
|
|
116
|
+
assert(skill.content.includes('## When to Use') || skill.content.includes('## 何时使用'));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('应包含至少 1 个代码示例', async () => {
|
|
120
|
+
const skill = await loadSkill('dsh-debugging');
|
|
121
|
+
const codeBlocks = skill.content.match(/```[\s\S]*?```/g) || [];
|
|
122
|
+
assert(codeBlocks.length >= 1);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('描述应含中文关键词', async () => {
|
|
126
|
+
const skill = await loadSkill('dsh-debugging');
|
|
127
|
+
assert(/[\u4e00-\u9fff]/.test(skill.description));
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 验收标准
|
|
133
|
+
- 新增技能 100% 通过质量门控
|
|
134
|
+
- Top 20 高频技能 100% 补全测试
|
|
135
|
+
- 质量评分可视化(`skill_search` 返回带 score)
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 方案 4: 触发条件验证(P2,2 天)
|
|
140
|
+
|
|
141
|
+
### 问题根因
|
|
142
|
+
原方案用 Jaccard 相似度,实测 0.103 是设计特性
|
|
143
|
+
|
|
144
|
+
### 实施方案
|
|
145
|
+
**验证"描述中的触发条件是否在内容中有对应章节"**:
|
|
146
|
+
1. 从描述提取触发条件("当 X 时使用"→X)
|
|
147
|
+
2. 检查内容是否有对应章节("## When to Use"或"## 何时使用")
|
|
148
|
+
3. 检查章节是否覆盖提取的触发条件
|
|
149
|
+
|
|
150
|
+
### 验证脚本
|
|
151
|
+
```javascript
|
|
152
|
+
// test/validate-triggers.js
|
|
153
|
+
function validateTriggers(skill) {
|
|
154
|
+
const { description, content } = skill;
|
|
155
|
+
|
|
156
|
+
// 提取描述中的触发条件(正则匹配"当...时"、"Use when...")
|
|
157
|
+
const triggers = extractTriggers(description);
|
|
158
|
+
|
|
159
|
+
// 检查内容是否有 When to Use 章节
|
|
160
|
+
const whenSection = content.match(/## (When to Use|何时使用)([\s\S]*?)(?=## )/);
|
|
161
|
+
if (!whenSection) return { pass: false, reason: 'Missing When to Use section' };
|
|
162
|
+
|
|
163
|
+
// 检查触发条件是否在章节中覆盖
|
|
164
|
+
const covered = triggers.every(t => whenSection[0].includes(t));
|
|
165
|
+
|
|
166
|
+
return { pass: covered, reason: covered ? 'OK' : 'Triggers not covered' };
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 验收标准
|
|
171
|
+
- 100% 技能有 When to Use 章节
|
|
172
|
+
- 80% + 技能的触发条件被章节覆盖
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 方案 5: Token 消耗实测(P4,0.5 天)
|
|
177
|
+
|
|
178
|
+
### 实施方案
|
|
179
|
+
```javascript
|
|
180
|
+
// test/token-usage.js
|
|
181
|
+
const catalogText = renderCatalogText(selectedSkills);
|
|
182
|
+
const tokenCount = Math.ceil(catalogText.length / 4); // 近似估算
|
|
183
|
+
console.log(`Catalog: ${catalogText.length} chars ≈ ${tokenCount} tokens`);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 验收标准
|
|
187
|
+
- 实测当前 token 消耗
|
|
188
|
+
- 评估懒加载收益(如>50% 则实施)
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 实施路线图
|
|
193
|
+
|
|
194
|
+
| 阶段 | 方案 | 预期工时 | 交付物 |
|
|
195
|
+
|---|---|---|---|
|
|
196
|
+
| **Week 1** | 1a: 描述本地化 | 1 天 | BM25 命中率≥95% |
|
|
197
|
+
| | 2: 元数据增强 | 2 天 | metadata schema + 验证脚本 |
|
|
198
|
+
| **Week 2** | 3: 质量门控 | 3 天 | Top 20 技能测试 + 门控逻辑 |
|
|
199
|
+
| | 4: 触发条件验证 | 2 天 | 验证脚本 + 修复清单 |
|
|
200
|
+
| **Week 3** | 5: Token 实测 | 0.5 天 | 实测报告 |
|
|
201
|
+
| | 按需实施懒加载 | 2 天 | 懒加载逻辑(如收益>50%) |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 风险与缓解
|
|
206
|
+
|
|
207
|
+
| 风险 | 概率 | 影响 | 缓解措施 |
|
|
208
|
+
|---|---|---|---|
|
|
209
|
+
| 描述本地化工作量大 | 中 | 中 | 优先处理 Top 50 高频技能 |
|
|
210
|
+
| 存量技能 metadata 补全难 | 高 | 中 | 半自动脚本 + 人工复核 |
|
|
211
|
+
| 质量门控阻碍新增技能 | 低 | 高 | 设 30 天缓冲期,期内警告不阻断 |
|
|
212
|
+
| 触发条件验证误报 | 中 | 低 | 人工复核 Top 20 技能 |
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 成功指标
|
|
217
|
+
|
|
218
|
+
| 指标 | 基线 | 目标 | 测量方法 |
|
|
219
|
+
|---|---|---|---|
|
|
220
|
+
| BM25 中文命中率 | 85% | ≥95% | `test/baseline-bm25.js` |
|
|
221
|
+
| 技能质量平均分 | 9.7/25 | ≥15/25 | `test/baseline-quality.js` |
|
|
222
|
+
| 有测试技能占比 | 0% | ≥50% (Top 20) | `test/validate-metadata.js` |
|
|
223
|
+
| 触发条件覆盖率 | 未知 | ≥80% | `test/validate-triggers.js` |
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 附录:实测脚本清单
|
|
228
|
+
|
|
229
|
+
| 脚本 | 用途 | 状态 |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `test/baseline-bm25.js` | BM25 命中率测试 | ✅ 已完成 |
|
|
232
|
+
| `test/baseline-quality.js` | 质量分布测试 | ✅ 已完成 |
|
|
233
|
+
| `test/baseline-consistency.js` | 描述一致性测试 | ✅ 已完成(需重新设计) |
|
|
234
|
+
| `test/scan-english-desc.js` | 扫描英文描述 | ⬜ 待开发 |
|
|
235
|
+
| `test/validate-metadata.js` | Metadata 验证 | ⬜ 待开发 |
|
|
236
|
+
| `test/validate-triggers.js` | 触发条件验证 | ⬜ 待开发 |
|
|
237
|
+
| `test/token-usage.js` | Token 消耗测试 | ⬜ 待开发 |
|