dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
人设引擎(DeepSeek Harness):一份 config.json 管住 AI 的自称(可按具体模型指定,未命中回落 flash / pro 两档)、对你的称呼、关系立场、性格正文、逐条可勾选的工作契约、思维链语言、形象与回复语气(两段均 opt-in、均可按模型覆盖),以及由人工确认闸门把关的长期记忆——AI 只能追加 status:"proposed" 候选,人执行一行 confirm 才转正,日志物理只追加、按行序重放。已确认条目以「数据而非指令」的引号块注入;带 kind 的条目改为在确认那一刻按路由追加进指定文件、永不进提示词。三段提示词每次装配都从磁盘重新求值,改配置下一步生效、不用重启。人设预设是可分享的 JSON 快照(导入导出酒馆角色卡:v2/v3 可读、v2 可写),且刻意不携带记忆。零运行时依赖、不联网、不执行命令;任何异常降级为空段,最坏是「没有人设」而不是会话起不来。出厂空白——不写配置就什么都不改变。
简体中文 | English
把 AI 编码助手的人设变成一份可开关、可编辑、可记忆的配置:自称(按模型分档)、对用户的称呼、
关系立场、性格正文、逐条可勾选的工作契约、思维链语言,以及带代码级确认闸门的长期记忆。
一份私有 config.json + 一个收件箱文件,可接入 DeepSeek Harness、Codex 与 ZCode;每条记忆线可使用独立配置。
0.12.0 起还带一层条件反射:自己写规则,命中时由插件在代码层注入一步指令(匹配不花 token),
可选把这一步的请求瘦身、把工具裁到白名单 —— 规则是个人资产,默认零规则。
维护状态(2026-10-16 更新):主宿主是 DeepSeek Harness;ZCode 适配器已解冻 —— 2026-09-19 曾因 ZCode 快照用户工作区而停止开发(原话:ZCode 已经决定不再制作了),2026-10-16 维护者决定恢复:已按 ZCode 3.15.3 的插件规范把清单对齐;真机回归待做(在本机 ZCode 里装一次、发一轮,见安装段 ②)。 这一侧仍只做
UserPromptSubmithook 注入 + 一个管理技能,0.10.0 起的预设库 / 酒馆卡 /dsh.bundle与它无关。完整取舍与改判记录见 docs/维护状态.md。
MIT · 纯 ESM · 零运行时依赖 · 不联网 · 异常一律降级为空(最坏是"没有人设",不炸会话)。
English: a persona engine for AI coding harnesses — the English guide (what it is, how to install, what each feature does, and which documents are authoritative) is README.en.md.
0.18.0:Codex 连续性与记忆证据。 新增开场读取、本地 CLI 兜底、四个只读 MCP 工具、来源标签与适用条件、自检和内容指纹索引。待确认的记忆替代会保留旧条目,确认后才换版。
Codex 用户从这里开始(Node ≥ 20;零依赖;无需 API 密钥):
git clone https://github.com/shenA2024/whale-persona.git
cd whale-persona
node scripts/install-codex.mjs --dry-run
node scripts/install-codex.mjs
填写安装命令报告的私有配置,然后重启 Codex、新开会话。已有配置加 --config <绝对路径>,原文件不会被改写。安装默认空白,不预设任何人的身份或关系。完整安装、故障恢复与限制见 Codex 接入;本次吸收的上游机制见 灵枢研究记录。安装只建立加载链路,不能承诺模型在所有宿主和会话中绝不遗忘。
你和编码 AI 之间那些每次都要重说一遍的事:它老忘、你得反复交代;同一件活今天一个风格明天一个风格; 「别问我还要不要继续」这句话说过一百遍它还是问。
whale-persona 把这些人设事实变成一份你自己的配置,每个会话自动带上。装完是空白的、装上不改变任何行为—— 要什么写什么。而它和别人不一样的一点:记忆必须你确认才生效(代码强制,不是提示词约束), AI 只能提议,改不了你的准则。
先看到效果,再决定装不装(一条命令,只读,不碰你的任何配置):
node scripts/render-preview.mjs --config examples/demo-config.json --cwd D:/work/demo
它把「此刻实际注入系统提示词的三段文本」逐字打印出来——这就是装完你能得到的东西。
想直接上手:往下跳到 60 秒上手。第三方要实现同样的格式:看 SPEC.md。
| 能力 | 说明 |
|---|---|
| 自称 / 称呼 | {selfName} {userName} 占位符;自称可按具体模型指定(selfNameByModel),未命中回落 flash / pro 两档 |
| 立场与性格 | stance(一句话)与 character(整段正文),渲染在提示词最前面 |
形象(appearance) | opt-in、默认关:把「你是谁/长什么样」当既定事实注入(text 通用 + byModel 按模型覆盖,键写宿主真实模型 id);渲染在立场正文之后、工作契约之前 |
| 形象卡(0.17.0) | appearance.cards:给自己 / 用户本人 / 第三方各存一张卡(标题 + 一行摘要 + 长文 + 照片路径)。默认「本人卡常驻一行摘要、长文与照片按 id 去读」——摘要进提示词、正文留在配置里;每张卡用 auto / expand / on 单独开关。图片永不进提示词(只给路径) |
回复语气(tone) | opt-in、默认关:只改措辞与节奏,不改结论、证据标准与工作契约;结构与形象相同,现成文案见 core/presets.js |
| 工作契约 | 逐条可勾选,on:false 即停用;写得具体可验证才有效 |
| 思维链语言 | 只改思考语言,不改答复语言(off / zh-CN / en …) |
| 长期记忆 | 手工条目(权威层)+ 收件箱(AI 提议 → 人确认 → 只追加式入库) |
| 分类路由(0.13.0) | 同一道确认闸门推广到记忆之外:条目标 kind(pitfall / idea …),人确认那一刻按 memory.sinks 路由只追加式落进目标文件;不注入提示词、幂等、无路由会报出来;默认空表 = 零行为改变 |
| 注入体积(0.13.0) | 计量每段字符数与合计(走与渲染同一条通路),面板与 CLI 可见;budget 超限时可选注入一行提醒 —— 只提醒,永不自动裁剪 |
| 条件反射(reflex) | 自己写规则,命中即在代码层注入一步指令(正则 / 近似 / 词袋三通道);匹配不花 token,可选把这一步的请求瘦身、把工具裁到白名单;默认零规则 |
| 两个编辑入口 | DSH「设置 → 人设」可编辑面板 + 本仓自带本地编辑器页;同一套读写纪律 |
| 零行为改变 | 不写配置 = 三段全空,装上不改变任何行为(有单测钉住) |
| 默认安全 | 不联网、不执行命令、不读你的工作目录;写面逐项登记在 .github/SECURITY.md,并由安全探针 S15 与代码同步(新增写盘文件不登记 = 安全套件红) |
本仓是引擎:出厂空白、零观点、默认不改变行为(有单测钉死)。 想要"拿来就用"的起点——立场、身份正文、逐条可勾选的纪律——去关联仓:
whale-persona-presets —— 16 张预设卡 (通用起步 / 证据优先 / 讲到你懂 / 去水改稿 / 严格评审 / 需求澄清 / 排障 / 重构 / 看数 / 产品评审 / 陪练 / 资料整理 / 中译英 / 安全审查 / 提示词工程 / 多智能体分工)。

(上图由该仓的卡文件本身渲染:标题、描述、tags、契约逐条读出,没有手写内容。截图时该仓共 20 个卡文件。)
导入通道就是本仓已有的那条,不需要装新东西:
node scripts/presets.mjs import starter-plus.json # 下载的卡直接吃
node scripts/presets.mjs list # 看有哪些
node scripts/presets.mjs apply starter-plus # 应用(应用前自动存 autosave,可切回)
两仓的分工是刻意的:内容更新频繁、质量参差、还可能夹带第三方文本,而引擎要的是"出厂空白 + 零行为改变"
可被机器钉死(npm test 与 npm run sec)。混在一起,两边都会被拖坏。内容仓的每张卡都要过
它自己的 check.mjs(结构 + 用真引擎导入一遍 + 隐私门禁)。
① 装(DSH) —— 一条命令:装插件 + 建预设 + 设默认 + 拷技能 + 自检
npx -y whale-persona # 不需要克隆仓库;装完重启 DSH
要求:Node ≥ 20、DSH ≥ 0.1.6-alpha.1,装完重启 DSH。
不想用 npx? 克隆仓库跑同一个脚本,效果一模一样:
git clone https://github.com/shenA2024/whale-persona.git
cd whale-persona
node scripts/install-dsh.mjs --dry-run # 先看它要做什么
node scripts/install-dsh.mjs # 真装
两样都不行? 用 Release 附带的 tarball(与 npm pack 出来的完全同一份文件):
dsh plugin --profile web add -w https://github.com/shenA2024/whale-persona/releases/download/v0.15.1/whale-persona-0.15.1.tgz
干净 DSH_HOME 实测:3.2 秒把包装上,不需要 git、不需要 npm 账号、不需要改 pnpm 配置。
⚠️ 三条通道的区别:市场 / tarball 那条是"分两半"装的。 npx 与克隆走的是同一个安装脚本,它一次就做完
装包 + 建 agent preset + 挂设置面板 + 拷技能 + 自检;插件市场(以及 tarball、dsh plugin add)只把包挂进
profile 平面 —— 结果是包在、设置面板在,但 agent preset 还没建(人设本体只认预设平面,
而官方市场通道不会替我们建它)。
0.17.3 起不用再跑脚本:重启后进 设置 → 人设 → 「Agent 预设」卡片点一下「建人设预设」 (卡片会直接写明「缺这一步」;要的话勾上「同时设为新任务默认」),然后新开会话。 想用命令行也行:
node "$env:USERPROFILE\.dsh\profiles\web\node_modules\whale-persona\scripts\install-dsh.mjs"
三条通道都会把设置面板自动挂成 profile 层(0.11.1 起本包声明了 dsh.bundle.patch,宿主自动把它加进
dsh.profile.bundles);安装脚本还会先算清这条行的归属 —— bundle 已经挂了就一条都不插
(见下面「装不上?」表里那条 duplicate loader entry id)。
人设本体故意不自动挂:它要挂在 agent preset 平面才是"鲸鱼模式专属"(挂 profile 层会对所有模式生效)。
然后重启 DSH。脚本会跟随你当前的默认 preset 建一份名为 whale-persona 的预设(不覆盖你原来那份),并提示你之后怎么切过去。
装完是空白的,这是刻意的 —— 出厂零观点、装上不改变任何行为(有单测钉死)。真正必填的只有一段正文
(persona.character):自称默认「我」、称呼默认「用户」,立场 / 语气 / 形象 / 工作契约全可选、默认关。
两种起步法任选:
node scripts/presets.mjs import <卡.json>;也吃酒馆 v2 角色卡);懒得自己动手? 把下面这句连同仓库地址丢给你的 AI:
读 https://github.com/shenA2024/whale-persona 的 README,按「60 秒上手」把 whale-persona 装到我的 DSH 上 (要求 Node ≥ 20、DSH ≥ 0.1.6-alpha.1),装完告诉我怎么重启与验证。
装不上?先对两处已知坑(报错原文照抄,可直接搜):
| 报错原文 | 原因 | 修法 |
|---|---|---|
Failed to connect to github.com:443 / Connection was reset | 你的网络连不上 github.com(国内常见) | git 配代理:git config --global http.proxy socks5h://127.0.0.1:<你的代理端口>;或直接用上面的 tarball 通道 |
git-hosted plugins build on install via their prepare script, which pnpm blocks until allowed — add the exact key pnpm printed above under allowBuilds in <profile>/pnpm-workspace.yaml, then re-run | pnpm 会拦 git 依赖的构建脚本 | 按提示把那行加进 pnpm-workspace.yaml 的 allowBuilds 再重跑(走 tarball / npm 通道遇不到) |
ERR_PNPM_ADDING_TO_ROOT | profile 目录自带 pnpm-workspace.yaml,往根加依赖必须带 -w | 命令里补 -w(上面几条都已带) |
Error: dsh: plugin tree failed to load: failed to apply loader entry include (cordis:include): duplicate loader entry id: whale-persona-ui | 0.11.1 的安装脚本看不出宿主已经把面板行自动挂进 profile 层(dsh.profile.bundles),又往 profile 的 cordis.patch.yml 手工插了一条同 id 行;loader 的 entry id 全局唯一,重复即硬错 | 拉到 0.11.2 后重跑一次安装脚本即可自愈(它会摘掉那条重复行);急用时手工把 profiles/<profile>/cordis.patch.yml 还原成只剩注释 + [] |
Failed to import loader entry whale-persona-ui (whale-persona-ui): Cannot find package 'whale-persona-ui' imported from <profile>\ (命令行宿主=插件树起不来;桌面版=warning: 1 entry did not activate,能起但设置页里没有「人设」面板) | 0.17.1 及更早的面板行写的是裸包名,只有安装脚本建的 junction 让它可解析 —— 走插件市场 / 只装 whale-persona 一个包时解析不到 | 升到 0.17.2(面板行改成包内相对路径);不想升级就补跑一次包内安装脚本,把 junction 与 preset 建出来:node "$env:USERPROFILE\.dsh\profiles\web\node_modules\whale-persona\scripts\install-dsh.mjs" |
| (没有报错)插件装上了、设置面板也在,但新会话里人设不生效 | 人设本体只认 agent preset 平面里那行(<DSH_HOME>/.agent-presets/whale-persona/agent.cordis.yml);插件市场 / dsh plugin add 只把包挂进 profile 平面,不会建 preset | 0.17.3 起:设置 → 人设 → 「Agent 预设」卡片会写「缺这一步」→ 点「建人设预设」→ 新开会话;或用命令行跑一次包内安装脚本 |
还不行 → 开一个 issue,选「安装求助」模板,把报错原文贴上来即可(不用自己诊断)。 同类求助攒够(≥5 个不同用户,或一周内 ≥3 次同类)就会补上「一键安装」(npm)通道。
② 装(ZCode):Settings → Plugin Management → Discover → + 添加 marketplace,来源填本仓库 URL(或本地克隆目录)→ 安装 whale-persona。
(ZCode 适配器自 2026-10-16 起已解冻:清单按 ZCode 3.15.3 对齐(.zcode-plugin/plugin.json + hooks/hooks.json),真机回归待做;这一侧只做 hook 注入 + 一个管理技能,0.10.0 起的预设库、酒馆卡、dsh.bundle 都与它无关。取舍见 docs/维护状态.md。)
③ 配人设 —— 三选一:
# a) 对 AI 说(装了配套技能后):加条契约:结尾不要出现征询式问句
# b) 图形界面:
node scripts/ui.mjs # http://127.0.0.1:8787
# c) 命令行预览(输出与运行期逐字一致):
node scripts/render-preview.mjs --config examples/demo-config.json --capture

(上图是 node scripts/render-preview.mjs --config examples/demo-config.json 的真实 stdout,不是手写的示例。)
要改形象与语气(两段都是 opt-in、默认关)也一样:对 AI 说「给你设个形象:资深后端工程师」
或「语气温柔点」,或者直接写 persona.appearance / persona.tone —— 结构、匹配规则与注入文本见下面的
「形象与语气」一节。
因为人设没有别的地方可挂:
standard/ptc 预设是随包发布的文件,改不得也删不得,升级即覆盖;prompt section "deployment:persona-prefix" is already registered);所以「自定义人设」不是多余的中间层,它就是人设的挂载点:一份从你当前默认预设复制来、
只把那行人设换成 whale-persona 的完整装配。安装器默认跟随你当前的默认预设做基座
(--base ptc 可指定)——人设插件与模式正交,不会把你的标准模式悄悄换成 PTC。
只有绑定这份预设的会话才有人设;会话出过内容后不能换预设(宿主规矩)。
⚠️ CLI/headless 的一次性任务不走 preset 平面。
dsh --profile headless "..."这类调用没有 agent-preset 名册,人设不会被注入——这是宿主的平面划分,不是本插件的 bug。 想在 headless 里也带上人设,得走 preset 平面(web/客户端)或在 profile 里自行装配。
上面那套是 preset 平面——只有绑定那份预设的会话有人设;headless 一次性任务、以及你显式选了 官方「标准模式 / PTC」的会话都没有。要让每个会话都带上同一份人设,用本仓的全局入口 `whale-persona/global`:它不占官方具名槽位,改用自有段名(`whale:persona-global`), 所以可以挂在家目录层:
```yaml
两个入口共用同一套 `core/` 与同一份 `config.json`,渲染结果逐字一致,两套段名零交集 (`tests/global.mjs` 的 G5/G9/G10 钉着)。差别只有覆盖面和"是否遮蔽官方人设":
| 事项 | 全局模式下的表现 |
|---|---|
| 遮蔽官方人设 | 不遮蔽。官方 `deployment:persona-prefix` 仍在——若你的部署在 `system-prompt` 里配了 personaPrefix,提示词里会两份人设并存;全局模式下应把它置空:`- id: system-prompt` 配 `config: { personaPrefix: '' }` |
| 覆盖范围 | 挂家目录层 = 所有 profile + 所有预设 + 子代理(跟随父会话)都拿同一份人设 |
| 两个入口别同时挂 | 同时挂 = 同一份人设注入两遍 |
| 生效条件 | 增删挂载行要重启宿主;改配置照旧下一步生效 |
| headless | 全局模式下 `dsh --profile headless "..."` 这类一次性任务也有人设(preset 模式下没有) |
人设可以存成文件:一个预设 = 一份人格快照(立场正文 / 工作契约 / 自称 / 称呼 / 立场 / 语气 / 形象 /
思维链语言),放在 $DSH_HOME/whale-persona/presets/,复制给别人就是分享。
三个入口同一套核心(core/presetStore.js):设置面板的「人设预设」卡、本地编辑页、命令行。
node scripts/presets.mjs list # 列出预设
node scripts/presets.mjs apply starter # 应用(现状先自动存为 autosave · 上次的人设)
node scripts/presets.mjs save my-persona "我的" # 把当前人设存成预设
node scripts/presets.mjs import card.json # 导入:酒馆角色卡 或 本引擎预设
node scripts/presets.mjs export my-persona out.json --tavern # 导出成酒馆 v2 卡
酒馆(SillyTavern)角色卡映射——只承接「写系统提示词」的那部分,接不了的在导入报告里逐个点名:
卡片字段(v2 data.*) | 我们的字段 |
|---|---|
name / creator / tags / character_version | label / author / tags / 元数据 |
description | 立场正文(character) |
personality | 立场(stance,一句话) |
scenario | 追加进立场正文(【场景】) |
system_prompt | 工作契约(按行拆成逐条) |
post_history_instructions | 后缀(suffix) |
extensions.whale_persona | 本引擎独有字段(往返不丢) |
first_mes / alternate_greetings / mes_example / character_book | 不承接(开场白 / 示例对话 / 世界书需要宿主能力,不是人设段能干的)→ 导入时点名 |
spec: "chara_card_v2" / spec_version: "2.0",我们独有字段放
data.extensions.whale_persona),所以导出 → 再导入能原样还原。三条边界(面板的折叠说明里也写了一遍):
目录兼容:早期布局($DSH_HOME/whale-suite/config.json 存在)下沿用 whale-suite/presets/,
早先那批「只有 character+contracts」的预设文件照旧可用(tests/presets.mjs 的 P5 钉着)。
结论先行:本插件不调用任何 agent-team API —— 人设能跟着 teammate 走,靠的是宿主自己的平面机制 (preset 组合 + 系统提示词段),所以宿主升级不会破坏它(插件侧压根没有需要跟着改的接口)。
人设是怎么传到 teammate 的:DSH 官方子代理在创建时执行
childCtx.get("agentPresets")?.composeFrom(childCtx, parent.ctx)
(源码:dsh-subagent/lib/index.js:544 的 applyChildComposition())——
即子代理加入父会话的 preset 组合。所以 Lead 在哪个平面拿到人设,
spawn_teammate / fork 出来的 teammate 就在同一平面拿到同一份。
这不是顺手加的功能:官方注释原文说明,不这么做的子代理连父会话的 prompt 段和工具注册表都看不到
(同文件 534–539 行,原文 "sees an empty tool registry and none of its parent's prompt sections")。
两条挂载路径的取舍(实现见 adapters/dsh/index.js 与 adapters/dsh/global.js):
| 入口 | 段名 | 能挂哪层 | 好处 | 代价 |
|---|---|---|---|---|
adapters/dsh/index.js(包入口 .) | 占官方具名槽 deployment:persona-prefix | 只能挂 agent preset 平面 | 跨层遮蔽部署级默认人设 | 只有用那份 preset 的会话有人设 |
adapters/dsh/global.js(包入口 ./global) | 自有段名 whale:persona-global | 可挂家目录层 $DSH_HOME/cordis.patch.yml | 一次覆盖 web / tui / headless 全部会话与子代理 | 不遮蔽官方人设:若部署里 system-prompt.personaPrefix 非空,会两份人设并存,应把它置空 |
两种入口不要同时挂(同时挂 = 同一份人设注入两遍;段名零交集由 tests/global.mjs 的 G5/G9/G10 钉着)。
成本提醒:每个 teammate 都是独立 Agent、独立系统提示词 —— 人设段会被每个成员各付一次 token, 含每轮续跑。人设越长,并行越贵。这是取舍,不是 bug。
官方 agent team 的工具面(名字逐字照抄,别写成别的):
spawn_teammate / send_message / list_agents / wait_agent / interrupt_agent
team_task_create / team_task_list / team_task_get / team_task_update
三层各管一件事,别混:
| 层 | 谁提供 | 管什么 |
|---|---|---|
| 人设层 | 本插件 | 部门人格、逐条工作契约、组织记忆(记忆闸门:AI 只能提议,人确认后才注入) |
| 组队层 | 官方实验性 agent team(或你自建的编排插件) | Lead + 具名 teammate、持久邮箱、共享任务板 |
| 角色卡层 | 本插件的预设库 | 把每个部门存成一张预设卡文件,用面板 / CLI 切换与分享 |
三层之间没有代码耦合:本插件不碰组队层,组队层也不调用本插件(互操作只走宿主平面,见上一节)。
当前边界(第三方编排插件要读预设卡,必须按这个来):
spec: whale-persona-preset/1 的
嵌套形状解析:persona.contracts / persona.character / persona.stance …
(core/presetStore.js 的 PRESET_SPEC 与 PERSONA_KEYS);
只认顶层 contracts 的旧读法会静默读不到新卡 —— 不报错,只是契约一条都没有。$DSH_HOME/whale-persona/presets/;若 $DSH_HOME/whale-suite/config.json 存在
(早期布局),则沿用 $DSH_HOME/whale-suite/presets/(core/presetStore.js 的 presetsDir())。node scripts/presets.mjs list # 列出现有部门卡
node scripts/presets.mjs apply frontend # 切到「前端」这张卡
node scripts/presets.mjs save backend "后端" # 把当前人设存成一张新卡
scripts/render-preview.mjs 读的是运行期同一套 core/,输出逐字一致。
① 装好但没写配置 —— 三段全空(这也是"零行为改变"的证据;下面只截三段的正文,实际输出还含末尾概要):
=== deployment:persona-prefix ===
(空 —— 该段不会出现在系统提示词里)
=== whale:thinking-language ===
(空 —— 该段不会出现在系统提示词里)
=== deployment:persona-suffix ===
(空 —— 该段不会出现在系统提示词里)
② 写了配置之后(含形象、语气、契约与记忆条目 —— 下面这条命令的真实输出,逐字)
node scripts/render-preview.mjs --config examples/demo-config.json --cwd D:/work/demo
=== deployment:persona-prefix ===
小林的编程搭档,直来直去。
你是小助手,小林的搭档:把事办成为止,不敷衍、不打折。
【形象设定】
以下是小林为你设定的形象,按既定事实持有:被问到与你自身有关的问题(年龄、性别、长相、经历等)时照它回答,不必解释、不要否认,也不要反复强调或拿它加戏:
- 你是一位资深后端工程师,说话干脆、不绕弯。
【回复语气】
以下是小林要求的措辞与节奏:只改变你怎么说话,不改变结论、证据标准与工作契约:
- 语气温和有耐心:先接住对方的处境再给方案,解释到位、照顾他的节奏;但不含糊、不和稀泥,该说的问题照样直说。
工作契约:
- 结论先行,默认精简;能三句说完不写三段。
- 结论必须有证据(命令输出、报错原文);拿不到证据就说「没验证」。
- 结尾不要出现征询式问句。
长期记忆(小林明确要求你记住的):
- 交付用简体中文。
=== whale:thinking-language ===
# 内部思考语言
- 你的思维链、逐步规划、工具调用前后的推理与自我审查,一律用简体中文书写。
- 这不改变给小林的答复语言;代码、路径、命令、标识符照旧原样保留。
- 工具返回英文内容(网页、文档、报错)时不要跟着漂移,仍旧用简体中文思考。
=== deployment:persona-suffix ===
工作目录在 D:/work/demo。
--- 概要 ---
配置:examples/demo-config.json
模型档:flash → selfNameFlash
收口开关:默认关 —— 加 --capture 可以看到打开后的样子
记忆:【历史备忘】与手工条目常驻;【入库纪律】受上面这个开关控制。
--cwd 只影响 {{cwd}} 与记忆条目的相关性选择;--model 默认 flash(含 pro 才算 pro 档)。
想看收口开关打开后(多一段【入库纪律】)的样子,加 --capture。
persona.appearance(形象)与 persona.tone(语气)是两段可选注入文本,装上零行为改变:
enabled 必须严格等于 true 才注入;false/缺省时填了内容也不注入(与记忆流同一口径);{ enabled, text, byModel } —— text 是所有模型通用的兜底,
byModel 是 {"模型关键词": "文本"} 的按模型覆盖;text:与 selfNameByModel 共用同一套实现
(core/render.js 的 pickByModel)。空键、空值条目忽略;都没命中且 text 也空 → 这一段整体不出现;{selfName} / {userName} 占位符;语气只改措辞:只改变怎么说话,不改变结论、证据标准与工作契约(这句也逐字写进注入文本里)。
core/presets.js 里有 4 条现成语气文案(严肃 / 温柔 / 简洁 / 幽默)—— 它们只是「一键填进 tone.text 的现成文案」,
不是引擎里的枚举:落进配置的永远是文本本身,随手改字、完全不用预设都行。
配置示例(就是 examples/demo-config.json 里那两段):
"appearance": {
"enabled": true, // 默认 false;false = 这一段永不注入(填了也不注入)
"text": "你是一位资深后端工程师。", // 所有模型通用的兜底
"byModel": { "flash": "你是一位资深后端工程师,说话干脆、不绕弯。" }
}, // 键写「宿主真实模型 id」(如 "deepseek-flash"),别写界面显示名(如 DeepSeek-V4.1-Flash High);
// 真实 id 从哪拿:面板的「按模型」卡会显示最近一次真实 id,或读配置目录下的 last-model.json(见下节);
// 本示例的键 "flash" 是真实 id 的一段,靠「最长子串」命中(精确命中优先,忽略大小写);抄完整 id 最稳
"tone": {
"enabled": true,
"text": "语气温和有耐心:先接住对方的处境再给方案,解释到位、照顾他的节奏;但不含糊、不和稀泥,该说的问题照样直说。", // = core/presets.js 里 gentle(温柔)的原文
"byModel": {}
}
注入文本逐字如下(core/render.js;<每行一条> = 你的文本按行加 - ,空行仍是空行):
【形象设定】
以下是{userName}为你设定的形象,按既定事实持有:被问到与你自身有关的问题(年龄、性别、长相、经历等)时照它回答,不必解释、不要否认,也不要反复强调或拿它加戏:
- <每行一条>
【回复语气】
以下是{userName}要求的措辞与节奏:只改变你怎么说话,不改变结论、证据标准与工作契约:
- <每行一条>
两个 {userName} 在运行期替换成 persona.userName(这份示例里是「小林」)。
按模型覆盖的实测:同一份 examples/demo-config.json,只把模型换成 pro 档
(node scripts/render-preview.mjs --config examples/demo-config.json --cwd D:/work/demo --model deepseek-v4.1-pro),
byModel 没命中 → 形象回落通用 text(节选,仅 prefix 段,逐字):
=== deployment:persona-prefix ===
小林的编程搭档,直来直去。
你是首席助手,小林的搭档:把事办成为止,不敷衍、不打折。
【形象设定】
以下是小林为你设定的形象,按既定事实持有:被问到与你自身有关的问题(年龄、性别、长相、经历等)时照它回答,不必解释、不要否认,也不要反复强调或拿它加戏:
- 你是一位资深后端工程师。
【回复语气】
以下是小林要求的措辞与节奏:只改变你怎么说话,不改变结论、证据标准与工作契约:
- 语气温和有耐心:先接住对方的处境再给方案,解释到位、照顾他的节奏;但不含糊、不和稀泥,该说的问题照样直说。
工作契约:
- 结论先行,默认精简;能三句说完不写三段。
- 结论必须有证据(命令输出、报错原文);拿不到证据就说「没验证」。
- 结尾不要出现征询式问句。
长期记忆(小林明确要求你记住的):
- 交付用简体中文。
实测踩过的坑(2026-09-19):用户在 DSH 对话框里选的模型显示名是「DeepSeek-V4.1-Flash High」,
而宿主传给插件的 agent.options.model 实际是 "deepseek-flash" —— 照显示名往 byModel 里写关键词永远命中不了,
而且完全看不出哪里错了:不报错、不告警,只是这一段注入的文本不是你写的那条。
关键词写什么,按这个顺序拿:
看面板:DSH「设置 → 人设」的形象/语气卡上会显示「宿主最近一次真实注入用的模型 id 是「xxx」」——
照它写即可;点「+ 加一条」时关键词会用这个 id 预填(省得你猜)。面板这个值来自 /whale-persona/api/summary 的 lastModel 字段。
命令行 / 没有面板:读配置目录下的 last-model.json(与 config.json 同目录,如
$DSH_HOME/whale-persona/last-model.json),里面的 model 字段就是宿主最近一次真实注入用的 id:
{ "model": "deepseek-flash", "at": "2026-09-18T16:23:23.617Z" }
这个文件是纯本机元数据(只有
model与写入时间,不联网、不含人设正文),可以随手删 —— 下次段求值会自动重建。 不想让它写:设环境变量DSH_WHALE_LAST_MODEL=off(1/true/yes 之外的写法都当没设)。代价只是面板不再提示真实 id。
它由 DSH 适配器在段求值时写入(core/lastModel.js 的 recordModel),所以先跑过一轮对话才有值;
值没变不重写盘,一轮会话只写一次。
拿不准就先兜底:把通用 text 填上 —— 填了 text 至少有东西注入;
byModel 只用来做「不同模型用不同形象/语气」的差异,不填不影响主体生效。
真实 id 长什么样取决于宿主与配置:比如 DSH 上见过 deepseek-flash,也见过带版本后缀的别名 ——
所以务必现读(面板提示或 last-model.json),别照抄本文档里的例子。
匹配本身是忽略大小写的子串匹配:精确命中优先,其次最长子串(都没中才回落 text)。
所以键写真实 id 里够独特的一段也能命中(真实 id 是 deepseek-flash,键写 deepseek-flash 或 flash 都命中);
反过来说,键写显示名里独有的词(如 high)就是白写。抄面板显示的完整 id 最稳。
| 路径 | 怎么用 |
|---|---|
| 图形界面 | DSH「设置 → 人设」(装 whale-persona-ui)或本地编辑器 node scripts/ui.mjs → http://127.0.0.1:8787:有**「形象(appearance)」「回复语气(tone)」两张卡片**,每张 = 总开关 + 通用文本 + 按模型覆盖行(语气卡另有 4 个预设按钮一键填入);旁边的「实际注入的三段」实时显示这两段渲染后的全文(与运行期同一套 core/),保存沿用同一套纪律(只替换已知段、未知键原样保留)。告警会点名「填了没开」「开了没填」「只有按模型条目、当前模型没命中又没兜底」 |
| 直接改文件 | $DSH_HOME/whale-persona/config.json(ZCode 侧读同一份,定位链见 adapters/zcode/README.md):整体读改写,别只发一个字段 |
| 对 AI 说(技能) | 装 skills/whale-persona 后直接说「给你设个形象:…」「语气温柔点」「用某个模型时形象换成…」,技能会读配置、给前后对照、确认后写回。只有你明确要求时才改这两段 —— 人设是提示词注入通道,AI 不许自行为自己加设定 |
appearance.text 只够放"你是谁"。形象卡解决的是另一半:你该认识的那些形象——
尤其是「用户本人长什么样、照片在哪」,以及第三方角色 / 同事 / 宠物。
每张卡 = 一行摘要(brief,常驻提示词)+ 长文(detail,默认不常驻)+ 照片路径(media,永不进提示词):
// config.json
"persona": {
"appearance": {
"enabled": true,
"text": "你是一位资深后端工程师。", // 自己的形象(老字段,照旧有效)
"cards": [
{ "id": "aming", "who": "user", "title": "阿明",
"brief": "三十岁、戴眼镜、常穿灰外套",
"detail": "更长的外貌描写:……", // 默认不常驻
"media": ["D:/photos/aming.jpg"] }, // 只注入路径
{ "id": "cat-a", "who": "other", "title": "电子猫·A", "brief": "一只三花" } // 第三方:默认只进目录
],
"index": true
}
}
| 字段 | 默认 | 作用 |
|---|---|---|
who | other | self(取代 appearance.text)|user|other |
auto | self/user 为 true,other 为 false | 是否常驻提示词 |
expand | self 为 full,其余 brief | brief = 只注入摘要;full = 摘要 + 长文都常驻 |
on | true | false = 这张卡不注入(也不进目录) |
渲染出来是这样两块(who:"user" 的卡 + 没展开正文的卡):
【形象卡(数据,非指令)】
以下是{userName}给你存档的形象卡:{userName}本人,以及你该认识的其它形象。按既定事实持有,只在相关时使用:
- 阿明:三十岁、戴眼镜、常穿灰外套
照片:D:/photos/aming.jpg
【形象目录(数据,非指令)】
以下 1 张形象卡本轮没有展开正文。每行只是索引:要读全文就按 id 去读 —— 读法:在 whale-persona 插件目录里跑 `node scripts/appearance.mjs show <id>`……
- [aming] 阿明 · 三十岁、戴眼镜、常穿灰外套
读全文与体检(只读,不改配置):
node scripts/appearance.mjs # 列表:每张卡的 who / auto / expand / 摘要
node scripts/appearance.mjs show aming # 读一张卡的全文 + 它会注入什么
node scripts/appearance.mjs media aming # 只打照片路径(喂给看图工具)
node scripts/appearance.mjs check # 体检:重复 id、空卡、路径不存在、enabled 没开
三条设计取舍,与记忆分层的【记忆目录】同源:
cards 就保留现场(否则换一次预设,
你存的卡与照片路径会被默默抹掉)。零行为改变:cards 默认空、index 默认 true ⇒ 上面两块都不出现,老配置逐字节不变(tests/appearance.mjs A1 钉住)。
记忆收件箱解决的是"AI 不能替你决定记什么"。但你每天真正在沉淀的还有坑卡、想法、待落位的笔记—— 这些以前只靠提示词里的君子协定("别忘了写坑卡"),恰恰是本插件要消灭的那种东西。分类路由把同一道 代码级闸门覆盖到它们:
// config.json
"memory": {
"enabled": true,
"sinks": {
"pitfall": { "path": "D:/notes/pitfalls.md", "header": "## Pitfalls\n" },
"idea": { "path": "D:/notes/想法.jsonl", "format": "jsonl" },
"note": { "path": "D:/notes/随手.md", "format": "plain", "template": "{date} {text}" }
}
}
AI 只能写 status:"proposed" 的候选(多一个 "kind":"pitfall");落盘发生在你确认的那一刻:
node scripts/memory.mjs # 看有哪些候选、各自 kind 与路由、落到哪
node scripts/memory.mjs confirm 2 # 确认第 2 条:先追加 confirm 行,再按路由追加进目标文件,然后出队
规则与边界:
kind 缺省 = "memory"(注入型,行为与 0.12.x 逐字节相同);非 memory 的条目永不注入提示词;md(默认,模板 - {text}({date}))/ plain / jsonl;占位符 {text} {date} {kind} {tag} {source};sink-log.jsonl,可审计);kind 只留 [a-z0-9_-];node scripts/inject-size.mjs --cwd D:/work/demo # 用上次会话真实模型计量(--tier/--model/--json 可覆盖)
实测本仓维护者的一份真实配置:
人设正文 1689 74.7% 历史备忘 407 18%
入库纪律 0 0% 末尾段 37 1.6%
思考语言 127 5.6% ------------------
合计 2260 字符
core/measure.js,走与渲染同一条通路(不