前言¶
在 DSH(DeepSeek Harness)裏跑長任務,遲早會遇到上下文被撐滿的問題。默認的壓縮策略是”快到窗口上限時,把最老的一段壓成摘要”,這在長任務裏有兩個實際麻煩:觸發太晚,等到窗口用了八成才動手,壓縮本身要重放整段歷史,請求已經貼着上限;壓完之後新舊內容混成一個摘要,模型分不清哪些是剛纔發生的事、哪些是十輪前的背景。
下面介紹的插件 btyg123/dsh-memory-ladder 換了一種組織方式:讓上下文按新舊分層,每層有各自的預算,最新一輪永遠有最大的空間,越遠越薄。它由 BTYG123 維護,MIT 許可,分類是記憶。
這是什麼¶
一句話定位:一個 DSH 的 bundle 插件,作爲壓縮後端替換默認策略。它繼承 @deepseek-ai/dsh-compaction-basic 的 BasicCompactionEngine,只替換觸發策略和 summarize(),壓縮事務(括號鎖、收縮校驗、表面替換)仍來自上游。
它不是備份工具,也不是無損壓縮。README 明確寫的是”它省的是容量,不是信息本身”,二級細節會隨層級下移被丟棄。
分層預算怎麼算¶
設路由模型的上下文窗口爲 T,各層預算如下:
| 層級 | 內容 | 預算 |
|---|---|---|
| 最新一輪 | 上次檢查點之後的全部原始消息 | T/3 |
| 上一輪 | 上一輪壓縮出的摘要 | T/6 |
| 上上輪 | 再往前的摘要 | T/12 |
| … | 每深一層減半 | T/(3·2^d) |
最新一輪與上一輪的比例因此穩定在 2:1。v0.2 還受 maxSummaryTokens 約束:當層級總預算超過摘要輸出空間時,摘要各層按幾何比例縮小,並保留檢查點框架空間。
v0.2 起多了原文歸檔¶
新版保留原有分層睡眠事務,並在壓縮前把用戶、助手及工具消息的文本寫入當前會話獨立的歸檔。之後根據用戶問題檢索已經離開上下文的原文,經 Harness 的動態上下文接口提供給模型;不會直接改寫凍結的模型請求。
關鍵行爲:
- 默認歸檔位置是會話工作目錄下
.dsh-memory-ladder/,文件名使用會話 ID 的 SHA-256。 - 原文保留消息 ID、角色和順序。重複壓縮不會重複歸檔同一消息。
- 系統指令、舊檢查點和運行時上下文不歸檔。
- 歸檔是本地明文狀態,不會自動上傳到其他服務;被選中的摘錄會隨下一次模型請求發送給當前模型服務商。
archiveRecall: false同時停用歸檔寫入和檢索。恢復同一會話 ID 可繼續讀取歸檔;fork 到新會話 ID 不會自動複製父會話歸檔。
一次睡眠發生什麼¶
按 README 的鏈路,一次睡眠有四步:
- 最新一輪的 token 數達到
T/3(或模型報告上下文溢出)。 - 一次模型調用把”舊階梯 + 本輪原始記錄”重寫成新階梯:舊 Tier 0 變成新 Tier 1,逐層下移,最深一層落進歸檔鏈接。
- 事務把被遮蔽的整段歷史替換成一條用戶消息(
surfaceOp: replace),原始事件仍留在會話日誌裏,可回放。 - 本輪從新階梯繼續。
/compact 手動命令複用同一條路徑,只是跳過閾值判斷。
安裝¶
前置條件是 dsh 的插件命令依賴 pnpm。README 給出的官方命令是:
npm i -g pnpm
# 方式一:插件管理器(推薦,已實測)
dsh plugin --profile <你的 profile> add git+https://github.com/BTYG123/dsh-memory-ladder.git
裝完會自動把 dsh-memory-ladder 加進該 profile 的 dsh.profile.bundles。
方式二是手動放進 profile 的 node_modules,並在 patch 裏停用官方壓縮後端、插入本插件:
- id: compaction-basic
disabled: true
- insert:
- id: memory-ladder
name: 'dsh-memory-ladder'
這一步不能省。ctx.compaction 只接受一個實現,兩個後端同時掛載會在啓動時報 service "compaction" has been registered。bundle 的 patch 已包含停用步驟,手動安裝時要自己加。
配置¶
配置項按 README 的配置表列出,均爲原文給出的字段與默認值:
# 睡眠觸發線:最新一輪佔窗口的比例
newestFraction: 1/3
# 階梯最多描述多少層
tierCount: 6
# 更深層級低於此預算就並進歸檔鏈接
minTierTokens: 200
# 睡眠請求的輸出上限
maxSummaryTokens: 8192
# 可壓縮跨度小於此值直接跳過,避免白花一次模型調用
minCompactionTokens: 512
# 留空則沿用最近一次路由的 provider/model
summarizationProvider: ""
summarizationModel: ""
# false 只保留手動壓縮
auto: true
# 壓縮前歸檔原文,下一步按問題檢索
archiveRecall: true
# 歸檔目錄,相對會話工作目錄,也可設絕對路徑
archiveDirectory: .dsh-memory-ladder
# 每次檢索上下文的 UTF-8 字節上限,含來源信息和框架
recallMaxBytes: 4096
兼容性¶
README 寫的驗證環境是 @deepseek-ai/dsh@0.2.1-rc.5 + Node 22/24,peerDependencies 已 pin 到 0.2.1-rc.5。需要注意兩點:
- 本倉庫的安裝根通過
overrides.zod = 4.6.6修復已知的集合校驗內存耗盡漏洞;npm 不會把依賴包內的 overrides 自動應用到宿主工程,作爲 bundle 安裝時仍需由 Harness 宿主統一管理 Zod 版本。 compactRegion()是公開契約,相對穩定;構造函數與基類字段屬於實現細節,上游重構可能導致本包失效。
已知限制¶
這些是 README 明確列出的限制,落地前值得先看清楚:
- 單條超大消息壓不動。一條 5 萬 token 的 assistant 輸出是不可分割單元,表面壓縮無法修復,屬於壓縮接縫的固有限制。
- 歸檔寫滿後該會話不能再壓縮。
archiveMaxBytes(默認 32 MiB)是硬上限,寫滿時拋ARCHIVE_CAPACITY,壓縮事務拒絕替換原始消息。代價是該會話此後每次睡眠都失敗、上下文一路增長,需要調大上限、輪轉歸檔或關掉archiveRecall。長期運行的會話建議提前調到 64–128 MiB。 - 睡眠本身要花一次調用。若摘要比原文還長,事務拒絕提交併記錄失敗;密集睡眠會推高輸入成本。
- 不是無損的。要無損就調大
newestFraction,或者不用本插件。 - 摘要質量取決於模型。本地無法校驗事實是否被忠實保留。
實測數據¶
README 在一次 20 分鐘的 CrowdStrike 事件調研上對比同一策略的兩種窗口:
默認 T/3(1M 窗口) |
32k 等效窗口 | |
|---|---|---|
| 觸發線 | 349,525 token | 10,905 token |
| 峯值上下文 | 185,359 token | 32,636 token |
| sleep 次數 | 0 | 10 |
| 會話表面節點 | 266 | 142 |
| 產出 | 41 KB / 23 個來源 | 16 KB / 10 個來源 |
在 12 個合成留出案例(每模型 96 題)中,Flash 從原版 76/96 提升到 90/96,Pro 從 84/96 提升到 91/96;費用相對原版增加約 32%/31%,相對完整歷史降低約 28%/34%,完整歷史仍爲 96/96。這是特定壓縮壓力下的質量/費用取捨,不是所有長任務都更優的證明。README 同時保留了消融組的一次失敗和逐題錯誤記錄。
適用場景與注意¶
適合的場景:長任務中需要保留”最近發生了什麼”的優先級,同時接受遠端細節衰減,並且願意爲摘要調用付一筆額外模型費用。期望無損或期望壓縮完全不出錯的場景不適合。
另外,插件以當前 dsh 進程權限運行。它是 bundle,自帶 cordis.patch.yml,會在啓動時停用 compaction-basic 並插入自己的實現——安裝前應該先讀一遍源碼和 patch,確認這符合你的 profile 預期。許可證是 MIT,歸屬聲明見倉庫 LICENSE。目錄頁是第三方社區站點,與 DeepSeek / 幻方無官方從屬關係。
結尾¶
它的價值在於把”什麼時候壓、壓多細”改成由分層預算決定,而不是等到窗口快滿才一次性清空。代價是每次睡眠的模型調用、歸檔容量上限和遠端細節衰減,三者都需要在配置裏做取捨。
目錄頁:https://www.skillhub.cn/plugins/BTYG123/dsh-memory-ladder
倉庫:https://github.com/BTYG123/dsh-memory-ladder