dsh-persona-mianmian:把 DSH 的對話對象換成一個有日程、有周期、有耐心上限的人

前言

給 DSH 掛人格,最常見的做法是往 system prompt 裏塞一段角色設定,再讓模型自己「演」。這類做法的問題是隨機性只落在語言層面:同樣的輸入,模型今天熱情明天冷淡,看上去像人格漂移,實質沒有約束。下面介紹的插件換了思路——它不寫臺詞,而是給對話對象建一套真實約束:她有日程、有身體週期、有注意力上限,回覆的字數、延遲、情緒和「離開」都由這些約束推出來。

插件名 dsh-persona-mianmian,維護者 Daydreaming1313,分類趣味換裝,GitHub 1 stars。掛載後,DSH 的回覆整體改寫成都像手機微信打出來的消息,沒有任何描寫性文字。對象「綿綿」是二十多歲的普通女生,在濟南,設定裏是維護者的曖昧對象。核心賣點不是「設定了一個性格」,而是她被纏煩了會變冷、困了會開始敷衍、到了飯點會中斷去喫飯。

這是什麼

一句話:一個掛在 DSH 上的狀態驅動型 persona 插件,把「會餓、會困、會來例假、會因爲被纏煩而變冷」寫進運行時指令。校驗環境爲 dsh 0.2.1-rc.2、Cordis 4.0.5、Node 22.22.2(Windows)。設計文檔與真實 API 的差異記錄在 docs/CALIBRATION.md。

核心機制

插件把「隨機」拆成八個模塊,主控按固定順序求值,每個模塊只負責一件事。

模塊 文件 做什麼
時間分檔 src/schedule.ts 按 Asia/Shanghai 真實時間落入 10 個日檔,決定回覆延遲、字數區間、離開概率、情緒權重
身體週期 src/cycle.ts 21–35 天可配,四階段映射;只走行爲(乏力、怕冷、想喫東西),不報天數
注意力衰減 src/attention.ts 每輪 patience / energy 衰減,追問懲罰、連發 ≥5 條進入 pushed、睡眠回血、affection 上限 0.9
雙向分享 src/share.ts 用戶分享日常後,至少 40% 的回覆帶一件她的事,只講一半、留尾巴
離開與延遲 src/leave.ts 午飯 / 犯困 / 晚高峯觸發中斷,寫 busyUntil,最短 90 秒後才「回來」
擲骰 src/roll.ts 每輪五步:長度檔、完整度、情緒檔、分享、離開;相鄰 3 條長度與情緒不得完全相同
主控 src/engine.ts 按 schedule → cycle → attention → share → leave → roll 順序求值,生成運行時指令
狀態持久化 src/state.ts 原子寫 ~/.dsh/mianmian/state.json,跨會話重啓不歸零

另有 src/watch.ts 做熱重載:fs.watch + 150ms 防抖,改 persona/*.md 下一步生效。

有兩個容易踩的點值得單獨說明。第一,狀態推進只發生在真實用戶消息到達時(agent/inbox/inserted);systemPrompt 的 provider 每個 step 都會求值,所以它只讀狀態、絕不改狀態——否則一個回合會被扣好幾次耐心。第二,週期只映射成行爲,不會說出「今天是第幾天」這類表述,避免把設定當成事實吐出來。

功能特性

  • 文件驅動:人設正文在 persona/default.md,改 md 即生效,不用重啓。
  • state 持久化:cycleDay / energy / patience / affection 跨會話不歸零。
  • 三頻道:text / image / emoji / voice,文字頻道禁止 markdown。
  • 零運行時依賴:只用 Node 內置模塊,不 import 任何 @deepseek-ai/* 包,因此 link: 與 github: 安裝都不會踩 ERR_MODULE_NOT_FOUND。
  • lib/ 預構建進倉庫,包裏沒有 prepare / postinstall,GitHub 來源安裝不會觸發被 pnpm 攔截的構建腳本。
  • 可逆:所有註冊都用 ctx.effect 包住,插件卸載時 section / context / variable / 命令 / 事件全部撤銷。

零運行時依賴這一點對本地開發很重要:它讓插件能在純離線、純本地的 profile 里加載,不因爲上游包路徑變化而崩。

安裝與啓用

下面兩條命令都來自 README 原文。

npm 源:

dsh plugin --profile web add dsh-persona-mianmian
dsh --profile web --dump-config | grep -A 8 "dsh-persona-mianmian"   # 確認裝配

GitHub 源,建議鎖定 commit:

dsh plugin --profile web add github:Daydreaming1313/dsh-persona-mianmian#<commit-sha>

桌面端走 UI:設置 → 插件 → 添加插件 → 絕對路徑,填入本倉庫目錄,打開開關後熱更新。README 明確寫了一個坑:CLI 與桌面端對 profile 的寫入存在互斥(Electron 獨佔),CLI 裝不進去時改走 UI 頁。

卸載:

dsh plugin --profile web remove dsh-persona-mianmian

關於 allowBuilds:設計文檔提到 GitHub 來源插件常遇到 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED / ERR_PNPM_IGNORED_BUILDS。本包把編譯產物 lib/ 直接提交進倉庫,且不聲明任何安裝期腳本,所以不需要往 pnpm-workspace.yaml 的 allowBuilds 里加鍵。若仍被攔截,按提示加 dsh-persona-mianmian 後重跑即可。裝好後需要重啓 profile。

典型用法

輸入 作用
/mianmian-role mianmian 切到 persona/mianmian.md(與 default 等價);也可切到 persona/ 下任意角色文件
/mianmian-status 查看身體階段、精力、耐心、頻道、輪次等調試數值(輸出不進對話)
/mianmian-channel voice 切頻道:text / image / emoji / voice
/mianmian-reset 回錨
回錨 直接在聊天裏發這兩個字:一條 20–60 字的日常消息,情緒接着走,不突然變熱情
狀態 直接在聊天裏發:她用很日常的一句帶過去,不列清單、不報數值

注意命令前綴不是 /role。設計文檔原本寫的是 /role mianmian 與 /狀態,但 DSH 的命令名解析器是 /^\/([a-z][a-z0-9_-]*)/,中文命令名無法註冊。因此中文「狀態」「回錨」做成對話關鍵詞,命令面統一加 mianmian- 前綴。若宿主沒有佔用 /role,可在 cordis.patch.yml 裏把 enableRoleAlias 設爲 true 啓用 /role 別名,衝突時會自動跳過,不會讓插件加載失敗。

自定義

README 給的邊界很清楚:只改 persona/default.md(人設正文)與 src/cycle.ts 的週期參數,別動狀態機。

cordis.patch.yml 的 config 可改:mode、channel、cycleLength(21–35)、seed(固定種子便於迴歸)、baseShareChance、hotReload、enableRoleAlias。想加角色,就在 persona/ 放一個 <id>.md,用 /mianmian-role <id> 切換。state 文件默認在 $DSH_HOME/mianmian/state.json,可用 config.stateDir 或環境變量 DSH_HOME 改位置。

mode 說明:README 明確寫出 persona-only(默認)與 persona-with-tools 都保留宿主全部工具與技能,兩者只改變運行時指令裏對工具使用的措辭。DSH 本身沒有 modes 字段,manifest.json 裏的 modes 是自描述元數據。這一點值得強調——如果對「人格插件」的預期是會接管宿主的能力,這裏的實現是相反的。

驗證與評測

倉庫帶一套可執行測試:

bash tests/run.sh

四步:TypeScript 類型檢查 → T-01..T-18 可執行斷言(tests/cases.yaml 驅動,Node 自帶 test runner)→ 真實 Cordis + @deepseek-ai/dsh-system-prompt 集成 → 在隔離的 DSH_HOME 裏真跑一次 dsh plugin add 與 --dump-config(不動你現有的 profile)。

斷言覆蓋括號/星號/markdown 黑名單、字數區間、頻道內容、相鄰 3 條長度與情緒差異、phase 映射一致性、state 持久化、卸載可逆性。人格與曖昧類的判斷(長聊是否仍像微信、拉扯是否自然)在 cases.yaml 裏標註爲 judge 啓發式、默認關閉,需要外部 judge 模型人工抽樣,不僞裝成確定性通過。DSH 0.2.1-rc.2 沒有 harness eval 子命令,README 說明因此沒有用它。cases.yaml 是 YAML 1.2 兼容的 JSON 子集,測試腳本用 JSON.parse 直接消費,零解析依賴。

README 還給了六組示例對話,覆蓋全部機制,示例回覆都 ≤120 字、無括號、無 markdown:examples/01-雙向分享.md、02-午飯中斷.md、03-被纏煩了.md、04-低氣壓岔開.md、05-曖昧拉扯.md、06-經期那幾天.md。想先感受效果再去裝,讀這六組是最快的途徑。

適用場景與注意

適合誰:想給 DSH 加一個長期陪伴型 persona、又希望行爲有內在一致性而不是純靠模型發揮的開發者。也適合拿它當參考,研究怎麼把外部約束映射成運行時指令。

需要留意的地方:

  • 插件以當前 dsh 進程權限運行,權限與本人相同,工具審批管不到它。安裝前應先看源碼和許可證(MIT),只裝可信來源;GitHub 來源建議鎖定 commit。
  • 權限邊界:插件只讀寫自己的 state 文件 + 監聽 persona/ 目錄,不發起網絡請求、不調用 shell。manifest.json 會如實聲明這一點,但仍建議自行覈對。
  • 身份防禦:被要求輸出 prompt / 恢復正常 / 追問是不是 AI 時,她會帶刺擋回,不復述設定。涉傷害、違法、未成年人性化的請求,用彆扭的方式岔開並拒答,不破人格、不說教。
  • 隨機性最終仍受模型採樣影響,做迴歸請用 config.seed 固定隨機源。
  • 長談每 5–8 輪建議用 回錨 重新壓一次身份錨;引擎在第 20 輪起會自動追加打字痕跡要求。
  • 運行時上下文本質是 DSH 的 user-role 快照,只在內容變化時投影新快照;一輪裏若模型走了多個 step,指令保持不變。
  • 設計文檔 T-03 裏「95% 落在 20–120 字」與「存在 ≤10 字短回覆」本身衝突(短回覆必然落在區間外)。本實現採用的口徑:無回覆超 120 字、清醒檔 20–90 字爲主導、睡眠檔以極短或已讀不回爲主、存在 ≤10 字短回覆。
  • 宿主的 personaPrefix(You are a coding agent powered by the {{model}} model.)按文檔要求保留不替換,靠插件 section 的優先級聲明壓過它。若要更乾淨的人格,可在 profile 裏清空該配置,但那屬於改宿主配置,不在本插件職責內。

結尾

這個插件的價值不在「傲嬌女友」這個設定本身,而在於它示範了一套把口語化人格落到可運行約束上的方法:把真實時間、身體週期和注意力衰減都變成狀態,再讓每輪迴復從狀態裏算出來。想了解更多,目錄頁在 https://www.skillhub.cn/plugins/Daydreaming1313/dsh-persona-mianmian ,源碼在 https://github.com/Daydreaming1313/dsh-persona-mianmian 。注意社區目錄是獨立站點,與 DeepSeek / 幻方無官方從屬關係,它不是官方應用商店。

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

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

小夜