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
羽毛球分组比赛记分
小程序二维码

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

小夜