优化: 缓存命中字段解析与提示词前缀稳定化

- openai 兼容口径补解析嵌套 cached_tokens
- cache 显示区分无数据与真零,估算轮不渲染
- 轨迹展开折叠空过程记录行降噪
- token 统计行恒完整可见不截断
- openai_compat 纳入 system 稳定易变拆分
- 系统警示与主题标记改尾部注入不位移前缀
- 工具结果视图压缩按入库时刻冻结
- 会话内工具集冻结仅增不减
- anthropic 缓存补消息级断点,设置页加开关
- 补 intent 测试对 Agent domain 覆盖
This commit is contained in:
lxy
2026-10-01 19:35:21 +08:00
parent 2187ffe173
commit 423acfdd00
14 changed files with 499 additions and 99 deletions
+13 -6
View File
@@ -11,7 +11,9 @@ use df_ai::provider::ChatMessage;
use crate::state::AppState;
use super::approval_timeout;
use crate::commands::ai::event_bus::emit_ai_chat_event;
use crate::commands::ai::prompt::{build_system_prompt_parts, cache_enabled, get_active_provider};
use crate::commands::ai::prompt::{
build_system_prompt_parts, cache_enabled, cache_split_provider_type, get_active_provider,
};
use crate::commands::ai::knowledge_inject::{build_knowledge_context_for_conv, inject_knowledge_into_prompt};
use crate::commands::ai::{AiChatEvent, AiSession, ErrorType, SessionState, GoalEntry};
use super::conv_state::ConvState;
@@ -20,9 +22,14 @@ use super::conv_state::ConvState;
///
/// 抽自 4 处同构骨架(insert_destructive_loop_warning / insert_stall_warning /
/// insert_tool_call_over_limit_warning / audit::cache insert_listing_bypass_warning):
/// 「lock → per_conv.contains_key 守卫 → messages.insert_at(0, system) → tracing::info」。
/// 「lock → per_conv.contains_key 守卫 → messages.push(system) → tracing::info」。
/// conv 已删(per_conv 无条目)→ 跳过插入与日志(对齐原守卫语义)。
///
/// **尾部追加而非 insert_at(0)**:历史头部插入会位移整条前缀,叠加警示文本逐条不同,
/// 自动前缀缓存(DeepSeek/GLM/Qwen)与 Anthropic 显式缓存全部失效。警示语义都是
/// 「提示当前轮」,追加到消息流末尾对 LLM 同样可见(紧邻本轮 tool_result 之后),
/// 且不破坏既有前缀字节。文本统一不含轮次计数(同类警示文本字节稳定)。
///
/// `log_tag` 为日志正文(不带 "[ai] " 前缀,helper 统一补);各站点特有字段
/// (iteration/loop_count 等)由调用方编入 tag 文本保留定位信息,helper 统一打 conv_id 字段。
pub(crate) async fn insert_system_notice(
@@ -34,7 +41,7 @@ pub(crate) async fn insert_system_notice(
let mut session = session_arc.lock().await;
if session.per_conv.contains_key(conv_id) {
let conv = session.conv(conv_id);
conv.messages.insert_at(0, ChatMessage::system(text));
conv.messages.push(ChatMessage::system(text));
tracing::info!(conv_id = %conv_id, "[ai] {}", log_tag);
}
}
@@ -121,10 +128,10 @@ pub async fn try_continue_agent_loop(
};
let lang = snap.agent_language.clone().unwrap_or_else(|| "zh-CN".to_string());
let conv_id_owned = conv_id.to_string();
// Anthropic prompt caching 模式组装(开关关 → 与旧路径逐字等价;仅 anthropic provider 拆分,
// OpenAI 零影响)。续跑路径无新 @ mention,不注入 augmentation(与旧路径一致)。
// prompt caching 拆分模式组装(开关关 → 与旧路径逐字等价;anthropic 系显式缓存 /
// openai_compat 前缀稳定)。续跑路径无新 @ mention,不注入 augmentation(与旧路径一致)。
let cache_on = cache_enabled(state).await
&& matches!(provider_config.provider_type.as_str(), "anthropic" | "anthropic_compat");
&& cache_split_provider_type(&provider_config.provider_type);
let (system_prompt, base_volatile) =
build_system_prompt_parts(state, &lang, &[], &[], cache_on).await;