본문으로 건너뛰기
X

dsh-soul-evolve

xiachuyoufeng/dsh-soul-evolve

Persona 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, support, chat) modulate reply length and warmth without changing the state itself; the Soul panel keeps a persona card plus weighted evolution entries and injects them into the system prompt persona-first under a 4000-character budget. Ships a browser panel with a persona card editor and a live state view (the five metrics plus the last six events), appends a JSONL trace to disk, and seeds a usable default persona on first install.

설치

dsh plugin --profile web add github:xiachuyoufeng/dsh-soul-evolve

README

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,避免崩溃损坏

升级(开发通道,默认关闭)

插件详情页的「从本地源码升级」按钮走这条路:

  1. 校验源码目录与已装副本都是 dsh-soul-evolve 包
  2. 把已装副本备份到 ~/.dsh-soul-evolve/upgrade-backups/<时间戳>/
  3. 把 src/*.js、package.json、cordis.patch.yml、README.md 逐文件原子写过去(临时文件 + rename)
  4. 失败自动回滚,并返回备份路径
  5. 完成后提示「重启 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:

  1. 只处理 source.kind === "user" 的真人消息(跳过 goal / 团队 / 系统注入)
  2. 取出文本块 → reflex() 算增量 → applyDelta → pushEvent → 原子写
  3. 所有写入走同一个串行队列,避免多条消息并发读-改-写互相覆盖
  4. 任何异常只告警,不影响会话

效果:状态会自己变——下一轮请求组装的提示词里读到的是新状态。 可用 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 没覆盖的部分。

三道闸,防止它给自己编人格

  1. 证据门槛:独立会话 ≥ 3、跨天 ≥ 3、相关轮次 ≥ 12,缺一不可。单次或单天出现的多半是任务要求,不是模式。
  2. 模型写不进去:qualifies() 明确拒绝 kind: "pattern",只有插件自己的沉淀通道能产出这类条目(qualifiesInferred())。模型不能自造证据。
  3. 低权重 + 快半衰:初始权重 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。

관련 플러그인