Idun233/dsh-gen

dsh-gen

DeepSeek Harness 的技能(Gem)管理器:把可复用的指令与本地知识文档打包成模型可调用的技能,并提供设置面板里的 Dsh-Gen 页面。

待分类deepseek-harnessdshdsh-plugins

dsh-gen

给 DeepSeek Harness 用的「技能」管理器,思路类似 Gemini 的 Gems: 把一段可复用的角色设定与若干本地文档打包成一个技能,模型在对话里能直接调用它, 并能按技能正文里的绝对路径读取挂载的知识文件。

  • 设置面板里新增 Dsh-Gen 页,用于新建、编辑、删除技能。
  • 每个技能会注册成 DSH 的 runtime skill,出现在模型的技能目录里。
  • 会话输入框左侧有一个技能选择器:选中即给当前会话套上该角色,无需回到设置页。
  • 知识文件是真拷贝,删技能时一起删除;原文件之后移动或改名都不影响已挂载的副本。

目录

  • 功能
  • 安装
  • 使用
  • 工作原理
  • 文件结构与关联
  • 数据存储
  • 开发
  • 常见问题
  • 许可

功能

设置面板:Dsh-Gen

  • 我的技能:卡片列表,每张卡显示头像、名称、说明、知识文件数与更新时间。
  • 新建技能 / 编辑技能:整页两栏编辑器。左栏 名称 / 说明 / 指令 / 知识,右栏实时预览。

预览里的「指令」按 Markdown 渲染,标题、列表、代码块、表格都与对话里一致。

  • 知识挂载:可以在首次保存之前就添加文件,随保存一起落盘,不必先存一次再加。
  • 每张卡片的四个操作(图标来自 QingIcon,MIT):

图标 · 作用 对话气泡 · 在 dsh-gen 工作区新建会话,并直接绑定该技能的角色 铅笔 · 打开编辑器修改 垃圾桶 · 删除技能(连同其知识文件) 眼睛 · 控制该技能是否出现在会话内的技能选择器里

会话内技能选择器

输入框工具行上的胶囊按钮。点开后是扇形卡组(每页 5 张,可翻页),支持三种选法:

  • 单击卡片:立即绑定当前会话。
  • 滚轮:在卡组内逐张切换高亮,不会滚动背后的页面。
  • 长按拖拽:按住约 0.22 秒后卡片"入手",向上拖动并松手即视为"出牌",

卡片会飞走并完成绑定;松手时未达阈值则回落,不选中。

弹出层锚定在胶囊按钮的正中央,带升起动效;静置 60 秒无操作会自动收纳, 下次点击重新展开。若技能较多,底部分页条显示 ← ●● → 共 N 第 x/y 组。

角色注入

选中的技能会以系统提示的形式注入会话,而不是塞进输入框,因此不会被后续提问稀释。 注入内容包含技能名称、说明、指令,以及知识文件的绝对路径清单。

安装

前置条件

  • 已安装 DeepSeek Harness(桌面版或 dsh CLI)
  • Node.js ^22.19.0 || >=24.0.0

方式一:从压缩包安装(推荐给使用者)

# 1. 解压到任意目录,例如
#    Windows: C:\Users\<你>\.dsh\plugins\dsh-gen
#    macOS/Linux: ~/.dsh/plugins/dsh-gen

# 2. 进入目标 profile 目录
cd ~/.dsh/profiles/desktop

# 3. 用 DSH 自带的 pnpm 安装(注意用 file: 前缀指向解压目录)
node ~/.dsh/dsh-runtimes/dsh-primary-runtime/dependencies/pnpm/bin/pnpm.cjs \
  add file:C:/Users/<你>/.dsh/plugins/dsh-gen --offline

方式二:手动挂载

  1. 把项目目录放到 ~/.dsh/profiles/desktop/node_modules/dsh-gen

(Windows 下建议用目录联接,便于改代码即时生效):

mklink /J "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-gen" "C:\path\to\dsh-gen"
  1. 编辑 ~/.dsh/profiles/desktop/package.json,在两处登记:
{
     "dependencies": {
       "dsh-gen": "file:C:/path/to/dsh-gen"
     },
     "dsh": {
       "profile": {
         "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-gen"]
       }
     }
   }
  1. 重启 DSH。

验证安装

启动后打开 设置 → Dsh-Gen,能看到「我的技能」页面即安装成功。 插件加载痕迹写在 $DSH_HOME/dsh-gen/load-trace.log,排查问题时先看它。

使用

新建一个技能

  1. 设置 → Dsh-Gen → 新建技能
  2. 填写 名称(必填)与 说明(会出现在选择器卡片上,建议一两句话说清用途)
  3. 在 指令 里写角色设定,支持 Markdown
  4. 在 知识 里添加本地文档(可选),右侧预览会同步显示
  5. 点右上角 保存

在对话里使用

  • 从设置页:点卡片上的对话气泡,直接开一个绑定好角色的新会话。
  • 从会话里:点输入框左侧的「选择技能」胶囊,选一张卡片。

当前会话立刻切换到该角色,下一轮提问就生效。

  • 想换回普通对话:再次打开选择器,点底部「取消技能角色」。

工作原理

技能如何注册

插件加载时读取技能存储,为每个启用的技能调用:

ctx.skills.register({
  name,          // kebab-case,中文名会退化成 skill-<id 后缀>
  description,   // 以「技能名」开头,便于模型匹配中文标题
  content,       // Markdown 正文:## 指令 + ## 知识库(含绝对路径)
  resourceBase: { kind: 'directory', path },
  metadata,
});

模型通过 DSH 的 skill 工具按需加载这些条目。

角色如何注入会话

插件注册了一个全局 system prompt 区段,其正文是一个按会话 id 查表的函数:

会话 id → 技能 id → 该技能的名称/说明/指令/知识路径

这样做的两个好处:

  1. 角色是系统级设定,不会因为用户后续提问而失效;
  2. 给已存在的会话切换技能,下一轮立即生效,不需要重建会话。

绑定关系存在 gens.json 的 bindings 字段里,删除技能时会一并清理。

前端如何拿到数据

宿主端(lib/index.js)通过 TypertRemoteService 暴露 9 个 RPC 方法:

方法 · 用途 listGens · 读取全部技能与工作区信息 saveGen · 新建或更新(同一事务里可附带知识文件与删除项) deleteGen · 删除技能及其知识文件 addKnowledge / removeKnowledge · 单独增删知识文件 openWorkspace · 取回 dsh-gen 工作区的 id 与路径 bindSession / unbindSession / sessionBinding · 会话与技能的绑定关系

浏览器端(lib/client.js)用 ctx.remote.$mount() 挂载这些描述符, 再通过 ctx.get('remote.dshGen') 调用。

文件结构与关联

dsh-gen/
├── package.json        插件清单:入口、导出、DSH 集成声明、peer 依赖
├── cordis.patch.yml    向 Cordis 插件树插入本插件的补丁行
├── README.md           本文档
└── lib/
    ├── index.js        宿主端:存储、RPC、技能注册、提示注入、工作区
    └── client.js       浏览器端:设置页 UI、会话内选择器、RPC 描述符

各文件职责与关联

`package.json` — 插件身份的来源,DSH 靠它发现插件:

{
  "main": "lib/index.js",          // 宿主端入口
  "exports": {
    "./client": "./lib/client.js"  // 浏览器端入口
  },
  "dsh": {
    "bundle": { "patch": "./cordis.patch.yml" },   // 自动注册进插件树
    "client": { "platform": "web", "inject": [...] } // 让前端 bundle 被发现
  }
}
  • dsh.bundle.patch 指向 cordis.patch.yml,DSH 启动时据此把插件插入 Cordis 树。
  • dsh.client 声明这是一个 Web 端插件,并列出它依赖的前端服务。
  • peerDependencies 只声明宿主会提供的包(cordis、typert-protocol、react),

因此插件本身不需要打包 node_modules。

`cordis.patch.yml` — 三行插件树补丁:

- insert:
    - id: dsh-gen
      name: dsh-gen

`lib/index.js`(宿主端) — 模块顶层 export const name = 'dsh-gen',apply(ctx) 里完成:

  1. 建立 $DSH_HOME/dsh-gen 目录并注册为工作区(标题 dsh-gen)
  2. 读取 / 写入 gens.json(按 mtime:size 检测外部改动并自动重读)
  3. 为每个启用的技能注册 runtime skill
  4. 注册 system prompt 区段做角色注入
  5. 通过 TypertRemoteService 暴露 RPC
  6. 写 load-trace.log 记录加载过程

`lib/client.js`(浏览器端) — 用 window.__ModuleLoader__.load({ id, factory }) 包装, 在 apply(ctx) 里挂两个 UI 槽位:

槽位 · 组件 · 说明 settings.section · DshGenSection · 设置页的 Dsh-Gen 选项卡 conversation.input.left · SkillPicker · 输入框工具行的技能选择器

同时用 ctx.remote.$mount() 挂载与宿主端对应的 RPC 描述符。

关联链路(一次「保存技能」的完整路径):

设置页点保存
  → client.js 调 api.saveGen({...})
  → RPC 序列化,走 connection 到宿主
  → index.js 的 DshGenService.saveGen
  → runtime.save():写 gens.json + 拷贝知识文件 + syncSkills()
  → 返回最新 view
  → client.js 用返回值刷新界面

数据存储

所有用户数据都在 $DSH_HOME/dsh-gen/(DSH_HOME 默认是 ~/.dsh):

$DSH_HOME/dsh-gen/
├── gens.json                技能与绑定关系
├── knowledge/<技能 id>/...   挂载的知识文件副本
└── load-trace.log           加载痕迹(有 256KB 上限)

gens.json 结构:

{
  "version": 1,
  "gens": [
    {
      "id": "gen_xxx",
      "name": "技能名",
      "description": "说明",
      "instructions": "指令(Markdown)",
      "disableKnowledge": false,   // 停用知识引用
      "enabled": true,             // 是否注册为 runtime skill
      "pickerHidden": false,       // 是否在会话内选择器中隐藏
      "knowledge": [
        { "id": "k_xxx", "name": "文档名", "fileName": "磁盘文件名",
          "path": "绝对路径", "bytes": 1234, "addedAt": "ISO 时间" }
      ],
      "createdAt": "ISO 时间",
      "updatedAt": "ISO 时间"
    }
  ],
  "bindings": {
    "session-xxxx": "gen_xxx"      // 会话 → 技能
  }
}
注意:gens.json 是用户数据,不在本项目仓库里,也不应该提交到 Git。
换机器时备份整个 $DSH_HOME/dsh-gen/ 目录即可。

开发

本地开发

推荐用目录联接(Windows)或符号链接(macOS/Linux)把项目挂进 profile, 这样改完代码刷新窗口即可看到效果,不必反复安装: