← Back to home@XuezuoYS

dsh-prompt-inject

这是一个适用于 Deepseek Harness 的插件,向用户提供 GUI 下自定义且区分模型的发送 Prompt 注入功能。

Stars
0
Language
JavaScript
Created
Oct 11, 2026
Updated
Oct 12, 2026

Introduction

dsh-prompt-inject

DeepSeek Harness(下称 dsh)的一个插件:在设置页里,按注入点和模型配置要注入的提示词。

装好之后,设置里多出一项「dsh-prompt-inject」。里面是八个注入点,每个注入点下可以写若干条规则,规则可以自己排序、单独开关、指定对哪些模型生效。改完自动保存,下一轮对话就用新值。

  • 目标适配最新 dsh rc 版本
  • 注入的位置分两类:夹在用户输入前后的前置词 / 后置词,和 system 里的六个位置。
  • 规则正文里可以写 {{model}} 这类模板字段,发送时换成这次请求的真实值。
  • 配置文件存在本地,一切改动只影响你自己。

安装

用 dsh 自带的插件命令,从 GitHub 装进你要用的 profile:

dsh plugin --profile web add github:XuezuoYS/dsh-prompt-inject

--profile 填你平时启动的那个 profile:命令行跑 Web UI 就是 web,桌面版是 desktop。第一次用某个 profile 时,这条命令会先把它初始化出来。新装的组合包默认就是启用的,不用再开。

命令里的 github:所有者/仓库 是 pnpm 的简写形式,转发给 profile 目录里的 pnpm 执行。想把版本钉死、不让后面的推送悄悄改变实际跑的代码,就带上 commit:

dsh plugin --profile web add github:XuezuoYS/dsh-prompt-inject#<sha>

本插件是纯 JS 的手写包,没有 prepare、install 之类的构建脚本,所以官方文档里 git 安装那道「授权构建」的坎在这里不存在(pnpm 10 只会拦带构建脚本的包)。

装完如果 dsh 正在跑,命令会告诉你是立刻生效还是要重启;重启一次 dsh 总是安全的。想确认装上了,可以只看配置层而不启动:

dsh --profile web --dump-config   # 应该能看到一行 "# == dsh-prompt-inject"

卸载会同时移除依赖和它贡献的配置层:

dsh plugin --profile web remove dsh-prompt-inject

卸载不动你的规则文件,重装回来配置还在。

设置页里怎么用

打开 设置 → dsh-prompt-inject,页面分两组:用户消息(前置词 / 后置词)和 system 段。system 组里各项按它们在 system 里出现的先后排列。

每个注入点是一个可折叠的小节,有规则的默认展开,点一下能收起。小节里点「+ 新增规则」写一条;同一个注入点的多条规则按列表从上到下的顺序拼接,中间空一行;不同注入点互不影响。

一条规则卡片上有这些东西:

元素干什么
开关关掉就不参与拼接,规则仍留在列表里
模型作用域一枚胶囊,写着 全部 / 已应用 N 个模型 / 无应用,点它开弹层多选
文本多行输入框,就是要注入的内容
↑ / ↓调整这一注入点内的顺序,首尾自动禁用
⧉复制一份,插在它后面
✕删除,删完 Toast 里给一次「撤销」,撤销会放回原来的位置

编辑后 500ms 自动提交,顶部一直显示保存状态:保存中 / 已保存 / 保存失败。没有单独的保存按钮,也不需要。写失败或版本对不上时会回滚到服务端的最新值并提示你,不会静默覆盖。

注入点

设置页里叫键怎么进 system落在哪
用户前置词user.prefix插入一条独立 user 消息当前用户输入之前,每轮只注入一次
用户后置词user.suffix同上当前用户输入之后,每轮只注入一次
system 头部system.personaHead替换官方那一段官方 persona 头(order 0),官方用来说明当前调用的模型名
工具说明之前system.beforeTools新加一段order 950
工具说明之后system.afterTools新加一段order 3050
结构化输出之前system.middle新加一段order 9800
system 尾部system.personaTail替换官方那一段官方 persona 尾(order 10200),官方用来说明当前工作目录
system 最末尾system.last新加一段order 10300,排在官方 persona 尾之后

system 那六项的两种做法分得很清楚:

  • 替换(头部 / 尾部):官方本来就有这两段,插件只把它们的内容换掉。没配规则的那一侧原样不动,还是官方默认。官方段不会因为替换而消失,也不会出现重复的两段。
  • 新加一段(工具说明之前 / 之后、结构化输出之前、system 最末尾):官方没有对应段落,插件在指定段位加一段。没配规则时这段解析成空串,官方渲染时会直接丢掉,等于不存在,不会覆盖任何官方内容。

细节和官方段位参照表见 docs/injectPoint.md。

用户前置词 / 后置词注入之后不会清除

这是设计决定,也是这个插件唯一需要你提前知道的代价,所以设置页在这两个小节顶部就写着这句话。

  • 注入的前后缀是独立的 user 消息,和你打的原文一样落进会话历史,之后每一轮模型都还能读到。
  • 注入之后就再也不动它:不压缩、不清除、不还原,一轮一轮累积下去。
  • 上一轮的后置词会夹在上一轮原文和这一轮输入之间,模型很可能把它当成「用户又补充了一句」。所以这两个位置只写不会引起误解的话,比如统一的输出要求;别写带时间、带轮次、带指代的内容。
  • 你发的那条消息本身不会被改动,聊天记录仍然是真的。
  • 到 provider 那边,连续的 user 消息会被 DeepSeek adapter 合并成一条 {前置词}原文{后置词};pi-ai 那类不合并,就是三条。
  • 一轮只注入一次:工具循环的续跑请求不会再加一遍,本轮那份继续保持。

想让某句话只在 system 里生效、不留在用户历史里,就配到 system 那几个注入点去。

模板字段

规则正文里可以写这几个字段,发送时会换成这次请求的真实值:

字段换成什么
{{model}}这次请求实际用的模型 id,比如 deepseek-flash
{{provider}}这个模型属于哪个 provider,比如 deepseek-account
{{cwd}}当前会话工作目录的绝对路径

它们就是官方 persona 头尾在用的那三个变量,值来自官方 agent 循环。设置页每个展开的小节顶部都有一行字段徽章,点一下就把 {{字段}} 插到最近编辑那条规则的光标处;一条规则都没有时,点徽章会直接新建一条。

渲染时机:

  • 用户前置词 / 后置词:注入那一刻就渲染成成品再落进历史。当时是什么就是什么,之后不会再变(注入是永久保留的)。
  • system 各项:每次组装 system 时渲染,渲染完这一段标成不再插值,官方的插值器碰不到我们注入的花括号。

认不出来、这次拼错、或者取不到值的字段,{{名字}} 会原样保留,不报错、也不会让这次请求失败。名字不合法的(中文、大写)一律当普通文本。想真的写出 {{model}} 这几个字而不是让它被换掉,写成 {{ model }} 或者换个大小写就行。

模型作用域

  • 模型键是 <provider>/<model>,比如 deepseek-account/deepseek-flash。
  • 弹层里「全部」和各模型同列,按 provider 分组,带搜索。
  • 点「全部」= 对所有模型生效,含以后新增的,而且不会清空其它勾选;取消「全部」之后,之前勾的模型继续生效。
  • 既没勾「全部」也没选模型,这条规则当前不生效,卡片上是 无应用(虚线圈出)外加一句轻提示。
  • 存过的模型键在当前目录里找不到了,会标成 已失效 并保持勾选,等你自己去取消;在你取消之前,它按不匹配处理,也就是不注入。
  • 目录跟着 dsh 实时走:在 dsh 里加删改模型、改密钥,设置页马上就能看到,不用重启 dsh、也不用刷新页面。某个 provider 的目录没拉全时,列表顶部会说一句,那些没列出来的模型就会显示成「已失效」——这不是规则坏了。
  • 「这一步算哪个模型」只有一套判据:优先官方模型选择投影(就是你在下拉里点的那个),拿不到再退到这次会话最近一次请求的路由,再退到 agent 建立时的选项。system 头部 / 尾部更准一层,直接用这次组装定下来的 provider 和模型。

配置存在哪里

~/.dsh/dsh-prompt-inject/rules.json(Windows 上一般是 C:\Users\<你>\.dsh\dsh-prompt-inject\rules.json,.dsh 文件夹路径由宿主决定)。

写入走原子替换,带跨进程文件锁,权限 0600。文件坏掉或者结构版本不认识时,它会带着时间戳改名成 rules.json.<时间戳>.bad 留着(现场可查),然后用空配置继续跑,不会静默把你的规则清空。这个文件由插件自己读写,建议只在设置页里改。

结构长这样,一条规则就是一个对象,八个注入点每个都只是一串规则,没有例外:

{
  "version": 1,
  "revision": 12,
  "slots": {
    "user.prefix": {
      "rules": [
        {
          "id": "r1",
          "enabled": true,
          "text": "总是用中文回答",
          "models": { "all": true, "list": ["deepseek-account/deepseek-flash"] }
        }
      ]
    },
    "system.beforeTools": { "rules": [] }
  }
}

revision 每次写入加一,用来挡掉两个窗口同时写的情况。

已知限制

  • 用户前置词 / 后置词永久留在会话历史里,见上面那一节。
  • 设置页不显示「当前模型」。多会话并行时那个值不可信,切换也未必及时反映,拿来提示只会误导。规则按 provider/model 匹配这件事,看卡片上的作用域结论和弹层就够了。
  • 没有「这条规则最终拼成什么」的预览区。目前暂不做。
  • 排序只有 ↑ / ↓,没有拖拽。
  • 模型目录的实时更新只覆盖 dsh 自己推送的变化。直接编辑 dsh 的配置文件、或者远端 provider 自己上下架模型,这边不会自动知道 —— 刷新页面就能拿到新目录。
  • 有几道硬上限:一个注入点最多 200 条规则,单条正文最多 20000 字,一条规则最多勾 500 个模型键。超了保存会失败,并逐条告诉你哪一项有问题,已有的配置不会因此丢掉。
  • 设置页文案目前以中文为主,注入点键名和模型键保持英文原样。
  • 目标版本写死在 engines.dsh(现在是 0.2.0-rc.2)。dsh 还在 developer preview,官方明确会有破坏兼容的改动,升级 dsh 之后如果发现这个插件加载不起来,多半就是要跟着适配。

开发

一个 npm 包,两半:

  • host:cordis 插件 lib/index.js,管配置文件、注入逻辑、HTTP 端点。
  • client:lib/client.js,手写浏览器包,不走构建链,由 package.json 的 dsh.client 声明。

设计文档在 docs/,都挺短:

文件讲什么
docs/injectPoint.md注入点、官方段位、两种语义的由来与约束
docs/injectFormat.md前后缀的注入格式、模板字段与渲染时机
docs/injectScopeAndSetting.md规则模型、怎么算「这次是哪个模型」、配置文件位置
docs/settingGUI.md设置界面、数据通道(三个 /api 端点)、目录刷新的事件表

动手前值得知道的几件事:

  • 注入点表有两份:lib/injectPoints.js 是权威的那份,lib/client.js 里内联了一份一模一样的(客户端半不能 import)。改表要两边一起改,.agents/checks/client-shape.mjs 会检查两边是否一致。
  • @deepseek-ai/* 一律声明成 peerDependencies,不能装成本地依赖 —— 那样会变成两份模块实例,instanceof 和模块私有的 WeakSet 都会失效。
  • 端点开在官方 /api 下面,鉴权由官方那层处理,插件自己不写;所以只支持 GET / HEAD / POST。
  • 校验脚本分两组:npm test 跑纯 node 那组(clone 下来就能验,不依赖任何官方包);npm run test:host 跑要官方包那组,没临时解包会直接退出码 2 并打印怎么搭(npm run test:all 两类一起)。细节见 .agents/checks/README.md。
  • host 侧代码改了要重启 dsh;只改客户端那一半刷新页面就行。
  • 项目协作规范见 AGENTS.md。

许可

MIT