上下文压缩引擎 — 对标 DeepAgents SummarizationMiddleware。
职责
- 监控上下文 token 使用量,在超过阈值时自动压缩旧消息
- 将被驱逐的消息 offload 到文件,保留完整历史
- ContextOverflow 时作为 fallback 强制压缩
- 生成摘要消息替换旧历史
Token 计数策略
采用 Provider usage 为主 + 字符估算 fallback:
- 优先使用
State.token_usage.total_tokens(Provider 返回的真实值) - Fallback:消息文本
byte_size / 4粗估
触发条件(二选一)
:token模式 —total_tokens >= trigger_tokens(默认 170_000):fraction模式 —total_tokens >= context_window * trigger_fraction(默认 0.85)
保留策略
:messages模式 — 保留最近 N 条消息(默认 6):fraction模式 — 保留最近占 context_window 的 fraction
使用方式
不直接调用,由 Agent run_turn 在 before_request 后自动检查:
case Compactor.maybe_compact(state) do
{:compacted, state} -> run_turn_with(state)
{:skip, state} -> run_turn_with(state)
end或在 ContextOverflow 时强制调用:
{:compacted, state} = Compactor.force_compact(state)
Summary
Functions
读取压缩配置。从 state.config 中获取,缺省使用默认值。
估算当前上下文 token 数量。
从消息列表估算 token 数(字符数 / 4 粗估)。
强制执行压缩(ContextOverflow fallback)。
检查是否需要压缩,需要时自动执行。
仅检查是否达到压缩阈值(不执行压缩)。
Types
@type compact_opts() :: %{ trigger: trigger(), keep: keep(), offload_dir: String.t(), summary_prompt: String.t() }
压缩配置。
@type keep() :: {:messages, pos_integer()} | {:fraction, float()}
消息保留配置。
@type trigger() :: {:tokens, pos_integer()} | {:fraction, float()}
压缩触发配置。
Functions
@spec compact_config(CMDC.Agent.State.t()) :: compact_opts()
读取压缩配置。从 state.config 中获取,缺省使用默认值。
支持的 config 键:
:compact_trigger—{:tokens, n}或{:fraction, f}:compact_keep—{:messages, n}或{:fraction, f}:compact_offload_dir— offload 文件目录前缀:compact_summary_prompt— 摘要生成提示词
@spec estimate_tokens(CMDC.Agent.State.t()) :: non_neg_integer()
估算当前上下文 token 数量。
优先使用 Provider 返回的 usage,fallback 到字符估算。
@spec estimate_tokens_from_messages([CMDC.Message.t()]) :: non_neg_integer()
从消息列表估算 token 数(字符数 / 4 粗估)。
@spec force_compact(CMDC.Agent.State.t()) :: {:compacted, CMDC.Agent.State.t()} | {:skip, CMDC.Agent.State.t()}
强制执行压缩(ContextOverflow fallback)。
忽略阈值检查,直接压缩。如果消息太少无法分割,返回原 state。
@spec maybe_compact(CMDC.Agent.State.t()) :: {:compacted, CMDC.Agent.State.t()} | {:skip, CMDC.Agent.State.t()}
检查是否需要压缩,需要时自动执行。
返回:
{:compacted, state}— 已执行压缩,state 中消息已替换{:skip, state}— 未达到阈值,不需要压缩
@spec should_compact?(CMDC.Agent.State.t()) :: boolean()
仅检查是否达到压缩阈值(不执行压缩)。
供 Agent 层在 :before_compact Plugin Pipeline 之前判断是否触发。