dsh-headwater:給 Harness agent 一套"自己的記憶"

前言

長對話的 agent 最常見的死法是”聊着聊着忘了你是誰”。靠上下文窗口硬塞,遲早要被壓縮擋掉;壓縮一發生,”他爲什麼嚴謹”就只剩”他是個嚴謹的人”,來路沒了。向量檢索能把句子存起來,但存進去的是碎片,答出來的是相似度,既沒有”該不該說”的判斷,也說不清這個印象是從哪句話長出來的。

下面介紹 h1-cat 維護的 dsh-headwater(分類:記憶,當前星標 0)。它的思路不是”存更多”,而是用三層文件把”原話”和”理解”分開:原話永不改寫,理解可以隨時改,改錯了也有個底賬可查。

這是什麼

一句話定位:給 DSH Harness agent 加一套自己的記憶——原話一句不丟,從原話里長出”我理解你是誰”,開口前先判斷這話現在該不該說。

  • 維護者:h1-cat
  • 分類:記憶
  • 許可:MIT
  • 不裝模型、不做向量檢索、不切塊,只有三層文件加十個小工具

它把記憶分成三層:

<workspace>/.composure/
├── episodes.jsonl    L0 流水賬  每句話原樣追加,永不改寫
├── book.json
│   ├── portrait      L1 報表    "當前理解":他是誰 / 還沒了結 / 最近狀態
│   ├── relation      L2 賬本    熟悉、張力、承受力
│   ├── open          L2 賬本    懸着的話
│   ├── stances       L2 賬本    我說過的立場
│   └── sayings       L2 賬本    我說過的重話 + 後續反應

規則只有一條:L0 永不改,L1/L2 隨便改。理解錯了改報表,上了下一次重算就覆蓋;事實丟了說明流水賬裏本來就沒有。

安裝與啓用

官方安裝命令不是 dsh plugin add github:...,而是帶上 profile:

dsh plugin --profile desktop add github:h1-cat/dsh-headwater

粘貼到 PowerShell 回車,DSH 開着也沒關係。如果用的是網頁版(web profile),把命令裏的 desktop 換成 web。

然後重啓 DSH,設置頁裏會多出一個「記憶」。

卸載:

dsh plugin --profile desktop remove dsh-headwater

記憶數據不會跟着刪。它留在工作區的 .composure 目錄裏,是你的數據,不是這個應用的。

最常見的報錯是第二步提示「找不到 dsh 命令」,說明裝的是精簡版、便攜版或商店版,沒帶命令行工具。這種情況要手動裝一次,步驟在倉庫的 INSTALL.md 裏。別的報錯可以直接到 Issues 提交。

會話開始:記憶自己出現

插件每一輪把一小段掛在提示詞末尾,section 名爲 headwater,order 850:

## 我記得的
他是誰:…
還沒了結:…
最近狀態:…
## 分寸
熟悉 0.30,張力 0.00,承受力 0.62。關係平穩。
懸着的話:…
我說過:…

這不是等模型調用工具纔會出現的記憶,而是自動注入,通常三到十行。

說之前:問一次

headwater_check(draft, intent, topic)
  → verdict: say | soften | warn_then_say | hold | stop

五檔含義依次是:直接說;說但先接住對方狀態;先說”這話可能不太好聽”再講;先別說;別聊正事了,收一句留門的話。

第 3 檔處理”難聽但還是要說”,第 5 檔處理”對方不想聊了”。這兩條都不是”少說話”,而是分寸上的差別。

要原話:回查

headwater_recall(query, days, role, limit)

按時間或關鍵詞翻原文,返回帶時間戳的片段,不是摘要。之前說過什麼、哪天說的,可以直接查。

重寫理解

headwater_portrait(action: 'write', who, threads, mood)

整段覆蓋,寫成當前的判斷,會隨認識加深而變化,”這是對的”。

它自己會重算

不需要誰記得去調用。每 8 輪對話、且距上次重算超過 30 分鐘,插件會用一次輕量模型調用把最近 40 條原文壓成新一版理解。

這兩個旋鈕寫在數據目錄的 settings.json 裏,改完立即生效:

headwater_config(everyTurns=8, minMinutes=30)   每幾輪重算一次、兩次之間的最小間隔

也可以手動催一次:

headwater_recompute()

它返回 detail 說明這一步走到哪了:material(原文不夠)/ empty(模型只產出了推理)/ json(沒給可解析的 JSON)/ finish(調用被中斷)。

一個實際坑:deepseek-flash 這類推理模型會先把 token 預算花在推理上,正文可能一個 text-delta 都沒有。插件會先探測模型支持的最小 reasoning effort,再給 maxTokens: 3000。如果換模型後重算總是 at: 'empty',就是這個原因。

說話方式也跟着上

記住一個人說什麼,和說得像個人,是兩件事。後者只能靠原文裏的口氣,寫一句”他是個務實的人”學不來。所以每輪提示末尾還多一段樣本:

他平時是這麼說話的(原話,一個字沒改。這是標尺不是臺詞——學句長、斷句和用詞,別複述內容):
- 「……」
- 「……」

五條樣本由代碼從 episodes.jsonl 裏挑,不經模型,規則包括:長度 10–160 字,跳過代碼塊,末尾是問號的不收;按長度分三檔各取幾條,免得全是同一口氣同一話題;剛說的兩條不收,避免模型把”他剛說的”當成”我該說的”原地復讀;> 引用塊裏混着”我說過的話”的整條跳過;總字數超過 260 從最長的開始砍。原文一個字不改,模型順手潤色一次,口氣就被磨平了。

關掉:headwater_config(voice=false)。

需要說明:樣本給的是”人怎麼說話”的標尺,不是”你該是個怎樣的人”的設定,它不替你定性格。

話掉在地上,插件自己撿

不需要模型記得調用 headwater_open。每一句話進來插件自己看:

短(≤20 字)+ 問句之外 + 含「算了 / 隨便 / 不想說 / 沒事 / 別問了 / 無所謂 / 沒意思 / 不想聊」
  → 自動掛一條 relational 的懸着的話

去重、最多同時掛 5 條、標記 auto: true。

不做成”等模型自覺”的原因很直接:那等於讓最關鍵的動作依賴最不可靠的東西,模型聊到第三十輪想不起來調工具是常態。自動掛上之後,它會在下一輪提示裏出現,模型自己決定接不接。

不會被更新抹掉

三個機制擋住壓縮磨平印象這件事。

一是錨點,不參與壓縮,只增不減,每一輪都出現:

headwater_anchor(action='add', text='他要的是能跑起來的東西,不是說法')

重算時模型也能提議新錨點(anchors_add),但代碼只負責去重和追加,絕不覆蓋已有的。移除必須顯式調用並給理由,同時記進變更日誌。

二是變更日誌,每次重算記下 who / threads / mood 從什麼變成了什麼:

who:   …… → ……(多了"擔心理解被磨成標籤"這一層)

headwater_state 返回最近 6 條,能回答”它什麼時候開始這樣看我的”。

三是核心印象有保護期,who 默認 6 小時才允許改寫一次,threads 和 mood 每次都能變:

headwater_config(whoMinutes=360)

細節變得快,輪廓變得慢。

第四個是數據自己有歷史。賬本目錄(默認 .composure)如果是 git 倉庫,插件會把變化自動提交進去:每 autoCommitMinutes(默認 20)分鐘檢查一次,有變化就提交;每次重算”當前理解”之後立刻提交一次;commit message 帶原因和時間,例如 memory: portrait recomputed 2026-11-05 04:20。目錄裏沒有 .git 時不替你 init,只記一條日誌,倉庫邊界由人劃。判斷只用 git 退出碼,不讀子進程輸出,受限環境也能跑,失敗只記日誌,絕不影響記憶寫入。

這樣”它什麼時候開始這樣看我的”不僅可查變更記錄,還能用 git log / git diff 看到具體哪一版賬本長什麼樣:

git -C .composure log --oneline -5

反應記錄

每說一句較有分量的話(通過 headwater_check 之後實際說了),系統會盯着下一條用戶消息:追問細節記 engaged,回”嗯”“哦”“隨便”記 dismissed,其他記 acknowledged。這是全套裏唯一別人不會替你記的東西——說完之後那個人怎麼樣了。

它不做的事

  • 不做向量檢索、不切塊、不裝模型
  • 不改人設,聲音層給的是標尺不是性格設定
  • 不替你寫臺詞,只給結論和理由

適用場景與注意

適合交流長、需要記住此前說過什麼、以及需要判斷分寸的對話型 agent。它默認以當前 dsh 進程的權限運行,安裝前應先打開源碼看一眼,並確認 MIT 許可符合你的分發要求。

一條紀律貫穿整個實現:寫入中立,讀取帶傾向。賬本記的是事實(他說了什麼、隔多久、用了什麼詞),傾向只發生在解釋的時候。這樣換人設、換模型,底賬都是同一份。

數據都在自己的工作區 .composure,不聯網、不上傳,唯一一次對外調用是重算”當前理解”,用的是你在 DSH 裏自己配的那個模型。

關閉

在 profile 的 cordis.patch.yml 裏給 headwater 那行加 disabled: true,或在插件管理器裏停用。停用後不寫入任何東西,賬本留着。

結尾

dsh-headwater 的價值不在”多記住幾句”,在於把原話、理解和分寸拆成三層,讓原話永遠在、理解可重寫、說出口前過一次判斷。目錄頁在 https://www.skillhub.cn/plugins/h1-cat/dsh-headwater ,源碼在 https://github.com/h1-cat/dsh-headwater 。

羽毛球分组比赛记分
小程序二维码

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

小夜