修复: 知识提炼改读消息表,不再依赖旧 JSON 列

消息存储拆分改造早前已覆盖主要读写路径(保存写消息表 / 切换 /
导出 / 跨端同步读消息表),但知识提炼仍在读旧的消息 JSON 列,
与改造目标不一致。

本次补全:
- 知识提炼改为优先读消息表
- 表空时回退旧 JSON 列(老数据库兼容,与其他读路径一致)
- 复用既有的记录转消息映射函数,与其他读路径同源

至此消息存储拆分全部完成,所有读写路径统一走消息表,旧 JSON
列仅作老库兼容回退,不再写入。
This commit is contained in:
2026-06-28 18:47:21 +08:00
parent 10629f88e8
commit 19b647e650
2 changed files with 20 additions and 7 deletions

View File

@@ -298,7 +298,11 @@
#### DEC-260628-04 消息级溯源 P2 切读 #### DEC-260628-04 消息级溯源 P2 切读
- **决策**:✅ b 一次性切读(保留原表数据不删) - **决策**:✅ b 一次性切读(保留原表数据不删)
- **记录**:读写路径从 `ai_conversations.messages` 切到 `ai_messages` 表,原列保留 - **记录**:读写路径从 `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 #### DEC-260628-05 SMELL-P1-6 String→newtype
- **决策**:✅ b 路线(仅 Rust 内部 newtype) - **决策**:✅ b 路线(仅 Rust 内部 newtype)

View File

@@ -12,7 +12,7 @@ use df_ai::provider::{ChatMessage, CompletionRequest, LlmProvider, MessageRole};
use df_ai::router::{ use df_ai::router::{
select_model_id, Modality, TaskRequirements, 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::db::Database;
use df_storage::models::{AiProviderRecord, KnowledgeRecord}; use df_storage::models::{AiProviderRecord, KnowledgeRecord};
@@ -566,12 +566,21 @@ async fn extract_knowledge_from_conversation(
.filter(|t| !t.trim().is_empty()) .filter(|t| !t.trim().is_empty())
.unwrap_or_else(|| "未命名对话".to_string()); .unwrap_or_else(|| "未命名对话".to_string());
let messages: Vec<ChatMessage> = match serde_json::from_str(&conv.messages) { // 消息读取:优先 ai_messages 表(消息拆分存储真相源),表空时 fallback 旧 messages JSON 列(老库兼容)
Ok(v) => v, let msg_repo = AiMessageRepo::new(db);
Err(e) => { let records = msg_repo.list_by_conversation(conv_id).await.unwrap_or_default();
tracing::warn!(error = %e, "[ai] 对话消息 JSON 解析失败,降级空消息(知识提炼跳过)"); let messages: Vec<ChatMessage> = if !records.is_empty() {
Vec::new() 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 条 // 过滤 user/assistant,取最后 6 条
let recent: Vec<&ChatMessage> = messages let recent: Vec<&ChatMessage> = messages