🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

人设卡 lyufeng000/dsh-persona-cards

DeepSeek 客户端
lyufeng00015 天前有提交已提供中文文档扫描:无法判定兼容 / 相关生态

按会话选用人设卡,拼进系统提示词即时生效

29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/17 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/lyufeng000/dsh-persona-cards.git

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 15 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
⚪ 扫描:无法判定尚无人工评估
展开查看扫描说明与证据 ↓

已尝试扫描但取不到可分析的源码:仓库或包不存在、已删除,或为非 JS/TS 实现、未随包发布源码。无法判定,不代表安全。

  • · 取源不可用(Error: github tarball HTTP 404 @ https:
  • · api.github.com
  • · repos
  • · lyufeng000
  • · dsh-persona-cards
  • · tarball)
扫描于 2026/11/1(0 天前)

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DSH 人设卡插件:按会话绑定的人设卡片与全局提示词段落,支持导入 .md 人设文件。

README

由 DeepSeek 最新模型翻译生成
dsh-persona-cards —— 人设卡 在 设置 → 人设卡 里管理人设,在每个会话里选用哪一套。选中的那张会拼进系统提示词,排在官方部署人设之后、所有工具说明之前。改完下一个模型步骤就生效,不用重启。 默认不注入任何人设 —— 要用哪张,得在会话里显式选。 另有一段全局设置:不挑会话、不挑人设,写了就永远注入(排在人设之前)。 见下面「全局设置」一节。 功能 - 设置里的独立一页:左侧设置导航新增「人设卡」,与「通用」「模型」「插件」并列。 - 全局设置(与人设卡并列的一块):一段对所有会话都成立的附加正文, 不管会话有没有选人设都会注入,且排在人设之前;留空即不注入。见下面专节。 - 多张卡:新建 / 编辑 / 删除,每张卡有名字、正文、启用开关。 - 卡片态 / 编辑态分离:列表默认是只读卡片(标题 + 正文预览 + 开关),点「编辑」才展开输入框,点「完成」回到卡片态、点「取消」直接丢弃草稿。 - 会话级选择:会话输入框左侧有个「人设」选择器。选中只作用于当前会话,别的会话不受影响;不选就是「不使用人设」。 - 自动保存:编辑完成时立即落盘;开关 / 删除 / 新建立即落盘;全局设置为 700ms 防抖落盘。 - 零污染:没选人设、全局设置也留空时,插件对那个会话的提示词完全不存在 (空段落会被 DSH 自动丢弃)。
展开完整文档(README 剩余 1 节)
dsh-persona-cards —— 人设卡

在 设置 → 人设卡 里管理人设,在每个会话里选用哪一套。选中的那张会拼进系统提示词,排在官方部署人设之后、所有工具说明之前。改完下一个模型步骤就生效,不用重启。

默认不注入任何人设 —— 要用哪张,得在会话里显式选。

另有一段全局设置:不挑会话、不挑人设,写了就永远注入(排在人设之前)。
见下面「全局设置」一节。

功能

- 设置里的独立一页:左侧设置导航新增「人设卡」,与「通用」「模型」「插件」并列。
- 全局设置(与人设卡并列的一块):一段对所有会话都成立的附加正文, 不管会话有没有选人设都会注入,且排在人设之前;留空即不注入。见下面专节。
- 多张卡:新建 / 编辑 / 删除,每张卡有名字、正文、启用开关。
- 卡片态 / 编辑态分离:列表默认是只读卡片(标题 + 正文预览 + 开关),点「编辑」才展开输入框,点「完成」回到卡片态、点「取消」直接丢弃草稿。
- 会话级选择:会话输入框左侧有个「人设」选择器。选中只作用于当前会话,别的会话不受影响;不选就是「不使用人设」。
- 自动保存:编辑完成时立即落盘;开关 / 删除 / 新建立即落盘;全局设置为 700ms 防抖落盘。
- 零污染:没选人设、全局设置也留空时,插件对那个会话的提示词完全不存在 (空段落会被 DSH 自动丢弃)。

全局设置:唯一一段"无条件的"注入

| 项 | 值 |
| 在哪写 | 设置 → 人设卡 → 页面顶部「全局设置」框 |
| 何时注入 | 永远 —— 不挑会话、不挑人设、不挑启用状态 |
| 注入位置 | 官方部署人设(0) 之后、选中的人设卡(100) 之前 → order = 90 |
| 段落名 | persona-cards:global |
| 存哪 | settings.json(与人设卡 cards.json 分开) |
| 空值语义 | 全空白 = 没写 = 整段被 DSH 丢弃,插件对这个会话不产生任何提示词 |

排序上刻意取 90:它是官方 SECTION_ORDERS 里 DEPLOYMENT_PERSONA_PREFIX(0) 与本插件人设段 (100) 之间的空位,不会和任何官方段撞号。语义是「全局总纲 → 具体身份」:对所有会话一致的要求先立,会话选的人设后置覆盖。

为什么单独一段,而不是拼进人设段:两段的生效条件完全不同(人设段"没选就空"、全局段"写了就在")。合成一段就得在渲染时拼字符串,还要额外处理"只有全局没有人设" 的边界;分开之后 DSH 的 renderPrompt() 空段丢弃规则天然把两种情况都处理干净。

为什么单独一个文件:卡片与全局设置的自动保存是各自独立触发的,共用一个文件就意味着每次落盘都写全量,一边的写入会把另一边刚改的内容按旧值覆盖回去。
分开之后两个文件各自「临时文件 + rename」,互不干扰。

{{model}} / {{cwd}} / {{provider}} 的引用规则与人设完全一致:落盘前过同一道
白名单闸门(normalizeBody()),其余引用一律拒绝保存并说明原因。

语义:设置页管仓库,会话管这次用哪张

| 层 | 存哪 | 作用 |
| 卡片 | cards.json | 设置页维护 |
| 启用开关 | 同上 | 只决定这张卡进不进备选池,不替任何会话做决定 |
| 会话 → 卡片 绑定 | session-cards.json | 会话界面的选择器写入 |
| 全局设置 | settings.json | 设置页维护,无条件注入,与会话/人设无关 |
| 注入什么 | —— | 全局设置(若有)+「绑了就用绑的那张;没绑就什么都不注入」 |

这个设计解决了一个实际问题:把几张卡全启用,新会话不会把几个人格叠加成互相打架的身份指令 —— 因为「启用」不等于「生效」。

会话 id 来自段落 text 函数收到的组装上下文 —— @deepseek-ai/dsh-agent 的
assembleContextFor() 返回的是 { agent, scope: agent, signal? },
所以 context.agent.id 就是当前会话。拿不到 agent(或绑的卡被停用/清空/删除)时一律按「不注入」处理,不会抛错、也不会误注入别的卡。

⚠️ 为什么选择器不在「新建会话」界面

试过了,DSH 的插槽体系里没有干净的位置:

1. 新建会话的空状态(hero)里,所有会话级控件都被官方显式跳过 —— dsh-client-ui-conversation 里写死了
input === undefined || sessionId === undefined ? null : renderSlot("conversation.input.left", {})。
新建会话时还没有 sessionId,所以这些槽一律不渲染。
2. hero 区唯一能挂东西的是 conversation.hero.agentPreset,但它是 single 槽且已被官方占用 (replaceRisk: shadows-shipped-ui)—— 用它就得顶掉官方的 agent 预设选择器。

所以选择器放在 composer 工具行左侧(conversation.input.left,list 槽、空着、replaceRisk: none)。
效果是:发出第一条消息后选择器出现,之后随时可切;新会话的第一条消息用设置里的默认规则。

安装

包已链接进 desktop profile。手工装的三步(本机开发用):

1. 包目录 junction 到 <profiles>/node_modules/dsh-persona-cards
2. <profile>/package.json 的 dependencies 加 "dsh-persona-cards": "0.2.0"
3. <profile>/cordis.patch.yml 追加 cordis.patch.yml 里的 insert 块

官方装法:dsh plugin --profile add dsh-persona-cards

⚠️ 改完 Host 代码必须完全重启 DSH

DSH 的 Loader 复用 ESM 模块缓存:禁用/启用插件、改配置文件、新建会话都不会重新 import lib/index.js。
两半的更新方式不一样,会错配:

| 半边 | 什么时候重新读盘 |
| lib/client.js | dsh-client-modules 对这个文件挂了 watch —— 改完刷新页面就是新的 |
| lib/index.js | 只在 DSH 进程启动时加载一次 |

所以「刷新页面」之后很容易出现界面是新的、Host 是旧的:新界面按新协议调 saveGlobal,旧 Host 的 switch 里没有这个 case,回一句 未知接口:saveGlobal,界面上就是「保存失败:未知接口:saveGlobal」。

现在这种错配会被显式认出来(快照里没有 global 字段 = Host 是旧版本),设置页直接给出警告并禁用输入框,不会再让人对着"能打字却存不下去"猜。
修法只有一个:完全退出 DSH 再重新打开。

数据

| 项 | 位置 |
| 卡片文件 | $DSH_HOME/persona-cards/cards.json |
| 全局设置 | $DSH_HOME/persona-cards/settings.json |
| 会话绑定 | $DSH_HOME/persona-cards/session-cards.json |
| 诊断输出 | $DSH_HOME/persona-cards/diag.json(排查注册问题用,可删) |

$DSH_HOME 解析顺序:环境变量 DSH_HOME → ~/.dsh。

默认即 %USERPROFILE%\.dsh\persona-cards\cards.json。同目录下已有的 .md 人设文件不会被读写或修改。

注入位置

两个提示词段落,order 取自 @deepseek-ai/dsh-system-prompt 的 SECTION_ORDERS:

HARNESS_IDENTITY(-1000) → DEPLOYMENT_PERSONA_PREFIX(0) → ★persona-cards:global(90)
→ ★persona-cards:user(100) → PLAN_POLICY(500) → TEAM_POLICY(600) → 各工具说明(1000+)

| 段落名 | order | 内容 |
| persona-cards:global | 90 | 全局设置,非空就注入,与上下文无关 |
| persona-cards:user | 100 | 当前会话选中的那张人设卡,没选就是空串(整段被丢弃) |

DSH 0.2.0 兼容性(重要)

DSH 从 0.1.7-rc.2 升到 0.2.0-rc.1 后,cordis 收紧了 ctx 代理: 没声明 inject 的服务属性访问会直接抛错 —— cannot get property "X" without inject(cordis/lib/index.js 的 ReflectService.handler.get)。

connection.rpc.handle() 内部是 register(owner = this.ctx, …) →
owner.webServer.register(route),而 this.ctx 解析成调用方 fiber 的 ctx
(官方注释明写:"channel registrations belong to the caller fiber")。
本插件刻意不声明 inject(声明会把 apply 推迟,见 whenService 注释),于是官方通道整条挂掉,表现就是设置页报:

读取失败:transport failure for /persona-cards/state: HTTP 405

(405 = 请求落到了 fallback,说明路由压根没注册。)

修法:官方通道优先,失败自动降级。

降级路径用 ctx.get('webServer') —— 官方给的服务可选访问形式,不受 inject 约束 —— 自己挂 {kind:'prefix'} 路由,信任校验复用 connection.admit(req):它是实例方法,只读 trustedHosts / browserAuth / operator 三个实例属性,完全不碰 ctx。

已在真实运行环境实测过(建了个独立探针包,新包名没有 ESM 缓存所以当场加载):

| 检查项 | 结果 |
| ctx.get('webServer') | object |
| webServer.register | function |
| ctx.get('connection') / .admit | object / function |
| 路由注册 | 成功 |
| 未认证请求打到该路由 | 401(不是 404/405),且确实进了处理器 |

401 是预期的:命令行的裸请求没有浏览器认证 cookie,被信任栅栏拦下;页面内发起的同源请求会带 cookie,正常通过。

两个保命设计(别改)

1. {{...}} 闸门。DSH 的 renderPrompt() 会对每个段落做变量插值,而
interpolate() 在三种情况下直接抛异常:引用了没注册的名字
(unknown prompt variable)、引用了已注册但本次组装没有值的变量、以及写法不合法(malformed prompt variable reference)。用户在人设里顺手写个
{{随手写的}} 就能把整个会话搞崩。

做法是开启插值 + 落盘前白名单校验:
- 开启插值,让 DSH 官方注册的变量真正替换成当前值;
- 保存时用 scanVariableReferences() 扫正文(顺序逐字复刻官方
interpolate(),包括「没有配对的单个 {{ 当普通文本放行」这条),
只放行 {{model}} / {{cwd}} / {{provider}},其余引用拒绝保存并说明原因。

全库扫过 DSH 的 289 个包:官方一共只注册了这三个变量(都在 dsh-agent-loop,取值形如 context.agent?.options.model),而且官方自己没有任何内置段落 引用它们 —— 这三个就是留给插件/组合层用的。

2. 不能用 deployment:persona-prefix 这个段名。它归 @deepseek-ai/dsh-persona 所有,而官方注释明写那个插件是 scope-only 的:全局挂载会与注册表自身的注册相撞并 fail loud。所以本插件用自己的段名。

实现要点

dsh-persona-cards/
├── package.json # dsh.bundle.patch + dsh.client(平台 web) ├── cordis.patch.yml # 挂载声明 └── lib/ ├── index.js # Host:持久化(卡片 / 绑定 / 全局设置)+ 两个提示词段落 + RPC 通道 └── client.js # Client:设置页 UI + 会话界面的人设选择器

Host ↔ Client 通信走 ctx.connection.rpc.handle(channel, handler)。
通道内部已经过 admit(req)(Host/Origin 信任栅栏 + 浏览器鉴权),被拒即 401/403 —— 所以本插件不需要自己写路由守卫,这也是没走
ctx.webServer.register() 野路子的原因。

客户端侧对应 ctx.connection.rpc.call(channel, endpoint, payload),返回 {ok:true,value} / {ok:false,error},业务失败不抛异常,必须自己解包。

service 获取刻意不用依赖注入:apply 用 whenService() 轮询取 systemPrompt / connection。本机实测踩过一次 ctx.inject([...], cb) 静默挂起(fiber 显示 active,但回调始终没跑,RPC 通道就这么没了)。

dsh.client.inject 填的是包名,不是 cordis 服务名(package.json 里):

"client": {
"platform": "web",
"inject": [
"@deepseek-ai/dsh-client-ui-settings",
"@deepseek-ai/dsh-client-locale",
"@deepseek-ai/dsh-client-connection"
]
}

它是客户端插件之间的加载顺序依赖,让提供方先于消费方加载;cordis 服务依赖写在 client.js 的 exports.inject 里(['slots','locale','connection'])。
这里一开始写成了服务名,虽然被宽容处理也能跑,但不合规范 —— 对齐参照物是官方 @deepseek-ai/dsh-client-ui-settings-general(同样注册独立设置页,用的正是这三个包)。

这个声明只在客户端启动时解析一次:改完它必须重启客户端,改 client.js 都不够。

已知限制

- 正文上限 100000 字符、单卡名 100 字符、最多 50 张卡;超限拒绝保存并给出原因,不做静默截断。全局设置共用这个正文上限。
- 卡片是全局的,不分会话/工作区;全局设置同样只有一段,不能按会话覆盖(要按会话区分就用人设卡)。
- 没有排序、导入导出、md 文件导入。
预览分享卡片长什么样 ↓lyufeng000/dsh-persona-cards
分享 · DeepSeek Harness Hub
lyufeng000/dsh-persona-cards

按会话选用人设卡,拼进系统提示词即时生效

回到顶部