dsh-token-summary:把長會話的上下文定期落成可交接的 Markdown 歸檔

前言

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 的行爲說明來介紹。

核心功能

  1. 按真實計費 token 觸發歸檔。只統計 assistant/message 事件裏 provider 返回的 usage 字段,不按字符估算、不按消息條數累加,避免壓縮後歷史被重寫導致的重複計數。
  2. 增量歸檔。每次只總結「自上次歸檔之後產生的事件」,避免短閾值下第 N 份文件與前面幾份大量重複。
  3. 全局計數器。閾值按跨會話累計用量推進,同時開幾個會話做不同的事,歸檔節奏也按總體用量走。
  4. 絕對邊界錨定。觸發點錨在整數倍刻度(0 → 499,999、500,000 → 999,999),一次響應跨過多個邊界時合併成一份報告,避免之後所有批次永久漂移。
  5. 壓縮前落盤。歸檔發生在自動 compaction 之前,相當於在信息被不可逆丟掉之前先留一份完整快照。
  6. 失敗也留痕。摘要調用失敗(無路由 / 上游 502 / 超時)時仍然寫文件,正文降級成統計兜底並附上失敗原因,游標不推進,下次觸發會重試。
  7. 後臺通知。歸檔完成後用 agent.inject() 在會話裏留一行通知但不喚醒空閒 agent,避免憑空多燒一次模型調用。
  8. 不污染會話日誌。插件不新增會話事件類型,計數器放內存,唯一的日誌寫入是一條 user/message 通知(kind: 'token-summary', form: 'notice')。
  9. 輸出可檢索。歸檔目錄下另維護一份 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 總開關

摘要用哪個模型,按以下優先級解析:

  1. 配置裏成對出現的 provider + model;
  2. 該會話 requestHeader() 中記錄的 route;
  3. 活躍 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 裏的示例數據,不是實測指標。

幾個常見用法:

  1. 長任務交接。重構跑了幾個小時、幾百輪工具調用,隨時拿最新那份 handoff-*.md 交給同事或另一個 Agent 接着幹,不用人工翻對話。
  2. 事後復盤。一週後想查「那次爲什麼把緩存層換掉」,直接看歸檔裏的「關鍵決策與理由」一節。
  3. 壓縮前的保險。自動 compaction 不可逆,歸檔發生在它之前。
  4. 團隊證據留存。讀索引 README.md 就能看到每批的時間、批次號、累計 token、會話 ID。

適用場景與注意

適合把 Harness 當日常主力工具、跑長週期任務、或需要在多會話之間追蹤進度的開發者。如果會話普遍很短(幾十輪內結束),這類歸檔的收益有限。

前置條件逐項確認:DSH >= 0.2.1-rc.2(需要 session/event 廣播、ctx.llm.stream()、ctx.agents);Node >= 22;至少一個註冊了 adapter 的 provider 用於摘要;對輸出目錄可寫。

幾點注意:

  1. 插件以當前 dsh 進程權限運行,能讀會話日誌、訪問工作目錄、調用模型路由。安裝前應自行檢查源碼與許可證(MIT),確認沒有超出預期的副作用。
  2. 計數器精度取決於 provider 上報的 usage,不同上游口徑不一致時,歸檔裏的累計 token 只能當參考值。
  3. 閾值調小會顯著增加模型調用次數,每次歸檔都消耗一次摘要 call,成本與 intervalTokens 成反比。
  4. 摘要由模型生成,屬於壓縮而非逐字記錄,關鍵路徑和命令以源碼爲準,不要只信歸檔。
  5. 插件刻意不寫自有事件類型以保護會話可重開性,如果改動源碼繞過這個約束,可能導致會話無法重開。

關於社區目錄頁: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
羽毛球分组比赛记分
小程序二维码

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

小夜