前言¶
用 DSH(DeepSeek Harness)跑中文工作流時,通常會遇到一個具體的麻煩:模型內部的推理(reasoning)會在長會話裏逐漸變成英文,用戶回覆也跟着漂移。在 ~/.dsh/AGENTS.md 裏寫「內部推理一律使用中文」往往壓不住,原因不在規則丟失,而是注入方式的問題:AGENTS.md 是以 user 角色的 <system-reminder> 注入的,框架文案明確它只是 guidance,不覆蓋 system/developer/user 指令,而且只在會話開始注入一次(baseline: true),長對話裏被稀釋到很遠的位置。
下面介紹的插件就是針對這一點做的最小修復:把同一條簡體中文策略以 system 角色、系統提示詞最後一段的形式追加進去。插件由 xia-sc 維護,分類爲「工作流」。這是一次性的 host-only bundle,只做這一件事。
這是什麼¶
@xia-sc/dsh-language-policy 是一個 Host-only DSH bundle,在 apply() 裏向 ctx.systemPrompt 註冊一個 section,把內置的簡體中文語言策略追加到系統提示詞的最末尾,讓模型的內部推理和用戶回覆保持中文。它不覆蓋任何上游配置,純追加;全局生效,主 agent、Agent Teams 的 teammate、subagent 都會帶上。
選擇「運行時插件」而不是「在 patch 裏覆蓋 system-prompt 的 config」是有意爲之:後者要複述 personaPrefix,相當於把上游文案複製一份,上游一改就會被舊副本蓋回去;本插件只追加,上游怎麼改都不受影響。
工作原理¶
插件註冊的 section 字段如下:
| 字段 | 值 | 說明 |
|---|---|---|
name |
policy:response-language |
policy: 前綴避免與第一方 section 撞名 |
order |
getSectionOrder('DEPLOYMENT_PERSONA_SUFFIX') + 1 |
跟隨上游段落順序,落在系統提示詞最後;取不到時回退 10201 |
interpolate |
false |
策略是純文本,不參與 {{變量}} 插值,避免裝配報錯 |
text |
見 index.js 的 DEFAULT_TEXT |
可用 config.text 覆蓋 |
插件零依賴,只用 systemPrompt 服務和 Cordis 的 ctx.effect 生命週期,不 import 任何 DSH 包,因此不受 cordis 4.0 → 4.1 升級影響。engines.dsh 聲明爲 >=0.2.1-rc.5 <0.3.0-0。
安裝與啓用¶
插件通過 plugin_manager 的 install_bundle 安裝,任選其一(都需要 plugin_manager):
install_bundle -> target: <本倉庫克隆目錄的絕對路徑> # 本地目錄
install_bundle -> target: @xia-sc/dsh-language-policy # 發佈 npm 後
本地目錄安裝時,plugin_manager 會把該絕對路徑以 link: 形式寫進 profile 的 dependencies,所以倉庫目錄不要移動或刪除。安裝會寫 profile 的 package.json(dependencies + dsh.profile.bundles)並立即生效。新開一個會話即可驗證;已存在的會話要等下一輪重新裝配系統提示詞。
配置¶
| 字段 | 默認 | 說明 |
|---|---|---|
enabled |
true |
false 時完全不註冊 section,用於 A/B 對照 |
text |
內置默認文本 | 覆蓋默認語言策略文本 |
要臨時關掉,在 profile 的 bundle 行上加 config(cordis.patch.yml 裏的註釋有示例):
- id: dsh-language-policy
name: '@xia-sc/dsh-language-policy'
config:
enabled: false
驗證¶
先做不啓動 DSH 的靜態檢查,再做提示詞落地檢查。
第一步,靜態檢查:
node --check index.js
node test/smoke.mjs
test/smoke.mjs 斷言三個導出形態、默認文本關鍵字、section 名/order/interpolate、enabled 與 text 開關、order 回退、patch 只 insert 不 override、manifest 的 locale/icon 資源。
第二步,提示詞落地檢查。新會話發一句話,然後解壓該會話日誌,確認系統提示詞裏出現了策略文本、且上游 personaPrefix 沒被動過:
// 用法: node check.mjs %USERPROFILE%\.dsh\sessions\<workspace>\<session-id>\session.v4.jsonl.zstd
import { readFileSync } from 'node:fs'
import { zstdDecompressSync } from 'node:zlib'
const magic = Buffer.from([0x28, 0xb5, 0x2f, 0xfd])
const buf = readFileSync(process.argv[2])
let text = ''
let off = 0
for (;;) {
const idx = buf.indexOf(magic, off)
if (idx < 0) break
try { text += zstdDecompressSync(buf.subarray(idx)).toString('utf8') } catch {}
off = idx + 4
}
const recs = text.split('\n').filter(Boolean).map((line) => JSON.parse(line))
const sys = recs.find((record) => record.type === 'system/message')
const prompt = sys?.data?.message?.content?.[0]?.text ?? ''
console.log({
hasPolicy: prompt.includes('語言策略'),
hasPersona: prompt.includes('You are a coding agent powered by'),
length: prompt.length,
})
README 記錄了 2026-11-04 在 DSH desktop 0.2.1-rc.5 上的實測:安裝前系統提示詞 7785 字符、不含策略;安裝後下一輪重新裝配後 7974 字符、含策略;差值 189 = 策略文本 187 字符 + 段落分隔符 2 字符,上游提示詞逐字未變。策略位於 23 個 section 之後,其後只有 DSH 自身的 runtime-context 快照(按架構總是排在最後)。
效果與已知邊界¶
README 給出了一組 A/B 對照(2026-11-04,本機 desktop 0.2.1-rc.5,2 組 × 3 樣本):兩組唯一差異是系統提示詞裏那 189 字符的策略段,題目、模型、AGENTS.md 基線、宿主全部相同。
| 組 | 三次樣本中文佔比 | 均值 | 極差 |
|---|---|---|---|
| 僅 AGENTS.md | 79.2% / 74.3% / 49.2% | 67.5% | 30.0pp |
| AGENTS.md + 插件 | 77.6% / 77.6% / 76.3% | 77.2% | 1.3pp |
結論是:AGENTS.md 本身有效,但長推理會出現英文漂移(一次掉到 49.2%);插件把中文佔比穩定在 76–78% 的窄帶內,主要收益是壓低漂移的尾部風險,而不是把均值拉滿。n=3 屬小樣本,不構成統計顯著性結論。
需要明確的邊界:
- 不承諾 100% 中文思考:長推理仍可能漂移,提示詞只能提高概率。
- 壓縮摘要的英文指令硬編碼在上游(
compaction-basic的 summarizer),插件改不了。 locale: zh隻影響界面,不影響模型提示詞;本插件補的就是這一塊。- 已知例外(與版本無關):
minimalpreset 的 persona 使用complete: true,會整體覆蓋系統提示詞,本插件段在該 preset 下不生效;standard/ptc/cordis/cc都是拼裝式,正常生效。
兼容性方面,已在 0.2.1-rc.5 和 0.2.2-rc.1 上實測:在 0.2.2-rc.1 上插件行 enabled: true / fiberPhase: active,新會話系統提示詞 8012 字符、含策略段,且 DEPLOYMENT_PERSONA_SUFFIX 仍爲 10200,order = 10201 依舊落在提示詞末尾,section() / getSectionOrder() 的實現與 0.2.1-rc.5 逐行一致。
適用場景與注意¶
適合:以中文爲主要工作語言、在 DSH 裏跑長會話和深度排障類任務、希望模型內部推理穩定在中文的用戶。插件以當前 dsh 進程的權限運行,安裝前應檢查源碼與許可證(MIT),必要時先用 enabled: false 做對照。
插件倉庫文件結構:index.js 是本體(name / inject / apply),cordis.patch.yml 只 insert 一行插件行,locale/{en,zh}.json 供 Plugin Manager / Settings 顯示,另有 test/smoke.mjs、CHANGELOG.md、OIDC 發佈 workflow 和 tools/ 實驗腳本。維護者發佈走 GitHub Actions + npm Trusted Publishing(OIDC),不需要長期 npm token。
結尾¶
回到最初的問題:這個插件的價值不在「讓中文變多」,而在「讓英文不偷溜進來」。它只追加 189 個字符,不復制、不覆蓋任何上游文案,DSH 升級時受影響面很小。目錄頁見 https://www.skillhub.cn/plugins/xia-sc/dsh-language-policy,源碼與安裝方式見 https://github.com/xia-sc/dsh-language-policy 。