给 DeepSeek Harness 写了个「技能熔炉」:任何来源的 SKILL.md 一条命令装上

前言:最近在玩 DeepSeek Harness(DSH),发现一个结构性缺口------技能包装了等于没装。本文讲我怎么填这个坑,以及其中几个比较较真的设计选择。

项目地址:github.com/UltimateJob...

目录

痛点:DSH 技能安装的结构性裂缝

DSH 是 DeepSeek 开源的 agent 框架,设计挺优雅------一切皆插件,Cordis 驱动。但用深了发现一个尴尬的事:它有"技能"和"插件"两条割裂的安装路径。

dsh plugin 路径:

  • 本质是 pnpm 转发器,只调和 dsh.profile.bundles
  • dsh.bundle 声明的包进 profile 层
  • 没有 dsh.bundle 的就当普通依赖装,触发警告:declares no dsh.bundle - installed as a plain dependency, not a profile layer

技能发现路径:

  • 只认 6 个固定根目录(<project>/.dsh/skills<project>/.agents/skills~/.dsh/skills~/.agents/skills$DSH_BUNDLED_SKILL_DIR)
  • 一层深,frontmatter 需 name(kebab-case) + description
  • dsh-skill-filesystem 插件扫描

裂缝出现:

纯技能 npm 包(比如 adversarial-review 做代码审查的)发到 npm,skills/<name>/SKILL.md 是 ship 了的,但包没有 dsh.bundledsh plugin add 把它装成普通依赖,SKILL.md 静躺在 node_modules/adversarial-review/skills/不在任何被扫描根里,永不被发现

用户被迫手动 cp~/.dsh/skills/。没版本管理、没安全检查、没回滚、没批量管理。

官方的 dsh-skill-manager只读 的------包描述明写 "Read-only browser",lib/index.js 仅一个 GET /api/skill-manager/list 路由,从不写文件,只扫 2 个根。连增删改都没有。

DSH 也没有 dsh skill 命令------CLI 只有 dsh plugin(→ pnpm)和 dsh web。技能安装无一等公民命令。

这个缺口真实且已机理性证实。所以我写了 dsh-skill-fusion(技能熔炉) 来填它。

fusion 是什么

一句话:任何来源的技能包,经统一四阶段(发现 → 审计 → 激活 → 固化)进入可用态。

与管插件的 dshmarket 严格互补,零功能重叠------fusion 只管技能,不管插件层。

四阶段生命周期设计

scss 复制代码
Discover(只读) → Audit(只读+缓存) → Activate(写 ~/.dsh/skills + manifest) → [native 发现/HMR]
                                                              ↓
                                          Freeze(重审→切链→快照) / Export / Uninstall

Stage 1 · Discover(发现,只读)

扫源、列候选,零写盘。是"可装技能的货架",与"已激活"分开。

源适配器各自返回候选不写盘:

  • npm :查 npm registry,拉 package.json,探测是否 ship 了 skills/<name>/SKILL.md、是否声明 dsh.bundle
  • github :拉 repo tree(GitHub API),递归找 **/SKILL.md(此源是唯一允许递归的,因为 GitHub repo 结构自由)
  • claude / codex / agents:读各自根目录(一层深)
  • local:读本地路径

Stage 2 · Audit(审计,激活前必跑)

详见下一节审计威胁模型

verdict 三档:

  • pass:无 flag,直接 activate
  • warn:有 injection 向量 flag,需用户显式确认
  • block:结构性无效(frontmatter 坏/引用断/name 冲突),拒绝

审计结果按 SKILL.md 内容哈希缓存到 ~/.dsh/skill-fusion/audits/<hash>.json,下次同内容免审。

Stage 3 · Activate(激活,写盘)

智能选激活路径:

js 复制代码
// lib/activate.js 简化逻辑
const strategy = canSymlink(source) ? 'symlink' : 'copy';
if (strategy === 'symlink') {
  await fs.symlink(source, target);  // 源更新即生效
} else {
  await copyDir(source, target);     // 回退
}
  • 源是已装 npm 包路径 / 本地 / git → symlink(省空间、源更新即生效)
  • symlink 失败(Windows 无 dev-mode / zip 拉取的 temp)→ copy 回退
  • dsh.bundle 的包 → fusion 只 symlink 其 ship 的 skill(若该 skill 未被发现),不代行插件安装

激活后 DSH 原生 watcher 自动发现、HMR 热加载------零重造发现轮子

orphan 清理 :fusion 启动时对账 manifest vs 文件系统------软链悬空(源被 pnpm remove)→ 标 orphan,UI 提示重链或移除。

Stage 4 · Freeze(固化)

  • Pin :写 frozenVersion,更新检查跳过 pinned 项(除非 --force)
  • Update :重新 fetch 源(npm 最新 version;github 最新 commit)→ 内容哈希变 → 重审 → pass/warn-confirmed → 切链/重拷;旧态先入 snapshots/
  • Rollback :copy 模式换到 snapshots/<name>@<v>/;symlink 模式 git 源 checkout 旧 commit 重链
  • Export :skill-fusion-bundle-<ts>.json = manifest 子集 + 已激活技能内容
  • Import:merge 语义(导出后新激活的保留)、写前校验、失败回滚

审计威胁模型:为什么扫指令而非扫脚本

这是整个项目我最较真的点。

DSH 技能是模型指令(body 进模型 context),不是可执行代码。恶意 SKILL.md 的真实攻击面是 prompt injection:

攻击向量 示例
调度劫持 恶意 whenToUse / description 触发短语让 skill 在不该触发时被自动调度
指令覆写 body 含 disregard above instructions / ignore previous / you are now ...
数据外泄 body 指示模型 fetch 外部 URL 带上 ~/.dsh/.credentials / env / secrets
越界写 body 指示写 skill 资源目录外、删非 fusion 管理文件

"扫描脚本"的框架会给人虚假安全感------技能没有可执行脚本,执行的是模型读指令后用工具。所以审计的实质是扫指令向量,标 warn 为主、显式确认放行,而非假装能"杀毒"。

hard block 仅用于结构性无效:

js 复制代码
// lib/audit.js 简化
const BLOCK_REASONS = [
  'invalid-frontmatter',     // name 非 kebab-case / 缺 description
  'broken-references',       // references/<file> 被提及但源 bundle 不存在
  'name-conflict',           // 与已存在 skill 同名
];
// injection 向量一律 warn,不 block

因为结构性无效激活了也 load 不起来或污染目录;injection 向量是用户的信任决定,fusion 只负责把风险显式化。

五种来源适配器

bash 复制代码
# 本地文件夹
skill-fusion activate --local ./my-skills --name my-skill

# npm 包
skill-fusion activate --npm adversarial-review --name adversarial-review

# GitHub 仓库(可指定 ref)
skill-fusion activate --github owner/repo --ref main --name my-skill

# 从 Claude 的技能库(~/.claude/skills/)
skill-fusion activate --claude --name my-skill

# 从 Codex 的技能库(~/.codex/skills/)
skill-fusion activate --codex --name my-skill

每个源适配器(lib/sources/<name>.js)实现统一接口:

js 复制代码
// lib/sources/npm.js 简化
export async function discover({ pkg }) {
  const meta = await fetchJSON(`https://registry.npmjs.org/${pkg}`);
  return extractSkillCandidates(meta);  // 返回 [{name, description, source}]
}

export async function fetch({ pkg, version, dest }) {
  const tarball = await downloadTarball(meta.dist.tarball);
  await extractSkillDir(tarball, dest);  // 只解压 skills/ 子目录
}

源适配器测试用 mock fetch,从不触网

一体四面架构:共享 lib

一个包,四种使用方式,共享同一份 lib/* 核心:

文件 职责 调用者
Host 插件 lib/index.js + cordis.patch.yml dsh.bundle 激活进 profile 层;注册 same-origin JSON 路由 GUI(浏览器 fetch)
浏览器设置页 client/client.js window.__ModuleLoader__.load + ctx.slots.register("settings.section") 人(点按钮)
CLI bin/skill-fusion.js + lib/cli.js 壳调用同一份 lib/* 的子命令 agent(bash)
技能 skills/skill-fusion/SKILL.md 教 agent 用 CLI 驱动四阶段 agent(模型)

四种调用方式任选,逻辑同源,改一处四面同步。SKILL.md 是 agent 入口指向 CLI(避免 agent 直连 localhost JSON 路由的别扭)。

安全模型

这块我比较较真:

  1. 社区技能落沙箱根 :~/.dsh/skills/(非 trustedHost),body 经 ctx.fs 沙箱读取,不享受受信待遇
  2. 绝不滥用 DSH_BUNDLED_SKILL_DIR :那是给 DSH 自带受信技能的(trustedHost: true → 裸 Node fs)。把未审计社区技能塞进受信根是安全降级
  3. POST 路由 same-origin 强制 :照搬 dsh-skill-manager 的 isSameOriginRequest(校 sec-fetch-site / origin / host)
  4. 不重启:技能变更高 HMR(watcher),无需 dshmarket 那套 loopback 重启
  5. 不跑构建脚本 :npm 源走全局 fetch 拉 tarball 解压到 fusion 缓存,不执行 scripts
  6. 激活前审计:技能激活后 body 每 session 进模型 context,审计必须在写盘前跑完

零运行时依赖

核实后可达成且与 dsh-skill-manager 一致:

  • HTTP(npm/GitHub 发现):全局 fetch(Node 18+)
  • fs:Node 内置
  • frontmatter 解析:自写行级正则解析(照 dsh-skill-manager 的 parseFrontmatter,无 yaml 依赖)
  • zip 解压:Node 内置 zlib + 手解 zip header
json 复制代码
// package.json
{
  "dependencies": {},            // 零 own deps
  "peerDependencies": {
    "react": ">=18",
    "@deepseek-ai/cordis": ">=1"
  }
}

不引入任何可信小依赖,连 yaml 解析都自己写------够用就行,复杂 YAML 交给发现层权威 dsh-skill-filesystem 去 skip,fusion 不重复其校验。安全模型对标 dsh-plugin-check。

与 dshmarket 的边界

fusion 与 dshmarket(管插件生命周期)严格互补,零功能重叠:

dshmarket dsh-skill-fusion
插件(install/update/uninstall/hot-disable/backup) 技能(discover/audit/activate/freeze)
触发 dsh.bundle 否(只 symlink 其 ship 的 skill)
重启机制 loopback 重启 HMR 热加载

fusion 不代行插件安装。有 dsh.bundle 的包 → fusion 只 symlink 其 ship 的 skill(若该 skill 未被发现);插件层(install/update/uninstall)归 dshmarket。

快速上手

1. 安装

bash 复制代码
# 从 npm 装(发布后)
dsh plugin --profile web add dsh-skill-fusion

# 或从本地源码装
dsh plugin --profile web add /path/to/dsh-skill-fusion

2. CLI 一条命令激活

bash 复制代码
# 从本地文件夹(最简单,先练这个)
skill-fusion discover --local ./my-skills
skill-fusion audit --local ./my-skills --name my-skill
skill-fusion activate --local ./my-skills --name my-skill

# 从 npm
skill-fusion activate --npm adversarial-review --name adversarial-review

# 从 GitHub
skill-fusion activate --github owner/repo --ref main --name my-skill

3. 浏览器 GUI

DSH Web UI → Settings → Skill Forge / 技能熔炉,两个 Tab:

  • Discover:选源 → 搜索 → Audit 看结论(pass/warn/block)→ Activate
  • Activated:Freeze / Update / Rollback / Uninstall / Export

4. 给 DSH Agent 说自然语言

fusion 自己的 SKILL.md 被 DSH 发现后,agent 就会自驱。直接说:

  • "把 ./my-skills/foo 这个技能激活一下"
  • "从 GitHub owner/repo 装一下 my-skill 技能"
  • "先审计一下这个技能,告诉我有没有风险"
  • "冻结 my-skill 在 1.0.0"
  • "更新所有未冻结的技能"

agent 会自动跑 discover → audit(把 verdict 报给你)→ 等你确认 warn/block → activate。

5. 生命周期管理

bash 复制代码
skill-fusion list                              # 查看所有管理的技能
skill-fusion freeze --name my-skill --version 1.0.0   # 锁版本
skill-fusion update --name my-skill            # 更新(自动重审+快照)
skill-fusion rollback --name my-skill          # 回滚
skill-fusion uninstall --name my-skill         # 卸载
skill-fusion export --out backup.json          # 导出清单
skill-fusion import --from backup.json         # 导入清单

数据目录设计

perl 复制代码
~/.dsh/skill-fusion/      # fusion 自有,不在任何被扫描根下
├── manifest.json          # 所有权登记簿(旁路,不耦合激活态)
├── audits/                # 审计结果按内容哈希缓存
├── snapshots/<name>/      # 回滚快照(copy 模式)
└── cache/                 # npm/github tarball 缓存
~/.dsh/skills/<name>/      # 被激活技能(原生发现、沙箱化,非 trustedHost)

~/.dsh/skill-fusion/~/.dsh/skills/ 的兄弟,不在被扫描根内,所以 manifest / audits / snapshots 永不被 dsh-skill-filesystem 扫成 skill。

两条线不耦合:

  • 删 manifest 不删 skill(只是 fusion 不再认领它)
  • 删 skill 不删 manifest(fusion 标 orphan、下次启动清理)

项目状态

三阶段计划均已完成:

  • Phase 1a --- 行走骨架(lib 核心 + CLI + 本地源)
  • Phase 1b --- host 插件 + 设置页 + npm 源
  • Phase 2 --- freeze 全套(pin/update/rollback/export)

19 个测试文件覆盖:纯函数单测、激活集成、源适配器(mock fetch 不触网)、平台矩阵、审计缓存、orphan 清理、e2e。

bash 复制代码
# 跑测试
node --test

写在最后

DSH 是个好框架,"一切皆插件"的架构很优雅。但"技能"作为 agent 干活的核心载体,却连个一等公民命令都没有------这事儿不合理。

fusion 不是要替代 dshmarket,也不是要重写 dsh-skill-filesystem 的发现契约。它就是填"纯技能包的安装/激活/生命周期"这一个空缺,复用 DSH 已有的发现根、HMR、watcher,不重造轮子。

MIT 协议,三阶段交付完成,19 个测试文件覆盖。欢迎来 PR、提 issue、或者就是 star 一下让我知道有人在用。

项目地址:github.com/UltimateJob...


相关阅读:

如果觉得有用,欢迎去 GitHub 给个 star ⭐


本文永久链接:github.com/UltimateJob...

作者:UltimateJob

License:MIT

相关推荐
七牛开发者2 小时前
告别反复调参,一个 Skill 让 AI 掌握论文图的视觉语法:以 DeepSeek V4.1 Flash 论文图为例
github·agent·deepseek
简创AIGC陶先生2 小时前
【直播切片工作台】第11章:剪辑项目模型 VideoProject
github
MicrosoftReactor3 小时前
技术速递|如何在不牺牲任务质量的前提下,让 AI 编码更具成本效益
人工智能·ai·github·copilot
中科三方3 小时前
DNS拨测到底在拨测什么?
开发语言·github·php·dns·云拨测
digital-Yun7 小时前
Harness 深度图解
github
码流怪侠9 小时前
Sunshine 深度拆解:当 NVIDIA GameStream 闭源后,如何用 4 万颗 Star 重建自托管云游戏
游戏·github·nvidia
Bmob后端云9 小时前
Bmob后端云实战|Python给备忘录接入AI摘要、文本润色功能
算法·github
夜焱辰9 小时前
EO2Weave 浏览器扩展正式上架 Chrome 应用商店:你的 AI 助手,从此住进每一个标签页
github
zzzzzz31012 小时前
anthropics/skills:高关注度“官方技能”项目,应该怎样读
人工智能·开源·github