DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

c137650 /

c137650/dsh-plugin-life

Verified

DeepSeek Harness plugin for agent to simulate a real life

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@16162de2

dsh-plugin-life

给 DeepSeek Harness 里的角色装一套模拟的生活,以及一套跨会话记忆。

设计目标来自一句话:「这种虚拟感来源于静止」。所以这个插件的全部工作, 就是让 agent 不再停在原地 —— 有作息、有状态、有自己去做的事、有记忆、 会因为被打断而生气,而且记得住你。

  • 她自己会去搜索、抓网页、把读到的内容存成笔记。不是模拟,是真发生。
  • 她有自己的作息:睡、吃、醒。醒着的时候在做事,做完会写感受。
  • 她和你说话时,上下文里会多出两段背景:她自己的生活,和关于你的事。

一、它到底做什么

1. 生理约束的有限状态机

睡 / 吃 / 清醒。睡眠 6–10 小时、两餐间隔 4–11 小时、连续清醒不超过 16 小时 —— 这些是边界,不是日程表。边界内的取值来自历史统计与随机采样。

第 2 天起由前 n 天决定:睡眠时长、醒来时刻、餐点都是前几天的条件采样结果。 跑久了会形成稳定的昼夜节律。

硬编码的只有生理学边界(在 lib/physiology.js 里,一眼能看全)。 其余全部随机 —— 这是刻意的:写死的东西不会让她像活的。

2. 真实活动

清醒时由插件自己真的搜索、真的抓取页面、真的把读到的内容存成笔记文件。

  • 搜索走 ctx.web.search
  • 抓取用 Node 自带的 fetch。不走 ctx.web.fetch —— 某些构建里注册了多个 fetch provider,web.fetch 不知道该用哪个会直接抛 WEB_PROVIDER_AMBIGUOUS, 连会话里的 web_fetch 工具都会跟着不可用。插件不该依赖一个需要全局配置才能工作的通道。
  • 为什么不是"派个后台 agent 去做":实测 ctx.agents.create() 不指定 agent preset 时 只继承根作用域插件,而网络工具挂在 preset 作用域里 —— 活动 agent 只能看到日程、 记忆和文件读写,它自己报告「没有浏览器,没有网络请求」。
  • 抓不到就试下一个:候选比目标多(最多多 2 个),凑够 1–3 个成功就停。

抓取链按"能拿到最多内容"分级,任何网址都能抓,不是白名单:

级别 什么时候用 得到什么
1. PDF 链接是 .pdf 或 arxiv.org/pdf/… 论文原文(PyMuPDF 抽文本)
2. 站点接口 arXiv / B站 / GitHub / Wikipedia 摘要、简介+字幕、README
3. 普通 HTTP 其余所有网址 HTML 剥标签
4. 无头浏览器 前一级内容太薄或返回 403 Playwright 渲染后取正文

第 4 级只在前面失败时触发,因为启动浏览器要几秒。

3. 事件库与压缩

每件真发生过的事写进 SQLite,带摘要、经过、感受、两个分数(interesting / important)。

只记录真发生的事:一次活动要入库,必须至少抓到一个 2xx 且正文非空的页面。 搜不到、抓不到、正文为空,一律丢弃重排 —— 写手给出的检索目标不会被当成事件写进去。 目标单独存 intent 列,与 content 分开。

压缩是分级的,不是一刀切:

综合分 = importance·0.6 + 新鲜度·0.4     新鲜度按 7 天半衰期衰减
分数前 10%   完整保留,不计入压缩次数
中间 70%     剥成"标题 + 感受"
最老 20%     只留标题
第 3 次被压缩 → 整行删除

keep = 1(importance ≥ 0.75 的重要事件)不参与压缩,永久保留。

4. 记忆挂载

每轮对话前把记忆挂进上下文,但永远不压过人格,也永远压不过你当前说的话。

挂载是有优先级的,而且是三块不重叠的:

永远记得的:   keep=1 的事件,全部挂载,不设条数上限
记得这些事:   importance ≥ 0.5 的,取 2 条
最近:         最近的,取 2 条

同一件事不会在两块里出现两次。

5. 跨会话记忆:关于你的事

这是和"她自己的生活"分开的一个库(captain.db)。 分开的理由很实际:任务和代码写进生活记忆只会污染它。

每轮对话结束后:

1. 规则闸门判「这一轮有没有关于你的持久信息」      —— 不花模型钱
2. 过了闸门的,把「你说的话 + 她回的话」一起喂给模型,
   压成一句第三人称、≤60 字的概括              —— 压整轮,不是只压你的原句
3. 用这句话的句向量去判重:像就去重,不像就入库

然后按热度决定谁能被挂载:

挂载 = 近 7 天写的  ∪  热度 ≥ 0.5
热度 = 每次"被你再次提到"贡献 exp(-Δt/τ),半衰期 14 天

没有任何事件能一直挂载。 7 天后只有半衰期内被提过的才继续浮着; 超过 14 天没提且热度低于 0.25,正文剥掉沉底 —— 但行和向量留着, 你下次再提,热度回来就又挂上了,只是细节想不起来了。

6. 打断与追赶

DSH 关掉期间的时间会被快进。重启后她说得出「你关掉的三个小时里有一顿饭被错过了」—— 不是空泛的抱怨,是按延迟算出来的严重程度。


二、安装

需要 DeepSeek Harness 已经跑起来,并且装了 dsh-plugin-masterprompt (人格来源;没有它插件也能跑,只是没有后台人格可用)。

# 1. 放到插件的搜索路径下
git clone <this-repo> "%DSH_HOME%/plugins/dsh-plugin-life"

# 2. 装依赖(只有一个 peer)
cd "%DSH_HOME%/plugins/dsh-plugin-life"
npm install

# 3. 重启 DeepSeek Harness

%DSH_HOME% 默认是 C:\Users\<你>\.dsh。 插件的加载靠 cordis.patch.yml,它在仓库根目录,不要改名字。

跨会话记忆使用了embedding模型完成相似任务(可选,不装也能用,只是没有向量):

# 模型:约 91 MB,从 modelscope 下(huggingface 在国内常常连不上)
python -c "from modelscope.hub.snapshot_download import snapshot_download; \
snapshot_download('AI-ModelScope/bge-small-zh-v1.5', cache_dir=r'C:\Users\<你>\.cache\modelscope')"

默认会到 ~/.cache/modelscope/AI-ModelScope/bge-small-zh-v1___5 找模型, 也可以放在 %DSH_HOME%/dsh-plugin-life/models/bge-small-zh-v1.5。 找不到模型时插件照常工作,只是判重和"提及检测"退化成纯关键词。

python 解释器默认找 E:\anaconda\envs\tts0\python.exe,可以用环境变量 CAPTAIN_PYTHON 指定。


三、依赖

运行时(必需)

依赖 用途 说明
@deepseek-ai/cordis ^4.0.1 插件宿主 peer dependency
Node.js ≥ 22 node:sqlite 用了内置的 DatabaseSync,不装 better-sqlite3

跨会话记忆(可选,但要先装好)

依赖 用途
Python 3.12 + PyTorch 跑句向量模型
transformers 加载 bge-small-zh-v1.5
modelscope 下载模型(也可以用 hf-mirror)
bge-small-zh-v1.5 512 维中文句向量,24M 参数,CPU 单句约 13 ms

抓取增强(可选,缺了就降级)

依赖 用途
Python requests / beautifulsoup4 / lxml 普通抓取
Python PyMuPDF PDF 抽文本
Python playwright 第 4 级渲染(反爬站点)
Python yt-dlp B 站视频信息

插件启动时会自检这些,结果写进 status.json。探不到就整块降级,不会崩。


四、目录结构

dsh-plugin-life/
├── cordis.patch.yml          插件声明(DSH 靠它加载)
├── package.json
├── README.md                 本文件
├── prompts/                  ★ 所有影响"她怎么说话"的文字
│   ├── README.md             怎么改,以及"后台人格必须和前台一致"
│   ├── system-wrapper.md     人格包装层(system prompt 的前两行)
│   ├── writer-task.md        写手:决定此刻去做什么
│   ├── writer-feeling.md     写手:做完之后写记录
│   └── captain-summary.md    跨会话记忆:把一轮对话压成一句
├── lib/
│   ├── index.js              入口:心跳、状态机、工具注册、记忆挂载
│   ├── clock.js              作息排程(边界内采样)
│   ├── physiology.js         ★ 唯一的硬编码生理边界
│   ├── schedule.js           活动排期与错过判定
│   ├── catchup.js            下线追赶
│   ├── activities.js         活动类别
│   ├── writer.js             ★ 两个写手调用(带人格)
│   ├── persona.js            ★ 人格来源 + system prompt 组装
│   ├── prompts.js            ★ prompt 加载器(md 优先,代码兜底)
│   ├── model.js              模型路由、额度熔断、流式收集
│   ├── db.js                 life.db 的表与查询
│   ├── compress.js           事件压缩(三级 + 第三次删除)
│   ├── memory.js             生活记忆挂载
│   ├── reading.js            笔记文件(reading/)
│   ├── webactivity.js        真实抓取
│   ├── pysupport.js          Python 桥自检
│   ├── netcheck.js           网络探活
│   ├── executor.js           活动执行与产出观察
│   ├── notify.js             主动通知
│   ├── icr.js                人格一致性回归(ICR)
│   ├── prng.js               确定性随机
│   └── captain/              ★ 跨会话记忆
│       ├── index.js          串流程:捕获 / 判定 / 挂载
│       ├── engine.js         热度、判重、挂载选择、沉底
│       ├── rules.js          规则闸门(词表可被 json 覆盖)
│       ├── db.js             captain.db 的表与查询
│       ├── summarize.js      LLM 压缩(带人格)
│       ├── bridge.js         常驻 python 子进程的管理
│       └── embed_server.py   句向量服务
├── test/                     自包含测试
│   ├── prompts.test.mjs
│   └── captain-memory.test.mjs
├── eval/probes.json          ICR 探针集
└── tools/                    开发用脚本

运行时数据全部在 %DSH_HOME%/dsh-plugin-life/,和代码分开:

life.db          她自己的生活事件
captain.db       关于你的事
status.json      加载状态、模型、熔断、上次错误
user_profile.md  你的偏好(影响活动选择,也会挂载进上下文)
persona.txt      可选:指定用哪个人格模板
reading/         她存的笔记
keepers.md       影响足够大的事(只写给看,不参与挂载)

五、配置与使用

配置文件

文件 作用
user_profile.md 你的偏好。影响活动选择、事件打分,并且每轮全量挂载
persona.txt 可选。写一个 masterprompt 模板 id 指定用哪个人格
captain_rules.json 可选。覆盖跨会话记忆的规则词表,{"plan": [...], "task": [...]}

对话里的两个工具

工具 用途
life_status 查心跳、库行数、上次错误。tick=true 强制一次心跳;probeWeb=true 真发一次搜索验证网络
life_open_detail 开一个新会话,详细讲某件事

诊断端点

DSH 的 GUI 里看不到插件日志,所以状态落盘 + 本机回环端点:

位置 用途
status.json 加载状态、人格、模型、熔断状态、上次错误
POST /dsh-life/icr/start 跑人格一致性回归(后台执行)
GET /dsh-life/icr/status 轮询进度
GET /dsh-life/icr/result 完整结果

六、设计逻辑

为什么不硬编码

写死的东西不会让她像活的。所以:

  • 硬编码的只有生理学边界(睡多久、隔多久吃饭、最多清醒多久)
  • 边界内的每一个数来自前几天的条件采样 + 确定性随机(按日期做种子,同一天重跑结果一致)
  • 活动内容由 LLM 生成,不是剧本

为什么两个 LLM 调用分成两步

环节 时机 人格的作用
composeTask 执行前 决定她挑什么、怎么框定。产出必须自包含(执行者是全新会话,没有记忆)
composeFeeling 执行后 决定语气和留下的痕迹。吃的是真实执行结果,不是预写

为什么分开:执行前写的感受是编的;而且两条指令要求相反 —— 任务要"冷",感受要"热"。 混在一起,人格腔调会渗进任务指令,产出"你最近不是想看 X 吗"这种依赖执行者记忆的说法。

人格怎么进后台

后台用的 system prompt 和前台会话里那份是同一份:包装层 + 人格全文。 不是摘要,不是"你是一个 XX 性格的助手"。

理由见 prompts/README.md:一旦两边不是同一份,读数据库里的记录就像在看 别人照着她的语气写的日记。

为什么记忆是"背景"不是"指令"

挂载的记忆作为 user 角色消息插在你当前这句话之前,而且明确写着 「是背景,不是对舰长的指令」和「舰长当前说的话优先于这里的一切;人格优先于记忆」。

不放进 system prompt 的原因:那会让记忆获得比人格更高的稳定性 —— 记忆是会陈旧的,人格不该被一段三周前的经历改写。

跨会话记忆为什么先过规则再过向量

  • 规则回答"值不值得当候选":词表加加减减,零成本,可解释,出错能定位到具体哪个词
  • 向量回答"是不是同一件事":这是相似度任务的看家本领,实测同一件事的不同说法 0.700.80,不同的事 0.290.55,中间空得很宽
  • 热度回答"以后还会不会被提到":这是时间的活,不是分类的活

三者分工明确。用 LLM 做第一件事会烧钱且不可解释;用规则做第二件事会误判。

为什么记忆要压缩成一句话而不是存原话

原话直存等于把"啰嗦"当成"重要"。一段五百字的吐槽和一句"我下周去杭州"占的预算差十倍, 但后者才是要记住的那件。

所以用同一个人格把整轮对话(你说 + 她回)压成一句 60 字的第三人称概括。 prompt 里钉了两条:不许照抄原句、超过字数硬砍(不信任模型自己数)。


七、技术实现

关键机制

机制 实现
心跳 ctx.interval,默认 4 分钟一次 tick
记忆挂载 agent/pre-step 钩子,把 payload 插到最后一条 user 消息之前。不写入会话历史,所以不会累积
回合判定 session/event 的 turn/end
状态机推进 每个 tick 读 status.json 的待办活动,判"该不该、来不来得及、错过了多重"
额度熔断 识别 429/402/quota/余额错误后关掉所有 LLM 入口,只保留不需要模型的部分
模型跟随 每次调用前重新读主会话的选择,主会话换模型后台最多 2 秒跟上

node:sqlite 的两个坑

  1. LENGTH() 数的是字符不是字节。中文一个字 3 字节,直接少算三分之二。 要用 length(CAST(x AS BLOB))。
  2. 统计库体积时漏掉最大的字段(detail,每个活动 10–11 KB),阈值会形同虚设。 两个错叠起来,2 MB 的阈值实际对应 11 MB 磁盘、1700 个事件,压缩几乎永远不触发。

句向量服务

常驻 python 子进程,模型只加载一次(冷启动约 4 秒),单句约 13 ms。

  • 不在插件加载时启动,推迟到"用户第一次开口" —— 那时候模型正在生成回复, 4 秒冷启动正好被盖住
  • 子进程 unref():宿主退出不等它;宿主一退 stdin 断开,python 自己就退了
  • 失败、超时、模型缺失 → 返回 null,判定退化成纯规则,不影响对话

八、不做什么

  • 不改人格。dsh-plugin-masterprompt 的 state.json 只读,一个字节都不动。
  • 不硬编码日程。所有可变的量来自历史 + 随机。
  • 不在用户会话里产生回复。活动不占用任何会话,结果只进数据库。 只有"值得说的事"才会通过通知进来。
  • 不跨库写入。生活事件进 life.db,关于你的事进 captain.db,互不读写。

九、已知限制

  • DSH 没有浏览器自动化,只有 HTTP。所以「看视频」是抓取视频页的标题/简介/评论, 「看电影」是抓取影评/剧情 —— 都是真实网络活动,但不是播放。
  • 活动 agent 会在 GUI 会话列表里短暂出现再消失(创建后立刻销毁)。DSH 的会话列表没有隐藏开关。
  • 跨会话记忆的规则闸门是词表:没见过的表达会漏判。要补就改 captain_rules.json。
  • 同一件事后来补充的细节不会写进已有那条记忆(那需要再压一次,现在没做)。
  • 挂载记忆有轻微的身份漂移风险。回归测试见 ICR 端点。

十、开发者

cd dsh-plugin-life
node test/prompts.test.mjs          # prompt 加载器(不依赖模型)
node test/captain-memory.test.mjs   # 跨会话记忆端到端(需要 python 环境 + 模型)

许可

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 16162de22bc6

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout