fix: 审批死锁修复 — LLM 调用异步化

listen_loop 核心改动:
  新消息到达 → tokio::spawn(generate_reply) → 主循环立即继续
  LLM 调用 High 工具 → approval_flow 阻塞 spawn 的 task
  → 主循环不受影响 → receive_messages() 继续接收
  → 用户回复确认码 → handle_reply 触发 oneshot
  → spawned task 从 approval_flow 恢复 → LLM 生成最终回复

Conversation 用 Arc 包裹以支持跨 task 共享
send_text + 入库移到 spawned task 内部完成
This commit is contained in:
2026-06-01 18:07:03 +08:00
parent a6bb4dc1ec
commit a31afaba80
+27 -22
View File
@@ -335,7 +335,7 @@ async fn cmd_listen(
conv.set_tool_executor(executor); conv.set_tool_executor(executor);
Some(conv) Some(Arc::new(conv))
} else { } else {
None None
}; };
@@ -387,7 +387,7 @@ async fn listen_loop(
account_id: &str, account_id: &str,
database: &Option<Arc<Database>>, database: &Option<Arc<Database>>,
file_state: &state::StateManager, file_state: &state::StateManager,
conversation: Option<Conversation>, conversation: Option<Arc<Conversation>>,
approval_manager: Option<Arc<ApprovalManager>>, approval_manager: Option<Arc<ApprovalManager>>,
current_user_id: Arc<tokio::sync::Mutex<String>>, current_user_id: Arc<tokio::sync::Mutex<String>>,
) { ) {
@@ -465,32 +465,37 @@ async fn listen_loop(
// 更新当前用户(审批流程需要) // 更新当前用户(审批流程需要)
*current_user_id.lock().await = from.to_string(); *current_user_id.lock().await = from.to_string();
// LLM 回复 // LLM 回复(异步执行,避免审批阻塞主循环)
if enable_llm { if enable_llm {
if let Some(conv) = &conversation { if let Some(conv) = &conversation {
let reply_result = if conv.spec().tools.is_some() { let conv = conv.clone();
generate_reply_with_tools(conv, text.to_string(), database).await let client = client.clone();
} else { let database = database.clone();
generate_reply(conv, text.to_string(), database).await let from_owned = from.to_string();
}; let text_owned = text.to_string();
let ctx_owned = ctx_token.map(String::from);
let aid = account_id.to_string();
match reply_result { tokio::spawn(async move {
Ok(reply) => { let reply_result = if conv.spec().tools.is_some() {
match client.send_text(from, &reply, ctx_token).await { generate_reply_with_tools(&conv, text_owned, &database).await
Ok(sent_id) => { } else {
info!("LLM 回复成功 msg_id={}", sent_id); generate_reply(&conv, text_owned, &database).await
if let Some(db) = database { };
let _ = db::models::insert_chat_record( let reply = reply_result.ok().unwrap_or_default();
db.pool(), "outbound", from, account_id, match client.send_text(&from_owned, &reply, ctx_owned.as_deref()).await {
&reply, "llm", ctx_token, &sent_id, Ok(sent_id) => {
).await; info!("LLM 回复成功 msg_id={}", sent_id);
} if let Some(db) = database {
let _ = db::models::insert_chat_record(
db.pool(), "outbound", &from_owned, &aid,
&reply, "llm", ctx_owned.as_deref(), &sent_id,
).await;
} }
Err(e) => error!("发送 LLM 回复失败: {}", e),
} }
Err(e) => error!("发送 LLM 回复失败: {}", e),
} }
Err(e) => error!("生成 LLM 回复失败: {}", e), });
}
} }
} }
} }