From 15e2a01decd4ba5e3bdc9bfebb8bbcd6454c5b7e Mon Sep 17 00:00:00 2001 From: wunianxiao Date: Tue, 2 Jun 2026 15:36:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=204=20=E9=A1=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. (High) SQL 语法错误 — UPDATE ... ORDER BY ... LIMIT 1 → CTE 子查询 2. (High) 用户串扰 — 暂保持单用户序列,多用户场景后续重构 3. (Medium) 用户记忆 — 切换时 memory_store.load(from) 4. (Medium) call_capability prompt — 完整参数透传到目标工具 --- src/main.rs | 20 ++++++++++++-------- src/tools/approval.rs | 2 +- src/tools/builtin.rs | 16 +++++++++++++++- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index dbe52bc..74615c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -365,13 +365,14 @@ async fn cmd_listen( return Ok(build_capability_list(&spec_list)); } - // 确定目标工具名(call_capability 需要提取 name 参数) - let target_name = if n == "call_capability" { + // 确定目标工具名和参数(call_capability 提取 name + 透传完整参数) + let (target_name, target_args) = if n == "call_capability" { let cp: serde_json::Value = serde_json::from_str(&args).unwrap_or_default(); - cp.get("name").and_then(|v| v.as_str()).map(|s| s.to_string()).unwrap_or_default() - } else { n.clone() }; + let name = cp.get("name").and_then(|v| v.as_str()).map(|s| s.to_string()).unwrap_or_default(); + (name, serde_json::to_string(&cp).unwrap_or_default()) + } else { (n.clone(), args.clone()) }; - // 内置工具(高风险先审批再执行) + // 内置工具 if tools::builtin::BuiltinRegistry::is_builtin(&target_name) { if tools::builtin::BuiltinRegistry::is_high_risk(&target_name) { let mut ctx = ExecutionContext::new(&user_id); @@ -384,14 +385,14 @@ async fn cmd_listen( Err(e) => return Ok(e), } } - if let Some(result) = tools::builtin::BuiltinRegistry::execute(&target_name, &args).await { + if let Some(result) = tools::builtin::BuiltinRegistry::execute(&target_name, &target_args).await { return Ok(result.output); } } // 回退到外部 SkillRegistry if let Some(ref r) = reg { - let params: serde_json::Value = serde_json::from_str(&args).unwrap_or(serde_json::json!({})); + let params: serde_json::Value = serde_json::from_str(&target_args).unwrap_or(serde_json::json!({})); let mut ctx = ExecutionContext::new(&user_id); ctx = ctx.with_approval(approval.clone()); ctx = ctx.with_wechat(send); @@ -455,7 +456,7 @@ async fn cmd_listen( file_state.save_runtime(&client.get_updates_buf().await); info!("已退出"); } - _ = listen_loop(&client, enable_llm, echo, &account_id, database, file_state, conversation, &approval_manager, listen_user_id) => {} + _ = listen_loop(&client, enable_llm, echo, &account_id, database, file_state, conversation, &approval_manager, listen_user_id, memory_store) => {} } } @@ -469,6 +470,7 @@ async fn listen_loop( conversation: Option>, approval_manager: &ApprovalManager, current_user_id: Arc>, + memory_store: &Arc, ) { let last_user_id = Arc::new(tokio::sync::Mutex::new(String::new())); loop { @@ -553,6 +555,8 @@ async fn listen_loop( s.current_user_id = from.to_string(); // 加载该用户最近 20 条历史 s.load_recent_messages(20).await; + drop(s); + memory_store.load(from).await; } } *last_user = from.to_string(); diff --git a/src/tools/approval.rs b/src/tools/approval.rs index d0dfb5d..db218e6 100644 --- a/src/tools/approval.rs +++ b/src/tools/approval.rs @@ -128,7 +128,7 @@ impl ApprovalManager { }; // 按 user_id + 最近 pending 记录更新 let _ = sqlx::query( - "UPDATE pending_approvals SET status = $1, consumed_at = NOW() WHERE user_id = $2 AND status = 'pending' ORDER BY created_at DESC LIMIT 1", + "UPDATE pending_approvals SET status = $1, consumed_at = NOW() WHERE id = (SELECT id FROM pending_approvals WHERE user_id = $2 AND status = 'pending' ORDER BY created_at DESC LIMIT 1)", ) .bind(status) .bind(user_id) diff --git a/src/tools/builtin.rs b/src/tools/builtin.rs index c7e6deb..da31c24 100644 --- a/src/tools/builtin.rs +++ b/src/tools/builtin.rs @@ -48,7 +48,21 @@ fn execute_datetime() -> SkillResult { } async fn handle_memos(args_json: &str) -> SkillResult { - let params: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default(); + let mut params: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default(); + // call_capability 透传完整参数,action/content 在顶层 + // 如果顶层没有 action,尝试从 prompt 推断 + if params.get("action").is_none() { + let prompt = params.get("prompt").and_then(|v| v.as_str()).unwrap_or(""); + let action = if prompt.contains("添加") || prompt.contains("新增") || prompt.contains("add") { + "add" + } else if prompt.contains("删除") || prompt.contains("移除") || prompt.contains("delete") { + "delete" + } else { + "list" + }; + let content = params.get("prompt").and_then(|v| v.as_str()).unwrap_or(""); + params = serde_json::json!({"action": action, "content": content}); + } let action = params.get("action").and_then(|v| v.as_str()).unwrap_or("list"); let path = ".data/memos.json"; if let Err(e) = std::fs::create_dir_all(".data") { return SkillResult::error(format!("创建目录失败: {}", e)); }