DSH Plugins Marketplace

DSH Plugins

Plugins

/

Development & Infrastructure

/

dsh-prompt-injector

y

dsh-prompt-injector

Manifest valid★ 1

DeepSeek Harness plugin: inject a reusable prompt preset into the front of a message at send time - next message only, or every turn. Prompt injector

UI (client)hasBundlePatch

English | 简体中文

dsh-prompt-injector · 提示词注入器

一个 DeepSeek Harness Web 插件:把可复用的提示词预设在发送的那一刻注入到消息首部,而不是填进输入框。

注入几次由你决定:💌 只注入下一条(默认)或 💖 每轮都注入。

萌系二次元画面风格:🌸 粉紫渐变、圆角卡片、会呼吸的小表情。

在 DeepSeek Harness 0.2.0-rc.2 上实测可用(Host 侧 67 个用例,浏览器半在真实 GUI 里跑通,见已验证)。

长什么样

先选注入方式,再点预设:

选预设:面板顶部先选「只注入下一条」或「每轮都注入」

选好之后 chip 变成 💌 名字 下一条,输入框里一个字符都不会多:

只注入下一条:chip 显示 💌 … 下一条,输入框内容不动

发送时才是注入发生的时刻。下面是「每轮都注入」连发两条的结果——两条消息都带上了预设块,而输入框始终只有你自己写的话:

每轮都注入:两条消息都带预设块,输入框依然是空的


和「提示词预设」类插件的区别

常见的预设插件提示词注入器
选中预设后把提示词填进输入框只在输入框左侧显示一个 💌 / 💖 徽章,输入框内容完全不动
你打字时要在一大段提示词后面接着写输入框里只有你自己写的话
注入几条选中就一直生效默认只注入下一条,发完自动松开;想每轮都注入得自己切
发送时提示词就是消息的一部分(你在输入框里看得见)Host 端在 agent/pre-step 把提示词拼到消息最前面
对话记录提示词和你的话混在一起记录里是完整的注入后文本(模型看到什么,记录就是什么)

实现位置:lib/inject.js 的 applyInjection(),注册在 agent/pre-step 瀑布事件上——这是消息进入会话日志之前的最后一个接缝。

为什么不是「往输入框塞标记」

最初的设计是往 composer 草稿里写一个不可见的选取标记,随消息一起送到 Host。实测下来这条路在当前版本走不通:

  • DSH 的 composer 是 Lexical 编辑器,没有任何公开的草稿写入服务;
  • Lexical 自己拥有文档状态,直接改 DOM 会被它下一次 reconcile 覆盖;
  • 通过 DOM 事件(execCommand('insertText'))写入也依赖当前选区,而打开面板后焦点已经不在编辑器上。

所以选择改由 Host 侧按会话记录:浏览器半把「这个会话选了哪个预设、注入到什么时候」通过插件的 HTTP 接口告诉 Host(lib/http.js 的 /selection),Host 在发送瞬间查表注入。这样完全不依赖编辑器的内部实现,也不需要污染消息文本——顺带还让「只注入一条」变得容易实现:Host 注入完就把这条选择丢掉。

早期版本还写了一套「尽力而为」的草稿写入代码,已经删掉了:它写得进去也不起决定作用(真正生效的是 Host 侧的选择),留着只会让「到底谁说了算」变模糊。现在插件完全不碰输入框。


安装

插件是「bundle」形态(根目录有 cordis.patch.yml),用 dsh plugin 安装:

dsh plugin --profile web add "github:yukk4159-bit/dsh-prompt-injector"

dsh plugin 把包管理转发给 pnpm,所以也可以装本地路径或指定分支:

# 本地检出(改代码时用这个,改完重启就生效)
dsh plugin --profile web add "/path/to/prompt-injector"

# 指定分支或 tag
dsh plugin --profile web add "github:yukk4159-bit/dsh-prompt-injector#main"

或者在没有全局 dsh 的环境里:

npx -p @deepseek-ai/dsh dsh plugin --profile web add "github:yukk4159-bit/dsh-prompt-injector"

装完重启该 profile,然后打开 Web GUI:

  • 输入框左边会多一个 🌸 预设 按钮 —— 点开挑预设;
  • 左侧边栏 → 设置 → 提示词注入器 —— 管理预设库(增删改查、搜索、标签过滤)。

用法

  1. 设置页写预设:名字、ID(可选)、标签、注入内容 → 保存。 保存会写进该 profile 的配置文件(cordis.patch.yml 的 config.presets),重启、切会话都不会丢。
  2. 在输入框左边点 🌸,面板顶部先选注入方式,再点一个预设:
    • 💌 只注入下一条(默认)——只有你紧接着发送的那一条会被注入,Host 注入完就把这条选择丢掉,再发就干干净净;
    • 💖 每轮都注入——一直生效,直到你点「取消」。 面板顶部的切换是立刻生效的:已经选了预设时改方式,等于同时把那条选择改成新方式。 chip 上会写清楚现在是哪种(💌 名字 下一条 / 💖 名字 每轮),不会出现「以为只注入一次、其实一直在注入」。
  3. 正常写你的话,回车发送。
  4. 发送瞬间,Host 把预设正文按模板拼到消息最前面再交给模型。
  5. 默认方式下,发完那一条之后 chip 自己回到 🌸(面板打开时每 3 秒问一次 Host,因为一次性选择是 Host 释放的,它没法主动通知页面)。

再点一次同一个预设、或点面板里的「取消」即取消。

不点面板也能用:{{ID}}

在消息里直接写 {{code}}(code 是这个预设的 ID),那一条就会用对应预设,发完即止——这本来就是一次性的,和 chip 的选择互不干扰。

  • 写了一个库里有的 ID:注入并把这处 {{code}} 从消息里拿走;
  • 写了一个库里没有的 ID:当成普通文字原样发出去,也不会挡住 chip 的选择;
  • 指向已停用的预设:不注入,文字原样保留。

注入取值的优先级

  1. 消息正文里的选取标记({{id}},或早期版本写入的私有区标记);
  2. 本会话的选择(就是上面第 2 步记录的那个);
  3. config.defaultPresetId。

私有区标记若指向一个不存在或已停用的预设,会刻意压掉后两级回退——你点名要它,就不该偷偷换成别的。手打的 {{id}} 没这个特权,因为它可能只是普通的文字。

关系

  • 选择是按会话的:不同会话可以选不同预设,互不干扰。
  • 「注入几条」记在浏览器里(localStorage),下次打开面板还是你上次选的方式。
  • 选择本身存在 Host 进程内存里,重启即清空——它表达的是「这一轮我想用哪个」,不是持久配置。想常驻就用 defaultPresetId(它按定义就是每轮都注入)。

预设长什么样

presets:
  - id: code
    name: 代码评审
    text: 请以资深工程师视角严格评审这段代码,指出风险与改进点。
    tags: [work, code]
    enabled: true

注入模板

config.template 决定包装形式,支持 {{name}}、{{id}}、{{text}}:

[{{name}}]
{{text}}
[/{{name}}]

配 config.separator(默认两个换行)拼在你自己的文字和预设块之间。 config.position 从 prefix(默认,消息首部)改成 suffix 就改成拼到末尾。


全部配置项

配置默认说明
enabledtrue总开关。关掉后 UI 还在,但不再注入。
positionprefixprefix 拼到消息首部,suffix 拼到末尾。
template[{{name}}]\n{{text}}\n[/{{name}}]预设正文外面的包装。
separator两个换行预设块与你自己的文字之间的连接串。
defaultPresetId空兜底预设:没人选、消息里也没有标记时用它。它按定义是每轮都注入;留空表示不注入。
agentPresets[]只对这些 agent preset 生效;留空表示全部。
libraryPath空可选的 JSON 副本路径(默认 <DSH_HOME>/prompt-injector/prompt-injector.json)。
presets[]预设库本体。

「注入几条」不是配置项:它是每次挑选时的一个手势,所以留在界面和浏览器里,不写进配置。


数据存在哪

  1. 预设库主存储:该 profile 的 cordis.patch.yml 里的 config.presets。 设置页保存时通过 Harness 的 settings.update() 写回,所以它同时是「插件配置」和「预设库」,设置页的自动表单也能直接编辑它。
  2. 可选 JSON 副本:设置在 settings 提供方不可用时(或者你想让库跟着仓库走)会退化写到 libraryPath;用一个 profile 里没有的 agent 跑时也能从这里读起来。
  3. 会话选择:只在 Host 进程内存里,不落盘(见上)。

HTTP 接口

浏览器半用这三条路由和 Host 半对话;它们挂在组合后的 Host Web 服务器上,因此同样受该服务器的浏览器信任栅栏与鉴权保护。

方法路径作用
GET/prompt-injector/prompt-injector/library读预设库、标签和全部会话选择
PUT/prompt-injector/prompt-injector/library整库覆盖写入
PUT/prompt-injector/prompt-injector/preset新增或更新一条(按 id)
DELETE/prompt-injector/prompt-injector/preset?id=…删除一条
GET/prompt-injector/prompt-injector/selection只读会话选择(面板的轮询用这条)
PUT/prompt-injector/prompt-injector/selection记录/清空某会话的选择,body 为 {sessionId, presetId, mode},mode 取 once / sticky

GET /library 返回的 selections 形如 {"session-…": {"presetId": "code", "mode": "once"}}。当 mode 是 once 时,Host 注入完那一条就会把这一项删掉——所以下一次 GET 里它就没了,面板据此把 chip 还原成 🌸。

路径里的 prompt-injector 是插件条目 id,两个实例不会互相覆盖。


项目结构

prompt-injector/
├─ cordis.patch.yml        # bundle 入口:一行 insert
├─ package.json            # dsh.bundle + dsh.client 声明
├─ lib/                    # Host 半(Node)
│  ├─ index.js             # 插件入口:Config、pre-step 注入、一次性选择的释放、路由与存储装配
│  ├─ config.js            # Config schema(单独一个文件,为了隔离 schemastery 依赖)
│  ├─ core.js              # 纯函数:预设规范化、标记编解码、注入方式、模板渲染、拼接
│  ├─ inject.js            # agent/pre-step 的注入逻辑(纯函数 + 消息重写)
│  ├─ store.js             # 预设库存储 + 会话选择(含 once/sticky):settings 写透 + JSON 原子副本
│  ├─ http.js              # /prompt-injector/<id>/library、/preset、/selection
│  └─ client/client.js     # 构建产物:浏览器半
├─ client/                 # 浏览器半(手写 lazy-CJS 工厂,无需打包器)
│  ├─ index.js             # 入口:apply/inject、槽位注册、库与选择同步、方式偏好
│  └─ src/                 # config / core / styles / composer / settings-page
├─ scripts/build-client.mjs # 把 client/ 组装成 client/client.js
├─ docs/                   # README 里的截图
└─ test/                   # node:test,无第三方依赖

client/client.js 是提交进仓库的构建产物:Harness 直接加载它,所以别人 clone 下来不需要构建就能装。

构建浏览器半

node scripts/build-client.mjs

脚本自己做四件事:解析相对的 import、把 react / react-dom 转成平台 require、把每个源文件包进各自的闭包、最后包成模块加载器要的 window.__ModuleLoader__.load({ id, factory })。没有打包器依赖。

测试

node --test --experimental-test-isolation=none "test/*.test.js"
# 或
node scripts/build-client.mjs && node --test --experimental-test-isolation=none "test/*.test.js"

必须写文件通配符:--experimental-test-isolation=none 下不能直接传目录。 在受限沙箱(workspace-write)里也必须保留 --experimental-test-isolation=none——默认的测试运行器会用管道 spawn 子进程,被沙箱拒绝(EPERM)。

覆盖:预设规范化 / 标记编解码(含手打 {{id}})/ 注入方式归一化 / 模板与拼接 / 消息重写(含非人类消息的排除、禁用预设、 allow-list、{{id}} 命中与否)/ 会话选择与一次性释放 / 存储写透与 JSON 原子副本 / 浏览器 bundle 的注册与槽位贡献 / Host 端 apply() 的完整 pre-step 路径。 共 67 个用例;其中「Config 默认值」需要 @deepseek-ai/schemastery 可解析,在纯仓库检出里会跳过。

已验证 / 待验证

已经实测:

  • 注入真的发生在发送时、并且真的到了模型请求里。用一个临时观察插件监听 llm/stream,对 agent/pre-step 改写后的消息取证:请求里拿到的是 [PROBE-INJECTED] Reply with exactly: PROBE-OK,说明改写后的内容就是模型收到的那一条。
  • 插件能被真实 profile 加载:dsh plugin --profile <name> add <本目录> 之后 bundle 层出现 - id: prompt-injector,Host 半正常 apply,禁用预设被过滤,HTTP 路由注册成功。
  • 浏览器半在真实 GUI 里跑通:输入框左侧出现 🌸 预设 chip;面板能打开、搜索、按标签过滤;选中后 chip 显示预设名与方式;Host 侧 /library 的 selections 里能看到该会话的选择;「取消」后选择被清空。
  • 「只注入下一条」真的只注入一条:在预览 profile 里真的打字 + 回车发送 4 条消息(该 profile 用的是假密钥,请求会 401,但注入发生在 agent/pre-step,即请求之前)。Host trace 与写进会话日志的正文分别是:
    • 第 1 条(💌 选 code-review):changed=true preset=code-review source=session
    • 第 2 条:changed=false source=none —— 选择已被释放
    • 第 3 条(切到 💖 novel):changed=true preset=novel source=session
    • 第 4 条:changed=true preset=novel source=session —— 每轮模式下继续注入 会话日志里逐条对应:第 1 条带 [严格代码评审]… 块,第 2 条原样,第 3、4 条都带 [小说续写]… 块。发完之后 GET /selection 返回 {},chip 自己回到 🌸。
  • 手打 {{id}} 也对:发一条 {{code-review}} 顺手看看 {{nope}} 这个写法,日志里记下的是预设块 + 顺手看看 {{nope}} 这个写法——命中的被注入并摘掉,没命中的原样留着。
  • 设置页能读能写:左侧「设置 → 提示词注入器」列出 6 条预设(含停用项,标 🚫),搜索与标签过滤正常,保存后 Host 的 /library 确实返回了改动后的库。
  • 选择 → 注入的完整链路(自动化用例):test/host.test.js 里一条测试直接走 HTTP 路由写选择,再驱动 agent/pre-step,断言消息首部出现 [小说续写],并断言 sticky 会连续两条都注入、once 只注入第一条且随后被释放。
  • Host 端 67 个用例,66 通过、1 跳过(跳过的那条需要本机可解析 @deepseek-ai/schemastery)。

没验证的:真机上模型真的回话(预览 profile 没有可用密钥)、以及 DeepSeek 官方路由以外 provider 的行为——这两件都和注入逻辑无关。

调试开关

DSH_PROMPT_INJECTOR_TRACE=1 会在 stderr 打出 Host 半每一步(apply、库大小、webServer 等待、pre-step 决策、注入结果)。


实现要点

  • 注入点是 agent/pre-step。它以 waterfall 方式运行,本插件先调用 next(),再改写返回的 messages,因此能和压缩等其它 pre-step 策略组合,而不是覆盖它们。
  • 只改写「人类写的」用户消息:source.kind === 'user'。运行上下文快照、技能提醒、工具追加都不会被注入。
  • 每个 step 最多改写一条,避免同一个预设被乘上多份。
  • 标记用私有区字符,正常文本里不可能出现,因此不会误伤。
  • 失败即放行:任何注入异常都会被记录并原样放行消息,绝不因为插件问题丢失你发出去的内容。
  • 资源都有归属:路由、效果、样式表都注册在插件 fiber 的 effect 上,插件卸载自动回收。

已知限制

  • 选择只在 Host 进程内存里,重启 DSH 会清空。它是「这一轮用哪个」的意图,不是持久配置;想常驻请配 defaultPresetId。
  • 预设库的写入依赖 Harness 的 settings 服务。该服务不可用时退化为 JSON 副本,此时设置页的修改只在当前进程内生效。
  • 注入后的文本会进入会话日志——这是有意为之:记录应当等于模型真正收到的东西。
  • 一次性选择由 Host 释放,页面只能事后知道:面板打开时每 3 秒 GET /selection 一次(只在有一次性选择待发时轮询)。所以发完消息后 chip 可能需要最多 3 秒才从 💌 变回 🌸——这不影响注入,只是徽章慢一拍。
  • {{id}} 只在 ID 命中库里的预设时才当作引用,所以预设 ID 起得越像普通词(例如 code),越有可能误伤正文里的 {{code}} 写法。介意的话把 ID 起得独特一点(例如 pi-code)。

贡献

欢迎开 issue 报 bug、提想法,或者直接 PR。改浏览器半的代码之后记得跑一次 node scripts/build-client.mjs——client/client.js 是要一起提交的构建产物。

许可

MIT,见 LICENSE。

Comments

Loading…

From the same category

awesome-dsh-plugin

by awesome-dsh-plugin

A curated list of plugins for DeepSeek Harness (dsh) · DeepSeek Harness 插件精选列表

Development & Infrastructure

★ 18.4k

CC0-1.0

Python

Oct 11, 2026

Index only — not installable

by 0xsline

DeepSeek Harness (DSH) ecosystem: curated plugins, tools, and infrastructure from dsh-external/hub and the public dsh-plugin topic.

Development & Infrastructure

★ 1.2k

CC0-1.0

Python

Oct 10, 2026

Index only — not installable

by pax-beehive

Open-source CLI, schemas, resolver, and DSH agent tools for DSH Plugin Hub

Development & Infrastructure

★ 406

MIT

TypeScript

Oct 6, 2026

Index only — not installable

by xiajiajun516

DeepSeek Harness (DSH) backup & restore plugin — export, import, migrate and sync your complete DSH configuration, plugins, MCP servers, skills and workspace. One-click migration to another machine.

Development & InfrastructureManifest valid

★ 177

MIT

TypeScript

Oct 11, 2026

dsh plugin --profile web add dsh-config-manager

by yjh051108

推荐组件(非必须):DeepSeek Harness 运行时注入器;已随 dsh-routing-suite 单仓库化保留,本仓库继续维护/发布。

Development & InfrastructureManifest valid

★ 165

TypeScript

Sep 18, 2026

dsh plugin --profile web add @dsh-external/dsh-super-injector

by jigjoy-ai

A CLI that turns a goal into a pull request - and a sandbox for testing concurrent AI coding agents on the Mozaik runtime.

Development & Infrastructure

★ 124

MIT

TypeScript

Oct 2, 2026

Index only — not installable