@xia-sc/dsh-language-policy:把簡體中文語言策略釘在系統提示詞末尾

前言

用 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 隻影響界面,不影響模型提示詞;本插件補的就是這一塊。
  • 已知例外(與版本無關):minimal preset 的 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 。

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

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

小夜