fix(identity): 接通身份信号提取与持久化 — 对话中起名跨会话记忆
Some checks failed
CI / Rust Check (push) Has been cancelled
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Rust Check (push) Has been cancelled
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
根因: 记忆提取管道(COMBINED_EXTRACTION_PROMPT)提取5种画像信号 但无身份信号(agent_name/user_name),不存在从对话到AgentConfig.name 或IdentityFiles的写回路径。 修复内容: - ProfileSignals 增加 agent_name/user_name 字段 - COMBINED_EXTRACTION_PROMPT 增加身份提取指令 - parse_profile_signals 解析新字段 + 回退推断 - GrowthIntegration 存储身份信号到 VikingStorage - post_conversation_hook 写回 soul.md + emit Tauri 事件 - streamStore 规则化检测 agent 名字并更新 AgentConfig.name - cold-start-mapper 新增 detectAgentNameSuggestion 链路: 对话→提取→VikingStorage→hook写回soul.md→事件→前端刷新
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use std::sync::Arc;
|
||||
use tauri::Emitter;
|
||||
use zclaw_growth::VikingStorage;
|
||||
|
||||
use crate::intelligence::identity::IdentityManagerState;
|
||||
use crate::intelligence::heartbeat::HeartbeatEngineState;
|
||||
@@ -56,12 +58,15 @@ pub async fn pre_conversation_hook(
|
||||
///
|
||||
/// 1. Record interaction for heartbeat engine
|
||||
/// 2. Record conversation for reflection engine, trigger reflection if needed
|
||||
/// 3. Detect identity signals and write back to identity files
|
||||
pub async fn post_conversation_hook(
|
||||
agent_id: &str,
|
||||
_user_message: &str,
|
||||
_heartbeat_state: &HeartbeatEngineState,
|
||||
reflection_state: &ReflectionEngineState,
|
||||
llm_driver: Option<Arc<dyn LlmDriver>>,
|
||||
identity_state: &IdentityManagerState,
|
||||
app: &tauri::AppHandle,
|
||||
) {
|
||||
// Step 1: Record interaction for heartbeat
|
||||
crate::intelligence::heartbeat::record_interaction(agent_id);
|
||||
@@ -200,6 +205,71 @@ pub async fn post_conversation_hook(
|
||||
reflection_result.improvements.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Step 3: Detect identity signals from recent memory extraction and write back
|
||||
if let Ok(storage) = crate::viking_commands::get_storage().await {
|
||||
let identity_prefix = format!("agent://{}/identity/", agent_id);
|
||||
|
||||
// Check for agent_name identity signal
|
||||
let agent_name_uri = format!("{}agent-name", identity_prefix);
|
||||
if let Ok(Some(entry)) = VikingStorage::get(storage.as_ref(), &agent_name_uri).await {
|
||||
// Extract name from content like "助手的名字是小马"
|
||||
let name = entry.content.strip_prefix("助手的名字是")
|
||||
.map(|n| n.trim().to_string())
|
||||
.unwrap_or_else(|| entry.content.clone());
|
||||
|
||||
if !name.is_empty() {
|
||||
// Update IdentityFiles.soul to include the agent name
|
||||
let mut manager = identity_state.lock().await;
|
||||
let current_soul = manager.get_file(agent_id, crate::intelligence::identity::IdentityFile::Soul);
|
||||
|
||||
// Only update if the name isn't already in the soul
|
||||
if !current_soul.contains(&name) {
|
||||
let updated_soul = if current_soul.is_empty() {
|
||||
format!("# ZCLAW 人格\n\n你的名字是{}。\n\n你是一个成长性的中文 AI 助手。", name)
|
||||
} else if current_soul.contains("你的名字是") || current_soul.contains("你的名字:") {
|
||||
// Replace existing name line
|
||||
let re = regex::Regex::new(r"你的名字是[^\n]+").unwrap();
|
||||
re.replace(¤t_soul, format!("你的名字是{}", name)).to_string()
|
||||
} else {
|
||||
// Prepend name to existing soul
|
||||
format!("你的名字是{}。\n\n{}", name, current_soul)
|
||||
};
|
||||
|
||||
if let Err(e) = manager.update_file(agent_id, "soul", &updated_soul) {
|
||||
warn!("[intelligence_hooks] Failed to update soul with agent name: {}", e);
|
||||
} else {
|
||||
debug!("[intelligence_hooks] Updated agent name to '{}' in soul", name);
|
||||
}
|
||||
}
|
||||
drop(manager);
|
||||
|
||||
// Emit event for frontend to update AgentConfig.name
|
||||
let _ = app.emit("zclaw:agent-identity-updated", serde_json::json!({
|
||||
"agentId": agent_id,
|
||||
"agentName": name,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for user_name identity signal
|
||||
let user_name_uri = format!("{}user-name", identity_prefix);
|
||||
if let Ok(Some(entry)) = VikingStorage::get(storage.as_ref(), &user_name_uri).await {
|
||||
let name = entry.content.strip_prefix("用户的名字是")
|
||||
.map(|n| n.trim().to_string())
|
||||
.unwrap_or_else(|| entry.content.clone());
|
||||
|
||||
if !name.is_empty() {
|
||||
let mut manager = identity_state.lock().await;
|
||||
let profile = manager.get_file(agent_id, crate::intelligence::identity::IdentityFile::UserProfile);
|
||||
|
||||
if !profile.contains(&name) {
|
||||
manager.append_to_user_profile(agent_id, &format!("- 用户名字: {}", name));
|
||||
debug!("[intelligence_hooks] Appended user name '{}' to profile", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build memory context by searching VikingStorage for relevant memories
|
||||
|
||||
@@ -324,6 +324,7 @@ pub async fn agent_chat_stream(
|
||||
|
||||
let hb_state = heartbeat_state.inner().clone();
|
||||
let rf_state = reflection_state.inner().clone();
|
||||
let id_state_hook = identity_state.inner().clone();
|
||||
|
||||
// Clone the guard map for cleanup in the spawned task
|
||||
let guard_map: SessionStreamGuard = stream_guard.inner().clone();
|
||||
@@ -380,12 +381,14 @@ pub async fn agent_chat_stream(
|
||||
let hb = hb_state.clone();
|
||||
let rf = rf_state.clone();
|
||||
let driver = llm_driver.clone();
|
||||
let id_state = id_state_hook.clone();
|
||||
let app_hook = app.clone();
|
||||
if driver.is_none() {
|
||||
tracing::debug!("[agent_chat_stream] Post-hook firing without LLM driver (schedule intercept path)");
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
crate::intelligence_hooks::post_conversation_hook(
|
||||
&agent_id_hook, &message_hook, &hb, &rf, driver,
|
||||
&agent_id_hook, &message_hook, &hb, &rf, driver, &id_state, &app_hook,
|
||||
).await;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user