From 6aac7efcfb684f9f8abf44077ed5978bfb16f979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Sun, 28 Jun 2026 18:47:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E6=8F=90=E7=82=BC=E5=88=87=E8=AF=BB=20ai=5Fmessages=20?= =?UTF-8?q?=E8=A1=A8,=E5=AE=8C=E6=88=90=E6=B6=88=E6=81=AF=E6=BA=AF?= =?UTF-8?q?=E6=BA=90=20P2=20=E6=9C=80=E5=90=8E=E4=B8=80=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 消息级溯源 P2 切读早前已覆盖主要读写路径(save_conversation 写 / switch/export/load_messages 读),但 knowledge_inject 的知识提炼 仍在读旧 ai_conversations.messages JSON 列,与 P2 切读目标不一致。 本次补全: - extract_knowledge_from_conversation 改为优先读 ai_messages 表 - 表空时 fallback 旧 messages JSON 列(老库兼容,对齐 switch 路径) - 复用 commands::record_to_message 映射函数(DRY,与其他读路径同源) 至此消息溯源 P2 切读全部完成,所有读写路径统一走 ai_messages 表, 旧 messages JSON 列仅作老库兼容 fallback,不再写入。 --- docs/待决策.md | 6 +++++- src-tauri/src/commands/ai/knowledge_inject.rs | 21 +++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/待决策.md b/docs/待决策.md index 63751f4..8c41310 100644 --- a/docs/待决策.md +++ b/docs/待决策.md @@ -298,7 +298,11 @@ #### DEC-260628-04 消息级溯源 P2 切读 - **决策**:✅ b 一次性切读(保留原表数据不删) - **记录**:读写路径从 `ai_conversations.messages` 切到 `ai_messages` 表,原列保留 -- **状态**:🟢 已决·待推进 +- **核验(2026-06-28)**:✅ 全部已完成—— + - 写路径:`save_conversation` 已全量重写 ai_messages + - 读路径:`ai_conversation_switch`/`ai_conversation_export`/`remote_bridge::route_load_messages` 优先读 ai_messages,表空时 fallback 旧 JSON(老库兼容) + - 最后一处补全:`knowledge_inject::extract_knowledge_from_conversation` 也切到 ai_messages +- **状态**:✅ 已决·已实施 #### DEC-260628-05 SMELL-P1-6 String→newtype - **决策**:✅ b 路线(仅 Rust 内部 newtype) diff --git a/src-tauri/src/commands/ai/knowledge_inject.rs b/src-tauri/src/commands/ai/knowledge_inject.rs index 450edd5..7206a94 100644 --- a/src-tauri/src/commands/ai/knowledge_inject.rs +++ b/src-tauri/src/commands/ai/knowledge_inject.rs @@ -12,7 +12,7 @@ use df_ai::provider::{ChatMessage, CompletionRequest, LlmProvider, MessageRole}; use df_ai::router::{ select_model_id, Modality, TaskRequirements, }; -use df_storage::crud::{AiConversationRepo, KnowledgeRepo}; +use df_storage::crud::{AiConversationRepo, AiMessageRepo, KnowledgeRepo}; use df_storage::db::Database; use df_storage::models::{AiProviderRecord, KnowledgeRecord}; @@ -566,12 +566,21 @@ async fn extract_knowledge_from_conversation( .filter(|t| !t.trim().is_empty()) .unwrap_or_else(|| "未命名对话".to_string()); - let messages: Vec = match serde_json::from_str(&conv.messages) { - Ok(v) => v, - Err(e) => { - tracing::warn!(error = %e, "[ai] 对话消息 JSON 解析失败,降级空消息(知识提炼跳过)"); - Vec::new() + // 消息读取:优先 ai_messages 表(消息拆分存储真相源),表空时 fallback 旧 messages JSON 列(老库兼容) + let msg_repo = AiMessageRepo::new(db); + let records = msg_repo.list_by_conversation(conv_id).await.unwrap_or_default(); + let messages: Vec = if !records.is_empty() { + records.iter().map(crate::commands::ai::commands::record_to_message).collect() + } else { + // 老库未迁移或坏数据:回退旧 messages JSON 列(与 ai_conversation_switch 同一兼容路径) + let has_legacy = conv.messages != "[]" && !conv.messages.is_empty(); + if has_legacy { + tracing::warn!( + conv_id, + "[KNOWLEDGE-EXTRACT] ai_messages 表为空,回退旧 messages JSON 列(老库兼容)" + ); } + serde_json::from_str(&conv.messages).unwrap_or_default() }; // 过滤 user/assistant,取最后 6 条 let recent: Vec<&ChatMessage> = messages