dsh-soul-evolve
IdentifiedPersona state and slow evolution for DSH: five state dimensions (valence, arousal, energy, intimacy, focus) move after each turn and decay toward baseline over time; four scene types (work, tech, supp
dsh-soul-evolve
人格状态与缓慢演化插件(DeepSeek Harness)。
- 状态层:情绪效价/激活/能量/亲近度,随时间衰减、随交互累积
- 反射弧:输入 → 状态增量 → 表达调制(长度/温度/耐心旋钮,不宣告情绪)
- 每轮自动更新:监听真人
user/message,自动跑反射弧累积状态(0.4.0 起) - 合成引擎:C1–C6 六条硬约束把事件提炼成人格条目
- 场景切面:work / 技术 / 情绪支持 / 闲聊 四类场景,带滞回(1.0.0 起)
- 观测层:每轮追加一行 JSON,
scripts/stats.mjs直接算验收指标(1.0.0 起) - 注入预算:演化条目按权重衰减排序,整段硬顶 4000 字(1.0.0 起)
- 适配层:DSH 依赖收敛到 adapter.js,升级只改这一处
结构
src/state.js 状态存储:schema 版本、原子写入、衰减、作用域、旧路径回退
src/reflex.js 反射弧与表达调制(纯函数)
src/scene.js 场景判定(打分 + 滞回,纯函数)
src/trace.js 每轮观测(JSONL 追加、去重、轮转)
src/persona.js Soul 面板:正文 + 演化条目 + 权重 + 注入预算 + 历史
src/evolve.js 六条约束(入口/格式/频率/冲突/体量/禁止)
src/adapter.js 版本探测、能力检测、降级
src/auto-update.js 每轮自动更新(session/event → 反射弧 → 场景 → 状态文件)
src/api.js 配置面板与升级按钮的后端路由
src/upgrade.js 升级:源码 → 已装副本的原子同步
src/index.js cordis 入口:soul_state / soul_evolve 两个工具 + 动态状态注入
client.js 浏览器半侧:插件详情页的「从本地源码升级」按钮
cordis.patch.yml 挂载声明(DSH 按它把插件挂进配置树)
安装(重要)
插件包必须这样声明,否则 DSH 启动时读不到挂载声明:
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
cordis.patch.yml 内容:
- insert:
- id: dsh-soul-evolve
name: 'dsh-soul-evolve'
安装:
dsh plugin add dsh-soul-evolve
装之前先跑预检(零风险,不碰 DSH 配置):
node scripts/verify-plugin.mjs # 74 项断言,全过才装
node --test test/soul.test.js # 35 个用例
数据
- 状态文件:
~/.dsh-soul-evolve/state.json(DSH_SOUL_HOME可覆盖) version字段 +migrate():结构升级时写迁移分支- 写文件用临时文件 + rename,避免崩溃损坏
升级(开发通道,默认关闭)
插件详情页的「从本地源码升级」按钮走这条路:
- 校验源码目录与已装副本都是
dsh-soul-evolve包 - 把已装副本备份到
~/.dsh-soul-evolve/upgrade-backups/<时间戳>/ - 把
src/*.js、package.json、cordis.patch.yml、README.md逐文件原子写过去(临时文件 + rename) - 失败自动回滚,并返回备份路径
- 完成后提示「重启 DSH 生效」
它只同步包文件,不碰 profile 依赖、不改 generation 元数据,所以不会触发依赖重装,也不会让启动校验失败。
背景:官方「更新」按钮要求包有
installTarget,而它只对 DSH 内置的官方目录生成,第三方本地包永远没有——所以这里在plugins.detail.actions插槽里自备一个入口。
这条通道只给开发用,公开发布版默认关闭:没有设置 DSH_SOUL_SOURCE 时,接口返回不可用,配置面板不渲染「升级」区块、插件详情页也不出现按钮——不会留一个点下去就报错的入口。
自己开发时这样打开:
export DSH_SOUL_SOURCE=/path/to/dsh-soul-evolve
接口:
GET /dsh-soul-evolve/api/status— 已装版本 / 源码版本 / 源码目录POST /dsh-soul-evolve/api/upgrade— 执行同步(要求x-soul-upgrade: 1头)
数据
- 状态文件:
~/.dsh-soul-evolve/state.json(DSH_SOUL_HOME可覆盖) - 改名前的旧状态:
~/.dsh-soul/state.json仍会被读取(新路径为空时回退,下次保存写入新路径) - 升级备份:
~/.dsh-soul-evolve/upgrade-backups/<时间戳>/ version字段 +migrate():结构升级时写迁移分支- 写文件用临时文件 + rename,避免崩溃损坏
每轮自动更新
0.4.0 起监听 session/event 里的 user/message:
- 只处理
source.kind === "user"的真人消息(跳过 goal / 团队 / 系统注入) - 取出文本块 →
reflex()算增量 →applyDelta→pushEvent→ 原子写 - 所有写入走同一个串行队列,避免多条消息并发读-改-写互相覆盖
- 任何异常只告警,不影响会话
效果:状态会自己变——下一轮请求组装的提示词里读到的是新状态。
可用 apply(ctx, { autoUpdate: false }) 关闭。
交互沉淀(1.2.0)
性格不只由「用户明确纠正」驱动,也会从交互本身慢慢长出来。
每 15 轮真人输入(sedimentEvery 可调)跑一次沉淀:把 turns.jsonl 里的逐轮观测聚合成候选,
够格的升级成演化条目,写回 Soul 面板。
两条轨道分开沉淀
「该怎么做」和「该用什么语气」是两件事,混在同一段里会互相盖掉。所以推断条目分两条轨道, 各自算门槛、各自写、各自注入:
| 轨道 | 写什么 | 检测器(按优先级) | 判据 | 初始权重 / 半衰 |
|---|---|---|---|---|
工作习惯 habit | 怎么干活 | habit:direct | 一句话指令占比 ≥ 12%(≥20 次) | 0.5 / 60 天 |
habit:ongoing | 每会话轮次中位 ≥ 12 | |||
性格表达 character | 以什么姿态出现 | character:curious | 追问命中占比 ≥ 5%(≥15 次) | 0.45 / 45 天 |
character:feedback | 正面反馈占比 ≥ 60%(≥20 次) | |||
character:steady | 每会话场景切换 ≤ 0.5 次 |
顺序按信息量排:能直接改行为的在前,「别乱变」这种最低的排最后。每个维度一轮只写 1 条, 所以排序决定的是「先长出哪一条」,不是「只有哪一条」。
场景不写条目:场景已经由「人格状态」段每轮实时注入,再沉淀一遍就是同义反复;
而且场景是内部枚举(work / tech / ...),写进条目对读的人没有指导意义。
注入时分成三段,互不干扰:
## Evolved ← 用户明说过的(纠正 / 重复犯错 / 偏好 / 场景)
## 工作习惯 ← habit 轨推断出来的
## 性格表达 ← character 轨推断出来的
习惯轨只收带数字的观测结论
「回复该短」这类话,面板正文早就手写了、memory 的 user 轨也存得下,再统计一遍只是把同一句话
重说一次。所以 1.2.0 移除了 habit:brief 检测器——习惯轨只留 memory 抓不到的那种结论:
「你 143/728 轮是一句话指令」没人会去确认它,但统计得出来。
写入前还有一道覆盖检查:把候选的「学到什么/以后怎么做」与面板正文、已有条目、memory-evolve 写下的记忆条目
(MEMORY.md / USER.md,只读、尽力而为)做字面相似度比较,达到 0.3 就判为已写过,跳过并记下原因。
阈值是实测的——同一句话的重述落在 45%,真实候选撞面板最高 9%、撞记忆最高 3%,取 0.3 两边都留余量。
它挡不住语义同义、措辞完全不同的重复,真正防重复建设靠的是上面那条选型。
与 memory-evolve 的分工
| memory-evolve | 本插件 | |
|---|---|---|
| 记什么 | 用户明说过的事实、偏好、规范 | 从行为里统计出来的模式 |
| 谁写的 | 模型提议 + 用户确认 | 统计推断,无人确认 |
| 保鲜 | 不衰减,靠定期审查合并去重 | 45–60 天半衰,推断错了自己淡出 |
| 注入位置 | memory:snapshot(order 500) | soul:persona(order 10) |
一句话边界:memory 存「他说过该怎么做」,本插件存「做下来一直是这么做」。 「回复该短」这种话 memory 存得下、面板正文也早就手写了,所以不进统计沉淀; 「220/909 轮是一句话指令」这种没人会去确认、但统计得出来的,才归这边。 同理「你的场合主要是 work」不写——那是描述,不是行为要求。
冲突时的优先级是隐式的、但确实存在:memory:snapshot 在 soul:persona 之后注入,且内容都经用户确认过,
所以 memory 更强势;soul 的推断条目只补 memory 没覆盖的部分。
三道闸,防止它给自己编人格
- 证据门槛:独立会话 ≥ 3、跨天 ≥ 3、相关轮次 ≥ 12,缺一不可。单次或单天出现的多半是任务要求,不是模式。
- 模型写不进去:
qualifies()明确拒绝kind: "pattern",只有插件自己的沉淀通道能产出这类条目(qualifiesInferred())。模型不能自造证据。 - 低权重 + 快半衰:初始权重 0.45–0.5、半衰 45–60 天。推断错了会自己淡出;被反复验证(同一
patternKey再次沉淀)权重 +0.1 才慢慢坐实。
另外几条节制:每个维度一次最多写 1 条(一边不够格不挡另一边)、两次自动沉淀至少隔 1 天、
同一 patternKey 冷却 14 天、条目里不许声称内在状态(BANNED 挡「我越来越亲近」这类)。
看得见也能催:配置面板「交互沉淀」区块按轨道分组显示观测统计、够格的候选、以及「还差几天」的候选
(例如 跨天 1/3(还差 2 天)),旁边一个「立即沉淀」按钮(POST /api/patterns/sediment,force 可绕过全局冷却)。
条目上的 evidence(会话数/天数/轮次/起止时间)会存进卡片并在面板显示为「依据 3 会话/30 轮」,删除照旧走 soul_persona(action=forget) 或面板上的「删」。
Soul 面板(0.5.0)
Soul 是"静态骨架",状态是"动态血肉":Soul 面板负责「我是谁、说话什么风格」,状态层负责「今天累不累、跟你熟不熟」。
- 格式:
soul.md风格 Markdown(与市场里 soul-md 系 Soul 面板互通,可直接导入/导出) - 存储:
~/.dsh-soul-evolve/personas.json(多张卡 + 当前激活) - 注入:激活卡作为
soul:persona段(order 10)注入提示词,改完下一轮请求生效,无需重启 - 硬约束:正文不能含
{{/}}(DSH 提示词变量语法,否则整个提示词组装会失败),保存时直接拒绝 - 工具:
soul_persona(list / get / set / delete / activate / evolve / forget / history / rollback),AI 可读写但不会自动改 - 开箱内容:首次安装会自动种入一份通用默认人格(
templates/default-soul.md),面板不是空白的;改它就是自己的,删掉不会自动长回来
正文与演化条目(1.0.0)
一张卡分两半:
| 部分 | 内容 | 谁写 | 注入策略 |
|---|---|---|---|
content | 手写基底(Identity / Personality / Boundaries…) | 用户或 set | 优先完整保留 |
entries[] | 演化条目(一条一句,带类别与权重) | evolve | 按权重排序,预算内取 Top-N |
权重与半衰期:correction 1.0 不衰减、repeat 0.9 不衰减、preference 0.6(90 天)、scene 0.4(30 天)。
写入时判重:同类别 Jaccard ≥ 0.5 视为再次命中,旧条目权重 +0.1,不新增。
整段注入硬顶 4000 字;被截掉的条目仍留在存储里,面板标灰「未注入」,权重回升后会自动回来。
set 覆盖正文时旧正文进历史(保留 10 版),可一键回滚。
场景切面(1.0.0)
四类场景 work / tech / support / chat,默认 work——判错场景比不判更糟。
滞回规则:新场景连续两轮占优,或单轮得分 ≥ max(2, 当前场景分 × 1.5) 才切换;间隔 6 小时以上连续计数清零。
场景偏移加在调制层(温度/长度/耐心),状态本身不分叉。
副语言是状态外显,不是开关:精力 < 0.35 才 hesitation,专注 < 0.4 才 fragmented,状态高时为 false 就不会出现。
配置面板(0.5.0)
插件页 → dsh-soul-evolve → 配置(plugins.bundle.config 插槽):
| 区块 | 内容 |
|---|---|
| 人格状态 | 五维数值 + 基线刻度 + 表达旋钮条形图 + 最近 6 条事件,可手动刷新 |
| Soul 面板 | 只有当前这一个人格:新建 / 编辑 / 保存 / 删除 / 导入 .md / 导出 .md。正文默认收起,点标题展开 |
| Evolved 条目 | 注入预算占用(X/4000 字)、条目列表(类别 · 有效权重 · 未注入标灰)、新增 / 删除、历史版本回滚 |
| 升级 | 从本地源码同步到已安装副本(点完重启生效) |
接口
GET /dsh-soul-evolve/api/state— 状态与调制POST /dsh-soul-evolve/api/state—{ delta }手动调整GET /dsh-soul-evolve/api/personas— Soul 列表GET /dsh-soul-evolve/api/personas/<name>— 单张 Soul 正文 + 条目 + 历史 + 注入占用POST /dsh-soul-evolve/api/personas/entries—{ name, text, category }加条目DELETE /dsh-soul-evolve/api/personas/entries?name=<n>&id=<id>— 删条目POST /dsh-soul-evolve/api/personas/rollback—{ name, index }回滚正文GET /dsh-soul-evolve/api/trace?limit=<n>— 观测记录PUT /dsh-soul-evolve/api/personas—{ name, content }新建/覆盖PUT /dsh-soul-evolve/api/personas/active—{ name }指定生效人格(null = 停用)。面板本身不提供切换入口,保存时自动指向当前卡片;保留此接口是给脚本和导入用的DELETE /dsh-soul-evolve/api/personas?name=<n>— 删除POST /dsh-soul-evolve/api/upgrade— 同步源码到已装副本
写操作要求 x-soul-action: 1 头(跨域预检挡 CSRF)。
已知限制
- 自动更新只覆盖真人输入;工具结果、模型回复不会改变状态
- 多会话共用一个状态文件(未按会话隔离);跨项目可用
stateScope: "workspace"按工作目录分桶,但同一目录下的多个会话仍共用一份 - Soul 只有"全局激活一张",尚未做按工作区/会话分档(人格连续性本身就是跨项目的,所以没做)
- 场景判定靠关键词与长度特征,反讽、长句闲聊这类边界情况会判错;判错时的代价是语气略偏,不是内容出错
- 「点击升级」只同步包文件,不更新 profile 里记录的依赖版本(
visibleVersion仍为安装时版本;插件页显示的版本读的是包内package.json,会正常变化)
开发与同步(迁移后工作流)
node --test test/soul.test.js # 36 个单元测试
node scripts/verify-plugin.mjs # 安装前预检(不碰 DSH 配置)
node scripts/stats.mjs # 观测数据验收(场景温差/切换率/面板预算)
node scripts/check-define.mjs # 用真实 defineTool 校验工具定义
bash scripts/sync-to-profile.sh # 同步到 DSH profile(自动备份)
卸载:dsh plugin remove dsh-soul-evolve。状态与 Soul 面板数据留在 ~/.dsh-soul-evolve/,不会被删——重装就接上。
License
MIT
发布与上架的完整步骤见 PUBLISHING.md。
Comments
Loading…
Similar plugins
Compiles an LLM persona in DSH from six dimensions (identity, tone, creative habits, feedback style, boundaries, run parameters), injects the compiled text into the system prompt at the end, ships six
★ 0
dsh plugin --profile web add dsh-persona-studioby Aliuyanfeng
Personalization for the agent: a Web UI panel (about-you, reply style & tone, traits for headings/lists and emoji, output language, custom instructions) and `/soul` commands, with nickname/occupation/
★ 9
↓ 2.3k/wk
MIT
JavaScript
Nov 4, 2026
dsh plugin --profile web add dsh-soulA photo-real desk pet for the DSH window: four personas with five expression sprites each, whose dance, hair-flip and walk actions play as 24 fps VP9 alpha video clips; proactive lines with an optiona
★ 2
MIT
JavaScript
dsh plugin --profile web add dsh-pet-pluginJudges the working model's own emotion each turn and silently re-injects it before the next turn, closing a self-observation → state carry-over → behavior shift loop. Ships a host plugin plus a compos
★ 0
dsh plugin --profile web add dsh-emotion-feedbackby penglai-doll
SOUL.md-backed living persona for DeepSeek Harness: injects a trusted SOUL.md as a system-prompt section refreshed at every session start, registers soul_view / soul_remember / soul_edit / soul_rewrit
★ 0
↓ 154/wk
MIT
JavaScript
Aug 17, 2026
dsh plugin --profile web add dsh-soulby yimengsuper2432
Persona and memory from a single AGENT.md: the file is injected into the system prompt of every session (the first 40,000 characters; longer text is truncated with a note), a Settings page edits it, a
★ 1
MIT
JavaScript
Oct 31, 2026
dsh plugin --profile web add dsh-virtual-person