新增: 上下文裁剪淘汰补偿摘要

超预算视图裁剪时,被淘汰文本提炼关键词概要插回发送视图头部,补偿无声丢弃的早期主题
摘要以 user 角色注入:system 角色会被兼容层折叠进带 cache_control 的顶层段,纯视图每轮再生成会持续改写缓存指纹
纯视图语义不落库,被淘汰原文存储态不变;压缩失败兜底摘要与淘汰摘要共用提炼核心
This commit is contained in:
lxy
2026-10-01 02:41:04 +08:00
parent 3e239a108f
commit 9b89610e38
3 changed files with 320 additions and 16 deletions
+21 -3
View File
@@ -22,7 +22,7 @@
mod sanitize;
use crate::context_helpers::{
classify_group, PLACEHOLDER_INTEGRITY_ENABLED, PROTECT_COUNT,
build_eviction_summary_text, classify_group, PLACEHOLDER_INTEGRITY_ENABLED, PROTECT_COUNT,
};
// 重导出:保持 `df_ai::context::TokenEstimator` / `df_ai::context::ContextConfig` 等
// 历史路径对外可见(agentic.rs / commands/ai/mod.rs 等调用方零变更)。
@@ -179,7 +179,8 @@ impl ContextManager {
/// 构建发送给 LLM 的消息列表
///
/// `sys_tokens` 为调用方已估算好的 system prompt token 数。
/// 超预算时自动裁剪旧消息(保护工具调用三元组 + 最近 PROTECT_COUNT 条)。
/// 超预算时自动裁剪旧消息(保护工具调用三元组 + 最近 PROTECT_COUNT 条),并在
/// 被淘汰区头部插入关键词补偿摘要(role=system,纯视图易变区,不落库不改存储)。
/// 返回 (消息列表, 是否发生了裁剪)。
pub fn build_for_request(&self, sys_tokens: u32) -> (Vec<ChatMessage>, bool) {
let budget = self.budget_limit();
@@ -235,10 +236,27 @@ impl ContextManager {
);
}
let msgs: Vec<ChatMessage> = self.messages[trim_end..]
let evicted: Vec<ChatMessage> = self.messages[..trim_end]
.iter()
.map(|t| t.message.clone())
.collect();
let mut msgs: Vec<ChatMessage> = self.messages[trim_end..]
.iter()
.map(|t| t.message.clone())
.collect();
// 淘汰补偿摘要:视图裁剪无声丢弃被淘汰内容,LLM 从此看不到早期主题,与压缩
// 路径(有摘要补偿)行为不一致。此处把被淘汰文本提炼成一短段摘要插回发送
// 视图最前部(淘汰区头部、保护消息之前)。角色必须 user:role=system 会被
// anthropic_compat 折叠进带 cache_control 的顶层 system 段,而本摘要纯视图
// 每轮即时再生成(淘汰区单调增长致内容逐轮漂移),落 system 即逐轮改写缓存
// 指纹使全部前缀缓存失效;连续 role 由既有 sanitize/协议层自愈兜底。摘要只进
// messages 易变区,严禁拼进 system_prompt/任何稳定段;纯视图语义:不落库、
// 不改 conv.messages 存储、不标记 compressed,被淘汰原文存储态不变。
// 极短/空淘汰文本提炼为空 → 跳过(不值得插)。
if let Some(text) = build_eviction_summary_text(&evicted) {
msgs.insert(0, ChatMessage::user(text));
}
tracing::info!(
"context_trimmed: skip {} messages, ~{} tokens (view-only, full history retained)",