跳到內容

壓縮與分支摘要

壓縮與分支摘要是兩種機制,用於在不遺失先前工作上下文的情況下保持長期會話的可用性。

  • 壓縮將當前分支上的舊歷史記錄重寫為摘要。
  • 分支摘要/tree 導航期間捕獲被放棄的分支上下文。

兩者都以會話條目的形式持久化,並在重建 LLM 輸入時轉換回使用者上下文訊息。

  • src/session/compaction/compaction.ts
  • src/session/compaction/branch-summarization.ts
  • src/session/compaction/pruning.ts
  • src/session/compaction/utils.ts
  • src/session/session-manager.ts
  • src/session/agent-session.ts
  • src/session/messages.ts
  • src/extensibility/hooks/types.ts
  • src/config/settings-schema.ts

壓縮與分支摘要是一等會話條目,而非普通的助理/使用者訊息。

  • CompactionEntry
    • type: "compaction"
    • summary,可選 shortSummary
    • firstKeptEntryId(壓縮邊界)
    • tokensBefore
    • 可選 detailspreserveDatafromExtension
  • BranchSummaryEntry
    • type: "branch_summary"
    • fromIdsummary
    • 可選 detailsfromExtension

當重建上下文(buildSessionContext)時:

  1. 活動路徑上最新的壓縮被轉換為一個 compactionSummary 訊息。
  2. firstKeptEntryId 到壓縮點的保留條目會被重新包含。
  3. 路徑上後續的條目會被附加。
  4. branch_summary 條目被轉換為 branchSummary 訊息。
  5. custom_message 條目被轉換為 custom 訊息。

這些自訂角色隨後在 convertToLlm() 中使用靜態範本轉換為面向 LLM 的使用者訊息:

  • prompts/compaction/compaction-summary-context.md
  • prompts/compaction/branch-summary-context.md

壓縮可以透過三種方式執行:

  1. 手動/compact [instructions] 呼叫 AgentSession.compact(...)
  2. 自動溢出恢復:在助理錯誤符合上下文溢出條件後觸發。
  3. 自動閾值壓縮:在成功回合後,當上下文超過閾值時觸發。
Before compaction:
entry: 0 1 2 3 4 5 6 7 8 9
┌─────┬─────┬─────┬──────┬─────┬─────┬──────┬──────┬─────┬──────┐
│ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool │
└─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴──────┘
└────────┬───────┘ └──────────────┬──────────────┘
messagesToSummarize kept messages
firstKeptEntryId (entry 4)
After compaction (new entry appended):
entry: 0 1 2 3 4 5 6 7 8 9 10
┌─────┬─────┬─────┬──────┬─────┬─────┬──────┬──────┬─────┬──────┬─────┐
│ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool │ cmp │
└─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴──────┴─────┘
└──────────┬──────┘ └──────────────────────┬───────────────────┘
not sent to LLM sent to LLM
starts from firstKeptEntryId
What the LLM sees:
┌────────┬─────────┬─────┬─────┬──────┬──────┬─────┬──────┐
│ system │ summary │ usr │ ass │ tool │ tool │ ass │ tool │
└────────┴─────────┴─────┴─────┴──────┴──────┴─────┴──────┘
↑ ↑ └─────────────────┬────────────────┘
prompt from cmp messages from firstKeptEntryId

兩種自動路徑有意設計為不同:

  • 溢出重試壓縮

    • 觸發條件:當前模型的助理錯誤被偵測為上下文溢出。
    • 失敗的助理錯誤訊息在重試前從活動代理狀態中移除。
    • 自動壓縮以 reason: "overflow"willRetry: true 執行。
    • 成功後,代理自動繼續(agent.continue()),在壓縮之後執行。
  • 閾值壓縮

    • 觸發條件:contextTokens > contextWindow - compaction.reserveTokens
    • reason: "threshold"willRetry: false 執行。
    • 成功後,若 compaction.autoContinue !== false,注入合成提示:
      • "Continue if you have next steps."

在壓縮檢查之前,可能會執行工具結果剪枝(pruneToolOutputs)。

預設剪枝策略:

  • 保護最新的 40_000 個工具輸出 token。
  • 要求至少 20_000 個總估計節省量。
  • 永遠不剪枝來自 skillread 的工具結果。

被剪枝的工具結果會被替換為:

  • [Output truncated - N tokens]

如果剪枝更改了條目,會話儲存會被重寫,且代理訊息狀態會在壓縮決策之前重新整理。

prepareCompaction() 僅考慮自上次壓縮條目(如果有的話)以來的條目。

  1. 找到上一個壓縮索引。
  2. 計算 boundaryStart = prevCompactionIndex + 1
  3. 在有可用的測量使用率時,使用其調整 keepRecentTokens
  4. 在邊界視窗上執行 findCutPoint()

有效的切割點包括:

  • 角色為以下的訊息條目:userassistantbashExecutionhookMessagebranchSummarycompactionSummary
  • custom_message 條目
  • branch_summary 條目

硬性規則:永遠不在 toolResult 處切割。

如果切割點之前緊鄰非訊息的中繼資料條目(model_changethinking_level_change、標籤等),則透過將切割索引向後移動直到遇到訊息或壓縮邊界,將它們拉入保留區域。

如果切割點不在使用者回合起始處,壓縮將其視為分割回合。

回合起始偵測將以下視為使用者回合邊界:

  • message.role === "user"
  • message.role === "bashExecution"
  • custom_message 條目
  • branch_summary 條目

分割回合壓縮生成兩個摘要:

  1. 歷史摘要(messagesToSummarize
  2. 回合前綴摘要(turnPrefixMessages

最終儲存的摘要合併為:

<history summary>
---
**Turn Context (split turn):**
<turn prefix summary>

compact(...) 從序列化的對話文字建構摘要:

  1. 透過 convertToLlm() 轉換訊息。
  2. 使用 serializeConversation() 序列化。
  3. 包裝在 <conversation>...</conversation> 中。
  4. 可選地包含 <previous-summary>...</previous-summary>
  5. 可選地將鉤子上下文注入為 <additional-context> 列表。
  6. 使用 SUMMARIZATION_SYSTEM_PROMPT 執行摘要提示。

提示選擇:

  • 首次壓縮:compaction-summary.md
  • 有先前摘要的迭代壓縮:compaction-update-summary.md
  • 分割回合第二輪:compaction-turn-prefix.md
  • 簡短 UI 摘要:compaction-short-summary.md

遠端摘要模式:

  • 若設定了 compaction.remoteEndpoint,壓縮會 POST:
    • { systemPrompt, prompt }
  • 預期返回至少包含 { summary } 的 JSON。

壓縮使用助理工具呼叫追蹤累計檔案活動:

  • read(path) → 讀取集合
  • write(path) → 修改集合
  • edit(path) → 修改集合

累計行為:

  • 僅當先前條目是由 pi 生成(fromExtension !== true)時,才包含先前壓縮的詳細資訊。
  • 在分割回合中,也包含回合前綴的檔案操作。
  • readFiles 排除同時被修改的檔案。

摘要文字透過提示範本附加檔案標籤:

<read-files>
...
</read-files>
<modified-files>
...
</modified-files>

在摘要生成(或鉤子提供的摘要)之後,代理會話:

  1. 使用 appendCompaction(...) 附加 CompactionEntry
  2. 透過 buildSessionContext() 重建上下文。
  3. 以重建的上下文替換即時代理訊息。
  4. 發出 session_compact 鉤子事件。

分支摘要與樹狀導航相關,而非 token 溢出。

navigateTree(...) 期間:

  1. 使用 collectEntriesForBranchSummary(...) 計算從舊葉節點到共同祖先的被放棄條目。
  2. 若呼叫者請求摘要(options.summarize),在切換葉節點之前生成摘要。
  3. 若摘要存在,使用 branchWithSummary(...) 將其附加到導航目標。

在操作上,這通常由 /tree 流程在 branchSummary.enabled 啟用時驅動。

Tree before navigation:
┌─ B ─ C ─ D (old leaf, being abandoned)
A ───┤
└─ E ─ F (target)
Common ancestor: A
Entries to summarize: B, C, D
After navigation with summary:
┌─ B ─ C ─ D ─ [summary of B,C,D]
A ───┤
└─ E ─ F (new leaf)

generateBranchSummary(...) 計算預算為:

  • tokenBudget = model.contextWindow - branchSummary.reserveTokens

prepareBranchEntries(...) 接著:

  1. 第一輪:從所有摘要條目中收集累計檔案操作,包含先前由 pi 生成的 branch_summary 詳細資訊。
  2. 第二輪:從最新到最舊遍歷,添加訊息直到達到 token 預算。
  3. 優先保留最近的上下文。
  4. 為了連續性,可能仍然在預算邊緣包含大型摘要條目。

壓縮條目在分支摘要輸入期間作為訊息(compactionSummary)包含。

分支摘要:

  1. 轉換並序列化選定的訊息。
  2. 包裝在 <conversation> 中。
  3. 若有提供則使用自訂指令,否則使用 branch-summary.md
  4. 使用 SUMMARIZATION_SYSTEM_PROMPT 呼叫摘要模型。
  5. 前置 branch-summary-preamble.md
  6. 附加檔案操作標籤。

結果以 BranchSummaryEntry 儲存,附帶可選的詳細資訊(readFilesmodifiedFiles)。

壓縮前鉤子。

可以:

  • 取消壓縮({ cancel: true }
  • 提供完整的自訂壓縮酬載({ compaction: CompactionResult }

預設壓縮的提示/上下文自訂鉤子。

可以返回:

  • prompt(覆蓋基礎摘要提示)
  • context(注入 <additional-context> 的額外上下文行)
  • preserveData(儲存在壓縮條目上)

壓縮後通知,包含已儲存的 compactionEntryfromExtension 旗標。

在預設分支摘要生成之前的樹狀導航時執行。

可以:

  • 取消導航
  • 提供自訂的 { summary: { summary, details } },在使用者請求摘要時使用

導航後事件,公開新/舊葉節點和可選的摘要條目。

  • 手動壓縮會先中止當前代理操作。
  • abortCompaction() 取消手動和自動壓縮控制器。
  • 自動壓縮為 UI/狀態更新發出開始/結束會話事件。
  • 自動壓縮可以嘗試多個候選模型並重試暫時性失敗。
  • 溢出錯誤被排除在通用重試路徑之外,因為它們由壓縮處理。
  • 若自動壓縮失敗:
    • 溢出路徑發出 Context overflow recovery failed: ...
    • 閾值路徑發出 Auto-compaction failed: ...
  • 分支摘要可以透過中止信號(例如 Escape)取消,返回已取消/已中止的導航結果。

來自 settings-schema.ts

  • compaction.enabled = true
  • compaction.reserveTokens = 16384
  • compaction.keepRecentTokens = 20000
  • compaction.autoContinue = true
  • compaction.remoteEndpoint = undefined
  • branchSummary.enabled = false
  • branchSummary.reserveTokens = 16384

這些值在執行時由 AgentSession 和壓縮/分支摘要模組使用。