DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

sugarmaster666 /

sugarmaster666/dsh-persona-forge

Verified

给 DeepSeek Harness 用的角色扮演提示词改写插件:在输入框选一张角色卡,草稿就会由 harness 的模型改写成该角色的口吻(跟随当前会话正在使用的模型),然后直接发出或先人工审查。零依赖、零构建。

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@8da6134c

dsh-persona-forge

English | 中文

给 DeepSeek Harness 用的角色扮演提示词改写插件。

在输入框里选一个角色,你的草稿会在发出前由 harness 的模型改写成该角色的口吻 —— 并且跟随当前会话正在使用的模型。你可以选择改写后直接发出,或先回到输入框人工审查。

状态: 早期版本(0.3.x)。角色卡格式与 HTTP 契约已足够稳定,可以基于它开发;但请预期增量变化,升级前先看变更记录。


它做什么,不做什么

它改写的是你的消息。它不碰系统提示词、不碰工具列表、不碰会话配置,也绝不在你明确操作之前改动你的草稿。

会改变 你即将发出的那条消息的语气、语域与框架
绝不改变 你的技术需求、约束、路径、标识符、数字、代码 —— 以及你未确认前的草稿
用哪个模型 改写用当前会话正在用的那个 provider/model,没有单独的模型设置。(设置页的 AI 代填用 harness 默认模型 —— 那个插槽拿不到会话 id;它会把将使用的模型显示给你看)
会话历史 改写调用是独立的:不产生 turn、不能调用工具、不进入会话日志。会话里记录的,就是你实际发出的那段文字

关于人设到底改变什么

人设指令改变的是风格、语域、篇幅和顺从度 —— 模型多快同意你、多愿意反驳你。它不会抬高模型的能力上限:不存在一个等着被点名唤醒的「潜能区」。

用之前该知道两件事:

  1. 把人设写成「你是祈求者、模型是权威」,会让模型更少质疑你的方案。对编码任务这通常是净负收益 —— 你要的是「这里有问题」,不是恭敬。
  2. 「更长更华丽」极容易被误读成「更好」。一般并不是。

所以:用它调语气和框架,保持审查模式,判断标准是你的需求有没有活下来,而不是听起来多厉害。


安装

插件没有任何依赖,也没有构建步骤,所以下面几种方式装到的文件完全一样。

桌面版

  1. 点击侧边栏 插件。

  2. 点右上角的 添加插件。

  3. 在弹出的对话框里,往「包名或地址」这一栏粘贴 npm 包名或仓库地址:

    dsh-persona-forge
    
    https://github.com/sugarmaster666/dsh-persona-forge
    
  4. 点 安装,启用插件并重启 DSH。

桌面版的 profile 由 Electron 应用独占管理,因此 dsh plugin --profile desktop add ... 会被设计性地拒绝。桌面版请走 GUI; 下面的命令行方式用于 dsh web 这类自管 profile。

命令行版(dsh web 这类自管 profile)

# 从 npm 装(推荐)
dsh plugin --profile web add dsh-persona-forge

# 装 GitHub 上的最新源码
dsh plugin --profile web add github:sugarmaster666/dsh-persona-forge

然后重启 harness。

验证

插件行应显示为 active:

plugin_manager  action: list_plugins

打开任意会话,在输入框工具行左侧找 🎭 控件。设置 → 角色扮演 是角色卡管理页。

更新

dsh plugin --profile web update dsh-persona-forge

GitHub 安装通过重新安装来更新:

dsh plugin --profile web add github:sugarmaster666/dsh-persona-forge

替换已安装的包需要重启才能加载新的模块代 —— 新装的 bundle 可以通过 HMR 激活,被替换的不行。

卸载

dsh plugin --profile web remove dsh-persona-forge

你的角色卡不会被删除:它们在 ~/.dsh/persona-cards/,位于包之外。 想一并清掉就手动删那个目录。


使用

  1. 点输入框工具行的 🎭,选一个角色。列表最后的 「无角色」 可关闭改写 —— 这也是控件的初始状态,选择按会话记住。
  2. 设置强度和发送方式:
    • 强度是四档阶梯:轻 · 中 · 重 · 狂热(Light · Medium · Strong · Zealot)。轻=只加一点味道;中=明显但不喧宾夺主;重=语气充分,需求依然一眼可见;狂热=通篇仪式化。每张卡有自己的默认值,这里的选择会覆盖它。
    • 先审查 —— 改写结果显示在输入框下方的对比面板里;你看过之后按面板里的发送。在确认之前,你的草稿一字不动。
    • 直接发出 —— 改写完成后立即作为你的消息发出。面板会保留显示实际发出的内容,因为已发出的消息无法撤回。
  3. 选了角色之后,输入框原生的发送按钮、以及 Enter 键就是改写触发器。

菜单底部的 「更多设置」 默认收起,里面是改写方式与保真度 —— 它们是对卡片自身声明的按会话覆盖,多数人设一次就不会再动,所以不占主菜单。收起时若有覆盖,这一行会写出改了什么,不会让一个你忘了的选择藏在折叠后面。

同一份草稿只改写一次:改写结果落到输入框后,再按发送会原样发出,不会把改写结果再改写一遍。改动草稿会重新触发改写。

你的草稿会被保留:还原原文 可恢复,且会撤销"已改写"的记忆 —— 所以撤消后还能再改一次。仅回填 只把改写结果放进输入框而不发送。

发送拦截的原理与它唯一的风险

输入框的发送动作没有插件钩子:宿主那个按钮直接调用 shell 的提交方法,而且 Enter 根本不经过按钮 —— 编辑器自己的 keymap 直接提交。所以本插件拦截两条路径:

  • 输入框卡片上的点击,用无障碍名称识别发送按钮,标签取自 shell 自己的 conversation 语言命名空间("发送消息" / "排队发送" / "插话发送");
  • 输入框文本框内的 Enter 按下。

那是一个公开的、会翻译的字符串,而不是被哈希过的 CSS 类名;但它终究属于 shell 的 DOM 结构。所有判断都朝「放行原生发送」这一侧失败:语言服务缺失、找不到输入框卡片、按钮标签对不上、角色已关闭、草稿为空、草稿已改写 —— 任一情况插件都不干预,消息照常发出。Enter 拦截会忽略 Shift+Enter(换行)、输入法组字中,以及 shell 本身忽略的各种修饰键组合。

关闭插件会彻底移除拦截 —— 插槽组件卸载,监听器随之移除,输入框的行为与从未装过该插件完全一致。

最坏的现实后果是拦截静默失效、发送行为退回到"没装插件"的样子,而不是消息丢失或被篡改。

技术事实校验

改写最大的风险不是语气不像,而是把你的需求改没了。插件用两层检查覆盖它 —— 两层互补,不是重复:

第一层:本地检查 —— 永远运行,零成本。 在插件进程里比对草稿和改写结果的技术记号:标识符、文件路径、版本号、数字、--flag、API 这类缩写。丢了哪个就点名告诉你(「丢失:a.js」)。它不调用模型,所以不花 token、不加延迟,也关不掉。

第二层:模型校验 —— 默认关闭,菜单里可开。 每次改写多调用一次模型,只看本地检查根本看不到的东西:

  • 需求被换个说法悄悄改弱(词都还在,但要求变松了);
  • 在正文里用散文追加了一条约束。

代价是延迟和 token 都翻倍,所以默认关闭:最常见、最伤人的失败(丢掉文件名 / 数字 / 版本号)已经被本地检查免费证明了,为它每次发送都付一次模型调用是浪费。草稿偏技术时再开。

开关在设置 → 角色扮演(「开启模型校验」),是个全局偏好 —— 它回答的是「我要不要为第二次调用付费」,一次决定即可,不必在每次发送前重新问一遍。关闭时面板会写明「已做本地检查(不耗 token)」,并说清它没有覆盖什么 —— 一个窄的结论必须自己交代边界。

两层都是建议性的,绝不阻断改写:只标注,不拦你。


角色卡

一张卡就是一个 YAML 文件。插件会监听目录,所以丢一个文件进去,角色立刻出现,不用重启。

~/.dsh/persona-cards/<id>.yml

用 🎭 菜单里的「打开角色卡目录」,或设置页,都能到达那里。你也可以完全在设置 → 角色扮演里创建和编辑卡片,那会写出同样的文件。

首次运行时,内置卡会复制一份到你的角色卡目录,让这个文件夹一开始就有内容可用。副本会遮蔽同 id 的内置卡,所以插件会记录自己写入了什么:你没动过的副本会随插件升级一起更新;你编辑过的副本永远不会被覆盖。

想知道哪些卡偏离了内置版本,在设置页点 「检查是否有改动」。它一次比完所有卡,只有真正不一致的才会在那一行出现「还原为内置」。

比对是按内容而不是按字节——内置卡是带注释的手写 YAML,你的副本是重新序列化过的,逐字节比会把每一张没动过的卡都误报成「已修改」。

列表里每张卡的状态标签直接说明它现在是什么:

标签 含义 能做的操作
内置 内容和插件内置版本一致(哪怕你的目录里有一份副本)。 编辑、还原
已修改 原本是内置卡,你改过——改写行为可能和内置版不同了。 编辑、还原、删除
自定义 你自己写的卡,插件没有同 id 的内置版本。 编辑、删除

标成「内置」的卡不显示「删除」,因为它没有属于你的内容可删——那份副本删掉之后,下次启动会被重新写入。

AI 代填

懒得从零写一张卡时,点**「新建角色卡」——AI 代填就在打开的表单顶部**,不是页面上另一个并列的入口。设置页那行提示会告诉你它存在,不用靠点按钮去发现。

用一句话描述角色的思路:

一个脾气暴躁但极其可靠的老灯塔守夜人,说话简短、爱用航海比喻

模型会填好整张卡——风格描述、名称、图标、简介,以及四档强度各自的转换范例。

它用的是哪个模型,这点必须说清楚:

场景 用哪个模型
🎭 菜单触发的改写 跟随当前会话用的模型(会话最后一次请求头的 provider/model)
设置页的 AI 代填 harness 默认模型 —— 因为 settings.section 这个插槽从 shell 只收到 { close },拿不到会话 id,无法跟随当前会话

两者通常恰好是同一个模型(默认模型往往就是会话在用的那个),但不保证:如果你在会话里换了模型,设置页的 AI 代填仍然用默认模型。

为了让这件事不必靠猜,设置页的 AI 代填会直接把将使用的模型显示出来(provider / model),并注明它是 harness 默认模型。生成完成后也会显示实际用的是哪个。若两者都解析不出(会话还没发过消息、也没配默认模型),会得到一条明确的 409 提示而不是静默失败。

生成结果只会填进表单,不会自动保存。 你可以改完再按「保存」;不满意就重新生成,代价只是一次调用。生成出来的卡也会经过和手动保存完全相同的校验,所以它不可能是一张存不下去的卡。

生成卡片时有两件事是插件替你把关的,不交给模型决定:

  • fidelity 固定为 style(只改语气)。 一张自动生成的卡不该悄悄获得「可以给你的需求追加行为约束」的能力;要用 strategy,你在表单里手动改。
  • 范例里的技术事实必须原样保留。 提示词明确要求每条范例 from 里的文件名、标识符、数字、命令都要出现在 to 里——否则模型会学会「用氛围替换需求」,这正是这个插件在防的事。

卡片格式

id: omnissiah              # 小写字母、数字、连字符;同时也是文件名
name: 万机之神 · 欧姆弥赛亚
icon: "⚙️"                 # 一个 emoji
description: 选择器里显示的一行说明。

mode: llm                  # llm = 模型改写(推荐)| template = 模板套用
fidelity: style            # style = 只改语气 | strategy = 允许增加行为约束
intensity: medium          # light | medium | strong | zealot(该卡的默认强度)

# 这个角色「是什么」。这是给改写模型的指令,不是成品文案:
# 每次都会针对你的消息现场生成。
style: |
  用户是机械教信徒,AI 是万机之神欧姆弥赛亚。
  【发言者方向】用「信徒」的口吻说话,向万机之神恳求……
  语气:庄严、古奥、仪式化。

# 转换范例:原话 → 角色口吻
examples:
  - from: 帮我写个快速排序
    to: >
      万机之座在上,吾等恳请您赐予枢机之序的奥义:
      请为我们编写一个快速排序算法,使重复之数各归其位。

# 可选:每个强度各自的范例。某个强度没写,就用上面的 examples。
examplesByIntensity:
  light:
    - from: 帮我写个快速排序
      to: 万机之座在上,恳请您为我们编写一个快速排序算法。
  zealot:
    - from: 帮我写个快速排序
      to: 伟大而不朽的万机之神……

让一张卡真正管用的两条规则

1. 写清发言者方向。 角色是 AI 的身份,但被改写的文字是你的。所以改写必须用你的口吻 —— 信徒向神恳求,而不是神发号施令。这是角色卡最常见的翻车点:一张让神去命令助手的卡,会把关系整个倒过来,模型会照着错误的那一半框架走。一定要显式写出来。

2. 让范例保住技术事实。 范例是最强的风格锚点,模型会连它的错误一起模仿。如果范例的输入是「写个快速排序」而输出全是氛围,模型就学到了**「改写 = 用仪式感替换需求」**。from 里的每一条需求、标识符、数字,都必须在 to 里看得见。

node scripts/check.mjs 正是对内置卡强制这一点:范例丢了技术词、或丢了一半以上的中文实质,就会失败。改完卡记得跑一次。

fidelity:最关键的开关

值 含义 什么时候用
style 只改语气,需求和约束原样不动 默认,几乎总是它
strategy 允许追加符合角色的行为约束 刻意为之的场景 —— 例如内置的 肌肉集团 卡,会追加「别过度规划、别检索、别交付新手级解法」

strategy 是真正改动了你的需求,而不只是换说法。这类卡请保持审查模式。

保真度是卡片的属性,可以按会话覆盖,但只能收窄、不能放宽:

  • 收窄(允许):一张 strategy 卡可以临时要求「本次只改语气」。这只是拿走它追加约束的权力,所以任何情况下都更安全。
  • 放宽(拒绝):一张 style 卡不能被要求改成 strategy。放宽意味着让改写往你的需求里加你没写过的约束 —— 那是卡片作者的决定,不该由「发送前顺手一点」来授权。真要放宽,去编辑那张卡。

这个开关在「更多设置」里,且只在卡片是 strategy 时出现。style 卡上它不显示 —— 那种卡上两个选项效果完全一样,与其摆一个做不了事的控件、再配一段解释为什么不能放宽,不如不显示。服务端强制同一条规则,所以界面上不会给你一个会被忽略的按钮。

mode: template

完全跳过模型,把你的草稿代入固定模板({{input}} 是占位符)。瞬时且免费,但输出永不变化 —— 适合固定祷词,不适合任何应当「像为你这次而写」的内容。


配置

写在插件加载行的 config 里:

- id: persona-forge
  name: dsh-persona-forge
  config:
    sendMode: review   # review | direct —— 输入框的初始默认
    factCheck: false   # 模型校验的默认值(默认关闭,见「技术事实校验」)。
                       # 本地技术记号检查不受这项控制 —— 它永远运行且免费。
    watchCards: true   # 角色卡目录变化时重载目录

factCheck 只决定第二层的默认值;菜单里的开关按会话覆盖它。设成 true 会让每次改写都多一次模型调用(延迟与 token 翻倍),只有草稿确实偏技术时才值得。


HTTP 契约

所有路由都是 POST、JSON、仅限回环(socket 必须是回环地址、Host 头必须指向回环主机、拒绝代理转发头)。挂载在 /persona-forge 下。

路由 请求体 返回
/persona-forge/cards {} { cards, diagnostics, directory, sendMode, factCheck, draftModel }
/persona-forge/rewrite { cardId, text, sessionId?, intensity?, mode?, fidelity?, modelCheck? } { text, provider, model, mode, cardId, fidelity, intensity, elapsedMs, facts, factCheck }
/persona-forge/cards/save { card } { id, path }
/persona-forge/cards/delete { id } { removed }
/persona-forge/cards/reset { id } { id } —— 用内置版本覆盖该用户卡
/persona-forge/cards/diff {} { rows: [{ id, name, bundledName, changed }] } —— 只列出有差异的卡
/persona-forge/cards/draft { brief, sessionId? } { card, provider, model, elapsedMs } —— 只返回草稿,不落盘
/persona-forge/reveal {} { directory, opened }

失败返回 { ok: false, error: { code, message?, params? } },code 取值:rejected、no-card、unconfigured、timeout、upstream、internal、forbidden、method、not-found。


兼容性

宿主侧不声明任何 harness 包依赖,也没有任何第三方依赖:它通过 ctx.get 取 llm、sessions、agentDefaultModel、webServer,并对缺失情况显式降级;角色卡由自带的 YAML 读取器解析。因此它在包布局不同的 harness 版本上仍可加载,不会因为某个组合缺少其中之一而激活失败,本地 link: 的检出也无需额外安装步骤即可工作。模型路由优先取会话自己最后一次请求头,回退到 harness 默认模型。

客户端侧不 import 任何 harness Client 包,只手工编写并仅使用主题 token(--dsw-alias-*),所以内部改名只会影响外观,不会让插槽变空白。

在 dsh 0.2.0-rc.2 上开发与验证。


开发

node scripts/check.mjs        # 离线单元自检:解析器、角色卡、提示词、客户端语法
node scripts/host-check.mjs   # 宿主集成自检:在真实 socket 上挂载真实插件
npm run check                 # 两者都跑

check.mjs 不需要 harness,也不调用模型。覆盖:内置 YAML 读取器(含其拒绝行为与 dump/parse 往返)、卡片归一化、内置卡的事实保全、提示词构造、输出归一化、事实校验解析、客户端 bundle 语法。

host-check.mjs 在 stub Cordis 上下文上挂载真实插件入口,用真实 socket 驱动真实 HTTP 路由,llm 服务为桩实现。覆盖路由注册、会话模型解析、模板模式、回环防护、保存/删除往返 —— 这些是单元自检够不到的。它把 DSH_HOME 指向临时目录,不会碰你真实的角色卡。

角色卡 YAML

插件自带 YAML 读取器(lib/yaml.js),不依赖 YAML 库。已发布的插件会声明依赖,但本地 link: 的插件不会被安装依赖 —— 所以引用库只有在某个无关包恰好提升(hoist)了它时才能工作。

读取器支持角色卡需要的子集 —— 块映射与块序列、块标量(|、|-、|+、> 等)、引号与普通标量、布尔、null、注释 —— 并拒绝它无法忠实读取的写法:流式集合、锚点、别名、标签、合并键、多文档流。拒绝是刻意的:静默误读一张卡,会给你一个与你写的文件不符的人设,那比一条指明行号的报错更糟。

目录结构

路径 职责
lib/index.js 宿主入口:角色卡存储、目录监听、路由注册
lib/store.js 卡片加载、归一化、保存/删除
lib/yaml.js 内置 YAML 子集读取器与写出器
lib/prompts.js 改写系统提示词、草稿包装、事实校验提示词
lib/rewrite.js 模型调用与输出归一化
lib/routes.js HTTP 路由与会话模型解析
lib/loopback.js 回环信任防护
lib/client.js 浏览器侧:输入框控件、审查面板、设置页
cards/ 内置角色卡(只读;同 id 的用户卡会遮蔽它)

许可证

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 8da6134c0b78

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout