mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
Merge pull request #3102 from xinnan-tech/fix-title-summary
fix:将标题总结从记忆流程中拆分为独立接口,支持异步执行
This commit is contained in:
@@ -150,6 +150,13 @@ public class AgentController {
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/chat-title/{sessionId}/generate")
|
||||
@Operation(summary = "根据会话ID生成聊天标题")
|
||||
public Result<Void> generateAndSaveChatTitle(@PathVariable String sessionId) {
|
||||
agentChatSummaryService.generateAndSaveChatTitle(sessionId);
|
||||
return new Result<Void>().ok(null);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新智能体")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
|
||||
+8
@@ -12,4 +12,12 @@ public interface AgentChatSummaryService {
|
||||
* @return 保存结果
|
||||
*/
|
||||
boolean generateAndSaveChatSummary(String sessionId);
|
||||
|
||||
/**
|
||||
* 根据会话ID生成聊天标题并保存
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean generateAndSaveChatTitle(String sessionId);
|
||||
}
|
||||
+14
-5
@@ -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<AgentChatHistoryDTO> chatHistory = getChatHistoryBySessionId(sessionId);
|
||||
if (chatHistory == null || chatHistory.isEmpty()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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]:
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user