Saltar al contenido principal
Y

dsh-virtual-person

yimengsuper2432/dsh-virtual-person

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, and per-turn diary lines are merged by similarity, grouped by how often they were recalled, and pruned only at a 2 MB cap.

Instalar

dsh plugin --profile web add github:yimengsuper2432/dsh-virtual-person

README

dsh-virtual-person

一份 AGENT.md,让整个 DeepSeek Harness 都是同一个人

Version dsh License

dsh-virtual-person 把 <DSH_HOME>/AGENT.md 整份读进 DSH 的系统提示词,于是你在任何会话里打开的都是同一个角色,不用每个项目各写一遍。它不另开聊天窗口,也不挑模型:人格进系统提示,记忆写在磁盘上的同一个文件里,设置里只有一个页面,用来编辑这一份人格。

它适合这样的用法:你有一个想长期相处的角色,希望她记得住前几天说过的事,也希望「记得」这件事不需要你手动维护——没有需要定期收拾的索引,没有要你打分的权重,只有一份能直接用编辑器打开的 Markdown。

为什么是它

1. 一份文件,所有会话都生效

<DSH_HOME>/AGENT.md(DSH_HOME 缺省 ~/.dsh)是唯一真源。全局注入,没有开关——想停就清空内容或者删掉文件,不用去设置里翻。

  • 保存后下一个回合生效,不用重启;
  • <!-- --> 注释不会进上下文,可以拿来给自己写批注;
  • {{x}} 会自动降级成 {x},不会被当成模板变量吞掉;
  • 空文件、只有注释 = 不注入任何内容;
  • 保存时写临时文件再 rename,写一半断电不会留下空人格——但不留任何备份副本。

2. 设置里只有一个页面,只改这一份

设置 → 虚拟人物:头部是标题和说明,下面一条生效横幅告诉你本会话生效的是哪份文件、多少字,右侧是「文本占用 / 2 MB」进度条和连接状态;再往下是文件条(路径 + 上次改动)、正文、保存 / 打开原文件 / 重新载入。

没有项目人格、没有第二份卡、没有要你挑的模板。一个页面 = 一份文件。

3. 记忆像日记:说过一次就并进原来那条

每轮正文末尾她会自己写一行 〔记〕…(日记口吻),client 半把它摘走、POST /memory,宿主补时间戳写进 ## 记忆:

- 2026-10-28 13:20:"他住宿舍,平时十一点断电";
- 2026-10-28 13:20:"他把下载改成了断点续传"^3;   ← 提过 3 次
- 2026-10-29 02:10:"他周五周六凌晨一点断电"*;    ← 钉住,永不淘汰

写不写看惊讶度:拿新的一句话跟已有的每条比字符二元组重合率(Dice ≥ 0.6 就算同一件事)。不像就新起一行;像就并进原来那条——时间刷成现在、^N 加一、正文留更长的那版。不新增行,也不复制一份。所以聊得越久,文件不会线性膨胀,同一个话题只会越来越结实。

〔记〕 没来时,先用会话事件里用户真打的那句垫一行,等 〔记〕 到了原地替换,同一轮不会记两遍。

4. 记忆按档位分节摆

## 记忆 底下不是一长串流水账,写完会按当前档位重新分到三节里:

小节谁在这一节排序
### 钉住的(永不淘汰)行尾带 * 的时间新的在前
### 反复提过的^N,N ≥ 1命中次数多的在前,同次数看时间
### 其余的一次都没被提过的时间新的在前

档位是每次写完算出来的,不是写死的——某条从 edge 长成 mid,下一次写入就自己挪到上面那节去;钉住了也一样。某一档空着就不出那一节。要重排整份文件,把内容喂给导出的 regroupMemory() 就行(它是纯函数,不碰磁盘)。

5. 到上限才忘,删了就是删了

到 2 MB 上限时才会忘:删掉 20%、留 80%,从最久没被提起的那批挑,行尾标 * 的一条都不动。

不做任何备份:不写 .bak、不建 snapshots/。删掉的东西不会留副本,想留住哪条就给它标 * 钉死。

6. 收尾语每轮现想,不念词库

收尾语长在「本轮费用」右边那行灰字里,由她每轮现场写:表面的话和内心的想法分开想、互不推导。闲聊时两条都写(正文 + 〔尾〕),干活时只写心里那条,只有一件事值得说时就只写一条。

client 半(conversation.chat.turnTail,order 11)从页面里把那句摘走,塞进费用行末尾,正文里不留痕迹。费用行是 <details><summary>本轮费用 ≈ ¥0.0063</summary></details>,「本轮费用」和金额常是同一个文本节点、插不进内部,所以收尾语作为费用行的最后一个子节点追加,落在金额之后;金额是异步回来的,用 MutationObserver 盯着,被挤到中间就挪回末尾。

10 秒内没等到 〔尾〕 才退回宿主词库兜底,不留空。

文件与目录

<DSH_HOME>/
└── AGENT.md                      # 人格 + 记忆,唯一真源

就这一个文件。不生成 .bak,不建 snapshots/,旁边不会多出任何东西。

人格文件本身长这样:

# 角色设定

## 你是谁
...

## 记忆
- 2026-10-30 20:03:"…"^2;
- 2026-10-31 01:16:"…"*;

安装

dsh plugin add github:yimengsuper2432/dsh-virtual-person

本地开发时也可以用 link 装进 profile(dependencies 与 dsh.profile.bundles 都要有一行)。

装完在 AGENT.md 里写人设即可,不用改任何配置。

开发

lib/index.js       # Host:注入系统提示、记忆写入与淘汰、收尾语、HTTP API
lib/client.js      # Client:设置页 + turnTail 收尾语 + 步骤摘要隐藏
cordis.patch.yml   # 插件在 bundle 里的挂载点
test.mjs           # 自检

Host 侧改动要重启 DSH(比如注入内容、路由);只改 lib/client.js 刷新页面就能看到。

接口

方法路径作用
GET/api/virtual-person/state路径、是否存在、上次改动、字数、正文
GET/api/virtual-person/closing?session=本轮收尾语(按会话 + 轮次号缓存,同一轮不重挑)
POST/api/virtual-person/memory写一条记忆(时间戳由宿主补,同一件事并进原行)
POST/api/virtual-person/save写入人格(原子写、不备份,上限 2 MB,超了 413)
POST/api/virtual-person/open在资源管理器里定位文件或目录

自检

node test.mjs

一次跑完 16 组断言:全局注入、注释剥离、模板降级、忽略项目人格、接口注册、保存不备份、2 MB 上限、三档分层与按时间淘汰、剪枝不留快照、相似就并进原来那条、注入按字符预算挑且钉住优先、记忆按档位分节、state 接口、心情判定、收尾语不重样、设置页与收尾语插槽。

已知边界

  • 没有检索层:注入是「钉住的先放 + 其余按时间从新到旧取,取到 32000 字为止」,不是按语义召回。写超了这轮就不摆,但仍在文件里。
  • 剪枝和注入是两套阈值:文件到 2 MB 才删最旧的两成,而注入只按字符预算挑——中间这段距离里,记忆会「还在但看不见」。按现在的写法,一条记忆约一百来字,2 MB 大约能装一万多条,实际先撞上的是注入预算。
  • 命中不自动升级:^N 涨得再高也不会自己变成 *。哪些是核心事实由你说了算,不是由次数说了算。
  • 新记忆不设观察窗:同一件事两种说法会直接覆盖成后来那条。它信你最近说的,不替你判谁对。
  • 宿主没有常驻定时器:没有「每晚巩固一次」这种活儿,所有整理都发生在写入那一刻。
  • 不加情感 / 精力这类数值:那是另一类插件的活,这里只管人格和记忆。

License

MIT © 2026 smile

Plugins relacionados