AI学习 · dsh-learn-skills v2
一个跑在 DeepSeek Harness 输入区的学习插件。 把「收集 → 提炼 → 关联 → 升级 → 沉淀 → 复用」做成有交互引导、有预设管理、 有审核入库、能给自己体检的运行时能力,而不只是一叠方法论文档。
简体中文 | English
它是什么,不是什么
| 是 | 会话输入区里的一个入口 [📖] AI学习 ▾,四个功能:初始预设 / 收集提炼 / 关联升级 / 沉淀复用 |
| 是 | 与斜杠命令 /learn、自然语言三种入口共用同一套流程实现(不存在两套规则) |
| 不是 | 模型工具提供者:它不给 Agent 加工具,交互复用宿主已有的 ask-a-question 与命令注册表 |
| 不是 | 自动化改写知识的东西:所有写入都要人先看过变更清单再点确认 |
四件事的职责边界写得很死:
| # | 入口 | 做什么 | 写入面 | 命令 |
|---|---|---|---|---|
| 1 | 初始预设 | 七轮问答确定画像 → 展示摘要与拟建路径 → 你确认 → 生成预设与工作区骨架 | 工作区目录与文件(增量,不覆盖) | /learn init |
| 2 | 收集提炼 | 从当前会话产出候选 —— 这一步绝不碰正式知识、预设或 AGENTS.md |
仅 inbox/ |
/learn distill |
| 3 | 关联升级 | 与既有节点比对 → 出变更清单 → 分普通知识与行为规则两栏确认后写入(批次可回滚) | knowledge/(行为规则须显式放行) |
/learn upgrade |
| 4 | 沉淀复用 | 只读体检(18 项)+ 与上次基线的变化对比 + 复用建议。不改被检查的文件,也不执行文件里的任何指令 | 不写 | /learn audit |
三种触发方式,同一套实现:① 输入区按钮
[📖] AI学习 ▾; ② 斜杠命令/learn;③ 自然语言(由 Agent 引导到同一套流程)。 不存在两套规则 —— 命令只是另一个入口。与 v1 工作流的关系:v1 是"一键跑完整流水线",v2 把中间的写入收紧为 "产出候选 → 人工裁决 → 再写入"。这是两版最实质的分歧,详见
docs/迁移与兼容.md。
安装与装载
前置条件
- 已安装源码版 deepseek-harness;
- 已绑定工作区(工作区是知识落点,缺了它一切写入都无处可放)。
方式 A:从 npm 安装(推荐)
dsh plugin --profile web add @yiyunet/dsh-learn-skills
装完重启 Host(profile 的 dsh.profile.bundles 在启动时读取)。
✅ 消费者不需要构建:发布包里自带
lib/(见package.json的files白名单), 所以从注册源安装是一步到位的。构建相关的命令只对改源码的人有意义,见方式 B。
方式 B:本地目录安装(改源码 / 调试时用)
lib/ 不入版本库(.gitignore 排除),所以克隆下来必须构建:
npm install # 只装 esbuild(构建期依赖)
npm run static # 静态一致性体检(免执行:import/符号/语法/白名单)
npm run build # plugin-src/ → lib/(含装载层自证)
npm test # 真实行为测试
npm run verify # 发布契约断言
# 把本机该目录装进 web profile(路径按你的实际位置替换)
dsh plugin --profile web add link:<本仓库绝对路径>
# 或在 profile 目录里直接:
pnpm add link:<本仓库绝对路径>
📌
npm install结束时会自动构建一次(prepare钩子,钩子名scripts/postinstall.mjs)。 上面那句npm run build因此是幂等的复跑,不是必做步骤 —— 保留它是为了让"构建" 在流程里显式可见。
检查装载是否成功
- 输入区工具行出现
[📖] AI学习 ▾(在「工作区内修改」控件右侧); - 斜杠菜单里出现
/learn; /learn help列出四个入口。
用法
按钮
点 AI学习 ▾ → 选一个入口。菜单顺序固定:初始预设 / 收集提炼 / 关联升级 / 沉淀复用。
- 执行中会显示状态;重复点击不会产生并发写入 —— 同一工作区同时只允许一个流程, 第二次会被明确拒绝;
- 有「取消」按钮:取消后不再启动后续写入步骤,已完成的步骤如实汇报;
- 未绑定工作区时菜单里直接给出引导;
- 尚未初始化时,后三项会先识别已有知识库:识别到就直接用,识别不到才引导你先做「初始预设」。
斜杠命令
/learn help 列出四入口
/learn init 七轮问答(确认创建要在界面上点,见下)
/learn distill 从当前会话产出候选
/learn upgrade [批次号] 出变更清单 / 写入
/learn audit 只读体检
/learn rollback <批次号> 回滚某个变更批次
命令与按钮背后是同一个 flows 实现:命令只是另一个入口,不附带第二套规则。
需要"确认"的步骤(创建预设、写入变更)在命令侧用 --confirm 表达,
但默认路径是界面上确认 —— 因为要让人先看清变更清单。
自然语言
插件不拥有模型工具,所以"自然语言入口"由工作区技能 learn-* 承担:你直接说
「帮我初始化学习体系」「把这次会话里的内容提炼成候选」,Agent 读技能后调用同一套流程。
七个问题问了什么
前两题固定,第 3 题起由模型逐轮现场生成(每轮带上已采集的全部信息、剩余轮次与已问话题); 模型不可用时逐轮回退到硬编码题库,流程不中断。
| # | 主题 | 要点 |
|---|---|---|
| 1 | 名字 | 自由输入;空值/长度/保留名/非法字符都校验;重名请你换名,不覆盖也不替你改名 |
| 2 | 身份 · 面向 · 期望 | 三段式一条文本:职业 | 面向什么(人群或事情)| 期望达到什么,带例子;回显第 1 题;只填一段就只当职业,其余保持未知;不索要单位/真名等无关信息 |
| 3 | 关注方向 | 模型按你的三参数出题与选项;降级时按「场景池 → 职业池 → 通用池」三级匹配给 7 个方向 +「其他,请输入」;支持多选与自定义 |
| 4 | 学习目标与典型任务 | 模型按上下文出题;降级时围绕固定主题给候选与自定义 |
| 5 | 当前基础与实际困难 | 同上 |
| 6 | 学习方式与输出偏好 | 同上 |
| 7 | 最想先解决的一件事 | 模型出题;降级时给"越快越好/先打基础/固定节奏"等候选 —— 这一项决定先做什么、后做什么 |
设计上刻意的三件事:
- 未填写 = 未知。跳过项在画像里是
null并标注"未知 —— 未填写",不编造画像。 - 推荐就是推荐。关注方向的措辞是"可能关注",不伪称统计出来的"职业最高关注度"。 每题都能「返回修改」;改前置答案时,后续题每轮现取,天然重算。
- 模型只定题面,落库槽位固定。模型的自主性在"问什么、给哪些选项";答案写进哪个 画像字段由插件固定,避免模型报的维度绕过"未填写=未知"的归一化而静默丢数据。
七题结束后展示:画像摘要 / 预设定位 / 知识框架草案 / 拟创建或修改的路径清单, 然后你才能点「确认创建」。创建完会给出结构自证、安装指引,并提示:
请安装该 bundle(本插件不代为安装),然后新建会话,选择预设
<名字>。
⚠️ 预设在一个进程内按 standing scope 挂载一次(源码:
packages/preset/agent-preset-registry的src/mount.tsroster/standing mount)。新预设不会在当前会话即时生效 —— 本插件不宣称热更新。
🔐 为什么"安装"这一步不代做:安装 bundle 会在 Host 进程执行新代码。官方入口
plugin_manager工具为此强制弹danger-full-access审批(源码:packages/boot/plugin-manager/src/tools.ts:34-41),而服务方法installBundle()本身没有审批闸(同包src/index.ts:417-447)—— 插件直接调它,等于替你越权。 所以本插件只生成、只出指引,点头的动作留给你:
# ① 终端一行(最省事)
dsh plugin --profile web add link:<刚生成的那个 bundle 目录>
# ② 或在「创造模式」会话里让 Agent 调 plugin_manager(会弹审批卡)
# action=install_bundle, target=<该目录>
# 复核:plugin_manager 的 list_plugins(找 preset-<id> 行)或 GUI「设置 → Agent 预设」看它是否进名册
# 撤销:dsh plugin --profile web remove @local/dsh-learn-preset-<id>
🗑 要删掉一个预设("干净删除"的完整四步):见
docs/删除预设.md。 先记住三件事:① 新版没有"删除预设"按钮(设置页只能查看/选择/设为默认),能删的是 它所在的那个 bundle;② 卸载之后还要删<工作区>/.dsh/preset-bundles/<id>/目录, 并且清掉known.json里的占用(否则同名重建会拿到-2后缀);③ 同包多预设时不能卸整包。
⚠️ 那个 bundle 目录在工作区内(
<工作区>/.dsh/preset-bundles/<id>/),它是活的: 宿主重启时按预设 id 在当前配置里解析,定义缺失的会话会被拒绝恢复。 所以不要删也不要移动它;要撤掉请先用上面的remove摘掉 bundle (完整步骤与"删错了怎么救":docs/删除预设.md)。
工作区结构
AGENTS.md 工作区原则与知识入口(精炼、稳定)
index/ 外部原始材料(保留来源与原文)
inbox/ 标准化材料、待审核候选、提炼批次
knowledge/
framework.md 正式知识体系的唯一总索引
nodes/ 正式知识节点
data/ 待分析数据(不默认转为知识或长期记忆)
task/ 学习任务、执行状态与待办
reports/ 体检报告、复盘与变化记录
.dsh/
skills/ 工作区技能
learn-skills/ 本插件状态:批次账 / 体检基线 / 变更日志 / 断点
preset-bundles/ 本插件生成的预设 bundle(★2.1.0 起落在这里;删它会连带废掉对应预设)
关于 skills/:默认不与 .dsh/skills/ 重复存技能。宿主的 skill-filesystem
只扫 <工作区>/.dsh/skills(rank 100),根目录下的 skills/ 不会被加载;
如果确有需要独立分发的技能,请用 .agents/skills(rank 200)并在文档里写明用途。
增量迁移既有工作区
初始化是增量的,绝不清空:
- 目录:不存在才建,已存在无操作;
- 文件:内容相同 → 跳过;内容不同 → 标为
conflict并一个字都不写, 在结果里明确告诉你哪个文件被挡下了、为什么; - 重复运行幂等:不会重复创建,也不会覆盖你后来的修改。
迁移与 v1
本版(v2)是完全重写:四个入口是运行时能力,不再以"一叠方法论技能"的形态分发。
- 不提供 v1 技能的迁移路径 —— v1 的
skills/learn-*技能包已从本仓移除; - 需要 v1 方法论文本的作者,请见本仓的历史版本(若已归档);
- v2 的功能不依赖任何 v1 技能:装好插件即可用四个入口与
/learn命令。
数据与状态
全部状态都在工作区内的 .dsh/learn-skills/,不写宿主配置:
| 文件 | 内容 | 寿命 |
|---|---|---|
session.json |
单次流程的断点(可取消、可续办) | 流程结束即清 |
batches.json |
已处理批次账 + 内容指纹 → 批次("不重复创建候选"的判据) | 长期 |
audit.json |
上次成功保存的体检基线 | 长期(只增) |
known.json |
已分配过的节点 id 与预设占用 | 长期(不回收) |
changes/<批次>.json |
变更日志(回滚依据;含写入前内容) | 长期 |
预设本体是一个可安装的 bundle,落在 <工作区>/.dsh/preset-bundles/<预设 id>/
(恰好两件:package.json + cordis.patch.yml)。目录名可用 config.presetDir 改,
整个根也可用 config.presetRoot(绝对路径)覆盖。
🗑 删掉一个预设:见
docs/删除预设.md—— 独立成包的走四步法(关行确认 → 卸包 → 删目录 → 清known.json占用); 与别人共用一个组合包时不能卸包(会带走同包所有预设),只能改那份 patch 的声明段; 以及删错了的黄金窗口(重启之前补回声明=零损失)。
⚠️ DSH 0.1.7-alpha.1 起预设改为声明式(提交
feat(preset): declare Agent compositions in profile YAML):旧的<dshHome>/.agent-presets/<id>/目录 已无人读取,预设必须是@deepseek-ai/dsh-agent-preset的一个 Loader 条目。 只写盘不算预设存在 —— 必须安装该 bundle 才会进名册;安装由你经plugin_manager(强制审批)或一行 CLI 完成,本插件不代做(见上文"为什么")。 0.1.6-alpha.2 及更早生成的旧式目录预设不会自动迁移,需手动转成 bundle (见docs/迁移与兼容.md)。
🧭 身份判据来自宿主名册:
init会先读ctx.agentPresets.list()(注册表 roster) 再判重 —— 因为新架构里预设身份=声明行里的config.id,而注册表不扫描目录 (一份组合包可以声明很多个预设:本机dsh-migrated-presets一个文件里就有 12 个)。 名册读不到时直接拒绝创建(ROSTER_UNAVAILABLE,一个文件都不写)—— 宁可不出件,也不出一个与既有预设重复的config.id(官方原话:「重复的 preset ID 会导致声明加载失败」)。
回滚
/learn rollback <批次号>
- 新建的节点被删除,更新过的被还原;
- 写入之后被你改过的文件一律不动,并列入"跳过"如实上报 —— 回滚不会变成第二次覆盖;
- 日志在
.dsh/learn-skills/changes/<批次号>.json。
配置
cordis.patch.yml 里的全部字段都有中文注释。默认值的关键两条:
allowWrite: true # 总闸。关掉后四项功能变纯只读(初始预设只出预览)
allowBehaviorRules: false # 行为规则(预设提示词 / AGENTS.md / 技能)默认不放行
第二条是刻意的:一条知识被认可,不会自动变成长期指令。会影响 Agent 行为的那类变更, 必须由人显式打开这个开关,才会在「关联升级」的确认里被执行。
语义增强(方案 B-1:双轨并存 + 冲突标记)
默认关闭,开关与预算都在 cordis.patch.yml:
allowModelSemantics: false # 打开后:提炼会把会话原文发给模型做语义判定
semanticBatchSize: 8 # 一次调用判多少条(越大越省,延迟越高)
semanticBudgetMs: 60000 # 总预算;用尽即停,剩余候选走基线轨
为什么默认关:打开后会话原文会发给模型。会话里可能有客户信息、平台账号线索、 内部报价 —— 这是数据出站,属合规红线,故默认不启用。
它做什么:给「收集提炼」加一条语义轨——模型判定每条候选的类型、以及它与
已有节点的关系(new/supplement/correction/conflict/duplicate/supersede/unverified),
并给出 why(依据哪几个字得出的)。
为什么不覆盖基线(这是设计地基):模型输出不可复现——同一份内容两次提炼可能给 不同判定。若覆盖基线,就会出现"同一知识两次入库分类不同 → 版本号无故 +1 → 索引抖动", 留痕与可回溯性当场失效。所以采用双轨并存:
| 轨 | 谁产生 | 可复现 | 落盘字段 |
|---|---|---|---|
| 基线轨 | 关键词正则 + 词面重合 | ✅ | type / relations(confidence: 'heuristic') |
| 语义轨 | 模型 | ❌ | typeSemantic / relationsSemantic(confidence: 'model')+ why |
两轨一致时不产生额外字段;不一致时并排显示并标 typeDivergence /
relationsDivergence,审核面板会把模型建议明确标注为「模型」——最终判定仍由你裁决。
降级纪律:没有模型 / 超时 / 输出不是合法 JSON / 超预算 ⇒ 静默回退基线轨, 候选照常产出,提炼流程一秒不停(与「初始预设」的"模型优先、硬编码兜底"同构)。
⚠️ 两个如实标注:① 语义增强部分生效时(达到预算或某批失败),界面与批次文件都会 标明"未跑完",不会假装全部完成;② 批次文件里的
semantic摘要记录了几次调用、几处 分歧,便于事后回溯。模型输出一律严格校验、绝不修补——编造的候选 id、非法 type/relation 一律丢弃。
开发
npm run static # 免执行静态体检(import/符号/语法/白名单)
npm run build # 构建(宿主半侧复制 + 客户端 esbuild 打包 + 装载层自证)
npm test # node --test:真实行为测试(真跑文件系统)
npm run verify # 10 组发布契约断言
npm run check # ★ 上面四步一次跑完:static → build → test → verify
npm run inspect # 产物体检
架构约定(与生态基准件 @yiyunet/dsh-dingtalk-connector 对位):
plugin-src/是唯一真源,lib/是产物,改产物会被下次构建覆盖;- 宿主半侧只用 Node 内置模块与宿主运行时提供的包,不得把
@deepseek-ai/dsh-*写进依赖节(运行包用模块局部 Symbol 做 key,装第二份物理副本会破坏 Host 查找); - 客户端半侧只能引 react 系(平台冻结模块表),第三方依赖一律不许进;
- 客户端产物必须由 wrapper 把 bundle 嵌进
__ModuleLoader__.load的 factory 内部 —— 放在外面会在脚本顶层执行到require("react"),整个/plugins拼接 bundle 会全灭。
排错
按症状查。每条都给出"为什么"和"下一步"。
装完看不到 [📖] AI学习 ▾
| 检查 | 说明 |
|---|---|
| 重启 Host 了吗 | ★ 最常见的原因。dsh.profile.bundles 在启动时读取,改完 profile 必须重启才生效 |
| 装对 profile 了吗 | 必须是 --profile web(客户端半侧只在 web profile 注册) |
| 包真的在吗 | dsh plugin --profile web list,或直接看 profile 的 bundles 列表 |
菜单能弹,但点了报 HTTP 404
报错形如 transport failure for /api/dsh-learn-skills: HTTP 404。
根因:HTTP RPC 端点没注册上。connection 服务属于 Web 运行时那一层,
在插件 apply() 那一刻可能尚未就绪 ⇒ 注册机会被错过。
为什么本插件不该出这个问题:它不把 connection 写进静态 inject(那会让插件在
无 Web 运行时的部署里整体 inactive),而是挂到它就绪之后再注册。
npm run verify 的 ⑤-b 组断言专门钉住这一点。
⇒ 若你确实看到这个 404:把 ~/.dsh/ 下的宿主日志里 learn-skills 相关行发出来。
点「初始预设」立刻报"无法读取宿主预设名册(agentPresets)"
这是刻意设计,不是 bug。 拿不到名册就无法保证不生成重复的 config.id
(官方明写"重复的 preset ID 会导致声明加载失败")⇒ 宁可不落盘。
⇒ 此时其余三个入口不受影响,手工加预设照旧。
点「确认创建」后提示"尚未安装"
这也是刻意的(2.1.0 起不代装):安装 bundle 会在 Host 进程执行新代码,
官方入口 plugin_manager 为此强制弹 danger-full-access 审批,而服务方法
installBundle() 本身没有审批闸 ⇒ 插件直接调它等于替你越权。
⇒ 照提示跑那行 dsh plugin --profile web add link:<该目录>,或按 README
「七个问题问了什么」末尾的说明操作。
「收集提炼」产出为空或候选很少
| 原因 | 判据 |
|---|---|
| 本次会话内容被判为噪声 | 寒暄、空内容不计入(isNoise,宁可漏判) |
| 这段消息已被处理过 | 会标 alreadyProcessed,不重复创建 |
| 会话历史不完整 | 报告里会标"部分范围";historyComplete: false 说明发生过 replace 压缩 |
| 上限截断 | stats 里如实反映,不静默丢失 |
语义增强(allowModelSemantics)没生效
默认就是关的(打开会把会话原文发给模型,属数据出站,须显式开启)。
打开后仍不生效时,按降级纪律逐条查 —— 以下任一都会静默回退基线轨(不抛错):
无模型 / 超时 / 输出不是合法 JSON / 超预算。批次文件里的 semantic 摘要会记
"几次调用、几处分歧",以及是否跑完(部分生效会明确标"未跑完",不假装完成)。
想确认"模型到底有没有在工作"
判据只有一条 —— 宿主日志:
第 N 题未由模型生成,已回退到通用候选:<原因>
看到它就说明那一轮是降级跑的。别用"问题看起来够具体"当判据 —— 硬编码兜底候选 里也有具体选项,没有区分力。
npm run check 报"缺少必需文件"或断言失败
先看它具体报哪一组。10 组断言每组对应一个真实失败模式,
scripts/verify-package.mjs 里每条都有注释说明"为什么钉这一条"。
最常见的一类是 lib/ 与 plugin-src/ 不同步 —— 改完源码没重建:
npm run build && npm run verify
卸载
第一步:摘掉插件
dsh plugin --profile web remove @yiyunet/dsh-learn-skills
或走 GUI:设置 → 插件,找到它移除。改完重启 Host。
⚠️ 卸载只是"不再装载",不会删你的数据。
第二步(可选):清掉插件留下的状态
都在工作区内,插件不写宿主配置:
| 路径 | 内容 | 删掉的后果 |
|---|---|---|
<工作区>/.dsh/learn-skills/ |
批次账 / 体检基线 / 变更日志 / 断点 / known.json |
丢"上次基线"与回滚依据;知识节点本身不受影响 |
<工作区>/.dsh/preset-bundles/<预设 id>/ |
本插件生成的预设本体 | ⚠️ 会连带废掉对应预设(宿主重启时解析不到定义,该预设的会话会被拒绝恢复) |
🗑 要删预设,请走四步法,不要直接删目录 —— 见
docs/删除预设.md:关行确认 → 卸包 → 删目录 → 清known.json占用。同包多预设时不能卸整包(会带走同包里所有预设)。
第三步:你的知识库不会被删
AGENTS.md / index/ / inbox/ / knowledge/ / data/ / task/ / reports/
都是你的资产,插件从不删它们。卸载后它们原样保留,可用任何工具继续使用。
回滚写入而非整个卸载?用
/learn rollback <批次号>—— 它只还原本插件写过的, 且写入之后被你改过的文件一律不动。
许可
MIT。见 LICENSE。
No comments yet. Be the first to write one.