前言¶
DSH 的理念是「一切皆插件」,但插件解決不了一個老問題:編程 Agent 跑長任務時,記憶只存在於上下文窗口裏。自動 compaction 觸發後,早期的路徑、命令、報錯信息會被壓縮掉;會話關了、機器換了,之前做過什麼、爲什麼這麼決策,沒人記得。人工翻幾百輪對話來整理一份交接文檔要十幾分鍾。
dsh-token-summary 就是針對這個場景的 host plugin。它監聽會話事件、累加真實計費 token,每跨過一個閾值就把新增的這段會話交給模型生成一份結構化總結,寫成獨立 Markdown 文件,並在索引裏留一條記錄。下面介紹它的安裝、配置和用法。
這是什麼¶
插件名 dsh-token-summary,倉庫 tech-zerolei/dsh-token-summary,維護者 tech-zerolei(package.json 的 author 字段爲 zerolei),分類「記憶」,類型 Host plugin,狀態 stable,許可證 MIT。當前 package.json 版本 2.0.1,要求 Node.js >= 22、DeepSeek Harness >= 0.2.1-rc.2。
一句話定位:把易失的上下文變成可長期保存的 Markdown 資產。歸檔文件裏帶任務目標、當前狀態、已完成項、關鍵決策與理由、未完成事項,可以直接交給同事、交給第二天的自己,或作爲背景交給另一個 Agent 接着幹。
閾值數字需要先說明一處口徑差異:倉庫 README 開頭寫的是「每累計 50 萬 token」並附有 500,000 token 的輸出示例,package.json 的 description 寫的是「每累計 1000 萬 token」,社區目錄頁同樣寫 1000 萬,而默認配置 intervalTokens 是 2000000(200 萬)。幾處不一致,實際觸發點以你在 cordis.patch.yml 裏配置的 intervalTokens 爲準。本文按 README 的行爲說明來介紹。
核心功能¶
- 按真實計費 token 觸發歸檔。只統計
assistant/message事件裏 provider 返回的usage字段,不按字符估算、不按消息條數累加,避免壓縮後歷史被重寫導致的重複計數。 - 增量歸檔。每次只總結「自上次歸檔之後產生的事件」,避免短閾值下第 N 份文件與前面幾份大量重複。
- 全局計數器。閾值按跨會話累計用量推進,同時開幾個會話做不同的事,歸檔節奏也按總體用量走。
- 絕對邊界錨定。觸發點錨在整數倍刻度(
0 → 499,999、500,000 → 999,999),一次響應跨過多個邊界時合併成一份報告,避免之後所有批次永久漂移。 - 壓縮前落盤。歸檔發生在自動 compaction 之前,相當於在信息被不可逆丟掉之前先留一份完整快照。
- 失敗也留痕。摘要調用失敗(無路由 / 上游 502 / 超時)時仍然寫文件,正文降級成統計兜底並附上失敗原因,游標不推進,下次觸發會重試。
- 後臺通知。歸檔完成後用
agent.inject()在會話裏留一行通知但不喚醒空閒 agent,避免憑空多燒一次模型調用。 - 不污染會話日誌。插件不新增會話事件類型,計數器放內存,唯一的日誌寫入是一條
user/message通知(kind: 'token-summary',form: 'notice')。 - 輸出可檢索。歸檔目錄下另維護一份
README.md索引,記錄每批的時間、批次號、累計 token、會話 ID,形成可審計的時間線。
觸發與增量讀取的核心邏輯:
ctx.on('session/event', (session, event) => {
if (event.type !== 'assistant/message' || event.data.usage === undefined) return;
const delta = usageTokens(event.data.usage);
if (delta <= 0) return;
totalTokens += delta;
if (totalTokens >= nextThreshold) onThreshold(session);
});
const events = session.ownEvents();
const since = sessionCursor.get(session.id) ?? 0;
const fresh = events.slice(since); // 只要新增的
上面第二段裏的游標 sessionCursor 只在文件寫成功後才推進,所以摘要失敗時那一段內容不會被吞掉。另外當歷史超過 maxTranscriptChars 時,插件從最新的對話往回填預算,最早的內容先丟——總結的價值集中在「事情最後做成了什麼」。
安裝與啓用¶
先確認環境:Node.js >= 22,DeepSeek Harness >= 0.2.1-rc.2。插件需要對輸出目錄有寫權限,默認輸出目錄是會話工作目錄下的 .dsh-summaries/。
推薦用本地 bundle 方式安裝:
dsh plugin add /path/to/dsh-token-summary
或用 profile 的包管理:
# 在 profile 目錄內
pnpm add link:/path/to/dsh-token-summary
然後在 package.json 的 dsh.profile.bundles 中加入 "dsh-token-summary"。
方式二是直接編輯 cordis.patch.yml 手動插入配置:
- insert:
- id: token-summary
name: 'dsh-token-summary'
config:
intervalTokens: 2000000
outputDir: ''
notifyInSession: true
enabled: true
安裝前應先查看 cordis.patch.yml 和源碼,確認配置項與當前 DSH 版本兼容。審查時留意:除摘要調用外插件不發起外部網絡請求,但摘要本身會走模型路由,若會話裏用的是有配額或付費的 provider,歸檔會額外消耗該模型的 token。
配置¶
所有字段都可選,在 cordis.patch.yml 的 token-summary 行下覆蓋即可:
| 字段 | 默認值 | 說明 |
|---|---|---|
intervalTokens |
2000000 |
一個批次覆蓋的 token 數(全局累計) |
outputDir |
'' |
輸出目錄;留空寫入 <會話工作目錄>/.dsh-summaries,填絕對路徑則所有會話集中寫入該目錄 |
provider |
'' |
摘要用的 provider 路由;必須與 model 同時填寫 |
model |
'' |
摘要用的精確模型 id |
maxSummaryOutputTokens |
2048 |
摘要請求的輸出 token 上限 |
maxTranscriptChars |
120000 |
送入模型的歷史字符上限,超出時從最早的對話開始丟棄 |
timeoutMs |
180000 |
單次摘要請求的端到端超時 |
notifyInSession |
true |
歸檔後是否在會話裏留一行通知 |
enabled |
true |
總開關 |
摘要用哪個模型,按以下優先級解析:
- 配置裏成對出現的
provider+model; - 該會話
requestHeader()中記錄的 route; - 活躍 agent 的
options。
provider 和 model 只填一個時會被忽略並輸出警告,路由回退到該會話最近一次請求的 route。如果會話從未發過請求、也沒有配置路由,就無法確定摘要路由,此時插件仍會寫一份統計兜底文件,但拿不到摘要正文。
輸出目錄有兩種取捨。outputDir 留空時歸檔跟着會話工作目錄走,每個項目一個 .dsh-summaries/:
/root/work/project-a/.dsh-summaries/handoff-訂單重構--txlsf8-0001-500000-....md
/root/work/project-b/.dsh-summaries/handoff-緩存優化--3auzbq-0001-500000-....md
需要統一檢索、備份或納入同一個倉庫時,填絕對路徑:
- id: token-summary
name: dsh-token-summary
config:
outputDir: /root/.dsh/summaries
要注意切換 outputDir 不會搬動已有文件,舊歸檔留在原目錄,索引 README.md 裏的路徑也只是寫入時的快照。想保留歷史就手動遷移。
典型用法¶
一個配置好路由的例子,閾值調到 50 萬、指定摘要模型、輸出到集中目錄:
- insert:
- id: token-summary
name: 'dsh-token-summary'
config:
intervalTokens: 500000
outputDir: /root/.dsh/summaries
provider: api23
model: deepseek-flash
notifyInSession: true
enabled: true
裝好之後正常使用會話即可,無需額外操作。插件監聽 session/event,觸發時讀取自上次歸檔後的新事件,摺疊成轉錄送給模型,然後把結果寫入 handoff-*.md 並更新索引。歸檔文件頭部是統計表,README 給的示例如下:
# 工程交接文檔 · 第 2 版(每累計 500,000 token 更新)
| 項目 | 值 |
| --- | --- |
| 生成時間 | 2026-11-05T01:30:00.000Z |
| 會話 ID | `session-3fbd1fea-...` |
| 會話標題 | 緩存重構 |
| 工作目錄 | `/root/work/my-project` |
| 本批序號 | 第 2 版 |
| 本批區間 | 500,000 → 999,999 token |
| 已覆蓋事件數 | 42 |
| 累計 token | 1,000,000 |
| 工具調用 | 188 |
| 工具失敗 | 0 |
正文按固定小節輸出:任務全景、當前狀態、已完成工作、關鍵決策與理由、文件與路徑清單、命令配置與數據、遇到的問題與解決、未完成事項與下一步、風險與注意。該示例是緩存層改造任務,第 2 版記錄了把單層 LRU 改成兩級緩存、Redis 封裝、熔斷策略,以及下一步「灰度 10% 流量觀察 24 小時」。注意這是 README 裏的示例數據,不是實測指標。
幾個常見用法:
- 長任務交接。重構跑了幾個小時、幾百輪工具調用,隨時拿最新那份
handoff-*.md交給同事或另一個 Agent 接着幹,不用人工翻對話。 - 事後復盤。一週後想查「那次爲什麼把緩存層換掉」,直接看歸檔裏的「關鍵決策與理由」一節。
- 壓縮前的保險。自動 compaction 不可逆,歸檔發生在它之前。
- 團隊證據留存。讀索引
README.md就能看到每批的時間、批次號、累計 token、會話 ID。
適用場景與注意¶
適合把 Harness 當日常主力工具、跑長週期任務、或需要在多會話之間追蹤進度的開發者。如果會話普遍很短(幾十輪內結束),這類歸檔的收益有限。
前置條件逐項確認:DSH >= 0.2.1-rc.2(需要 session/event 廣播、ctx.llm.stream()、ctx.agents);Node >= 22;至少一個註冊了 adapter 的 provider 用於摘要;對輸出目錄可寫。
幾點注意:
- 插件以當前 dsh 進程權限運行,能讀會話日誌、訪問工作目錄、調用模型路由。安裝前應自行檢查源碼與許可證(MIT),確認沒有超出預期的副作用。
- 計數器精度取決於 provider 上報的
usage,不同上游口徑不一致時,歸檔裏的累計 token 只能當參考值。 - 閾值調小會顯著增加模型調用次數,每次歸檔都消耗一次摘要 call,成本與
intervalTokens成反比。 - 摘要由模型生成,屬於壓縮而非逐字記錄,關鍵路徑和命令以源碼爲準,不要只信歸檔。
- 插件刻意不寫自有事件類型以保護會話可重開性,如果改動源碼繞過這個約束,可能導致會話無法重開。
關於社區目錄頁:skillhub.cn 是獨立站點,與 DeepSeek / 幻方無官方從屬關係。本環境裏目錄頁正文未抓取到可覈對的額外細節(頁面渲染爲空),所以本文只採用了插件名與歸屬信息,星標、版本等信息均以 README 與 package.json 爲準,未採用任務線裏未經複覈的數字。
結尾¶
dsh-token-summary 的價值是把 Agent 上下文裏的記憶,在壓縮和會話結束之前先固化成可長期保存、可直接交接的 Markdown。代價是每次觸發多一次模型調用,並且要接受歸檔是模型生成的摘要而非逐字日誌。想試試的話,先看倉庫 README 與 cordis.patch.yml 確認配置,再決定閾值取多大。
- 插件目錄頁:https://www.skillhub.cn/plugins/tech-zerolei/dsh-token-summary
- GitHub:https://github.com/tech-zerolei/dsh-token-summary