aymidasing/dsh-thinking-lang ↗★ 0

dsh-thinking-lang

DSH plugin: set the agent's reasoning language (Settings → General) 适合需要强制约束或自定义 DSH 智能体在思考和规划时所用语言的用户。

パッケージ
dsh-thinking-lang
互換性
未検証
Harness ピア範囲
>=0.1.0-rc.5
バージョン
0.1.0
ライセンス
MIT
最終更新
2026/10/01

インストール

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:aymidasing/dsh-thinking-lang

ドキュメント

README 全文を読む ↗

dsh-thinking-lang

面向 DeepSeek Harness(dsh-desk) 的插件:设置智能体内部推理(思考)所用的语言。

目标环境: DeepSeek Harness 0.2.0-rc.2+(dsh -V)。License: MIT。

本项目的代码、文案与文档均由 DeepSeek 生成(在人工指导下迭代产出)。它不是 DeepSeek Harness 官方组件,未经官方审核;使用前请自行阅读源码。


它做什么

在设置 → 通用里新增一行「思考语言」,位于宿主自身的语言设置项下方。整行只有一个下拉框:

配置项取值作用
思考语言自动 / 简体中文 / 繁體中文 / English内部推理与规划所用的语言

auto 是怎么判定的

选「自动」时,按顺序取第一个能映射到语言目录的来源:

顺序来源取值方式
1操作系统语言POSIX 环境变量 LC_ALL / LC_MESSAGES / LANG;Windows 上这些通常为空,改用 Intl.DateTimeFormat().resolvedOptions().locale(跟随系统区域设置)
2DSH 界面语言设置服务里 locale 命名空间的 preference 字段
3兜底硬编码的 zh-CN(FALLBACK_TAG)

标签先被清洗成 BCP-47(_→-,去掉 .UTF-8、@latin 这类 POSIX 修饰),再交给平台自带的 Intl.Locale 拆成语言/文字/地区三段,不手写标签文法。查找从最具体到最一般:完整标签 → 带文字的 → 带地区的 → 裸主标签,所以 en-GB 会落到 en。

中文是唯一的例外——两个条目共用 zh 这个主标签,决定简繁的是文字而不是地区。这部分交给 CLDR:maximize() 会把短标签省略的文字补出来,Hant 判为繁體、其余判为简体。于是 zh-TW/zh-HK/zh-MO/zh-Hant 都是繁體,zh/zh-SG/zh-Hans 都是简体,两张对照表都不需要。

系统界面结果
zh-CNen-US简体中文
en-USzh-CNEnglish
fr-FR(目录里没有)en-USEnglish(落到下一级)
取不到取不到简体中文

在下拉里显式选了某个语言时,那个选择优先于上面全部三级,与系统/界面语言无关。


提示词是怎么注入的

一份提示词被拆成两半,注入两个不同的位置——区别不在内容,而在求值时机。

systemPrompt.sectionsystemPrompt.context
由谁生成sectionTextcontextText
内容一行规则,直接写出语言名只有语言名加一句强调
随什么变化随思考语言变随思考语言变
求值时机每会话组装一次每次模型调用重新求值
顺序order: 1order: -10

实际注入的文本

两段文本都由 src/core.ts 生成。这里是公开文档,不转载注入原文——它们会直接进入模型上下文,属于实现细节,只以「去看代码」的方式给出入口。

systemPrompt.sectionsystemPrompt.context
生成函数sectionTextcontextText
形状一行规则,句中嵌入所选语言的英文名一个固定标记,后接所选语言的英文名与一句强调
长度124–136 字符(每种语言一份)53–65 字符
语言名形式英文名,如 Simplified Chinese / Traditional Chinese / English同左

要看注入原文,读 src/core.ts 里这两个函数;改完 npm run build 即可生效。

这个拆分带来两个直接结果:

  1. 切语言在下一轮立即生效 —— context 每一步都重新求值,不必重启或新开会话。
  2. section 里那个语言名要等新会话 —— 它在会话组装时定稿。同一个会话中途改语言,context 那行会立刻跟着变,但 section 里的名字要等下次组装才跟上。

两段都带语言,是有意的冗余:section 是权威版本(位于 persona 之后、所有工具说明之前),context 只负责实时性。想去掉重复就删掉 context——代价是切语言必须新开会话。

顺序与优先级

  • section 的 order: 1 紧跟在宿主的 persona 之后、第一个策略段之前——也就是渲染出来的 persona 那句话的下一行。
  • context 的 order: -10 排在其它每步注入块之前。注意:section 与 context 属于两个不同的序列,两者的 order 数值不可直接比较。

取值依据是 DSH 自己的分段表:@deepseek-ai/dsh-system-prompt 里的 SECTION_ORDERS——身份 -1000、部署 persona 0、PLAN_POLICY 500、工具段从 1000 起。order: 1 正好落在 persona 与第一个策略段之间。


Token 开销(实测)

用 DeepSeek V3 的词表实测(@lenml/tokenizer-deepseek_v3,不计特殊 token):

语言sectioncontext
简体中文2916
繁體中文2916
English2815

语言名本身只占 1–2 token,所以换语言对每轮开销几乎没有影响。

section 每会话一次,不随轮次增长;context 每轮一次。跑 100 轮:

语言sectioncontext × 100合计
简体 / 繁體2916001629
English2815001528

⚠️ 切换语言的缓存代价

这才是本插件唯一需要留意的开销,量级远大于上面那几十个 token。

DeepSeek 的上下文缓存自动生效、无需改代码,但它只认从第 0 个 token 起完全相同的前缀——官方原话:「只有当两个请求的前缀内容相同时(从第 0 个 token 开始相同),才算重复。中间开始的重复不能被缓存命中。」(API 上线硬盘缓存)

而 section 内嵌了所选语言的名字,位置又相当靠前(order: 1,紧跟 persona)。于是:

改一次思考语言,从 section 那一行往后的前缀全部改变——包括其余 system prompt(工具说明等)和全部对话历史,那一轮统统按「缓存未命中」计费。

差价是 50 倍(deepseek-flash 空闲时段:命中 0.02 元 / 百万 token,未命中 1 元 / 百万 token;高峰时段同样 50 倍。见模型 & 价格)。

context 那行同样随语言变化,只是它 inject 的深度不同、影响面可能小一些;两段中任何一段变了,从它往后的前缀都不再匹配。

好消息是一次性的:切完语言后新前缀会重新被缓存,从下一轮起恢复正常命中。所以代价约等于「重新算一遍完整上下文」这一次。

实用建议:选定一种语言就别频繁来回切,尤其别在一轮对话中间切。切换不影响正确性,只影响钱。


安装

npm install
npm run build
dsh plugin --profile desktop add .

装完重启 GUI。语言约束对新会话生效;切语言本身在已有会话的下一轮就生效。


构建

npm install
npm run check     # typecheck + build

构建分两段:

  1. tsc -p tsconfig.json 编译宿主端到 lib/;
  2. tsdown 把客户端打成 CommonJS 到 client/client.raw.cjs,再由 scripts/wrap-client-bundle.mjs 套上 DSH 的 __ModuleLoader__ 工厂外壳,输出 client/client.js。

选 CommonJS 是为了让产物天生就是 require(...) 形态——DSH 的客户端工厂只提供 require,若先打成 ESM 就还得事后用正则把导入语句改回来。

Node(构建)需要 ^22.18.0 || >=24.11.0;插件运行时不依赖本机 Node。


目录结构

路径说明
src/core.ts语言目录、配置归一化、两份提示词生成。纯函数,无宿主依赖,客户端与宿主端共用。locale 解析走平台自带的 Intl.Locale
src/index.ts宿主入口:Config schema、section 与 context 注册、语言解析(含操作系统语言探测)
src/client/index.ts客户端入口:在 settings.general.item 插槽注册设置行
src/client/panel.ts单行设置控件与样式
src/client/locales.ts三套行文案(简 / 繁 / 英)
cordis.patch.ymlbundle patch,登记宿主入口
PROVENANCE.md设置行样式的来源记录,以及从 app.asar 读宿主源码的方法(维护用)
lib/ · client/构建产物(不入库)

依赖面

类型包说明
运行时@deepseek-ai/schemasteryDSH 的设置 schema 格式,export const Config 必需
构建期typescript · tsdown · @types/node只在 npm run build 时用到
宿主提供@deepseek-ai/dsh-client-ui-primitives客户端用 tryRequire 按需取用;宿主没有时降级为原生控件

不依赖任何 DSH 类型包。 宿主上下文由 src/index.ts 里的本地 HostContext 接口结构化描述,所以包里没有 @deepseek-ai/cordis(既不是 dependency 也不是 peerDependency)。

宿主端对包只暴露四个导出:name、inject、Config、apply——其余全部是模块内私有。

设置行文案的语言(预留功能,默认关闭)

设置行的文案可以跟随思考语言而不是界面语言——选 繁體中文,这一行的标题与说明就变繁体。整套机制已经实现并接线完毕,但当前不生效。

开关src/client/locales.ts 里的 COPY_FOLLOWS_THINKING_LANGUAGE,默认 false
启用改成 true → npm run build → 重启 GUI
还要改别的文件吗不用。三套字典与选取函数 dictionaryFor 都在 locales.ts 里

因为开关是编译期常量,构建时 rolldown 会把这条死分支连同繁体字典一起摇掉——当前产物里没有这些文案,行为与「只跟随界面语言」完全一致。语言选择器的选项始终显示各语言的本族语名(简体中文 / English),所以即使文案换成了你不熟悉的语言,也能从同一个下拉里切回来。


已知限制

  • section 的位置绑住宿主的分段表:order: 1 取自 @deepseek-ai/dsh-system-prompt 的 SECTION_ORDERS(persona 0、PLAN_POLICY 500)。宿主若调整那张表,改 src/index.ts 的 SECTION_ORDER。
  • 设置行的位置绑住宿主语言项的 order:宿主语言行注册在 order: 0(@deepseek-ai/dsh-client-locale),本插件用 order: 1 紧贴其后。宿主若调整,改 src/client/index.ts 的 ROW_ORDER 即可。
  • 只支持三种思考语言:简体中文、繁體中文、English。要增删语言,改 src/core.ts 的 CATALOGUE,并在 src/client/locales.ts 补上对应文案。
  • 没有聊天命令:切换语言只走设置页。如需 /thinking-language,可参照 dsh-thinking-language 的命令实现补上。
  • auto 依赖运行时能报出系统语言:Linux/macOS 走环境变量,Windows 走 Intl。两者都拿不到时会静默落到界面语言,再不行才是 zh-CN——顺序与取值方式见上文「auto 是怎么判定的」。
  • 与其它语言插件互斥:本插件的插件 id 与设置命名空间都是 thinking-language,与同名的思考语言插件无法共存;包名 dsh-thinking-lang 与 dsh-thinking-language 也极为接近,注意区分。

License

MIT,见 LICENSE。