dsh-edu-mode
已验证dsh-edu-mode · v0.1.0 · MIT
Education mode for DeepSeek Harness: research a course, teach it, write the class notes, and quiz the learner.
安装
dsh plugin add dsh-edu-mode 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
标签
作者
说明文档
dsh-edu-mode — 教育模式
English | 中文
给 DeepSeek Harness(DSH)加一个教育模式:进入这个模式后,DSH 会把「讲清楚一门课」当成任务本身——先真的去查资料,再讲明白,把课堂笔记写下来,最后出题检查你到底会不会。
它不是一个提示词模板,而是一个真正的插件:一个可切换的会话模式、一段注入到每次模型请求里的教学协议、三个模型必须遵守的工具,以及落盘的课程文件。
它做什么
| 阶段 | 行为 | 由什么保证 |
|---|---|---|
| 1. 调研 | 用 web_search / web_fetch 找权威来源(教材、大学课程页、标准、论文),记录标题和链接;需要多来源时并行开子代理 |
教学协议 + 现有 web 工具 |
| 2. 讲解 | 先讲为什么有用 → 直觉/类比 → 精确定义(先定义符号)→ 推导 → 完整例题 → 常见误区 → 与前后知识的联系 | 教学协议 |
| 3. 笔记 | 每讲一次课就调用 edu_notes,把讲解、要点、易错点、来源写成 markdown 存到课程目录 |
edu_notes 工具 |
| 4. 出题 | 每讲结束调用 edu_quiz,在 GUI 自带的问答界面里让你做题并自动判分;答错的点会被重新讲一遍再重考 |
edu_quiz 工具 |
| 5. 进度 | 模块自测通过后调用 edu_course 打勾,跨会话保留课程进度 |
edu_course 工具 |
三个工具:
edu_course—open建立/改写课程大纲(目标、水平、模块清单、参考来源)并把课程设为当前课程;status报告进度和下一个模块;complete给某个模块打勾。edu_notes— 为某一讲写课堂笔记(正文 + 要点 + 常见误区 + 来源),写入课程笔记文件。同一讲重复写会被拒绝,除非显式replace: true。edu_quiz— 出题、通过对话内问答组件收答案、判分、把结果写进测验记录。选择题自动判分(单选/多选),开放题标记为「需要模型自己判」。没有可交互问答通道时(headless、ACP、子代理)不会报错,而是把题目交回给模型在对话里问。
课程文件
默认写在会话工作区的 edu-notes/<课程名>/:
edu-notes/线性代数/
├── syllabus.md # 课程目标、水平、模块清单(- [ ] / - [x])、参考来源
├── notes.md # 课堂笔记,一讲一节
└── quizzes.md # 测验记录,一次一节,含得分与错题
课程名会做安全化处理(保留中文,去掉路径分隔符),所以 /edu 线性代数 会得到 edu-notes/线性代数/。
安装
插件是一个标准的 DSH 组合包(dsh.bundle + cordis.patch.yml)。lib/ 是构建产物并已随仓库提交,因此不需要授权任何构建脚本。
# 1) 从 npm 安装(有 release 之后可用)
dsh plugin --profile web add dsh-edu-mode
# 2) 从 GitHub Release 下载的 tarball
dsh plugin --profile web add ./dsh-edu-mode-0.1.0.tgz
# 3) 直接装 git 仓库
dsh plugin --profile web add github:1Vewton/dsh-edu
注意安装方式。 请用 npm / tarball / git 安装(安装到 profile 的
node_modules里的是一份真实目录)。不要用dsh plugin add <目录路径>:那是 pnpm 的link:,Node 会把它解析到源目录的真实路径,于是插件的@deepseek-ai/*导入会命中源码仓库自己的node_modules,从而加载第二份 cordis / dsh-tools 实例,DSH 就无法把它识别成插件了。DSH 源码检出内开发(pnpm dsh+--patch)不受影响。
用源码检出运行时,把上面的 dsh 换成 pnpm dsh。验证层已生效(不启动服务):
dsh --profile web --dump-config # 应能看到 "# == dsh-edu-mode" 这一段
装完需要重启该 profile 的服务(例如 pnpm dsh web)才会加载。卸载:dsh plugin --profile web remove dsh-edu-mode。
配置
cordis.patch.yml 里的 config(插件加载时会严格校验,写错会直接报错而不是静默取默认值):
- insert:
- id: edu-mode
name: 'dsh-edu-mode'
config:
notesDir: edu-notes # 课程文件根目录(工作区相对路径)
maxQuizQuestions: 6 # 一次 edu_quiz 最多几道题
passRatio: 0.8 # 自测通过线(自动判分部分的比例)
# policy: | # 需要的话,用整段自定义教学协议覆盖内置的
使用
会话里用 /edu 命令开关模式:
| 命令 | 效果 |
|---|---|
/edu 线性代数 |
打开教育模式并把这门课设为当前课程,同时把你这句话作为请求发给模型 |
/edu |
打开教育模式(课程待定,模型会先问你想学什么) |
/edu status |
查看当前状态和课程 |
/edu off |
退出教育模式 |
典型开场:
/edu 线性代数,我从行列式开始,基础一般
之后模型会按教学协议走:先检索 MIT 18.06 / 教材等来源 → 讲清一个模块 → 写笔记 → 出题 → 根据错题回炉。你也可以随时插话:「这块再讲一遍」「出难一点的题」「跳过这节」。
状态存在会话日志里(edu/mode 事件,最后一个生效),所以会话恢复、分叉都会自动带上模式和当前课程;课程进度则落在工作区的课程文件里,跨会话可用。
发布
发布由 GitHub Actions 完成(.github/workflows/release.yml):推一个版本 tag 就会构建、测试、发 npm,并创建带 tarball 附件的 GitHub Release。
# 1) 改版本号并提交
npm version patch --no-git-tag-version # 或手改 package.json
pnpm build && pnpm test # 确保 lib/ 与 src/ 一致
git commit -am "chore: release v0.1.1"
git push
# 2) 打 tag 触发发布
git tag v0.1.1 && git push origin v0.1.1
一次性配置:在 npm 生成一个 Automation token(npmjs.com → Access Tokens),加到仓库 Settings → Secrets and variables → Actions,名字用 NPM_TOKEN。没有这个 secret 时 workflow 会在发布步骤明确报错,不会静默跳过。
workflow 会拦住这些情况:tag 与 package.json 版本不一致、提交的 lib/ 与 src/ 不一致(防止发出去的构建产物是旧的)、单元测试失败、该版本已经在 npm 上存在。发布走 npm publish --access public --provenance,所以在 npm 页面能看到该包由这次 GitHub Actions 运行构建的出处证明,GitHub Release 上也会附上同一个 tarball。
也可在 Actions 页面手动 dispatch:默认 dry_run = true,只跑构建/测试/打包,不发版。
之后可选升级到 npm 的可信发布(OIDC):包在 npm 上存在后,在包的 Settings → Trusted publishing 里填
1Vewton/dsh-edu/release.yml,之后就能删掉NPM_TOKEN(workflow 已经带了id-token: write)。首次发布仍需要 token,因为可信发布只能在包已存在时配置。
开发
pnpm install
pnpm check # = pnpm build + pnpm test(改完 src 用这个,别只跑 test)
pnpm test # 108 个测试,约 0.3 秒(测的是 lib/,也就是安装后真正被加载的产物)
测试跑的是 lib/ 而不是 src/,这正是关键:安装到 profile 里被加载的就是 lib/,所以测试要么先 pnpm build(用 pnpm check),要么测的就是旧产物。
测试文件与它们锁定的行为:
| 文件 | 覆盖 |
|---|---|
tests/paths.test.js |
课程名安全化、课程文件布局、markdown 渲染、模块勾选/解析 |
tests/quiz.test.js |
判分规则:单选/多选/开放题/未作答、得分与通过线、报告渲染 |
tests/store.test.js |
课程文件读写:相对路径约束、工作区解析、沙箱策略、fs/observed、追加语义 |
tests/tools.test.js |
三个工具的真实行为:大纲、进度、笔记、测验记录,以及所有降级路径 |
tests/plugin.test.js |
真实 cordis Context 上的装配:服务、提示段、/edu 命令、模式状态机、会话日志契约 |
tests/bundle.test.js |
组合包契约:patch 行、files、入口、以及「所有运行时裸导入必须是 peer」 |
tests/mode.test.js |
配置校验与 edu/mode 折叠 |
CI(.github/workflows/ci.yml)在 ubuntu 与 windows 上、Node 22 与 24 上跑同一套:pnpm install --frozen-lockfile → pnpm build 并校验提交的 lib/ 与 src/ 一致 → pnpm test。Windows 那一档不是摆设:插件要解析工作区相对路径并写课程文件,两个平台都跑一遍才放心。.gitattributes 把文本统一成 LF,好让 lib/ 的对比在哪个平台都成立。
源码结构:
src/index.ts 插件入口:EduMode 服务、edu:policy 提示段、/edu 命令、状态机
src/policy.ts 默认教学协议文本(可被 config.policy 覆盖)
src/course.ts edu_course / edu_notes 工具
src/quiz.ts 判分逻辑(纯函数)+ edu_quiz 工具
src/store.ts 课程文件读写(走 ctx.fs,受沙箱约束)
src/paths.ts 纯函数:课程名安全化、文件布局、markdown 渲染
tests/ 测试与 tests/helpers.mjs(临时工作区、假 fs/会话/工具注册表)
改完代码后,profile 里装的是一份拷贝,必须重新 remove + add 才会更新。
设计取舍
- 状态放会话日志,而不是插件内存:恢复/分叉会话不需要额外镜像。在回合内切换模式时,选择会挂起到下一个 step 边界再落盘(借用 plan-mode 的成熟做法),避免在 append 发布期间重入会话。
- 写文件走
ctx.fs,并按会话解析 sandbox 策略,因此写入受部署的沙箱模式约束(workspace-write下写不到工作区外)。本插件对自己的文件只做追加/精确替换,所以不申请读写前置意图,而是补发fs/observed,让模型自己的write/edit看到最新版本。 - 判分是纯函数,规则集中在一处:单选命中任一可接受答案即对;多选需完全一致,真子集半分;无选项的开放题永不自动判分,交回模型;未作答计入分母。
- 不做 UI 卡片:测验直接复用界面自带的问答组件,所以无需客户端构建;模式状态通过
/edu命令回执和会话记录可见(后续可以再加输入栏 chip,需要客户端插件)。
限制
- 模式状态的作用范围是单个会话;
/edu off不影响其它会话。 - 判分只覆盖可机判的题型;开放题依赖模型自己的判断(工具会把参考答案一并返回)。
- 笔记文件按课程名分目录,同名课程视为同一门课;重命名课程等于另起一门。
- 教育模式不会限制其它工具,模型仍可能被要求做别的任务——协议里明确要求它把「讲懂」当作交付物。
许可证
MIT