dsh-doctor:給編程題助教加一道「先跑代碼、再回答」的門

前言

用 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

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜