← Back to home@hongweifei

dsh-prompt

No description

Stars
0
Language
JavaScript
Created
Oct 6, 2026
Updated
Oct 7, 2026
GitHub repo

Introduction

@dsh-external/dsh-prompt

DSH 提示词注入插件:把可复用的提示词片段注入到模型的三个位置,并随时增删改。

「一个提示词」其实有三种完全不同的生命期,所以本插件分成三条通道:

通道注入位置适合放什么生效范围
系统提示词 system系统提示词里的一个 section固定人设与规则每一轮、每个请求
运行时上下文 context宿主的 runtime-context 快照(一条 user 角色消息)每轮的环境事实每轮刷新
每轮用户消息 message每轮开头的一条用户消息临时要求每轮一次

三条通道各自可开关。是否注入由两层决定:

  • 默认模式(设置页):出厂为默认关。没单独设置过的会话按它执行。
  • 会话开关(输入框旁按钮 / 设置页的「覆盖」):只影响当前会话,跟随默认 / 强制开 / 强制关。

装完不会立刻改变任何请求——这是刻意的:注入会改写每一次请求,所以它必须由你手动打开。

三个入口

  • 设置页:设置 → 「提示词片段」。完整管理:增删改、启停、同通道内上移下移、会话内片段、注入预览、存储路径,以及默认模式(默认开 / 默认关)。
  • 输入框旁的快捷开关:对话输入框工具行里的一枚小按钮。一次点击把当前会话切到相反状态;当「跟随默认」已经能给出想要的结果时,它会清掉覆盖而不是留下一条多余的记录。鼠标悬停提示「下一次点击会变成什么」。
  • 斜杠命令:无 Web 连接的 profile 也能用。
    • /prompt — 列出片段与各通道状态(/prompt on|off 改默认模式)
    • /prompt-add <名称> | <system|context|message> | <正文>
    • /prompt-remove <id>
    • /prompt-session [status|auto|on|off|add <正文>|clear]

变量

片段正文里可以写三个变量,三个通道都支持:

变量含义
{{cwd}}当前会话的工作目录
{{model}}当前模型 id
{{provider}}当前 provider

变量取自正在组装的那次请求所属的 agent,不是宿主进程的目录 —— 否则面板报的路径和会话实际所在的目录会不一致。

两处必须知道的实现约束

  1. 系统提示词通道注册为 interpolate: false。 宿主的 systemPrompt.section 默认会对文本做 {{变量}} 插值,遇到未知变量会抛错并让整次模型调用失败。本插件自己做变量替换,然后把字面文本交出去,所以用户写 {{ 永远不会炸。

  2. 运行时上下文通道无法关闭插值(PromptContext 没有这个字段),所以该通道会把每一处 {{ 改写成 { {,让宿主的扫描器看不到变量组。这个改写会在注入预览里明确提示,不会静默发生。

    ⚠ 这里有个真实踩过的坑:天真的 replaceAll('{{', '{ {') 只处理不重叠的成对括号。用户写 {{{a}}} 时它得到 { {{a}}} —— 里面仍然有一个 {{a}} 组,宿主读到未知变量就抛错,整次模型调用直接失败(现场报错:unknown prompt variable "{{a}}" in context "prompt:user-snippets")。现在的实现是逐字符扫描,无论连续多少个花括号都不会留下 {{。test/interpolate.test.mjs 把宿主自己的 interpolate() 逐字抄来当裁判,用 27 组对抗性文本验证转义结果。

配置

放在 profile 的 cordis.patch.yml 里,作为 id: prompt 覆盖:

- id: prompt
  name: '@dsh-external/dsh-prompt'
  config:
    enabled: true
    maxChars: 8000      # 三通道合计字符上限;0 = 不限制
    sectionOrder: 100   # 系统提示词 section 的位置(0 = persona,500 = plan policy)
    contextOrder: 200   # 运行时上下文的位置(宿主自带的是 110/115/120)
    refreshMs: 3000     # 存储快照重读间隔;0 = 只靠启动与面板写入刷新

数据

片段存在 <DSH_HOME>/prompt/snippets.json,一个文件,跨项目共享(提示词是偏好,不属于某个项目)。可以直接手改;插件每 3 秒重读一次,保存后立即生效。

写入走宿主自己的 ctx.fs 与新鲜度校验:面板保存时会重新读取并重放在别人写入之后,不会把并发修改覆盖掉。

安装

# 在 profile 目录里(web 这类 CLI 管理的 profile)
dsh plugin --profile web add "link:<本仓库路径>"

desktop 这类由 Electron 管理的 profile 只能用宿主内的 plugin_manager 安装,安装后需要重启应用(宿主半边在进程启动时载入)。客户端半边改完只需刷新页面。

测试

node test/run.mjs          # 全部:单元 43 + 插值安全 6 + 集成 41 + 客户端 22 + 架构 12 = 124
node test/check-encoding.mjs  # 全树扫描 CP936 损坏与 BOM(提交前必跑)
node test/preview.mjs      # 渲染真实面板 → test/preview.html
node test/shots.mjs        # 截图到 test/shots/(zh/en × light/dark)
node test/probe-live.mjs   # 用真实存储回读三个通道的最终文本(排查现场用)
node test/probe-session.mjs <会话目录>   # 逐帧解压会话日志,查某段文本是否真的注入过

test/shots/*.png 是随仓库提交的文档,改面板就要重新生成;shots.mjs 会比较四张图的哈希,两张图字节相同就直接报错(Edge 会按 URL 缓存 file://,否则第二次截图会静默复用第一张)。

⚠ 不要用 PowerShell 改本仓库的文本文件

这台机器的 PowerShell 5.1 是 CP936 代码页:Get-Content -Raw + Set-Content / WriteAllText 的往返会把这个 UTF-8 文件按 CP936 解码再按 UTF-8 写出,每一个非 ASCII 字符都会变成乱码,且部分字节直接丢失、不可还原(— 会变成 ?)。本项目因此损坏过 lib/client.js 两次、test/unit.test.mjs 一次。

用 read/edit/write 工具,或写一个 Node 脚本(Node 的 fs 严格按 UTF-8 读写)。提交前跑 node test/check-encoding.mjs——架构测试里也有一道同样的守卫。

许可

MIT