Liking cljdoc? Tell your friends :D

im.ttalk.agent.filter.compaction

摘要式上下文压缩(designs/runtime-borrow.md §1;借 Keelson Advisors/CompactionAdvisor.cs / CompactionBudget.cs,判据又照 Reactor / Codex)。

长会话迟早撞窗。本 filter 按预算比例触发:

  1. 微压缩:更早的工具结果正文换成占位符、保留的截到上限(不调模型);
  2. 仍超 → 在安全切点(不劈开 assistant 调用与它的结果)把前缀换成一条摘要;
  3. 没有摘要器 / 无可摘前缀 / 摘要失败 → 丢最老(按安全段丢);
  4. provider 报上下文超长 → 强压后重发一次。

挂法::chat filter,注册在 memory filter 之后(看到的是展开后的完整历史)。

与 ChatMemory 的关系(缺省不写回):store 仍是完整历史的唯一真相——AG-UI 的 消息快照、timeline、审计都读它。压缩只改这一次发给模型的视图;为了不每轮重摘, filter 按会话记一份摘要缓存 {:prefix-n :prefix-hash :summary}:历史前 n 条没变 就直接换成缓存的摘要(零 LLM 调用)。再超阈值时在视图上继续压(旧摘要进新摘要的 素材,逐次合并)。要「有界存储」就给 :write-back-store:摘要后用压缩结果改写 store (mem-clear + mem-add,消息 id 保留;前端看到的历史随之变短,代价自负)。

两个可替换挂点(2026-11-08 用户拍板:缺省不改写、不落盘,但逻辑要能整个换掉, 同 Keelson Agui.Bitcask 那样由外部库提供实现):

  • :store:CompactionStore 协议,存每会话的可持久部分 {:cache :failures} (摘要缓存 + 摘要器连续失败数)。缺省 in-memory-store(LRU,进程停即丢)。 外部实现可落盘,重启后不必重摘。缓存对不上(前缀 hash 变了)只会触发重摘,不会出错。
  • :write-back:(fn [conversation-id compacted original]),摘要成功后调用,决定怎么 处置历史(改写 / 归档原文再改写 / 只记审计…)。缺省 nil = 不写回; :write-back-store 是它的语法糖(= (store-write-back store))。
摘要式上下文压缩(designs/runtime-borrow.md §1;借 Keelson
`Advisors/CompactionAdvisor.cs` / `CompactionBudget.cs`,判据又照 Reactor / Codex)。

长会话迟早撞窗。本 filter 按预算比例触发:
1. **微压缩**:更早的工具结果正文换成占位符、保留的截到上限(不调模型);
2. 仍超 → 在**安全切点**(不劈开 assistant 调用与它的结果)把前缀换成一条**摘要**;
3. 没有摘要器 / 无可摘前缀 / 摘要失败 → **丢最老**(按安全段丢);
4. provider 报**上下文超长** → 强压后重发一次。

**挂法**:`:chat` filter,注册在 memory filter **之后**(看到的是展开后的完整历史)。

**与 ChatMemory 的关系(缺省不写回)**:store 仍是完整历史的唯一真相——AG-UI 的
消息快照、timeline、审计都读它。压缩只改**这一次发给模型的视图**;为了不每轮重摘,
filter 按会话记一份**摘要缓存** `{:prefix-n :prefix-hash :summary}`:历史前 n 条没变
就直接换成缓存的摘要(零 LLM 调用)。再超阈值时在视图上继续压(旧摘要进新摘要的
素材,逐次合并)。要「有界存储」就给 `:write-back-store`:摘要后用压缩结果改写 store
(`mem-clear` + `mem-add`,消息 id 保留;前端看到的历史随之变短,代价自负)。

**两个可替换挂点**(2026-11-08 用户拍板:缺省不改写、不落盘,但逻辑要能整个换掉,
同 Keelson `Agui.Bitcask` 那样由外部库提供实现):
- `:store`:`CompactionStore` 协议,存每会话的**可持久部分** `{:cache :failures}`
  (摘要缓存 + 摘要器连续失败数)。缺省 `in-memory-store`(LRU,进程停即丢)。
  外部实现可落盘,重启后不必重摘。缓存对不上(前缀 hash 变了)只会触发重摘,不会出错。
- `:write-back`:`(fn [conversation-id compacted original])`,摘要成功后调用,决定怎么
  处置历史(改写 / 归档原文再改写 / 只记审计…)。缺省 nil = 不写回;
  `:write-back-store` 是它的语法糖(= `(store-write-back store)`)。
raw docstring

chat-model-summarizerclj

(chat-model-summarizer chat-model
                       &
                       {:keys [prompt options]
                        :or {prompt default-summary-prompt
                             options {:temperature 0.2}}})

用一个 ChatModel 生成摘要:(fn [transcript] -> 文本 | nil)。 opts: :prompt 系统提示(缺省 default-summary-prompt)、:options 额外调用选项 (如 {:temperature 0.2 :max-tokens 2000})。

用一个 ChatModel 生成摘要:`(fn [transcript] -> 文本 | nil)`。
opts: :prompt 系统提示(缺省 `default-summary-prompt`)、:options 额外调用选项
(如 {:temperature 0.2 :max-tokens 2000})。
sourceraw docstring

cleared-markerclj

source

compactclj

(compact msgs thr {:keys [budget est-opts summarizer]})

需要就压:微压缩 → 摘要替换前缀 → 失败 / 没得摘退回丢最老。

参数:msgs、threshold、opts {:budget :est-opts :summarizer(nil = 不摘要)} 返回:{:kind :none|:micro|:summary|:drop :messages :before :after :cut(:summary 时,msgs 里的下标):summary-msg :summary-failed?}

需要就压:微压缩 → 摘要替换前缀 → 失败 / 没得摘退回丢最老。

参数:msgs、threshold、opts {:budget :est-opts :summarizer(nil = 不摘要)}
返回:{:kind :none|:micro|:summary|:drop :messages :before :after
       :cut(:summary 时,msgs 里的下标):summary-msg :summary-failed?}
sourceraw docstring

compaction-filterclj

(compaction-filter
  &
  {:keys [budget summarizer overflow? recover-overflow? use-reported-usage?
          write-back-store write-back store on-compaction max-conversations]
   :or
     {recover-overflow? true use-reported-usage? true max-conversations 1024}})

上下文压缩 :chat filter。注册在 memory filter 之后。

opts:

  • :budget 覆盖 default-budget 的键(:context-window 最常用)
  • :summarizer (fn [transcript] -> 摘要文本 | nil);chat-model-summarizer 现成。 不给 → 只做微压缩 + 丢最老
  • :overflow? (fn [throwable-或-响应] -> bool),缺省 default-overflow?
  • :recover-overflow? 超长时压缩重发一次(缺省 true)
  • :use-reported-usage? 用 provider 报的 input token 校准(缺省 true)
  • :write-back-store ChatMemory:摘要后改写 store(缺省不写回;= :write-back (store-write-back s))
  • :write-back (fn [conversation-id compacted original]):摘要后处置历史的策略 (可替换挂点,优先于 :write-back-store)
  • :store CompactionStore:摘要缓存 / 失败数存哪(可替换挂点,缺省 (in-memory-store {:max-conversations ..}))
  • :on-compaction (fn [{:keys [conversation-id kind before after]}]) 观察 kind ∈ :micro :summary :drop :overflow-recovery
  • :max-conversations 摘要缓存最多记几个会话(缺省 1024,按最近使用淘汰;只对缺省 store 生效)
上下文压缩 `:chat` filter。注册在 memory filter **之后**。

opts:
- :budget            覆盖 `default-budget` 的键(:context-window 最常用)
- :summarizer        (fn [transcript] -> 摘要文本 | nil);`chat-model-summarizer` 现成。
                     不给 → 只做微压缩 + 丢最老
- :overflow?         (fn [throwable-或-响应] -> bool),缺省 `default-overflow?`
- :recover-overflow? 超长时压缩重发一次(缺省 true)
- :use-reported-usage? 用 provider 报的 input token 校准(缺省 true)
- :write-back-store  ChatMemory:摘要后改写 store(缺省不写回;= `:write-back (store-write-back s)`)
- :write-back        (fn [conversation-id compacted original]):摘要后处置历史的策略
                     (可替换挂点,优先于 :write-back-store)
- :store             `CompactionStore`:摘要缓存 / 失败数存哪(可替换挂点,缺省
                     `(in-memory-store {:max-conversations ..})`)
- :on-compaction     (fn [{:keys [conversation-id kind before after]}]) 观察
                     kind ∈ :micro :summary :drop :overflow-recovery
- :max-conversations 摘要缓存最多记几个会话(缺省 1024,按最近使用淘汰;只对缺省 store 生效)
sourceraw docstring

CompactionStorecljprotocol

每会话压缩状态里可持久的那部分:{:cache {:prefix-n :prefix-hash :summary-msg :head} :failures n}(EDN 可往返)。进程内的瞬时量(上次送出的视图、provider 报的 token) 不经过它。实现方:调用发生在请求路径上,要快;抛异常 filter 会吞掉并当作没有缓存。

每会话压缩状态里**可持久**的那部分:`{:cache {:prefix-n :prefix-hash :summary-msg :head}
:failures n}`(EDN 可往返)。进程内的瞬时量(上次送出的视图、provider 报的 token)
不经过它。实现方:调用发生在请求路径上,要快;抛异常 filter 会吞掉并当作没有缓存。

cs-delete!clj

(cs-delete! s conversation-id)

删掉该会话的状态。

删掉该会话的状态。

cs-getclj

(cs-get s conversation-id)

状态 map 或 nil。

状态 map 或 nil。

cs-put!clj

(cs-put! s conversation-id state)

整体替换。

整体替换。
sourceraw docstring

default-budgetclj

source

default-overflow?clj

(default-overflow? x)

缺省超长判定:异常(含 cause 链)的 ex-data :type 为 :context-overflow-error (provider 侧契约),或响应 finish-reason 为 :context-window-exceeded(GLM 以响应 而非异常报告)。x 是 Throwable 或 ChatClientResponse / ChatResponse。

缺省超长判定:异常(含 cause 链)的 ex-data `:type` 为 `:context-overflow-error`
(provider 侧契约),或响应 finish-reason 为 `:context-window-exceeded`(GLM 以响应
而非异常报告)。x 是 Throwable 或 ChatClientResponse / ChatResponse。
sourceraw docstring

default-summary-promptclj

source

drop-oldestclj

(drop-oldest msgs threshold est-opts)

丢最老(摘要失败 / 没得摘时的退路):从地板(system + 摘要)之后按整条安全段往前丢, 直到整包 < threshold;至少留最后一条。

丢最老(摘要失败 / 没得摘时的退路):从地板(system + 摘要)之后按整条安全段往前丢,
直到整包 < threshold;至少留最后一条。
sourceraw docstring

find-safe-cutclj

(find-safe-cut msgs
               min-cut
               {:keys [keep-min-tokens keep-min-text keep-max-tokens]})

尾部起始下标(保留 msgs[cut..],msgs[min-cut..cut) 换成摘要):从尾部按整条往前攒, 到 ≥ keep-min-tokens 且 ≥ keep-min-text 条文本消息就收;攒过 keep-max-tokens 还没凑够 ⇒ 停在上一个安全位。没有安全位 ⇒ nil。

尾部起始下标(保留 msgs[cut..],msgs[min-cut..cut) 换成摘要):从尾部按整条往前攒,
到 ≥ keep-min-tokens 且 ≥ keep-min-text 条文本消息就收;攒过 keep-max-tokens 还没凑够
⇒ 停在上一个安全位。没有安全位 ⇒ nil。
sourceraw docstring

forbidden-cutsclj

(forbidden-cuts msgs)

拆对表:(nth v c) 为真 = 在 c 处下刀(尾部 = msgs[c..])会把某个调用与它的结果劈开。 孤儿(有调用没结果 / 有结果没调用)不设限。返回长度 n+1 的向量。

拆对表:`(nth v c)` 为真 = 在 c 处下刀(尾部 = msgs[c..])会把某个调用与它的结果劈开。
孤儿(有调用没结果 / 有结果没调用)不设限。返回长度 n+1 的向量。
sourceraw docstring

head-endclj

(head-end msgs)

开头 system 消息的条数(永远留在头部,不进摘要素材)。

开头 system 消息的条数(永远留在头部,不进摘要素材)。
sourceraw docstring

in-memory-storeclj

(in-memory-store)
(in-memory-store {:keys [max-conversations] :or {max-conversations 1024}})

缺省的压缩状态存储:进程内,最多记 :max-conversations 个会话(缺省 1024,按最近 写入淘汰),进程停即丢。

缺省的压缩状态存储:进程内,最多记 `:max-conversations` 个会话(缺省 1024,按最近
写入淘汰),进程停即丢。
sourceraw docstring

micro-compactclj

(micro-compact msgs keep-recent max-chars)

只动 tool 结果:更早的(不含最近 keep-recent 条)正文换成 cleared-marker(id 与配对不动), 保留下来的超长正文头尾保留截到 max-chars。位置不变;幂等。

只动 tool 结果:更早的(不含最近 keep-recent 条)正文换成 `cleared-marker`(id 与配对不动),
保留下来的超长正文头尾保留截到 max-chars。位置不变;幂等。
sourceraw docstring

render-transcriptclj

(render-transcript msgs)

一段消息 → 「角色: 内容」纯文字(摘要素材):调用带名字与参数,结果长文掐头去尾。

一段消息 → 「角色: 内容」纯文字(摘要素材):调用带名字与参数,结果长文掐头去尾。
sourceraw docstring

safe-cut?clj

(safe-cut? msgs c)

c 处下刀是否不拆对(0 与 n 不算:那是整条都留 / 都走)。

c 处下刀是否不拆对(0 与 n 不算:那是整条都留 / 都走)。
sourceraw docstring

store-write-backclj

(store-write-back memory-store)

写回策略:用压缩结果改写 ChatMemory(mem-clear + mem-add,消息 id 保留)。 :write-back-store 就是它。

写回策略:用压缩结果改写 ChatMemory(`mem-clear` + `mem-add`,消息 id 保留)。
`:write-back-store` 就是它。
sourceraw docstring

summary-messageclj

(summary-message summary)

摘要消息:user 角色(Anthropic 不收中途 system),带 :compaction/summary 标记。

摘要消息:user 角色(Anthropic 不收中途 system),带 `:compaction/summary` 标记。
sourceraw docstring

summary-message?clj

(summary-message? m)
source

thresholdclj

(threshold {:keys [context-window trigger-ratio]})

触发阈值(token):窗口 × 比例;窗口 < 512k 时比例至少 0.75;地板 64k;地板把阈值 顶出窗口时取窗口的 0.85(仍不许 ≥ 窗口)。窗口 ≤ 0 → 1(永远超、压到最小)。

触发阈值(token):窗口 × 比例;窗口 < 512k 时比例至少 0.75;地板 64k;地板把阈值
顶出窗口时取窗口的 0.85(仍不许 ≥ 窗口)。窗口 ≤ 0 → 1(永远超、压到最小)。
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close