优化: F-15上下文基础(is_active正面白名单+push token仅active计入 零行为变化)
This commit is contained in:
@@ -176,7 +176,13 @@ impl ContextManager {
|
||||
pub fn push(&mut self, message: ChatMessage) {
|
||||
let tokens = self.estimator.estimate_message(&message);
|
||||
let group = classify_group(&message);
|
||||
self.history_tokens += tokens;
|
||||
// 仅 active 消息计入 token 预算(F-15 §3.3):truncated / archived_segment /
|
||||
// compressed 不进 LLM 上下文,token 虚高会致 build_for_request 误判超预算
|
||||
// 触发不必要裁剪。!active 消息仍 push 到 self.messages 全量保留(持久化不受影响),
|
||||
// sanitize_messages step0(is_active 过滤)在发送视图统一剔除。
|
||||
if message.is_active() {
|
||||
self.history_tokens += tokens;
|
||||
}
|
||||
self.messages.push(TrackedMessage {
|
||||
message,
|
||||
token_count: tokens,
|
||||
@@ -851,6 +857,59 @@ mod tests {
|
||||
assert_eq!(mgr.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_token_only_active() {
|
||||
// F-15 §3.3:!active 消息(truncated / archived_segment / compressed)仍 push
|
||||
// 到 self.messages(全量保留,持久化/前端视图自管),但不计入 history_tokens,
|
||||
// 避免 build_for_request 误判超预算触发不必要裁剪。
|
||||
|
||||
// 1) 直接 push 路径
|
||||
let mut mgr = ContextManager::new(cfg(100_000));
|
||||
let active_msg = ChatMessage::user("这条是 active 的");
|
||||
let active_tokens = TokenEstimator::default().estimate_message(&active_msg);
|
||||
let mut inactive_msg = ChatMessage::assistant("这条被截断了不该计 token");
|
||||
inactive_msg.status = Some("truncated".to_string());
|
||||
let inactive_tokens = TokenEstimator::default().estimate_message(&inactive_msg);
|
||||
|
||||
mgr.push(active_msg);
|
||||
mgr.push(inactive_msg);
|
||||
|
||||
// 全量保留(两条都在内存)
|
||||
assert_eq!(mgr.len(), 2, "active + !active 都应 push 到 self.messages");
|
||||
assert_eq!(mgr.all_messages_clone().len(), 2, "持久化全量不受 push 修正影响");
|
||||
// token 预算只含 active
|
||||
assert_eq!(
|
||||
mgr.history_tokens(),
|
||||
active_tokens,
|
||||
"history_tokens 应只含 active,多算了 {}(inactive 应被忽略)",
|
||||
mgr.history_tokens().saturating_sub(active_tokens)
|
||||
);
|
||||
assert!(
|
||||
inactive_tokens > 0,
|
||||
"前提:inactive 消息本身确有 token,否则无法证明它被排除"
|
||||
);
|
||||
|
||||
// 2) restore_from_messages 路径(调 push,token 同步仅 active)
|
||||
let mut mgr2 = ContextManager::new(cfg(100_000));
|
||||
let mut a = ChatMessage::user("active 一");
|
||||
a.status = Some("active".to_string());
|
||||
let mut b = ChatMessage::user("archived 一");
|
||||
b.status = Some("archived_segment".to_string());
|
||||
let mut c = ChatMessage::user("compressed 一");
|
||||
c.status = Some("compressed".to_string());
|
||||
mgr2.restore_from_messages(vec![a, b, c]);
|
||||
|
||||
assert_eq!(mgr2.len(), 3, "restore 后全量保留三条");
|
||||
// 只 active 一条计 token(b/c 是白名单外状态,is_active 返回 false)
|
||||
let only_active_tokens = TokenEstimator::default()
|
||||
.estimate_message(&ChatMessage::user("active 一"));
|
||||
assert_eq!(
|
||||
mgr2.history_tokens(),
|
||||
only_active_tokens,
|
||||
"restore 后 history_tokens 应只含 active 一条,archived/compressed 不计"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_history_returns_empty() {
|
||||
let mgr = ContextManager::new(cfg(100_000));
|
||||
|
||||
Reference in New Issue
Block a user