dsh-prompt-optimize
Manifest validDSH Web UI 原生插件:输入框「优化提示词」按钮,原地回填草稿,一次撤销可还原
dsh-prompt-optimize
一个 DSH Web UI 原生插件:在输入框发送按钮的左侧放一个 优化提示词 按钮,点一下就用 当前会话自己的模型路由改写草稿,并通过编辑器原有路径原地回填——一次撤销即可还原原文。
输入区工具行:[引号 ▾] [思考强度 ▾] [优化提示词] [发送]
^ conversation.input.right
安装
# 在 DSH Web GUI / Agent 里把本目录作为 bundle 安装
plugin_manager({ action: "install_bundle", target: "<本包的绝对路径>" })
Bundle patch 只插入自己这一行(cordis.patch.yml),不改动任何 DSH 核心文件,
dsh.bundle.patch 保持为一个自包含的 insert。
日常构建可以跳过重启:plugin_manager({ action: "set_plugin", target: "include:prompt-optimize", enabled: false }) → 覆盖新文件 → set_plugin … enabled: true。首次安装,以及新增 npm
依赖后的重装,仍然需要重启 dsh web(Host 的模块表在启动时就固定了)。纯客户端改动会被
客户端重载器直接拾取,两种方式都不用。
安装时的两个坑(DSH 行为,不是本插件的问题)
-
会话中途安装的 bundle 只在“下一次重载”时挂载。 启动时,如果 Loader 行对应的包还不在 profile 的运行时解析表里,这一行就拿不到 entry,也不会被 import;host 只会在那一次启动 打印
dsh: warning: 1 entry did not activate,之后的重载不再提示。所以当plugin_manager list_plugins里看不到include:prompt-optimize行、而 Config inspect 仍 然列着这个包时,重启dsh web——任何会话内开关都补不出缺失的 entry。 -
任何
plugin_manager包操作都会清理包内的node_modules。dsh-prompt-optimize是以link:方式指向本目录安装的,但 pnpm 仍会管理 link 目标内部 的node_modules,并在每次 install/remove/upgrade 时清理它,留下失效符号链接。Host 半边 需要@deepseek-ai/cordis(应用自己的那份)和@deepseek-ai/schemastery;链接一断,entry 就会以ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/schemastery'停止 import。 任何 plugin-manager 操作之后重建链接即可,无需重启:cd <本包目录> rm -rf node_modules && mkdir -p node_modules/@deepseek-ai # `dsh` 指向 <npx root>/node_modules/@deepseek-ai/dsh/lib/bin.js NPXROOT="$(dirname "$(dirname "$(dirname "$(dirname "$(readlink -f "$(command -v dsh)")")")")")" ln -s "$NPXROOT/@deepseek-ai/cordis" node_modules/@deepseek-ai/cordis ln -s "$HOME/.dsh/profiles/web/node_modules/@deepseek-ai/schemastery" \ node_modules/@deepseek-ai/schemastery
按钮行为
| 场景 | 按钮 |
|---|---|
| 草稿为空 | 禁用(提示「草稿为空」) |
含附件、引用 chip(文件/选区),或以 /命令 开头 | 禁用 |
| 输入框正在提交 / 判定中 / 已被认领 | 禁用 |
| 请求进行中 | 旋转图标 +「优化中…」,无法重复触发 |
| 调用成功 | 原地替换草稿(或追加),一次撤销即还原 |
| 调用失败 | 草稿保持原样,按钮旁只显示一行短错误 |
如果请求期间草稿被改动(你继续打字),结果不会覆盖你的编辑,按钮提示
草稿在优化期间被改动,未回填。
配置
Host 与 Client 读同一份 Config schema,默认值直接写在 schema 里,因此
apply(ctx, config) 拿到的就是「用户值或默认值」。在设置里改完立即热替换,无需重启。
设置页
插件在 设置 → 通用设置 → 提示词优化 贡献自己的一行(settings.general.item,id
prompt-optimize,order 5)。这一行直接编辑本插件 Config 的全部六个字段,带 保存 和
恢复默认 按钮,并额外提供:
- Provider / 模型下拉框,选项来自
ctx.remote.session.modelCatalog()——部署已知能连通的 provider 与模型清单。选 provider 之后模型下拉才启用。 - 推理强度下拉框,仅当所选模型声明了可选强度时出现。
- 目录加载失败或尚未就绪时,下拉框自动退化为文本框,可以手填任意 provider/model id。
几个实现细节:
- 这一行通过共享设置镜像(
ctx.configForms)读写,键是本插件的设置命名空间prompt-optimize——也就是cordis.patch.yml里的 Loader 行 id,这是设置文档给 entry 命名的 方式(entry.options.id),不是带前缀的include:…。Host 文档始终是事实来源,所以你手改 profile patch 后,下一次 describe 会在行里同步显示。 - 「恢复默认」是清空字段,而不是把默认值写回去。清空后字段直接读 schema,profile patch 里只留你真正选择过的值。
- 行由
ctx.inject(['slots', 'configForms', 'remote', 'remote.session'], …)注册;万一某个部署 没有 settings 域,还有一条兜底注册,让这一行以「当前无法读取这项配置」的状态出现,而不是 整行消失。 - schema 里三个基础字段带
.volatile(),这不只是热替换通道,还是这一行能存在的前提:settings.describe()只投影 volatile 字段(volatileForm),没有 volatile 字段的 schema 不会 暴露命名空间,那一行就永远不会被服务。apply()随后拿到的是活的{ get() }引用,resolveOptions()在每次点击时读取。 - 值在离开页面前校验:占位符非空且不超过 64 字符,超时是 1000…2147483647 的整数。值非法时 保存按钮禁用,并在按钮下方给出原因。
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
datePlaceholder | string | {{DSH}} | 草稿中提到日期/时间的位置替换成的文本。 |
replaceMode | 'replace' | 'append' | 'replace' | 原地写回;或保留草稿,空一行后追加优化结果。 |
timeoutMs | number(1000…2147483647) | 30000 | 单次模型调用的端到端超时。 |
provider | string | '' | 固定 provider 路由。留空则跟随会话自己的模型。 |
model | string | '' | 固定模型 id。provider 未设时忽略。 |
reasoningEffort | string | '' | 固定路由下的推理强度。留空则跟随路由。 |
provider 与 model 是一对:两个都填才固定路由,两个都空则跟随当前会话(取不到时回退部署
默认)。半填的组合会被设置页拒绝,因为它实际上固定不了任何东西。
插件从不接收 base URL 和 API Key,也从不存储它们:鉴权始终留在 ctx.llm 背后那层 provider
适配器里。如果某个 provider 的目录加载失败,设置页会说明原因,固定路由字段在那里改不了,
但 patch 文件仍接受任何 Host 认的标识符。
也可以在 profile patch 里直接固定:
- id: prompt-optimize
name: dsh-prompt-optimize
config:
datePlaceholder: '[[DATE]]'
replaceMode: append
timeoutMs: 20000
provider: deepseek-account
model: deepseek-flash
优化规则(system prompt)
模型被要求只回优化后的提示词正文——没有前言、引号或代码围栏——并遵守六条硬约束:
- 不添加任何情景假设:不得出现
假设你是…、设想…、在某个场景中…、Imagine…、Suppose…、Pretend you are…、In a scenario where…。只针对提示词本身做结构化、明确化, 不为它编造使用场景。 - 不写具体日期、时间、地点。草稿中每个日期时间位置都替换成
datePlaceholder。 - 保留原草稿的语言。
- 路径、标识符、命令、参数、代码、数字、单位、URL、版本号原样保留。
- 只补充原稿隐含的具体信息,不加别的。
- 只输出提示词正文。
错误与日志
| reason | 按钮旁文案 | 触发条件 |
|---|---|---|
EMPTY_DRAFT | 草稿为空 | 没有可优化的内容 |
NO_MODEL | 没有可用的模型 | 会话无路由且部署无默认路由,或该路由没有适配器 |
TIMEOUT | 模型调用超时 | timeoutMs 用尽 |
EMPTY_RESULT | 模型没有返回内容 | 模型结束但没有文本 |
TRUNCATED | 结果被截断,未回填 | 输出撞到 token 上限 |
TOO_LARGE | 草稿太长,超出模型上限 | 草稿超过 20000 字符或超过模型上下文窗口 |
PROVIDER | 模型调用失败 | 其它上游失败 |
CANCELLED | 优化已取消 | 调用被中止 |
DISCONNECTED | 与后台的连接已断开 | 页面失去 RPC 通道 |
BUSY | 上一次优化还没结束 | 已有调用在飞时再点 |
CHANGED | 草稿在优化期间被改动,未回填 | 调用期间继续打字,结果被丢弃 |
按钮旁的文案保持简短。鼠标悬停显示 Host 返回的诊断事实,不打开日志也能定位 provider 故障:
按会话路由 deepseek-account/deepseek-flash · HTTP 429 · RATE_LIMITED · 842ms
它由这些字段拼成:route(是哪条规则选出来的——config / session / default——以及
provider 和模型)、status(上游 HTTP 状态)、code(模型层自己的错误码)、message、
elapsedMs。失败没带的字段直接省略,不会瞎猜。
调用日志
错误行右侧有一个 详情 / 收起 按钮,点开弹出调用日志面板(最多保留最近 20 条,仅内存, 刷新即清空):
调用日志 [复制]
12:03:41 started request · 42 chars · session abc123
12:03:42 failed model · PROVIDER · HTTP 401 · 会话:deepseek-account/x · 1204ms
12:03:58 ok model · model:deepseek-account/deepseek-flash · 42 → 380 chars
- 每次调用都记录:
stage(request/model/client/guard)、outcome(started/ok/failed/skipped)、reason、code、HTTP、实际路由及其来源、 字数变化、耗时。 - 成功后也记录,方便确认占位符替换和路由是否符合预期。
- 「复制」一键复制全部记录,便于报障。
- 同时向浏览器 Console 输出一条
[prompt-optimize] <outcome> <stage> — <detail>(中文说明「它因何失败」,常见问题是setError覆盖了更具体的信息)。
模型路由
没有任何硬编码。Host 半边每次调用按以下顺序解析路由:
- 显式配置的
provider+model(两个都设)——错误提示里来源显示为config; - 会话待生效的模型选择(
modelSelection投影); - 否则取会话 header 里记录的选择(其中自带的
reasoningEffort只在适配器没有默认值时使用); - 否则
agentDefaultModel.currentSelection()——部署默认模型。
随后调用走 ctx.llm.prepareCall / PreparedLlmCall.stream,因此插件永远看不到 provider 密钥,
并继承部署已有的重试与凭据策略。
文件
| 路径 | 作用 |
|---|---|
lib/index.js | Host 半边:Config、PromptOptimizeService(promptOptimize.optimize Remote)、apply。 |
lib/index.d.ts | Host 半边的纯类型面。 |
lib/client.js | Client 半边:输入区按钮(conversation.input.right)、通用设置行(settings.general.item)、样式、文案、RPC 调用、日志面板。 |
src/index.ts | Host 半边的源码,npm run build(tsc)编译成 lib/index.js。 |
tsconfig.json | src/index.ts 的构建/类型检查配置。 |
cordis.patch.yml | Bundle patch:对本包的一次 insert(不修改任何核心文件)。 |
locale/*.json | 插件管理器里显示的 bundle 标题/描述。 |
构建
npm run typecheck # tsc --noEmit
npm run build # tsc -p tsconfig.json -> lib/index.js + lib/index.d.ts
@deepseek-ai/schemastery 是唯一声明的运行时依赖。@deepseek-ai/cordis 是 dev 依赖:
Host 本来就在跑 Cordis,编译后的 entry 必须解析到应用自己的那份(应用的 node_modules 是本包
的祖先目录),绝不能装第二份。其余 Host 服务(llm、agents、agentDefaultModel、
sessionProjections)全部通过 ctx.get() 获取并在 src/index.ts 里以结构类型描述,所以编译后的
entry 不再 import 别的包。
entry 故意声明 export const inject = []:服务在每次调用时解析,模型路由不可用会变成一条
NO_MODEL 回执。反过来,硬写 inject: ['llm'] 会让端点在等一个只有完整重启才重新评估的依赖。
src/index.ts 刻意只使用 Node 能剥离的语法(没有 enum、namespace、参数属性),并拆分类型导入
(import type { Context } …),因此同一份源码既能被 Node 22 直接 import,也能被编译。
DSH 实际加载的 entry 始终是 tsc 的输出 lib/index.js。
重载行为
Entry.refresh() 在 fiber 已存在时直接返回,所以一次重载会重跑 init() 并再次挂载
apply(),而旧实例仍占着 promptOptimize 这个名字。与其销毁旧实例(它的 provide effect 属于
父 fiber,在 apply 内部释放不了这个名字),apply() 捕获重复注册失败,通过
reconfigure() 把最新的 Config 交给正在运行的服务。浏览器已经持有的端点因此在每次重载后
依然可达,新配置在下一次点击时就生效。
Comments
Loading…
Similar plugins
by xiong356
为 DeepSeek Harness (DSH) 打造的输入框草稿优化插件:点一下 ✦,用当前默认模型把提示词草稿改得更完整、更有效,原地替换、绝不自动发送。
★ 1
MIT
JavaScript
Oct 28, 2026
dsh plugin --profile web add dsh-draft-optimizerby kee0012
DSH 提示词优化插件:输入框工具栏一键 ✨ 优化(直接写回可还原),风格/上下文/还原等设置收纳进 ▾ 弹出层。
★ 2
↓ 356/wk
MIT
JavaScript
Oct 13, 2026
dsh plugin --profile web add dsh-prompt-optimizerby saibei404
DeepSeek Harness 提示词优化插件:输入框旁一键把草稿改写成清晰可执行的提示词(DeepSeek V4.1 Flash)
★ 0
JavaScript
Oct 24, 2026
dsh plugin --profile web add dsh-prompt-polishby xyavid
DeepSeek Harness Web 插件:输入框模型名旁的小星星按钮,一键把草稿改写成结构化的可执行提示词并写回
★ 4
↓ 120/wk
MIT
TypeScript
Oct 15, 2026
dsh plugin --profile web add @xyavid/dsh-prompt-polishby konglong87
dsh 常用内容插件:保存、编辑提示词,点击回填输入框,不自动发送。deepseek harness 插件
★ 2
MIT
JavaScript
Sep 23, 2026
dsh plugin --profile web add dsh-input-listby sojo-negai
DSH 提示词优化插件 —— 发送前点一下 ✨,模型结合上下文给出「补丁式」建议,勾选回填,不抢发送权。
★ 0
↓ 127/wk
MIT
TypeScript
Sep 11, 2026
dsh plugin --profile web add dsh-prompt-refine