From 2daf492e00363f09c231c5d4864acf782a0e1f1a Mon Sep 17 00:00:00 2001 From: rainv123 <2148537152@qq.com> Date: Thu, 16 Apr 2026 10:36:48 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=E5=B0=86=E6=A0=87=E9=A2=98=E6=80=BB?= =?UTF-8?q?=E7=BB=93=E4=BB=8E=E8=AE=B0=E5=BF=86=E6=B5=81=E7=A8=8B=E4=B8=AD?= =?UTF-8?q?=E6=8B=86=E5=88=86=E4=B8=BA=E7=8B=AC=E7=AB=8B=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E5=BC=82=E6=AD=A5=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent/controller/AgentController.java | 18 +++++++++++++++ .../service/AgentChatSummaryService.java | 8 +++++++ .../impl/AgentChatSummaryServiceImpl.java | 19 +++++++++++----- .../modules/security/config/ShiroConfig.java | 1 + .../config/manage_api_client.py | 12 ++++++++++ main/xiaozhi-server/core/connection.py | 22 ++++++++++++++++++- .../core/providers/memory/mem0ai/mem0ai.py | 6 ----- .../memory/mem_report_only/mem_report_only.py | 3 --- .../providers/memory/powermem/powermem.py | 6 ----- 9 files changed, 74 insertions(+), 21 deletions(-) diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java index b669ed6b..37a32777 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java @@ -150,6 +150,24 @@ public class AgentController { } } + @PostMapping("/chat-title/{sessionId}/generate") + @Operation(summary = "根据会话ID生成聊天标题(异步执行)") + public Result generateAndSaveChatTitle(@PathVariable String sessionId) { + try { + new Thread(() -> { + try { + agentChatSummaryService.generateAndSaveChatTitle(sessionId); + System.out.println("异步执行会话 " + sessionId + " 的标题生成完成"); + } catch (Exception e) { + System.err.println("异步执行会话 " + sessionId + " 的标题生成失败: " + e.getMessage()); + } + }).start(); + return new Result().ok(null); + } catch (Exception e) { + return new Result().error("启动异步标题生成任务失败: " + e.getMessage()); + } + } + @PutMapping("/{id}") @Operation(summary = "更新智能体") @RequiresPermissions("sys:role:normal") diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java index 418ee9a0..33605849 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatSummaryService.java @@ -12,4 +12,12 @@ public interface AgentChatSummaryService { * @return 保存结果 */ boolean generateAndSaveChatSummary(String sessionId); + + /** + * 根据会话ID生成聊天标题并保存 + * + * @param sessionId 会话ID + * @return 是否成功 + */ + boolean generateAndSaveChatTitle(String sessionId); } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java index 02e54145..a3b78b18 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java @@ -104,7 +104,6 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { if (memModelId == null || memModelId.equals(Constant.MEMORY_MEM_REPORT_ONLY)) { log.info("会话 {} 使用仅上报聊天记录模式,跳过记忆总结", sessionId); - generateAndSaveChatTitle(sessionId, agentId); return true; } @@ -128,7 +127,6 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { log.info("会话 {} 使用 {} 模式,跳过记忆总结", sessionId, memModelId); } - generateAndSaveChatTitle(sessionId, agentId); return true; } catch (Exception e) { @@ -137,16 +135,24 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { } } - private void generateAndSaveChatTitle(String sessionId, String agentId) { + @Override + public boolean generateAndSaveChatTitle(String sessionId) { try { + // 自动获取agentId + String agentId = findAgentIdBySessionId(sessionId); + if (StringUtils.isBlank(agentId)) { + log.warn("会话 {} 无法获取智能体信息,跳过标题生成", sessionId); + return false; + } + List chatHistory = getChatHistoryBySessionId(sessionId); if (chatHistory == null || chatHistory.isEmpty()) { - return; + return false; } List meaningfulMessages = extractMeaningfulMessages(chatHistory); if (meaningfulMessages.isEmpty()) { - return; + return false; } StringBuilder conversation = new StringBuilder(); @@ -160,9 +166,12 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { if (StringUtils.isNotBlank(title)) { agentChatTitleService.saveOrUpdateTitle(sessionId, title); log.info("成功保存会话 {} 的标题: {}", sessionId, title); + return true; } + return false; } catch (Exception e) { log.error("生成会话 {} 的标题时发生错误: {}", sessionId, e.getMessage()); + return false; } } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java index 29b9ad2f..80b93c6d 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java @@ -90,6 +90,7 @@ public class ShiroConfig { filterMap.put("/agent/chat-history/report", "server"); filterMap.put("/agent/chat-history/download/**", "anon"); filterMap.put("/agent/chat-summary/**", "server"); + filterMap.put("/agent/chat-title/**", "server"); filterMap.put("/agent/play/**", "anon"); filterMap.put("/voiceClone/play/**", "anon"); filterMap.put("/**", "oauth2"); diff --git a/main/xiaozhi-server/config/manage_api_client.py b/main/xiaozhi-server/config/manage_api_client.py index a9d8d601..e6f7c918 100644 --- a/main/xiaozhi-server/config/manage_api_client.py +++ b/main/xiaozhi-server/config/manage_api_client.py @@ -195,6 +195,18 @@ async def generate_and_save_chat_summary(session_id: str) -> Optional[Dict]: return None +async def generate_and_save_chat_title(session_id: str) -> Optional[Dict]: + """生成并保存聊天标题""" + try: + return await ManageApiClient._instance._execute_async_request( + "POST", + f"/agent/chat-title/{session_id}/generate", + ) + except Exception as e: + print(f"生成并保存聊天标题失败: {e}") + return None + + async def report( mac_address: str, session_id: str, chat_type: int, content: str, audio, report_time ) -> Optional[Dict]: diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index 31c79f5f..501b52e0 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -37,7 +37,7 @@ from core.auth import AuthenticationError from config.config_loader import get_private_config_from_api from core.providers.tts.dto.dto import ContentType, TTSMessageDTO, SentenceType from config.logger import setup_logging, build_module_string, create_connection_logger -from config.manage_api_client import DeviceNotFoundException, DeviceBindException +from config.manage_api_client import DeviceNotFoundException, DeviceBindException, generate_and_save_chat_title from core.utils.prompt_manager import PromptManager from core.utils.voiceprint_provider import VoiceprintProvider from core.utils.util import get_system_error_response @@ -284,6 +284,26 @@ class ConnectionHandler: async def _save_and_close(self, ws): """保存记忆并关闭连接""" try: + # 守护线程1:独立生成标题(不依赖记忆模型) + if self.session_id: + def generate_title_task(): + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete( + generate_and_save_chat_title(self.session_id) + ) + except Exception as e: + self.logger.bind(tag=TAG).error(f"生成标题失败: {e}") + finally: + try: + loop.close() + except Exception: + pass + + threading.Thread(target=generate_title_task, daemon=True).start() + + # 守护线程2:走老流程记忆保存(仅记忆,不含标题) if self.memory: # 使用线程池异步保存记忆 def save_memory_task(): diff --git a/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py b/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py index 8c593669..79d0770a 100644 --- a/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py +++ b/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py @@ -2,7 +2,6 @@ import json import traceback from ..base import MemoryProviderBase, logger -from config.manage_api_client import generate_and_save_chat_summary from mem0 import MemoryClient from core.utils.util import check_model_key @@ -59,11 +58,6 @@ class MemoryProvider(MemoryProviderBase): except Exception as e: logger.bind(tag=TAG).error(f"保存记忆失败: {str(e)}") - # Generate and save chat summary (SLM summarizes session title) - # This is independent of Mem0AI's memory saving - if session_id: - await generate_and_save_chat_summary(session_id) - return None async def query_memory(self, query: str) -> str: diff --git a/main/xiaozhi-server/core/providers/memory/mem_report_only/mem_report_only.py b/main/xiaozhi-server/core/providers/memory/mem_report_only/mem_report_only.py index 7654b0c5..2fd1fdff 100644 --- a/main/xiaozhi-server/core/providers/memory/mem_report_only/mem_report_only.py +++ b/main/xiaozhi-server/core/providers/memory/mem_report_only/mem_report_only.py @@ -3,7 +3,6 @@ """ from ..base import MemoryProviderBase, logger -from config.manage_api_client import generate_and_save_chat_summary TAG = __name__ @@ -14,8 +13,6 @@ class MemoryProvider(MemoryProviderBase): async def save_memory(self, msgs, session_id=None): logger.bind(tag=TAG).debug("mem_report_only mode: No memory saving or summarization is performed.") - if session_id: - await generate_and_save_chat_summary(session_id) return None async def query_memory(self, query: str) -> str: diff --git a/main/xiaozhi-server/core/providers/memory/powermem/powermem.py b/main/xiaozhi-server/core/providers/memory/powermem/powermem.py index d7787f5a..cd55caf8 100644 --- a/main/xiaozhi-server/core/providers/memory/powermem/powermem.py +++ b/main/xiaozhi-server/core/providers/memory/powermem/powermem.py @@ -16,7 +16,6 @@ import traceback from typing import Optional, Dict, Any from ..base import MemoryProviderBase, logger -from config.manage_api_client import generate_and_save_chat_summary TAG = __name__ @@ -211,11 +210,6 @@ class MemoryProvider(MemoryProviderBase): logger.bind(tag=TAG).error(f"Error saving memory: {str(e)}") logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") - # Generate and save chat summary (SLM summarizes session title) - # This is independent of PowerMem's memory saving - if session_id: - await generate_and_save_chat_summary(session_id) - return None async def query_memory(self, query: str) -> str: From fbaf28de5c2fadc4eda8e4d67a0936010b501b41 Mon Sep 17 00:00:00 2001 From: rainv123 <2148537152@qq.com> Date: Thu, 16 Apr 2026 14:43:16 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99try?= =?UTF-8?q?=E5=92=8C=E6=80=BB=E7=BB=93=E6=A0=87=E9=A2=98=E7=9A=84=E5=BC=82?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent/controller/AgentController.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java index 37a32777..77907cc5 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java @@ -151,21 +151,10 @@ public class AgentController { } @PostMapping("/chat-title/{sessionId}/generate") - @Operation(summary = "根据会话ID生成聊天标题(异步执行)") + @Operation(summary = "根据会话ID生成聊天标题") public Result generateAndSaveChatTitle(@PathVariable String sessionId) { - try { - new Thread(() -> { - try { - agentChatSummaryService.generateAndSaveChatTitle(sessionId); - System.out.println("异步执行会话 " + sessionId + " 的标题生成完成"); - } catch (Exception e) { - System.err.println("异步执行会话 " + sessionId + " 的标题生成失败: " + e.getMessage()); - } - }).start(); - return new Result().ok(null); - } catch (Exception e) { - return new Result().error("启动异步标题生成任务失败: " + e.getMessage()); - } + agentChatSummaryService.generateAndSaveChatTitle(sessionId); + return new Result().ok(null); } @PutMapping("/{id}")