Back to home@QWE13-ART

dsh-skill-folder

Fold the DSH skill catalog prompt surface: static KV-cache-stable catalog + BM25/bge-m3 hybrid skill_search + autoRoute hints. v0.3.0. npm: dsh-skill-folder

Stars
0
Language
JavaScript
Created
Aug 30, 2026
Updated
Aug 30, 2026
GitHub repo

Introduction

dsh-skill-folder

根治「DSH 每轮把全部技能 description 平铺进 prompt 吃 token」问题。

v0.3.2(2026-08-30,否定词过滤 + frontmatter 检索 + 配置持久化)

  • routeHint 否定词过滤:消息含「不需要/不用/无需/不要/别用/别再/别管/不必」时视为反意图,直接不路由(「不需要验证这个方案」不再错误路由到 verifier)。只用多字组合,单字「别/不」不误伤「告别/不错」;「别忘了记住这个」仍正常命中 viking。
  • 技能 frontmatter 元数据进检索:新增 lib/frontmatter.jsextractMeta 解析 SKILL.md frontmatter YAML 子集 —— category/tags/whenToUse,含 metadata.* 嵌套与扁平 tags:),filterPool 保留 metadata 字段,searchSkills/searchSkillsHybrid 把 frontmatter 词并入 BM25 索引(加权:出现 2 次 > description 的 1 次)。无 frontmatter 的技能检索路径完全不变(poolDocs 逻辑保持等价)。审查接线:宿主 toSummary 不输出 metadata,故检索前经 skills.get(name).path 读 SKILL.md 原文解析合并(enrichPoolWithFrontmatter,进程级缓存,fail-safe)。
  • 配置三级持久化:新增 lib/storage.jsreadState/writeState~/.dsh/settings/~/.dsh/state/ → 进程内内存 逐级降级,任何 fs/JSON 失败不抛错返回默认)。index.js 用之持久化真实 autoRoute 命中统计 {routes: {skillName: count}, lastTs},上限 200 技能名,超出清最旧。
  • 77 条测试全绿(新增 25 条:否定词 3 + frontmatter 12 + storage 6 + routeSkillName 1 + 接线 3)。

v0.3.1(2026-08-30,路由收紧 + 稳定性)

  • routeHint 误路由修复:多技能同时命中 → 不路由(「验证这个方案安全吗」不再同时猜 verifier+injection-guard);短消息(<5 字)不路由(「验证一下」「安全吗」是闲聊不是意图);名 token 改词边界匹配(remem 不再误命中 memory,完整 part 才命中)。普通对话的 <skill-route> 噪声大幅减少。
  • 52 条测试全绿(新增 3 条误路由回归)。

v0.3.0(2026-08-30,语义混合 + 自动路由)

  • 语义检索腿skill_search 升级为 BM25 + 本地 bge-m3(Ollama)RRF 混合检索——中文意图可直接命中英文技能(BM25 词面鸿沟补上)。语义索引按内容指纹懒构建 + 落盘缓存(~/.dsh/state/semantic-cache.json),Ollama 离线/超时自动降级纯 BM25,绝不比旧版慢或差。
  • 自动路由提示autoRoute,默认开):用户消息明显指向某技能时(aliases 命中或技能名 token 命中),在用户消息尾部追加一行 <skill-route> 提示——用户区本就是动态区,catalog 前缀字节不变,KV 缓存零破坏。无关消息不路由,幂等,fail-safe。
  • 新配置:semanticEnabled / ollamaBase / embedModel / autoRoute

v0.2.0(KV-cache-stable):静态稳定 catalog(前缀永不变化)+ skill_search 检索工具(按需精准发现)。这是 Deferred loading 模式(Anthropic Tool Search / SkillRouter 同款)——动态裁剪目录文本会破坏 prompt cache 前缀(每轮数万 token 重算,净收益为负),静态 catalog + 检索工具则两者兼得:token 省 + 缓存命中 + 选择精准

架构评审版规格:docs/system_design.md(权威,含宿主契约行号 A-F 全章节)

核心机制(一句话)

catalog 静态渲染(保缓存前缀)+ skill_search 工具(保选择质量)。

  • 宿主 @deepseek-ai/dsh-tool-skill 负责:skill 工具注册、/name 手势注入(L1)、catalog 发布/更新/digest/历史(L2)。
  • 本插件以 ctx.on("agent/pre-step", fn, true)(prepend → 最外层) 挂在瀑布最外层:等 L1/L2 全部完成拿到含全量 catalog 的最终 decision 后,只替换 message.content[0].text(模型看到的渲染),绝不动 message.source.entries(digest 输入)
  • 静态渲染:目录文本只依赖技能集合(core 全量 + 其余截断 + deny 剔除),永不随 query 变 → 每轮字节相同 → DeepSeek 自动前缀缓存 100% 命中。
  • skill_search:注册 skill_search(intent) 工具(静态前缀),按意图 BM25 检索(name+description+aliases,中文可命中英文技能),结果追加消息尾部 → 不碰前缀。

安装

  1. 把本目录放进 DSH 插件搜索路径(或 bundle 依赖),cordis.patch.yml 会把 skill-folder 插入 profile 组合。
  2. package.jsondsh.bundle.patch 指向 ./cordis.patch.yml;也可在 profile 自己的 cordis.patch.yml 里按 id: skill-folder 覆盖配置。
  3. (可选)语义检索腿需要本地 Ollamahttp://127.0.0.1:11434)+ bge-m3 模型:ollama pull bge-m3。不装也能用——自动降级纯 BM25,功能与 v0.2.0 完全一致,只是少了中英跨语言语义命中。
cd dsh-skill-folder
npm install          # 只需 @deepseek-ai/schemastery(宿主已内置 cordis/dsh-tools 作 peer)
npm test             # node:test,49 条测试,零外部测试依赖

⚠️ 不要禁用 @deepseek-ai/dsh-tool-skill:会导致 skill 工具消失、/name 注入消失,模型无法按名加载技能。本插件与其共存,最小干预 = 最小风险。

配置(schemastery,profile 可按 id: skill-folder 覆盖)

默认说明
enabledtrue总开关:关闭则完全不注册监听器
core["dsh-injection-guard", "dsh-verifier"]P0 常驻:每轮全量描述可见(安全底线,不截断;deny 不能删除 core
deny["autotelic-evolution", "dsh-team-orchestra"]P3 剔除:精确名或 prefix*;core 技能豁免
aliases12 技能中文映射意图词→技能:skill_search 检索索引(BM25 加分),中文意图可命中英文技能
maxDescLength100非 core 技能描述最大长度(core 不受限)
maxFoldMs5裁剪耗时上限(ms),超时仅告警,结果仍应用
toolSearchEnabledtrue注册 skill_search 检索工具(静态前缀,结果追加尾部,不破坏缓存)
maxDescLength100动态条目描述最大长度;core 不受限
maxFoldMs5裁剪耗时上限(ms),超时仅告警,结果仍放行

默认 aliases

{
  "viking-memory-guide":      ["记忆", "回忆", "记住", "memory", "remember"],
  "dsh-grilling":             ["访谈", "对齐", "先问我", "grilling", "问清楚", "开工前"],
  "dsh-delegation-checklist": ["委派", "子智能体", "subagent", "delegate", "openhands"],
  "dsh-context-language":     ["术语", "词汇表", "领域语言", "context", "语言"],
  "dsh-injection-guard":      ["注入", "安全", "不可信", "injection", "外部内容"],
  "dsh-verifier":             ["验证", "检查完成", "防假完成", "verify", "验证器"],
}

core / deny / aliases 的键(技能名)均支持精确名prefix* 前缀匹配。

文件结构

dsh-skill-folder/
├─ package.json            # name: dsh-skill-folder, type: module, main: lib/index.js
├─ cordis.patch.yml        # profile patch: insert {id: skill-folder, name: 'dsh-skill-folder'}
├─ README.md
├─ lib/
│  ├─ index.js             # 插件入口:name/inject/Config/apply + pre-step 监听器(prepend 最外层)+ 注册 skill_search
│  ├─ bm25.js              # 原样 vendor dsh-tool-folder/lib/bm25.js(零依赖,CJK bigram)
│  ├─ pattern.js           # matchesAnyPattern(精确名或 prefix*,deny/core 共用)
│  ├─ select.js            # selectEntries(entries, cfg) -> 静态有序选择(core 豁免 deny)
│  ├─ catalog.js           # findCatalogMessage / trimDecision(静态渲染:只改 content,不动 entries)
│  ├─ render.js            # renderCatalogText(selected, [], opts):保留宿主 framing
│  ├─ skill-search.js      # 纯函数检索(BM25 over name+description+aliases)
│  └─ tool-skill-search.js # defineTool 包装 skill_search(依赖 ctx.skills snapshot)
├─ test/
│  ├─ fixtures.js          # 10 技能 fixture(含 cordis 技能)+ 宿主渲染/digest 复刻
│  ├─ apply.test.js        # 插件入口回归:prepend/disabled/fail-safe/next 传播/慢告警/disposer/tool 注册
│  ├─ catalog.test.js      # T1-T6 裁剪 + T5b KV 稳定性 + T5c 全列 + T5d 安全底线 + digest 一致性 + 放行
│  ├─ skill-search.test.js # S1-S10 检索质量(中文→英文技能命中/deny/确定性/纯函数)
│  └─ waterfall.test.js    # T13-T14 瀑布集成 + digest 一致性回归
├─ node_modules/@deepseek-ai/  # 测试专用轻量 stub(schemastery/dsh-tools,npm install 会被真实包覆盖)
└─ docs/                   # system_design.md + class/sequence mermaid(架构评审版)

红线(规格 Shared Knowledge)

  1. catalog 消息 source.entries 是全量快照,永不裁剪——只替换 content[0].text(digest 一致性红线,否则每轮重发全量 token 更爆炸)。
  2. 注册必须 prepend:true(最外层)——普通 ctx.on 是最内层,在 L2 之前运行,改不到 catalog。
  3. 所有改写返回新对象(spread),绝不 mutate 冻结消息;失败一律原样放行,绝不 throw。
  4. catalog 渲染必须静态(只依赖技能集合,不随 query 变)——否则 KV cache 前缀失效,净收益为负。
  5. 渲染保留宿主 framing(<system-reminder>/<available_skills>/加载指引/直呼指引)。
  6. 全零运行时依赖(除 schemastery + cordis/dsh-tools peer)。

测试

node --test "test/*.test.js"

49 条测试(node:test + node:assert,零外部依赖;经 node_modules/@deepseek-ai/ 下的轻量测试 stub 直接 import lib/index.js):

  • apply.test.js:插件入口回归——exports 契约(name/inject/Config);disabled 不注册监听器;agent/pre-stepprepend=true(最外层)注册;skill_search 工具注册 / toolSearchEnabled=false 不注册;trim 抛错 → catch → 原样放行(fail-safe);next() 抛错 → 传播不吞错;maxFoldMs 超时 → 告警但结果仍放行;disposer 幂等。
  • 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 全部裁剪。
  • skill-search.test.js:S1 deny 剔除;S2-S7 中文意图命中英文技能(cordis 插件/composition/委派/审查/记忆/规划);S8 空意图空结果;S9 确定性;S10 纯函数无副作用。
  • hybrid-route.test.js(v0.3.0):routeHint 中文/英文 alias 命中、无关消息不误路由、deny 技能不提示;searchSkillsHybrid 无语义降级 BM25、语义命中、双命中 RRF 排序、语义抛错降级;appendRouteHint 尾部追加 / catalog 引用不变 / 幂等 / fail-safe。
  • waterfall.test.js:T13 模拟 L2 注入全量 catalog + 外层监听器裁剪,其它消息顺序/引用不变;T14 下轮 digest 判定「无变化」不追加(防 republish 死循环)。

已知边界(v1 接受)

  • 技能集变更时宿主会追加一条裁剪版 catalog(~600-1000 字符),远小于全量;v2 可研究 session surface 重写。
  • maxDescLength=100 为初始值,落地后按真实 token 收益调参。
  • deny 只影响目录,不影响用户 /name 直呼(L1 绕过目录,仍能注入)。