修复: system 锚点避让悬空 tool 调用头防 400
- df-ai 新增 ContextManager::push_system_anchor 统一出口:末条为悬空 ToolCallHead 时插其前,否则尾部追加(不位移前缀) - 5 处 system 尾部 push 全量收口(压缩成功/兜底提交、警示单点、主题标记、续轮 nudge),从源头杜绝后台压缩摘要/警示楔入工具三元组致上游 400 insufficient tool messages - 单测 push_system_anchor 两态锁定;cargo test -p df-ai --lib context 155 passed / cargo check -p devflow EXIT 0 - 看板归档:全量完成态条目与段落迁 docs/07-项目管理/todo归档/2026-10-10.md(零措辞改动机械搬运,未完成项110条零丢失/完成项37条全入归档/行数守恒)
This commit is contained in:
@@ -1200,3 +1200,37 @@ fn eviction_no_user_text_no_summary() {
|
||||
);
|
||||
assert_eq!(mgr.all_messages_clone().len(), 16, "裁剪污染了内存全量");
|
||||
}
|
||||
|
||||
// ── 源头守静(909):push_system_anchor 避开悬空 tool_call 头 ──
|
||||
|
||||
#[test]
|
||||
fn push_system_anchor_appends_on_normal_tail() {
|
||||
let mut mgr = ContextManager::new(cfg(100_000));
|
||||
mgr.push(ChatMessage::user("问"));
|
||||
mgr.push(ChatMessage::assistant("答"));
|
||||
let avoided = mgr.push_system_anchor(ChatMessage::system("警示"));
|
||||
assert!(!avoided, "常规尾部应走追加(不避让)");
|
||||
assert_eq!(mgr.len(), 3);
|
||||
assert_eq!(mgr.messages_mut()[2].message.content, "警示", "system 应在尾部");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_system_anchor_avoids_dangling_tool_call_head() {
|
||||
// [user] [assistant(tool_calls)] ← 末条为悬空头(其 tool 响应尚未回填)
|
||||
let mut mgr = ContextManager::new(cfg(100_000));
|
||||
mgr.push(ChatMessage::user("跑工具"));
|
||||
mgr.push(ChatMessage::assistant_with_tools(
|
||||
"",
|
||||
vec![ToolCall::new("c1".to_string(), "t", "{}")],
|
||||
));
|
||||
let avoided = mgr.push_system_anchor(ChatMessage::system("## 摘要"));
|
||||
assert!(avoided, "末条为悬空 tool_call 头应避让插入");
|
||||
// 序列应为 [user, system(摘要), assistant(tool_calls)] —— 头仍是末条,三元组不被劈开
|
||||
let msgs = mgr.all_messages_clone();
|
||||
assert_eq!(msgs.len(), 3);
|
||||
assert_eq!(msgs[1].content, "## 摘要", "摘要应插在悬空头之前");
|
||||
assert!(
|
||||
msgs[2].tool_calls.as_ref().is_some_and(|c| !c.is_empty()),
|
||||
"悬空头应保持末条,其 tool 响应可紧随其后回填(协议不变式成立)"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user