前言¶
DSH 的会话转录每天都在涨,但多数天里其实没什么值得记住的事。如果给智能体挂一个「每天自动写一篇日记」的能力,最直接的问题是:谁来判断哪天值得写,写的时候也不该去碰正在跑的会话。dsh-gulu-diary 把这件事拆成了独立的一条链路:只在日界之后采集前一天的会话素材,先判定 YES / NO / WAIT,值得写才用人格文件加只读记忆生成一篇日记,存进独立的 diary.db,同时导出一份可直接打开的 Markdown。它不创建、不恢复、不 steer 任何会话,写日记是一次独立的模型调用。
下面介绍这个插件。它由 TheCarey 维护,分类是「记忆」,在社区目录 SkillHub 的页面上星标为 0,当前版本 0.9.1(GitHub README 给出),许可证 MIT。
它解决什么问题¶
智能体长期运行后,对话内容散落在会话转录里,事后回顾往往要做大量的筛选工作。已有做法的常见问题是:要么每天固定写一篇,把没有信息量的日子也写出来;要么只在对话里顺便记,归属于会话状态,不方便事后审计。这个插件把「判断值不值得写」和「真正动笔」分成两步,并且在两者之间保留了完整的素材、信号和提示词留档,方便事后检查某天为什么被判为 NO。
它面向的是跑在 DSH 里的、有长期人格设定的使用场景。日记正文只给自己看,不联网上传,不发消息,不做云同步。
核心功能¶
按窗口采素材。插件把一天的会话转录(session.v4.jsonl.zstd)解出来,按时间戳逐条过滤,只留 04:00 → 次日 04:00 的内容,日界小时可在配置里改。
先判定,再动笔。窗口关闭后插件只花一次很短的模型调用回答 YES / NO / WAIT。判 NO 就只留一条判定和理由,不生成正文;窗口还没结束不判;当天用户说话少于 3 句直接跳过,不调模型。WAIT 表示素材像半截,下个 tick 再问,连续两次后要求给出结论。每一天只判一次,判定结果落在 diary_verdicts,重启后直接复用,想重判用 CLI 的 judge --force。
三段式记忆注入。先宽召回候选(soul 全部 + 关键词/项目名/标题打分,上限 60 条),再让模型挑真正和这天有关的 5–12 条,最后动笔前先让模型把那天想明白,产出四小节场景理解,内部留档、不直接进正文提示词。记忆库全程以 readOnly: true 加 PRAGMA query_only 打开,读完即关,不写 memory.db。
独立存储与可审计。日记写在自己的 diary.db,正文另存 日记/YYYY-MM-DD · 标题.md;每次都会发生的事情全部留在 pending/<日期>/:素材、判定信号、两段提示词、模型原始返回、记忆挑选记录、场景理解、重试记录等,生成日志另记一份 generation.log。素材默认保留 7 天,到期只删素材目录,不碰日记正文和 .md。
设置页。DSH 设置里的「日记本」可以按日期阅读、切模型、看待写日期和本次注入了几条记忆。列表按日期倒序,只取元数据,不点开不加载正文;被跳过的日子显示为一行淡字,不会被误看成漏写。
工作链路¶
整体流程如下,每一节只做一件事。
DSH 启动 / 每 15 分钟
→ prepareDue:日界后缺哪天 → 采素材 + 拼基础提示词 + 压一份 ≤2000 字判定信号
→ judgeDay:只在窗口关闭后判;说话 < 3 句直接跳过,不调模型
→ WAIT:素材像半截,不记判定,下个 tick 再问(连续两次后要求给结论)
→ NO:写判定结果,素材留着,不写正文
→ YES:
loadPersona(完整读取人格文件,不改写、不概括)
recallMemoryCandidates(只读 memory.db,上限 60)
selectMemoriesWithModel(素材摘要 + 候选清单 → 挑 5–12 条)
buildUnderstanding(素材 + 挑中的记忆 → 四小节场景理解,内部留档)
buildGroundedPrompt(system = 人格 + 挑中的记忆;user = 写法 + 素材 + 场景理解)
ctx.llm.stream(独立调用,不带 sessionId)
校验 frontmatter / 日期 / 视角
→ diary.db + 日记/YYYY-MM-DD · 标题.md + pending/<date>/ 留档
→ 每个 tick 末尾:清理超过 retentionDays 天的 pending 素材
它明确不做这几件事:不创建 / 恢复 / steer 会话,请求里不带 sessionId;不写记忆库;不联网上传日记;也不保证每天一篇,「不值得写」是这个插件的正常结论之一。
两种降级不挡成篇:记忆挑选失败(超时、读不懂、id 全对不上)时降级成「soul 全部 + importance 最高的 3 条 user」;场景理解失败或四小节不齐时丢掉它,直接拿素材写。两种降级都会写进 generation.log 和 pending/<date>/meta.json 的 degradedSteps。
安装与启用¶
先看依赖,readme 里写明的三项前置条件缺一不可。
1、Node ≥ 24,插件用到 node:sqlite、node:zlib 的 zstd 解码。
2、一个能跑 DSH 插件的 profile,且具备 llm、tools、webServer、settings 这几项服务。
3、可读的会话转录目录(sessionsDir)和人格文件(persona.path,默认 ~/.dsh/AGENT.md),以及一个 meow-memory 风格的 memory.db。
手动放进 profile 分三步:
- profile 的
package.json:dependencies 里加"dsh-gulu-diary": "link:<这个目录的绝对路径>",并把dsh-gulu-diary加进dsh.profile.bundles。 - profile 的
cordis.patch.yml:加一条 entry,可以参考仓库根目录的cordis.patch.yml。 - 在 profile 的
node_modules/里建一个指向本目录的链接(Windows 下是 junction)。
也可以用仓库自带的脚本,它会先备份 profile 的 package.json / cordis.patch.yml,再写配置、建链接、检查 BOM:
node scripts/install-profile.mjs --profile "$HOME/.dsh/profiles/desktop" --package-path .
脚本不会重启 DSH,也不会碰 memory.db。这不是一条 dsh plugin add 类的命令,plugin 目录页和 README 里都没有给出这种一键安装方式。
配置参考仓库根目录的 cordis.patch.yml,最小可用配置如下:
- insert:
- id: dsh-gulu-diary
name: 'dsh-gulu-diary'
config:
enabled: true
dataDir: '~/.dsh-gulu-diary'
sessionsDir: '~/.dsh/sessions'
windowStartHour: 4
persona:
path: '~/.dsh/AGENT.md'
memory:
enabled: true
dbPath: '~/DSH-Memory/memory.db'
schedule:
enabled: true
配置里 ~ 会展开成当前用户的家目录,也支持相对路径,最终都会解析成绝对路径。常用于调整行为的键包括:windowStartHour(日界小时,0–23)、memory.maxEntries / memory.maxChars(最终注入条数与总字数上限,默认 20 / 6000)、memory.recallLimit(召回候选上限,默认 60)、memory.pickMin / pickMax(默认 5 / 12)、schedule.checkMinutes(默认 15)、schedule.maxCatchUpDays(默认 7)、generation.maxTokens(默认 4096)、retentionDays(默认 7,0 表示不自动删)、judge.minUserMessages(默认 3)、judge.maxSignalChars(默认 2000)。完整键表见 README 的「配置项」一节。
inject、工具、路由都是模块级注册,改完配置必须重启一次 DSH。重启后打开 <dataDir>/plugin-load.log,确认三件事:有 apply.done;有 routes.registered 且 failed 为空;工具列表里出现 diary_*。再刷新 DSH 设置 → 日记本,能看到库路径和篇数,就说明 Host 和前端都挂上了。
典型用法¶
走 CLI 可以在不启动 DSH 进程的情况下核对素材、调判定标准、手动补写。所有命令默认只写 --data-dir 和必要的 output/,不碰 memory.db。先采素材,只读转录、不写日记:
$NODE = "node"
$DATA = "$HOME/.dsh-gulu-diary"
$SESS = "$HOME/.dsh/sessions"
& $NODE src/cli.js collect --date 2026-11-01 --sessions-dir $SESS --print-prompt
再把素材和提示词放进 pending/:
& $NODE src/cli.js prepare --date 2026-11-01 --sessions-dir $SESS --data-dir $DATA
只判定不写正文,--force 忽略已有判定重判:
& $NODE src/cli.js judge --date 2026-11-01 --force --data-dir $DATA --sessions-dir $SESS `
--base-url http://127.0.0.1:8080/v1 --model your-model --api-style chat
手动写一篇:先判定,判 skip 就不写,--force 可无视判定强写:
& $NODE src/cli.js generate --date 2026-11-01 --data-dir $DATA --sessions-dir $SESS `
--base-url http://127.0.0.1:8080/v1 --model your-model
只跑记忆筛选,打印挑中 / 丢掉和原因,不写日记也不写 pending:
& $NODE src/cli.js select --date 2026-11-01 --data-dir $DATA --memory-db "$HOME/DSH-Memory/memory.db"
素材保留期先干跑再真删,只动 pending/<日期>/:
& $NODE src/cli.js cleanup --dry-run --data-dir $DATA
& $NODE src/cli.js cleanup --days 7 --data-dir $DATA
其余还有 pending、list、read、search、hooks、plan、doctor 几个子命令,以及读取正文、按关键词搜索的能力。judge / generate / select 都需要一个 OpenAI 兼容端点(--base-url、--model、--api-key、--api-style chat|responses),也可以用环境变量 DSH_DIARY_JUDGE_URL / DSH_DIARY_JUDGE_MODEL / DSH_DIARY_JUDGE_API_KEY 给出。
后端同时开了 HTTP 接口,列表和详情只读,生成与模型切换是显式写操作:
GET /gulu-diary/api/list
GET /gulu-diary/api/detail?date=YYYY-MM-DD
GET /gulu-diary/api/pending
POST /gulu-diary/api/generate {"date":"YYYY-MM-DD"}
GET /gulu-diary/api/models
GET /gulu-diary/api/model-info?provider=...&model=...
POST /gulu-diary/api/model {"mode":"follow"} 或 {"mode":"explicit", ...}
GET /gulu-diary/api/health
list 不返回 content,只带 pending 和 skipped;detail 才返回全文。模型接口只接受 ctx.llm 当前目录里真实存在的组合。
数据与目录¶
日记库、正文、留档都落在 dataDir 下,注意日记库是独立 SQLite,不是记忆库:
<dataDir>/
├── diary.db 日记库(独立 SQLite,不是记忆库)
├── 日记/
│ └── YYYY-MM-DD · 标题.md 可直接双击阅读的正文
├── pending/<日期>/ 素材与留档,默认保留 7 天
│ ├── materials.md 素材原文
│ ├── signal.md 判定用的 ≤2000 字信号
│ ├── prompt.md 基础写作提示词
│ ├── prompt.system.md 实际注入的人格 + 记忆
│ ├── prompt.user.md 实际注入的写法 + 素材
│ ├── prompt.grounded.md system + user 合并
│ ├── response.md 模型原始返回
│ ├── understanding.md 动笔前的四小节场景理解(内部)
│ ├── memory-selection.json 候选 / 挑中 / 丢掉 + 理由
│ ├── attempts.json 重试记录
│ └── meta.json degradedSteps 等元信息
设置界面是只读为主的:顶部显示库路径、日记/ 路径、总篇数、素材保留天数;可以按 provider 挑写日记用的模型和推理档,选中即写回 profile 配置并热生效;详情按标题 / 段落 / 引用渲染,有 pending 时顶部可以「现在生成一篇」。日记正文只读,不提供编辑和删除,也不自动打开资源管理器。
适用场景与注意¶
适合两类人。一是给 DSH 配了长期人格、希望有一份不受会话干扰的跨天记录的开发者;二是想事后审计「哪天为什么被判 NO」、需要完整提示词与模型返回留档的人。不想每天被填满无意义正文的人,比想天天记录的人更适合它。
注意事项按实际约束写清楚。插件以当前 DSH 进程的权限运行,安装前建议先检查源码与 package.json(版本、dsh 字段、LICENSE),别凭目录页的简介直接上生产 profile。它需要 Node ≥ 24,老版本的 Node 环境跑不起来;memory.enabled 关掉后插件会拒绝生成,不允许裸模型写日记,所以不要想在没有记忆库的情况下简化使用。判定模型和正文模型默认跟随 DSH 当前主模型,若改为指定 provider 和 model,两个都要填。README 里列出的质控信号(说话少于 3 句不判、窗口未关不判、WAIT 连续两次后要求结论等)都在代码里实现,如果自己改配置,注意 judge.minUserMessages 调低会让更多天进入模型调用。
结尾¶
dsh-gulu-diary 的价值不在于写得多,而在于把「值不值得写」当成了一个独立判断,并且用独立存储和完整留档让这个判断可以被事后检查。记忆库只读、日记库独立、素材自动清理,三条约束让它可以长期挂在 profile 里而不干扰主流程。目录页见 SkillHub 的插件页,源码与完整 README 见 GitHub 仓库,安装与配置细节以这两个来源为准。
- SkillHub 目录页:https://www.skillhub.cn/plugins/TheCarey/Dsh-GuLu-diary
- GitHub:https://github.com/TheCarey/Dsh-GuLu-diary