清理废弃代码,重构长期记忆系统

核心变更:
- 删除 worker.rs + ipc.rs(废弃进程架构)
- 删除 ias listen 路径(cmd_listen / listen_loop / ToolExecutor)
- 合并 ias service → ias daemon --log-file
- 修复 main.rs call_capability 不处理上下文工具的 bug

MemoryStore 重构:
- write_for 空内容防御 + 精确去重
- 缓存结构 Vec<String> → Vec<(i32, String)>(用数据库 id 标识)
- 新增 delete_for / update_for 方法 + LLM 工具定义
- load_if_needed 避免每次消息查 DB
- 缓存 TTL(1h) + 每用户 200 条上限
- read_for limit 参数(默认 30 条)

daemon.rs:
- execute_tool 统一为递归派发,消除 call_capability 重复路由

净减少约 1400 行代码,0 warning,20 测试通过
This commit is contained in:
2026-06-16 11:56:36 +08:00
parent 5a54368437
commit 1d78567102
19 changed files with 1060 additions and 1895 deletions
+98 -88
View File
@@ -34,15 +34,14 @@
//! - **Tool Consumer** — 执行内置工具,高风险工具先走审批
//! - **Send Consumer** — 将 LLM 回复通过 WeChat API 发回给用户
//!
//! ### 与旧架构的关系
//! ### 架构
//!
//! 旧架构使用 `daemon + worker` 分离模式(UDS IPCworker 已废弃
//! 当前是统一的单进程 tokio task 架构。
//! 采用统一的单进程 tokio task 架构。旧版 daemon+worker 分离模式(UDS IPC已在 v2 中移除
use crate::channel::ChannelId;
use crate::context::MemoryStore;
use crate::db::Database;
use crate::ipc::HistoryEntry;
use crate::context::HistoryEntry;
use crate::llm::{ChatResult, Conversation, ConversationConfig, ToolExecutor, DEFAULT_SYSTEM_PROMPT, ASYNC_MARKER};
use crate::queue::{ConsumerChannels, EnqueueHandle, MessageKind, PipelineMessage, QueueRunner};
use crate::state::StateManager;
@@ -100,7 +99,7 @@ pub async fn run(
if let Some(r) = file_state.load_runtime() { client.set_updates_buf(&r.get_updates_buf).await; }
if let Err(e) = client.notify_start().await { error!("注册监听器失败: {}", e); return; }
let account_id = auth.account_id.clone();
info!("Daemon 已启动 (无 worker 进程模式)");
info!("Daemon 已启动");
// 4-6. 审批管理器、工具列表、模型配置
let approval_manager = Arc::new(ApprovalManager::new(
@@ -315,7 +314,7 @@ async fn llm_consumer_loop(
info!("LLM Consumer: user={} text={:.60}", user_id, text);
// 加载数据
ctx.memory_store.load(&user_id).await;
ctx.memory_store.load_if_needed(&user_id).await;
let history = load_history(&ctx.db, &user_id).await;
let sys_prompt = if ctx.system_prompt.is_empty() {
@@ -595,86 +594,77 @@ async fn tool_consumer_loop(
// ─── 统一工具执行 ───
/// 所有工具的统一执行入口 — 仅此一处
async fn execute_tool(name: &str, arguments: &str, ctx: &DaemonCtx, user_id: &str) -> String {
// 上下文工具
match name {
"read_memories" => {
let mems = load_memories(&ctx.memory_store, user_id).await;
if mems.is_empty() { return "暂无长期记忆".to_string(); }
let lines: Vec<String> = mems.iter().enumerate()
.map(|(i, v)| format!("{}. {}", i + 1, v)).collect();
return lines.join("\n");
}
"write_memory" => {
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
let content = params.get("content").and_then(|v| v.as_str()).unwrap_or("");
if content.is_empty() { return "请提供 content 参数".to_string(); }
ctx.memory_store.write_for(user_id, content).await;
return format!("已添加长期记忆: {}", content);
}
"read_summaries" => {
let sums = load_summaries(&ctx.db, user_id).await;
if sums.is_empty() { return "暂无历史摘要".to_string(); }
return sums.iter().enumerate()
.map(|(i, s)| format!("{}. {}", i + 1, s))
.collect::<Vec<_>>().join("\n---\n");
}
"query_capabilities" => {
let specs = crate::tools::specs();
return crate::tools::build_capability_guide(&specs);
}
"call_capability" => {
// 解包 {name, prompt} → 提取目标工具名和参数,直接派发
let cp: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
let target_name = cp.get("name").and_then(|v| v.as_str()).unwrap_or("").to_string();
if target_name.is_empty() {
return "请提供 name 参数".to_string();
///
/// `call_capability` 通过递归派发到此函数,需 boxing 避免无限大小。
fn execute_tool<'a>(
name: &'a str,
arguments: &'a str,
ctx: &'a DaemonCtx,
user_id: &'a str,
) -> std::pin::Pin<std::boxed::Box<dyn std::future::Future<Output = String> + Send + 'a>> {
Box::pin(async move {
// 上下文工具
match name {
"read_memories" => {
return ctx.memory_store.read_for(user_id, None).await;
}
let target_args = serde_json::to_string(&crate::tools::unpack_call_params(&cp))
.unwrap_or_default();
// 重新派发到 execute_tool(通过顶层 match 分支避免 async 递归)
// 上下文工具
match target_name.as_str() {
"read_memories" => {
let mems = load_memories(&ctx.memory_store, user_id).await;
if mems.is_empty() { return "暂无长期记忆".to_string(); }
return mems.iter().enumerate()
.map(|(i, v)| format!("{}. {}", i + 1, v)).collect::<Vec<_>>().join("\n");
}
"write_memory" => {
let params: serde_json::Value = serde_json::from_str(&target_args).unwrap_or_default();
let content = params.get("content").and_then(|v| v.as_str()).unwrap_or("");
if content.is_empty() { return "请提供 content 参数".to_string(); }
ctx.memory_store.write_for(user_id, content).await;
return format!("已添加长期记忆: {}", content);
}
"read_summaries" => {
let sums = load_summaries(&ctx.db, user_id).await;
if sums.is_empty() { return "暂无历史摘要".to_string(); }
return sums.iter().enumerate()
.map(|(i, s)| format!("{}. {}", i + 1, s))
.collect::<Vec<_>>().join("\n---\n");
}
"query_capabilities" => {
let specs = crate::tools::specs();
return crate::tools::build_capability_guide(&specs);
}
_ => {}
"write_memory" => {
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
let content = params.get("content").and_then(|v| v.as_str()).unwrap_or("");
if content.is_empty() { return "请提供 content 参数".to_string(); }
ctx.memory_store.write_for(user_id, content).await;
return format!("已添加长期记忆: {}", content);
}
// 内置工具注册表
match crate::tools::execute(&target_name, &target_args).await {
Some(r) => return r.output,
None => return format!("未知工具: {}", target_name),
"delete_memory" => {
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
let memory_id = params.get("memory_id").and_then(|v| v.as_i64()).unwrap_or(-1) as i32;
if memory_id <= 0 { return "请提供有效的 memory_id 参数".to_string(); }
return ctx.memory_store.delete_for(user_id, memory_id).await;
}
"update_memory" => {
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
let memory_id = params.get("memory_id").and_then(|v| v.as_i64()).unwrap_or(-1) as i32;
let content = params.get("content").and_then(|v| v.as_str()).unwrap_or("");
if memory_id <= 0 { return "请提供有效的 memory_id 参数".to_string(); }
if content.is_empty() { return "请提供 content 参数".to_string(); }
return ctx.memory_store.update_for(user_id, memory_id, content).await;
}
"read_summaries" => {
let sums = load_summaries(&ctx.db, user_id).await;
if sums.is_empty() { return "暂无历史摘要".to_string(); }
return sums.iter().enumerate()
.map(|(i, s)| format!("{}. {}", i + 1, s))
.collect::<Vec<_>>().join("\n---\n");
}
"query_capabilities" => {
let specs = crate::tools::specs();
return crate::tools::build_capability_guide(&specs);
}
"call_capability" => {
// 解包 {name, prompt} → 提取目标工具名和参数,递归派发
let cp: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
let target_name = cp.get("name").and_then(|v| v.as_str()).unwrap_or("").to_string();
if target_name.is_empty() {
return "请提供 name 参数".to_string();
}
// 防止嵌套 call_capability
if target_name == "call_capability" {
return "不支持嵌套调用 call_capability".to_string();
}
let target_args = serde_json::to_string(&crate::tools::unpack_call_params(&cp))
.unwrap_or_default();
// 递归派发
return execute_tool(&target_name, &target_args, ctx, user_id).await;
}
_ => {}
}
_ => {}
}
// 内置工具注册表
match crate::tools::execute(name, arguments).await {
Some(r) => r.output,
None => format!("未知工具: {}", name),
}
// 内置工具注册表
match crate::tools::execute(name, arguments).await {
Some(r) => r.output,
None => format!("未知工具: {}", name),
}
})
}
// ─── Send Consumer ───
@@ -738,13 +728,6 @@ async fn load_history(db: &Option<Arc<Database>>, user_id: &str) -> Vec<HistoryE
}
}
async fn load_memories(memory_store: &MemoryStore, user_id: &str) -> Vec<String> {
let text = memory_store.read_for(user_id).await;
// read_for() 返回 "暂无长期记忆" 表示无记忆,否则返回 "1. ...\n2. ..." 格式
if text.starts_with("暂无") { vec![] }
else { text.lines().map(|l| l.trim().to_string()).filter(|l| !l.is_empty()).collect() }
}
async fn load_summaries(db: &Option<Arc<Database>>, user_id: &str) -> Vec<String> {
let Some(db) = db else { return vec![]; };
match crate::db::models::load_summaries(db.pool(), user_id, 5).await {
@@ -794,6 +777,33 @@ fn build_tools_list() -> Vec<serde_json::Value> {
"parameters": { "type": "object", "properties": {} }
}
}));
list.push(serde_json::json!({
"type": "function", "function": {
"name": "delete_memory",
"description": "删除指定 id 的长期记忆。id 来自 read_memories 返回的 [id] 前缀。",
"parameters": {
"type": "object",
"properties": {
"memory_id": { "type": "integer", "description": "要删除的记忆 id" }
},
"required": ["memory_id"]
}
}
}));
list.push(serde_json::json!({
"type": "function", "function": {
"name": "update_memory",
"description": "更新指定 id 的长期记忆内容。id 来自 read_memories 返回的 [id] 前缀。",
"parameters": {
"type": "object",
"properties": {
"memory_id": { "type": "integer", "description": "要更新的记忆 id" },
"content": { "type": "string", "description": "新的记忆内容" }
},
"required": ["memory_id", "content"]
}
}
}));
let specs = crate::tools::specs();
for spec in &specs {