dsh-gulu-diary:只在值得写的日子生成日记的 DSH 插件

前言

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 分三步:

  1. profile 的 package.json:dependencies 里加 "dsh-gulu-diary": "link:<这个目录的绝对路径>",并把 dsh-gulu-diary 加进 dsh.profile.bundles。
  2. profile 的 cordis.patch.yml:加一条 entry,可以参考仓库根目录的 cordis.patch.yml。
  3. 在 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
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜