chore: 增强日志追踪与代码格式化

- daemon: 添加 LLM 回合开始/结束标记日志,工具执行结果追踪
- daemon: 发送消费者日志增强(显示完整文本、截断用户 ID)
- conversation: 移除日志截断,输出完整工具调用参数和结果
- deepseek: 代码格式化,预留请求/响应摘要日志
- 删除 systemd/ias.service
This commit is contained in:
2026-06-16 22:53:19 +08:00
parent f608c27f8a
commit e20cd19e57
4 changed files with 181 additions and 141 deletions
+141 -99
View File
@@ -109,9 +109,9 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
info!("Daemon 已启动");
// 4-6. 审批管理器、工具列表、模型配置
let approval_manager = Arc::new(ApprovalManager::new(
Some(Arc::new(database.pool().clone())),
));
let approval_manager = Arc::new(ApprovalManager::new(Some(Arc::new(
database.pool().clone(),
))));
let tools_list = build_tools_list();
let system_prompt = std::env::var("WEIXIN_LLM_SYSTEM_PROMPT").unwrap_or_else(|_| String::new());
let model = std::env::var("DEEPSEEK_MODEL").unwrap_or_else(|_| "deepseek-v4-flash".to_string());
@@ -158,28 +158,28 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
let sched_enqueue = enqueue_handle.clone();
tokio::spawn(async move {
let scheduler = crate::scheduler::Scheduler::new(Some(Arc::new(sched_db)));
scheduler
.run(move |to: &str, name: &str, msg: &str| {
let eq = sched_enqueue.clone();
let uid = to.to_string();
let text = msg.to_string();
let tn = name.to_string();
tokio::spawn(async move {
eq.enqueue(PipelineMessage {
channel: ChannelId::wechat(uid),
correlation_id: Uuid::new_v4(),
kind: MessageKind::ScheduledTask {
text,
task_name: tn,
},
})
.await;
});
Ok(())
})
.await;
});
info!("定时任务调度器已启动");
scheduler
.run(move |to: &str, name: &str, msg: &str| {
let eq = sched_enqueue.clone();
let uid = to.to_string();
let text = msg.to_string();
let tn = name.to_string();
tokio::spawn(async move {
eq.enqueue(PipelineMessage {
channel: ChannelId::wechat(uid),
correlation_id: Uuid::new_v4(),
kind: MessageKind::ScheduledTask {
text,
task_name: tn,
},
})
.await;
});
Ok(())
})
.await;
});
info!("定时任务调度器已启动");
// 12. QueueRunner
let runner_handle = tokio::spawn(async move {
@@ -349,8 +349,6 @@ async fn llm_consumer_loop(
context_token,
approved_tool,
} => {
info!("LLM Consumer: user={} text={:.60}", user_id, text);
// 加载数据
ctx.memory_store.load_if_needed(&user_id).await;
let history = load_history(&ctx.db, &user_id).await;
@@ -399,45 +397,46 @@ async fn llm_consumer_loop(
let session = conv.session();
let approved_tool = approved_tool.clone();
let executor: ToolExecutor = Arc::new(move |name: &str, args_json: &str, tc_id: &str| {
let eq = eq.clone();
let uid = uid.clone();
let corr = corr;
let session = session.clone();
let n = name.to_string();
let args = args_json.to_string();
let is_approved = approved_tool.as_ref() == Some(&n);
let tool_call_id = tc_id.to_string();
let executor: ToolExecutor =
Arc::new(move |name: &str, args_json: &str, tc_id: &str| {
let eq = eq.clone();
let uid = uid.clone();
let corr = corr;
let session = session.clone();
let n = name.to_string();
let args = args_json.to_string();
let is_approved = approved_tool.as_ref() == Some(&n);
let tool_call_id = tc_id.to_string();
Box::pin(async move {
let ch = ChannelId::wechat(&uid);
let sid = { session.lock().await.session_id };
if is_approved {
eq.enqueue(PipelineMessage::approved_tool_call(
ch,
corr,
sid,
&tool_call_id,
&n,
&args,
None,
))
.await;
} else {
eq.enqueue(PipelineMessage::tool_call(
ch,
corr,
sid,
&tool_call_id,
&n,
&args,
None,
))
.await;
}
Ok(format!("{}{}", ASYNC_MARKER, n))
})
});
Box::pin(async move {
let ch = ChannelId::wechat(&uid);
let sid = { session.lock().await.session_id };
if is_approved {
eq.enqueue(PipelineMessage::approved_tool_call(
ch,
corr,
sid,
&tool_call_id,
&n,
&args,
None,
))
.await;
} else {
eq.enqueue(PipelineMessage::tool_call(
ch,
corr,
sid,
&tool_call_id,
&n,
&args,
None,
))
.await;
}
Ok(format!("{}{}", ASYNC_MARKER, n))
})
});
conv.set_tool_executor(executor);
// 缓存 Conversation(记录创建时间用于 TTL
@@ -465,11 +464,6 @@ async fn llm_consumer_loop(
result,
context_token,
} => {
info!(
"LLM Consumer: 工具结果回喂 user={} tool={}",
user_id, tool_name
);
// 从缓存恢复会话,注入工具结果并恢复 LLM 循环
let should_resume = {
let mut map = sessions.lock().await;
@@ -532,6 +526,8 @@ async fn run_llm_round(
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
enqueue: &EnqueueHandle,
) {
info!("══════════════════════════════════════════════════");
info!("🔄 LLM 回合开始 user={} text={}", user_id, text);
let result = {
let map = sessions.lock().await;
let conv = match map.get(&correlation_id) {
@@ -575,6 +571,8 @@ async fn resume_llm_round(
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
enqueue: &EnqueueHandle,
) {
info!("══════════════════════════════════════════════════");
info!("🔄 LLM 恢复 user={}", user_id);
let result = {
let map = sessions.lock().await;
let conv = match map.get(&correlation_id) {
@@ -636,6 +634,7 @@ async fn handle_chat_result(
enqueue.enqueue(msg).await;
}
sessions.lock().await.remove(&correlation_id);
info!("✅ LLM 回合结束 user={}", user_id);
}
// ─── Tool Consumer ───
@@ -660,7 +659,10 @@ async fn tool_consumer_loop(
context_token,
approved,
} => {
info!("Tool Consumer: user={} tool={}", user_id, tool_name);
info!(
"Tool Consumer: user={} tool={} args={}",
user_id, tool_name, arguments
);
// 高风险工具 → 审批(除非已被用户批准)
if !approved && crate::tools::is_high_risk(&tool_name) {
@@ -734,7 +736,9 @@ fn execute_tool<'a>(
// 上下文工具
match name {
"read_memories" => {
return ctx.memory_store.read_for(user_id, None).await;
let result = ctx.memory_store.read_for(user_id, None).await;
tracing::info!("🔧 execute_tool: read_memories → {}", result);
return result;
}
"write_memory" => {
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
@@ -743,7 +747,9 @@ fn execute_tool<'a>(
return "请提供 content 参数".to_string();
}
ctx.memory_store.write_for(user_id, content).await;
return format!("已添加长期记忆: {}", content);
let result = format!("已添加长期记忆: {}", content);
tracing::info!("🔧 execute_tool: write_memory → {}", result);
return result;
}
"delete_memory" => {
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
@@ -754,7 +760,9 @@ fn execute_tool<'a>(
if memory_id <= 0 {
return "请提供有效的 memory_id 参数".to_string();
}
return ctx.memory_store.delete_for(user_id, memory_id).await;
let result = ctx.memory_store.delete_for(user_id, memory_id).await;
tracing::info!("🔧 execute_tool: delete_memory({}) → {}", memory_id, result);
return result;
}
"update_memory" => {
let params: serde_json::Value = serde_json::from_str(arguments).unwrap_or_default();
@@ -769,26 +777,35 @@ fn execute_tool<'a>(
if content.is_empty() {
return "请提供 content 参数".to_string();
}
return ctx
let result = ctx
.memory_store
.update_for(user_id, memory_id, content)
.await;
tracing::info!("🔧 execute_tool: update_memory({}) → {}", memory_id, result);
return result;
}
"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");
let result = if sums.is_empty() {
"暂无历史摘要".to_string()
} else {
sums.iter()
.enumerate()
.map(|(i, s)| format!("{}. {}", i + 1, s))
.collect::<Vec<_>>()
.join("\n---\n")
};
tracing::info!("🔧 execute_tool: read_summaries → {}条", sums.len());
return result;
}
"query_capabilities" => {
let specs = crate::tools::specs();
return crate::tools::build_capability_guide(&specs);
let result = crate::tools::build_capability_guide(&specs);
tracing::info!(
"🔧 execute_tool: query_capabilities → {}个工具",
specs.len()
);
return result;
}
"call_capability" => {
// 解包 {name, prompt} → 提取目标工具名和参数,递归派发
@@ -807,6 +824,11 @@ fn execute_tool<'a>(
}
let target_args = serde_json::to_string(&crate::tools::unpack_call_params(&cp))
.unwrap_or_default();
tracing::info!(
"🔧 execute_tool: call_capability → {} args={}",
target_name,
target_args
);
// 递归派发
return execute_tool(&target_name, &target_args, ctx, user_id).await;
}
@@ -815,8 +837,19 @@ fn execute_tool<'a>(
// 内置工具注册表
match crate::tools::execute(name, arguments).await {
Some(r) => r.output,
None => format!("未知工具: {}", name),
Some(r) => {
tracing::info!(
"🔧 execute_tool: {} → success={} output={}",
name,
r.success,
r.output
);
r.output
}
None => {
tracing::warn!("🔧 execute_tool: 未知工具 {}", name);
format!("未知工具: {}", name)
}
}
})
}
@@ -836,7 +869,12 @@ async fn send_consumer_loop(
context_token,
} => {
let user_id = &msg.channel.user_id;
info!("Send Consumer: user={} text_len={}", user_id, text.len());
info!(
"Send Consumer: user={} text_len={} text={}",
user_id,
text.len(),
text
);
match ctx
.client
@@ -844,18 +882,22 @@ async fn send_consumer_loop(
.await
{
Ok(sent_id) => {
info!("回复成功 user={} msg_id={}", user_id, sent_id);
info!(
"回复成功 user={} msg_id={}",
user_id.chars().take(5).collect::<String>(),
sent_id
);
let _ = crate::db::models::insert_chat_record(
ctx.db.pool(),
"outbound",
user_id,
&ctx.account_id,
&text,
"llm",
context_token.as_deref(),
&sent_id,
)
.await;
ctx.db.pool(),
"outbound",
user_id,
&ctx.account_id,
&text,
"llm",
context_token.as_deref(),
&sent_id,
)
.await;
}
Err(e) => error!("发送回复失败: {}", e),
}
+2 -2
View File
@@ -313,7 +313,7 @@ impl Conversation {
calls.len(),
calls
.iter()
.map(|c| format!("{}({:.80})", c.function.name, c.function.arguments))
.map(|c| format!("{}({})", c.function.name, c.function.arguments))
.collect::<Vec<_>>()
.join(", ")
);
@@ -344,7 +344,7 @@ impl Conversation {
continue;
}
tracing::info!("📦 {} → {:.150}", tc.function.name, result);
tracing::info!("📦 {} → {}", tc.function.name, result);
self.session.lock().await.add_tool_result(
&tc.id,
&tc.function.name,
+38 -23
View File
@@ -154,6 +154,13 @@ async fn stream_deepseek(
return Err(format!("HTTP {}: {}", status, text));
}
// 记录请求摘要
// tracing::info!(
// "🌐 LLM 请求: model={} messages_count={}",
// body.get("model").and_then(|v| v.as_str()).unwrap_or("?"),
// body.get("messages").and_then(|v| v.as_array()).map(|a| a.len()).unwrap_or(0),
// );
let mut full_text = String::new();
let mut full_reasoning = String::new();
let mut usage: Option<Usage> = None;
@@ -216,33 +223,33 @@ async fn stream_deepseek(
// 检查最后的 buffer
if !buf.trim().is_empty()
&& let Some(parsed) = parse_chat_chunk(buf.trim()) {
match parsed {
ParsedChunk::Text(t) => full_text.push_str(&t),
ParsedChunk::ToolCallDelta {
index,
id,
name,
arguments,
} => {
let entry =
tool_call_builders
.entry(index)
.or_insert((None, None, String::new()));
if let Some(call_id) = id {
entry.0 = Some(call_id);
}
if let Some(call_name) = name {
entry.1 = Some(call_name);
}
entry.2.push_str(&arguments);
&& let Some(parsed) = parse_chat_chunk(buf.trim())
{
match parsed {
ParsedChunk::Text(t) => full_text.push_str(&t),
ParsedChunk::ToolCallDelta {
index,
id,
name,
arguments,
} => {
let entry = tool_call_builders
.entry(index)
.or_insert((None, None, String::new()));
if let Some(call_id) = id {
entry.0 = Some(call_id);
}
ParsedChunk::Usage(u) => {
usage = Some(u);
if let Some(call_name) = name {
entry.1 = Some(call_name);
}
_ => {}
entry.2.push_str(&arguments);
}
ParsedChunk::Usage(u) => {
usage = Some(u);
}
_ => {}
}
}
// 构建 tool_calls
let tool_calls: Option<Vec<ToolCall>> = if tool_call_builders.is_empty() {
@@ -267,6 +274,14 @@ async fn stream_deepseek(
)
};
// 记录完整的响应输出
// tracing::info!(
// "🌐 LLM 响应: text_len={} tool_calls={} usage={:?}",
// full_text.len(),
// tool_calls.as_ref().map(|tc| tc.len()).unwrap_or(0),
// usage
// );
let _ = tx
.send(StreamChunk::Done {
text: full_text,
-17
View File
@@ -1,17 +0,0 @@
[Unit]
Description=iAs - 微信 AI 智能助手
After=network.target postgresql.service
[Service]
Type=simple
User=xiao
WorkingDirectory=/home/xiao/project/iAs
EnvironmentFile=/home/xiao/project/iAs/.env
ExecStart=/home/xiao/project/iAs/target/release/iAs service
Restart=on-failure
RestartSec=10
StandardOutput=null
StandardError=null
[Install]
WantedBy=multi-user.target