前言¶
用 DSH 给编程题做助教时,常见的两个失败不是模型不够聪明,而是流程上没有约束:一是凭印象下结论,比如「你这里下标写错了」,其实没跑过学生代码;二是忍不住给答案,学生说一句「就给我看一眼完整代码」,模型就把正解贴出来了。这两件事靠自觉挡不住,靠一条系统提示也挡不住。
buguoshixc/dsh-doctor 的思路是把回答链路拆成工具:归因必须来自一次真实执行的裁决结果,面向学生的回复发出前必须先过一道门控,门控判定「能跑出结果」或「与正解高度相似」就拦下来。它不是又一个提示词模板,而是插进 DSH 工作流里的插件。
这是什么¶
插件维护者是 buguoshixc,分类为工作流,采用 MIT 许可证(package.json 里声明 "license": "MIT")。一句话定位:给 DSH 桌面端加一个「诊断模式」agent 预设,外加一个零第三方依赖的 Python 判定核,让模型在回答之前先跑代码拿证据,用苏格拉底式提问带学生自己修。
作者给出的自我界定很明确,是这几样;不是另外几样:
| 是 | 不是 |
|---|---|
| DSH 插件(bundle 加 agent preset) | LLM 应用;不调用任何模型 API |
| 离线判定核(Python 3.11+ 标准库,零第三方依赖) | 判题云服务;不接 judge0、不联网 |
| 两个原生工具加一份教学技能加一个预设 | Web 平台;没有前端、没有数据库、没有 HTTP 服务 |
| 一个答案门控 | 安全沙箱;本地执行只有超时、内存上限、尽力禁网 |
架构叫「架构 C」:会话里的模型负责出用例和正解,工具只负责裁决。判定核不生成任何东西,也不需要 API key,只做三件确定性的事——跑代码、比输出、挡答案。
安装与启用¶
官方安装命令来自 README 和 docs/INSTALL.md,装到桌面 profile:
dsh plugin --profile desktop add github:buguoshixc/dsh-doctor
装完必须重启 DSH,然后在新建会话的预设选择器里选「诊断模式」。有两种等价做法:把 docs/INSTALL-PROMPT.md 里的 prompt 粘给 agent,让它自己体检加安装加验收;dsh plugin 会把参数转发给 profile 目录里的 pnpm,GitHub 短名解析、缓存、锁文件都是 pnpm 的行为。
前置条件按 docs/INSTALL.md 的检查项:
| 依赖 | 要求 | 检查命令 |
|---|---|---|
| DSH | 桌面版 0.2.1-rc.3 及以上(0.3.0 以下) |
dsh --version |
| Python | 3.11+,且能被 python 或 python3 找到 |
python --version |
| Git | 从 GitHub 安装时需要 | git --version |
package.json 里写明的版本约束是:
"engines": { "node": ">=22.0.0", "dsh": ">=0.2.1-rc.3 <0.3.0" }
本包没有构建步骤,纯 ESM 加纯 Python,不需要 prepare 脚本。Python 找不到时典型报错是 E_NO_PYTHON,README 给的做法是用 DSH_DOCTOR_PYTHON 写死解释器绝对路径,或确认它装在 PATH 里,Windows 下 py -3 是最后一个候选。
两个工具¶
插件注册两个原生工具,JSON Schema 与执行体在 lib/tools.mjs。
doctor_judge:唯一裁决器¶
只要学生给了代码加题目,就先调它。不跑就下结论等于编造证据。
参数(来自 README):
problem 题面全文(Markdown,含示例)
code 学生代码全文
cases 用例集 JSON,形如 {"func_name":"…","cases":[{"case_id","input","expected","kind"}]}
refs 1~4 份正解代码,会交叉校验
func_name / timeout_ms / memory_mb 可选
它先跑正解做自校验:任何一份正解与 expected 不符,立刻返回 verdict=REF_REJECTED,不执行学生代码、不进入归因。这样「模型自己写错正解」不会变成「学生被冤枉」。
成功路径返回 verdict、compile、ref_status、failure_distribution、counterexample、category / category_label、confidence 和 reason。README 给了一个 lc-053-maximum-subarray 边界错误样本的节选:
{
"verdict": "WA",
"phenotype": "WA",
"compile": { "passed": true, "errors": "" },
"ref_status": "trusted",
"failure_distribution": { "total": 5, "failed": 1, "pattern": "boundary_only" },
"counterexample": {
"case_id": "b1",
"input": [[-3, -2, -5]],
"expected": -2,
"actual": 0,
"kind": "boundary"
},
"category": 3,
"category_label": "边界处理错",
"confidence": "high",
"reason": "1/5 条用例失败,失败模式 boundary_only,归类为第 3 类(边界处理错)"
}
counterexample 里写死的 case_id、input、expected、actual,是模型唯一可以写进回复的事实。字段全表见 docs/SCHEMA.md。
doctor_gate:回答门控¶
任何面向学生的回复,发出之前必须调它。三层判定:
1、layer1 能跑即拦:抽出回复里的代码片段真的执行,能跑出结果就判 block。只看语法不够——while 左指针 < 右指针: 语法合法,但它跑不起来,是合格伪代码。
2、layer2 相似度防漏:与正解做 AST 结构相似度(0.55 权重)加 token Jaccard(0.45 权重)加权;>=0.75 判 downgrade-to-level2,>=0.60 打 suspect=true 放行但标记。
3、fail-closed:内容像代码却分析不了,一律拦或降级,绝不当成「安全」。
返回里的 action 只有 pass 和 block,downgrade-to-level2 落在 recommended_level 和 reasons 里。README 给的样本:
{
"action": "block",
"reason": "layer1 判定为可运行代码(定义了 twoSum)",
"layer1": { "runnable": true, "detail": "定义了 twoSum" },
"layer2": { "similarity": 1.0, "ast_sim": 1.0, "token_sim": 1.0, "suspect": true },
"leaked_fragments": ["def twoSum(nums, target):\n # 正解 B:哈希表一次遍历,O(n)\n …"],
"recommended_level": 1,
"reasons": ["extract:found_fragment", "layer1:runnable"]
}
「诊断模式」预设与技能手册¶
预设 id 是 diagnose,名称是「诊断模式」。README 给了它的声明方式与组合说明,另附一份旧格式的等价描述(presets/diagnose/,说明里写 DSH 0.2.1 起不读该目录,留作对照):
| 项 | 内容 |
|---|---|
| persona | 中文助教人格;铁律:过 gate、不给完整代码、只用工具证据、REF_REJECTED 时先修不归因 |
| 声明的工具面 | bash(POSIX)/ pwsh(Windows)、read/write/edit、glob/grep、skill |
| 插件自带工具 | doctor_judge、doctor_gate |
| 技能 | doctor,只在预设里可见:includeDefaultRoots: false 加随包目录 |
| 继承的 host 工具 | present、ask_user_question 等,由 profile 决定 |
真正的「度」在 presets/skills/doctor/SKILL.md 这份教学手册里。技能不是装饰,它是提示工程的一部分,作用是让模型每次会话都读到「先跑代码、再组织回复、再过门控」这个顺序。另外预设不到 {{cwd}} 这类提示变量——DSH 0.2.1 起在 preset persona 里会整轮失败,只用 {{model}}。
怎么验证装对了¶
docs/INSTALL.md 给的验收流程很具体:新建一个「诊断模式」会话,贴一段有明显错误的代码,例如:
def maxSubArray(nums):
best = 0
for x in nums:
best = max(best, best + x)
return best
期望的模型行为按顺序是:先写一份自己的用例集和正解,调用 doctor_judge;回复里引用具体的反例,case_id、输入、期望值、实际值都要有;组织回复后调用 doctor_gate,action 不是 pass 就改写重试;最终回复里没有完整可运行代码,而是一到两个引导性问题。
只要「工具调用」面板里出现了 doctor_judge 和 doctor_gate,说明插件装对了。如果模型压根没调工具,说明技能没被加载或它没读技能。
想核对包本身的质量,README 给的命令是:
bash scripts/verify.sh
它跑环境自检、零第三方依赖扫描、Python 单元测试(README 写的是 93 个)、Node 单元测试(README 写的是 22 个,含用本机真实 @deepseek-ai/dsh-tools 校验 schema 的契约测试)、三题 fixture 演示(AC/WA/CE/TLE/REF_REJECTED 加门控三种动作)、包元数据契约、stdout 纯净性,全绿退出码 0。
包的暴露面和依赖关系也值得看一眼。files 白名单包含 lib/、doctor_core/、presets/、locale/、icon.svg、cordis.patch.yml、scripts/verify.sh 和部分 docs/;三个 peer dependency 都标了 optional: true:
"peerDependencies": {
"@deepseek-ai/dsh-tools": ">=0.2.1-rc.3 <0.3.0",
"@deepseek-ai/dsh-agent-preset": ">=0.2.1-rc.3 <0.3.0",
"@deepseek-ai/cordis": ">=4.0.0 <5.0.0"
}
诚实的边界¶
README 自己写了几条不保证,这些不是免责声明式的套话,而是设计上的固有代价。
正解由模型生成,这是架构 C 的固有代价。 只能做到「正解与题面示例不符就终止」,做不到「保证正解正确」。所以 doctor_gate 是最后一道防线,不是唯一一道。
本地执行不是安全沙箱。 只有 5 秒墙钟超时、256MB RLIMIT_AS,以及尽力而为的网络屏蔽(替换 socket.socket)。Windows 上 resource 模块不存在,内存上限不生效,会带 memory_limit_not_enforced 标记。真正的隔离要靠 OS 级沙箱。
只支持 Python。 C++ 与 Java 的 wrapper、原平台的 tree-sitter 相似度都没搬过来。
「必须先过 gate」是提示工程,不是机制。 工具能拦住被送进来的回复,拦不住模型干脆不调用它。降低风险的办法是技能里写死步骤,加上 persona 里的第一条铁律。
适用场景与注意¶
适合的情况写清楚:
- 编程题助教、在线判题辅导,需要「先跑再答」的流程约束;
- 想用确定性执行证据替代凭印象下的归因;
- DSH 桌面版版本在
0.2.1-rc.3到0.3.0之间,且本机有 Python 3.11+。
限于当前的资料,下面这些需要读者在装之前自行确认,资料里没有给出结论:
- 多用户并发场景下的行为,资料没有提;
- 非 Python 语言、以及需要更严格隔离的执行环境,README 明确说不支持;
- 正解正确性对应的模型能力上限,插件本身只能做到
REF_REJECTED终止,不能保证; - 这个插件以当前 DSH 进程的权限运行,判定核会真的执行回复里抽出来的代码。安装前应打开 GitHub 仓库和
docs/SCHEMA.md、docs/PLUGIN_API.md、docs/EXTRACTION.md核对一遍源码与许可证。社区目录页是独立站点,与 DeepSeek / 幻方无官方从属关系。
结尾¶
dsh-doctor 的价值不在多一个工具,而在把「先跑代码再回答」「答案由门控挡不由自律挡」写进了插件。对做编程题助教的人来说,它比单纯写一条系统提示更硬的地方是:归因必须带 case_id 和实际输出,完整可运行的代码一定被 doctor_gate 拦下。代价是正解仍由模型给出,本地执行也不是安全边界。
目录页:https://www.skillhub.cn/plugins/buguoshixc/dsh-doctor
GitHub:https://github.com/buguoshixc/dsh-doctor