修复: 知识提炼改读消息表,不再依赖旧 JSON 列
消息存储拆分改造早前已覆盖主要读写路径(保存写消息表 / 切换 / 导出 / 跨端同步读消息表),但知识提炼仍在读旧的消息 JSON 列, 与改造目标不一致。 本次补全: - 知识提炼改为优先读消息表 - 表空时回退旧 JSON 列(老数据库兼容,与其他读路径一致) - 复用既有的记录转消息映射函数,与其他读路径同源 至此消息存储拆分全部完成,所有读写路径统一走消息表,旧 JSON 列仅作老库兼容回退,不再写入。
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user