前面介绍了说ASJ 2中把Memory给干掉了,但是我们介绍ASJ 1.0的时候,说过一个上下文自动压缩,他也是基于Memory机制扩展的。那么2.0中又是则呢么做的呢? 2.0 里这套机制部以 middleware 形式插到 onReasoning 钩子上,自动运行、可独立开关: | 策略 | 处理的问题维度 | 实现 | | --- | --- | --- | | 对话摘要压缩 | context 的 | CompactionMiddleware | | 大工具结果外置 | context 的 | ToolResultEvictionMiddleware | | 溢出兜底重试 | 模型端仍抛 context_length_exceeded | HarnessAgent.recoverFromOverflow | | 预压缩参数截断/结果精简 | 老消息里的 tool_call 参数与 tool_result 数据太长 | CompactionConfig.TruncateArgsConfig |
四条策略默认全开、也可以单独关闭。
对话摘要压缩
CompactionMiddleware 是一个Middleware,重写了onReasoning方法。
public Flux<AgentEvent> onReasoning(Agent agent, RuntimeContext ctx,
ReasoningInput input,
Function<ReasoningInput, Flux<AgentEvent>> next) {
// ① 拆 system 与 conversation
Msg systemMsg = /* messages[0] if SYSTEM */;
List<Msg> conv = /* rest */;
// ② 动态阈值展开
CompactionConfig eff = resolveEffectiveConfig();
// ③ 交给 ConversationCompactor
return compactor.compactIfNeeded(ctx, conv, eff, agentId, sessionId)
.flatMapMany(opt -> {
if (opt.isPresent()) {
// ④ 就地覆写 AgentState.contextMutable()
applyToContext(state, opt.get());
// ⑤ 构造新 ReasoningInput
List<Msg> newMsgs = withSystem(systemMsg, opt.get());
return next.apply(new ReasoningInput(newMsgs,
input.tools(),
input.options()));
}
return next.apply(input); // ⑥ 未压缩透传
})
.onErrorResume(e -> next.apply(input)); // ⑦ 任何异常降级
}
核心压缩算法在ConversationCompactor中,执行流程如下
messages
│
▼
【1】truncateArgs ← 非-LLM,只对老 ASSISTANT.tool_use 入参截断
│
▼
【2】pruneToolResults ← 非-LLM,精简老 TOOL 结果(保留最近 40K tokens)
│
▼
【3】shouldCompact? ← messages 数或 token 数超阈值
│ 是
▼
【4】determineCutoffIndex ← 按 keepTokens 或 keepMessages 找切点
│ └─ findSafeCutoffPoint:不拆 ASSISTANT/TOOL pair
│
▼
【5】prefix + tail = split(messages, cutoff)
│ └─ prefix 会先 filterSummaryMessages() 剔掉老的 __compaction_summary__
│
▼
【6】flushBeforeCompact? ← 抽取事实到 daily ledger / MEMORY.md
│
▼
【7】offloadBeforeCompact? ← 整段 messages 追加到 sessions/<sessionId>.log.jsonl
│ 取回落盘路径供 summary 消息引用
│
▼
【8】summarizePrefix ← 一次 model.stream(),用结构化 prompt 生成摘要
│
▼
compacted = [summaryMsg(role=USER, name="__compaction_summary__")] + tail