dsh-language-guard
Verifieddsh-language-guard · v0.3.6 · MIT · Web UI
Lock model reasoning, replies and tool-call descriptions to a target language in DeepSeek Harness (Simplified Chinese by default): three strength levels, configurable prompt sections, periodic reminders, a settings panel with a quick language switch and a
Install
dsh plugin add dsh-language-guard Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
语言守卫(dsh-language-guard)
English | 简体中文
让 DSH 的智能体用你选定的语言思考与回复——默认简体中文,三档强度,下一条消息即生效,零依赖、零构建。

目录
- 为什么需要它
- 它做了什么
- 安装
- 三档强度
- 配置项
- 自动语言模式
- 设置面板与快速语言切换
/language命令- 验证约束确实进了 system prompt
- 与 dsh-context 共存
- 与官方 cot-translation 插件的区别
- 已知限制
- 设计边界
- License
为什么需要它
DSH 的 system prompt 由许多 section 拼装而成:身份、计划策略、团队策略、工具说明、交付物引用、部署人设。其中体量最大的一块是工具说明——英文写的,而且正好落在提示词的中间。
在 DSH 0.2.1-rc.2 上实测(取 order 50 之后的全部内容):98.1% 的字符、99.8% 的 ASCII 来自英文工具说明,首段与末段之间隔着 5.7K–14K 字符。模型读到的绝大多数 token 是英文,于是它漂移:
- 开头几轮还是你的语言;
- 一旦开始连续调用工具、读长文件、做长链推理,内部推理就滑回英文;
- 你的设置一个字都没改——只是会话变长了。
在 system prompt 最顶端写一句「请用中文回答」,短会话够用。长会话不够,因为那句话距离真正影响模型的地方(工具说明)有上万字符。
本插件把约束放在三个位置:最前、工具说明之后、最后。默认档(强化)每次请求多花约 146 个字符,换来一次紧跟在英文工具说明之后的重申。
它做了什么
三个常驻 system prompt section,通过 ctx.systemPrompt.section() 注册,永不注销,切档只切换它们的文本:
| section 名 | order | 位置 | B | C | D |
|---|---|---|---|---|---|
language-guard:head |
-2000 |
最前,早于 harness:identity(-1000) |
✅ | ✅ | ✅ |
language-guard:mid |
5100 |
全部工具说明(tools:sdk,5000)之后、交付物引用(9000)之前 |
❌ | ✅ | ✅ |
language-guard:tail |
11200 |
最后,晚于 deployment:persona-suffix(10200) |
✅ | ✅ | ✅ |
约束覆盖的四个语言通道(0.3.0 起补齐第四个):
| 通道 | 约束写在哪 | 生效范围 |
|---|---|---|
| 内部推理(reasoning / thinking) | head + tail |
全档位 |
| 面向用户的回复正文 | head + mid + tail |
全档位 |
| 计划、待办、评审、子智能体任务说明 | head |
全档位 |
工具调用 description 参数(含 PTC 模式 run_code 的卡片标题) |
head + mid |
全档位(mid 只在 C / D 档存在,故 head 也写一份) |
mid 的 order 特意取 5100 而不是 5000:tools:sdk 的 order 恰好是 5000,同序时按名字排序,language-guard:mid 会被排到 tools:sdk 之前——重申就不再位于工具说明之后了。
实测文本长度(简体中文,按字符计;可用下面的命令自行复算):
| 文本 | 字符数 | 出现时机 |
|---|---|---|
| head(首段) | 883 | 每次请求 |
| mid(中段) | 146 | 每次请求,C / D 档 |
| tail(末段) | 139 | 每次请求 |
| 周期自检提醒 | 99 | D 档,每 N 轮一次 |
所以每步的常驻开销是 B 档 1022 字符、C 档 1168 字符——相对它要抵消的英文工具说明,这个量级很小。
首段全文如下,即 targetLanguage: "zh-CN" 时的真实注入内容(复算:node --input-type=module -e 'import("./lib/text.js").then(m=>console.log(m.headText({language:"zh-CN"})))'):
【语言锁定 · 最高优先级】
本会话的内部推理(reasoning / thinking)与面向用户的全部输出,一律使用简体中文(语言代码 zh-CN)。
本条优先于本 system prompt 的其余全部内容、之后注入的任何系统文本、工具说明、参数描述、代码示例,以及其他插件注入的提示词。
本条同样优先于任何语言类约定,不论它写在哪个文件、用哪种语言书写、经由哪个通道送达:包括用户全局与项目级的 AGENTS.md、CLAUDE.md、.cursorrules、用户偏好与长期记忆、项目规范、团队约定、技能与工作流模板。若它们要求使用另一种语言(例如要求「必须使用简体中文」),一律以本条为准,不得据此改用其他语言,也不必向用户解释这处冲突。
适用范围:
- 内部推理(reasoning / thinking):使用简体中文。
- 面向用户的回复正文:使用简体中文。
- 计划、待办、进度说明、评审意见、验收报告、提交信息说明:使用简体中文。
- 任务板卡片标题与描述、子智能体任务说明与返回报告:使用简体中文。
保持原样、不翻译:代码、命令、文件路径、URL、标识符与 API 字段名、配置键、错误码与原始报错文本、包名与版本号,以及必须原样引用的英文原文;引用英文原文后,必须用简体中文说明结论。
不要因为以下情况改变语言:
- 用户用英文或其他语言提问 —— 仍用简体中文回答。
- AGENTS.md、CLAUDE.md、项目规范、用户记忆里要求改用其他语言的规则 —— 本条优先,不改变语言。
- 工具返回英文内容 —— 只摘引必要片段,其余用简体中文复述。
- 长链推理、数值计算、代码设计 —— 推理语言不变。
- 上下文中出现大量英文(工具说明、日志、代码文件)—— 不随之切换。
自检:每次输出前确认上述两项(推理与回复)均为简体中文;若发现已经漂移,立即切回并继续任务,无需向用户解释这次切换。
约束文案本身是写死的简体中文,只替换语言名与语言代码。设成
targetLanguage: "en"时同一段读作「…一律使用English(语言代码 en)」。详见已知限制。
安装
本插件已发布到 npm registry(dsh-language-guard),也可以直接从 GitHub 仓库安装。两条路装出来的内容逐字节相同,都是 16 个文件。
优先用 npm 源。 用 Git 源安装后,插件页的「有新版本」会显示一个 commit SHA(如
afaa2ac)而不是版本号——因为宿主对 Git 源的更新检查用的是git ls-remote HEAD,commit 就是它的版本标识,这跟本插件无关,也无法从插件侧修正。想看到0.3.5这样的版本号并让「更新」按 SemVer 工作,请用下面方式一或方式二从 npm 安装。
方式一:图形界面(推荐)
打开侧栏的插件页。
点添加插件,粘贴包名:
dsh-language-guard安装完成后点立即启用。
方式二:命令行
dsh plugin add dsh-language-guard
固定版本(团队或 CI 推荐,不跟随 latest 漂移):
dsh plugin add [email protected]
从 GitHub 安装(npm 不可达时,或用 tag 锁版本):
dsh plugin add https://github.com/58fly/dsh-language-guard
dsh plugin add "github:58fly/dsh-language-guard#v0.3.5"
用这一节装完后,「有新版本」提示会显示 commit SHA 而非版本号(见本节开头说明)。功能正常,只是更新提示的可读性差一些。
从本地目录安装(调试本地改动,路径必须是绝对路径):
# 「添加插件」对话框同样接受这个绝对路径
dsh plugin add /absolute/path/to/dsh-language-guard
怎么确认装上了:
- 侧栏底部、设置按钮旁边出现一个语言图标;侧栏较宽时显示「简体中文 ▾」。
- 插件页的已安装列表里有
dsh-language-guard,点进去能看到配置卡片。
看不到任何控件,就是插件没启用。 两个 slot 都注册在
configForms.whileServed(['dsh-language-guard'], …)内部;插件被禁用时表单服务不可用,不会注册任何界面。这是有意为之,不是 bug——先去插件页启用它。
环境要求:DSH >= 0.2.1-rc.2,Node ^22.19.0 || >=24.0.0。dependencies 为空、peer 依赖全部 optional,所以运行时不需要额外下载依赖。从 npm 安装只需能访问 registry.npmjs.org;从 GitHub 安装则需要能访问 github.com(或 codeload.github.com)。
三档强度
下面三条说明与设置面板上显示的文字完全一致:
| 档位 | 配置值 | 说明 |
|---|---|---|
| 基础 | B |
只在提示词首尾各放一段语言约束,成本最低,适合短会话。 |
| 强化 | C |
在基础之上,于工具说明之后补一段重申,抵消英文工具说明带来的语言漂移,推荐默认。 |
| 周期 | D |
在强化之上,每 N 轮在当轮开头追加一条语言自检提醒,适合长会话;提醒会作为一条会话记录出现,可在会话日志中看到。 |
行为差异:
| 行为 | B | C | D |
|---|---|---|---|
| 常驻 system prompt 段数 | head + tail | head + mid + tail | head + mid + tail |
| 工具说明之后重申 | ❌ | ✅ | ✅ |
| 每 N 轮自检 | ❌ | ❌ | ✅(N 默认 8,范围 3–50) |
| 会话日志新增记录 | 无 | 无 | 每 N 轮一条(source.kind = "language-guard") |
| 注入时机 | — | — | 命中轮的第一个 step,随当轮用户消息一起进入模型视图,不额外发请求 |
| 重启 / 恢复后计数 | — | — | 不归零:轮次取自会话日志的 turn 投影,turn % N 稳定 |
| 适合 | 短会话、在意 token | 默认选择 | 长会话,或已经观察到漂移 |
怎么选:
- 先用默认的「强化」。 它是唯一针对真正根因(英文工具说明)的档位,每步只多约 146 字符。
- 会话很短、或每个 token 都要省 → 降到基础。3–5 轮的会话,首尾两段足够。
- 一轮会话几十轮起步,或已经看到推理滑回英文 → 升到周期。N 越小提醒越密,默认 8 是折中,N = 3 会明显啰嗦。
配置项
七个字段,全部是 volatile(改动立即生效,不需要重启,也不重挂插件):
| 字段 | 类型 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|
languageMode |
"" | "auto" | "manual" |
"" |
三态枚举,大小写敏感 | 语言模式。"auto" 跟随界面语言、"manual" 固定用 targetLanguage、"" 表示「还没选过」,按迁移规则推断(见下节) |
targetLanguage |
string |
"zh-CN" |
合法 BCP-47 标签(/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/) |
目标语言。zh-CN 简体中文、zh-TW 繁體中文、en English、ja 日本語、ko 한국어;其他合法标签按原样使用 |
strength |
"B" | "C" | "D" |
"C" |
枚举,大小写敏感 | 强度档位,见上 |
injectEveryNRounds |
number |
8 |
3–50 的整数 | 周期自检的间隔。只在 D 档生效,其他档位该控件禁用 |
preserveCodeTerms |
boolean |
true |
true / false |
true:代码、命令、路径、标识符、API 字段与原始报错保持原样。false:技术名词优先用目标语言表达,必要时括号附英文 |
extraRules |
string |
"" |
任意文本 | 追加在内置规则之后。留空表示只用内置规则 |
overrideHead |
string |
"" |
任意文本 | 完全替换内置首段文本(extraRules 仍会追加在其后)。留空或纯空白回退内置文本 |
targetLanguage 到语言显示名的映射是固定的(不做语言猜测):zh-CN / zh-Hans / zh-SG → 简体中文,zh-TW / zh-HK / zh-MO / zh-Hant → 繁體中文,en / en-US / en-GB → English,ja / ja-JP → 日本語,ko / ko-KR → 한국어;其他合法标签直接显示代码本身。
配置落在 profile 的 cordis.patch.yml 里。patch 对 config 是整体替换,不做深合并——手写覆盖时必须把每个字段都写全,否则漏掉的字段会丢:
- id: dsh-language-guard
name: dsh-language-guard
config:
languageMode: auto
targetLanguage: zh-CN
strength: C
injectEveryNRounds: 8
自动语言模式
languageMode 决定目标语言从哪来:
| 模式 | 行为 |
|---|---|
| 自动 | 目标语言跟随 DSH 的界面语言。你把界面切成英文,下一条消息就变成英文约束 |
| 手动 | 目标语言固定用 targetLanguage,与界面语言无关 |
默认是自动。 新装用户什么都不用设:界面是简体中文就约束简体中文,界面是英文就约束英文。
解析顺序
自动模式只有两个来源,按顺序取第一个有效的:
- 界面语言(
locale配置里的preference)——这是Settings → General → Language那一项。 targetLanguage配置值(默认zh-CN)——界面语言读不到时兜底。
映射规则(zh 开头的标签 → zh-CN、en 开头的标签 → en,其余合法标签原样保留):
| 界面语言 | 目标语言 | 约束读作 |
|---|---|---|
zh / zh-CN / zh-Hans |
zh-CN |
简体中文 |
en / en-US |
en |
English |
ja |
ja |
日本語 |
ko |
ko |
한국어 |
| 其他合法 BCP-47 | 原样 | 该语言的显示名或代码 |
为什么没有「系统语言」这一档? Host 半是
ELECTRON_RUN_AS_NODE=1的子进程,require("electron")直接抛Cannot find module 'electron';Intl.DateTimeFormat().resolvedOptions().locale恒返回en-US,会把中文用户误判成英文。Host 侧没有可靠的系统语言信号,所以插件不去猜,也不编一个假信号出来。
从 0.1.x 升级
老用户手选过 targetLanguage(比如设成 ja)却没碰过 languageMode 的,升级后仍然是手动模式,语言不会被界面语言静默覆盖。规则是:languageMode 为空串且 targetLanguage 不等于默认值 zh-CN → 判为手动。
想改成跟随界面,在设置卡片里点「自动」即可;一旦显式选过,迁移规则就永久失效。
怎么知道现在解析成什么
设置卡片第一组控件就是语言模式,选「自动」时下面直接写出当前结果与来源:
当前解析为 简体中文
来自界面语言
来源行显示「来自界面语言」时,指的是你在 DSH 设置里显式选过的界面语言;从没选过时显示「未识别到界面语言,用配置值兜底」,而不是拿浏览器或系统语言去猜——Host 与设置面板读的是同一个值(settings.describe() 里 locale 命名空间的 preference),所以面板显示什么、实际就注入什么,不会分叉。侧栏快速切换按钮的标签也会读作「自动,简体中文」这样的当前状态。
改界面语言不需要重启:插件在启动后缓存界面语言,并订阅 settings/document-updated(locale 命名空间变化时刷新),所以下一次装配 system prompt 就用了新值。
设置面板与快速语言切换
设置面板
路径:侧栏插件页 → 已安装插件 → dsh-language-guard。配置卡片渲染在组合包页面上、描述与行列表之间(plugins.bundle.config slot,以包名为 key)。设置里的插件分区只保留只读信息,配置在这里改。
卡片自上而下:
- 语言模式——自动 / 手动两行单选。选自动时下面直接写出「当前解析为 X」与来源(界面语言 / 配置值),切换界面语言后这里跟着变。自动态下第 3 项的目标语言控件置灰。
- 强度档位——三行单选(
fieldset+ 同名radio),三条说明并列可见,C 档带「推荐」徽标。用单选组而不是<select>,因为三档差别必须一眼可比。 - 注入间隔(轮)——数字输入框,3–50,非周期档时禁用并给出说明。
- 目标语言——文本框 + 五个预设 chip(简体中文 / 繁體中文 / English / 日本語 / 한국어)。自定义标签直接填进文本框,输入即校验,非法时保存按钮禁用。
- 保留代码与术语原文——开关(
role="switch"+aria-checked)。 - 附加约束——文本框,带本地草稿与「保存 / 放弃更改」。
- 覆盖首段规则(高级)——默认折叠。改它等于整段替换最高优先级的约束,所以收起来。
每个字段都有「恢复默认」(清掉用户值,回到 schema 默认值)。
保存态文案是「已保存 · 无需重启,下一条消息即生效」。档位、开关、间隔是变更即写;文本类字段走本地草稿 + 显式保存,避免每敲一个字都写一次盘。
快速语言切换
在侧栏底部、设置按钮旁边(sidebar.footer.action slot,id = "language-guard",order = 100):
- 侧栏较宽:图标 + 该语言的自称 +
▾。 - 侧栏收成 56px 窄条:只显示图标,
aria-label仍然完整。 - 点击弹出下拉菜单(不是循环切换)。第一项是「自动」(跟随界面语言),后面才是五个预设。当前项打勾;自动态下按钮标签读作「自动,简体中文」,菜单里「自动」那一项下方读作「当前自动解析为 简体中文」。当前值不属于五个预设时显示原始代码,预设项都不打勾。
- 键盘:Enter / Space 打开,Escape 关闭并把焦点还回按钮,↑↓ 在菜单内移动。
- 菜单底部有一行提示,指向设置页文本框填其他语言。
选一项即写入配置(选具体语言会同时把模式切成手动,模式与语言同批原子写入,不会留下半截状态),侧栏标签与设置页文本框双向同步。写入完成后按钮上方会出现一行 4 秒后自动消失的提示:改成新语言时是「已保存 · 无需重启,下一条消息即生效」,选中项本来就是当前语言时是「当前已是 X」(不会静默无动作),写入失败时显示失败原因。
改配置不需要重启 DSH。配置写入走
dsh-config-editor,它在改完磁盘后立即让 Loader 应用;volatile 字段只替换运行中的引用、不重挂插件(cordis-plugin-loader的_commitVolatile→loader/volatile-update),而 system prompt 是每次请求重新装配的,所以下一条消息就带上新语言。需要重启的只有改插件代码(例如升级版本),不是改配置。
/language 命令
在输入框里用的快捷方式。它只设置本次运行的内存覆盖,不写配置,重启后回到配置值:
/language 报告当前语言与配置值
/language ja 本次运行切到日语
/language reset 清除覆盖,回到配置值
/language default 等同 reset
非法标签返回错误且不改任何东西。覆盖优先于配置:覆盖 > 配置值 > 默认值。
验证约束确实进了 system prompt
档位、面板、开关,在没看到模型真正收到什么之前都只是说法。DSH 会把每次请求的 system prompt 作为 system/message 事件记进会话日志(~/.dsh/sessions/<转义后的工作目录>/<会话 id>/session.v4.jsonl.zstd)。下面三步在任何装好本插件的环境里都能复现。
第一步:改档位,不重启
- 打开设置面板(侧栏插件页 →
dsh-language-guard)。 - 把强度从「强化」切到「周期」。
- 卡片底部显示「已保存 · 无需重启,下一条消息即生效」。
第二步:读会话日志,看 system 节点
在任意会话里发一条普通消息(比如「你好」),然后执行:
# 1. 找最近写入的会话日志
latest=$(ls -t "$HOME"/.dsh/sessions/*/*/session.v4.jsonl.zstd | head -1)
echo "$latest"
# 2. 解压成 JSONL(macOS 上没有 zstd 先 brew install zstd)
zstd -dc "$latest" > /tmp/dsh-session.jsonl
# 3. 打印每个 system 节点的长度,并检查三段是否都在
node -e '
const fs = require("node:fs");
const lines = fs.readFileSync("/tmp/dsh-session.jsonl", "utf8").split("\n").filter(Boolean);
const sys = lines.map((l) => JSON.parse(l)).filter((o) => o.type === "system/message");
const marks = ["【语言锁定 · 最高优先级】", "【语言锁定 · 中段重申】", "【语言锁定 · 收尾复述】"];
console.log("system 节点数:", sys.length);
sys.forEach((o, i) => {
const text = o.data.message.content.map((p) => p.text || "").join("");
const hit = marks.filter((m) => text.includes(m)).length;
console.log(`#${i} turn=${o.data.turn} step=${o.data.step} 长度=${text.length} 三段命中=${hit}/3`);
});
'
在「强化」(或「周期」)档下应看到 三段命中=3/3。
第三步:切到「基础」,看中段消失
- 把强度切到「基础」,再发一条消息。
- 重跑第二步的命令。
新的 system 节点应报 三段命中=2/3——缺的是【语言锁定 · 中段重申】——且长度比上一个节点短 146 个字符,正好是中段的长度(见它做了什么)。基础档下 language-guard:mid 返回空字符串,renderPrompt 会把空文本过滤掉,所以不会留下空 section 或多余空行。
补充检查
- 周期提醒:把强度设为「周期」、间隔设为 3,然后聊 3 轮。会话日志里会出现
user/message事件,其data.message.source.kind为"language-guard"、form为"notice"、summary为"语言自检"。界面上它渲染成折叠的一行「上下文注入 · language-guard · 语言自检」——它不会伪装成用户的一轮发言。 - 子代理被跳过:周期档下派一个子智能体,它的会话日志里没有
source.kind = "language-guard"的记录(子代理继承父会话的 system prompt 约束,紧凑上下文不去打扰它)。 - 切换语言:从侧栏菜单选「日本語」,发一条消息,新 system 节点读作「一律使用日本語(语言代码 ja)」。
- 停用插件:停用后没有 section、没有注入,新 system 节点报
三段命中=0/3。这是干净的回退,不是残留。
一键自检
上面这些手工步骤可以合并成一条命令:
node scripts/verify-installed.mjs
它会自动找最近写入的会话日志、解压、统计每个 system/message 节点的长度与三段命中数,并直接给出两个结论:旧方案(dsh-context 的 promptPrefix/promptSuffix)是否还有残留、本插件是否生效。也接受显式路径:
node scripts/verify-installed.mjs ~/.dsh/sessions/<项目>/<会话>/session.v4.jsonl.zstd
需要系统里有 zstd 命令(macOS:brew install zstd)。
与 dsh-context 共存
第三方插件 dsh-context 注册的 promptPrefix / promptSuffix 两段,order 与本插件的完全撞值(-2000 与 11200)。
- 不会出错:排序是
order - order || compareNames(name),同 order 不等于同名。"dsh-context:prompt-prefix" < "language-guard:head"(d<l),所以 dsh-context 的两段稳定排在本插件前面。 - 但 token 会烧两遍:两者都在往同一个位置写语言约束——同一件事,说了两次。
- 建议:装好本插件后,把
dsh-context的promptPrefix/promptSuffix清空,或直接停用dsh-context。
设置卡片底部有一行常驻提示,locale/{zh,en}.json 的 meta.description 里也写着同一句。本插件不检测也不改写 dsh-context 的配置——写别的插件的配置违反最小惊讶原则。
与官方 cot-translation 插件的区别
@deepseek-ai/dsh-experimental-client-ui-cot-translation(配合 cot-translation-bundle)是官方实验插件,做的是渲染层翻译:把界面上展开的推理内容机器翻译成另一种语言显示。它自己的 README 明确写着:翻译会把展示出来的推理内容发送给选定的外部服务,其中可能包含私有代码或会话细节;原始推理仍保留在会话与模型上下文中。
本插件做的是生成层约束:在 system prompt 里告诉模型,用你选的语言思考与回答。
| cot-translation | dsh-language-guard | |
|---|---|---|
| 作用于 | 已经渲染到界面上的推理文本 | 模型的下一次生成 |
| 层次 | Client 渲染(conversation.chat.reasoning.body slot) |
Host system prompt(ctx.systemPrompt.section()) |
| 联网 | 会,把文本发给外部翻译服务 | 不会,零网络请求 |
| 读会话内容 | 会,把推理正文送出 | 不会,只写自己的约束段 |
| 原始推理的语言 | 不变(仍在会话与上下文中) | 就是你配置的语言 |
两者是互补而非竞争:一个决定模型用什么语言思考,另一个决定这份思考显示成什么语言。可以同时装——但如果本插件已经把思考钉在简体中文,把 cot-translation 也指向简体中文就没有增量;它有价值的设置是反方向(模型用中文思考,你读英文)。
已知限制
- 约束文案本身是写死的简体中文。 只替换语言名与语言代码,所以
targetLanguage: "en"时注入文本读作「…一律使用English(语言代码 en)」。模型能正常处理,但行文没有本地化。模板本地化留待后续版本。 - 周期提醒在会话界面上可见,折叠成一行「上下文注入 · language-guard · 语言自检」。英文标识符
language-guard无法隐藏——客户端把source.kind原样当作注入标签渲染,官方agent-instructions、skill-invocation同样如此。要本地化它得为上下文节点写一个自定义conversation.chat.node渲染器,首版不做。 - Client 半拿不到轮次计数,所以面板无法显示「下一次注入会落在第几轮」。
- 快速切换菜单只列五个预设加一条「当前自定义」。 自定义 BCP-47 标签必须在设置页文本框里填;手动模式且当前值不在预设内时,菜单会多出一条「当前自定义:<语言名>」并处于勾选态,避免六项全无勾选看起来像「没设过语言」。菜单无法枚举任意标签,切换其它自定义值仍要回设置页。
- 绝对锁定,不给单轮开口子。 有意的设计取舍:本插件是「设定一次、全程有效」,所以「这次先用英文回答」这类当轮指令不会让它临时切走——想换语言就改设置或用
/language。这与「按轮灵活切换」是相反的取向,需要按轮切换的用户不适合用本插件。 - 检测不到是否装了
dsh-context,所以提示是常驻的(见上)。 - 周期档不会唤醒空闲会话。
agent/pre-step只在轮次运行中触发,不发消息就没有提醒——语言守卫不应该自己产生对话。 mid的位置可能被未来的插件挤压。 如果有插件在 order5000到9000之间插段,「工具说明之后」的语义会被稀释。届时的修法是把mid提到8900(仍在交付物引用9000之前);再不行就回退到基础档,它不依赖mid。- 无法自动区分简体与繁体。 DSH 内置界面语言只有
zh与en(dsh-client-locale/lib/index.js:11LOCALE_IDS = ["zh","en"]),zh不区分简繁;而 Host 侧拿不到系统语言。因此界面语言为「中文」时,自动模式一律解析为zh-CN。需要繁体输出的用户请把语言模式切到「手动」并选zh-TW。日本語 / 한국어 同理:标准安装下自动模式只能解析出zh-CN或en,需要这两种语言请切到「手动」自行选择——若你装了对应的外部语言包,它的语言标签会原样透传。 - 不覆盖 API 的
tools参数本身。 真正传给模型的工具 schema 在请求的tools参数里,不在 system prompt 内;本插件只在提示词侧要求模型用目标语言书写description参数,无法改写 schema 里的英文措辞。模型若不遵从,工具卡片标题仍会是英文——这是提示词级约束的固有边界,不是配置问题。 - 不自动跟随操作系统语言。 插件读取的是你在 DSH 设置里选择的界面语言,不是操作系统语言。DSH 的 Host 进程以
ELECTRON_RUN_AS_NODE=1运行,拿不到 Electron 的app.getLocale();Node 的Intl在该进程里恒为en-US,用它会把中文用户误判成英文,所以我们选择不用它。 - 自动模式跟的是界面语言,不是你输入的语言。 界面是简体中文、你用英文提问,约束仍然是简体中文(这是设计意图:插件声明默认语言,不猜单轮输入)。想按输入语言变,只能手动切。
- 切档会改变 system prompt,因此 DSH 会追加一个新的 system 节点(in-history 替换)。这是一次性的 token 开销,属于预期行为。
设计边界
本插件刻意不做下面这些事——每一件都会破坏宿主约定,或者让排查变得不可能:
- 不监听
system-prompt/assemble去增删宿主文本;只用ctx.systemPrompt.section()声明自己的段。 - 不改写
llm/stream的 reasoning delta——绝不伪造「思考过程」。 - 在
agent/pre-step里除messages之外不碰任何 decision 字段,也不追加新的事件类型;周期路径返回{ ...decision, messages },保留startsRequestSeries等其余字段。 - 不联网、不读会话内容、不上报任何数据。
dependencies为空;Client 半只从宿主模块表里require('react')。- 约束文本在会话内保持稳定,避免频繁 in-history 替换与 prompt cache 失效。
License
MIT © 58fly
仓库:https://github.com/58fly/dsh-language-guard · 问题反馈:https://github.com/58fly/dsh-language-guard/issues