From 2f5f20a25745f9388b1b4b78ebffe35c99f1b2ed Mon Sep 17 00:00:00 2001 From: ljwwd2 <1205484020@qq.com> Date: Wed, 14 May 2025 03:27:32 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=99=BA=E6=8E=A7?= =?UTF-8?q?=E5=8F=B0=E7=AE=A1=E7=90=86=E3=80=90=E6=9C=AC=E5=9C=B0=E8=AE=B0?= =?UTF-8?q?=E5=BF=86=E3=80=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent/controller/AgentController.java | 19 +++++++++++++++++++ .../xiaozhi/modules/agent/dto/AgentDTO.java | 4 ++++ .../modules/agent/dto/AgentUpdateDTO.java | 4 ++++ .../modules/agent/entity/AgentEntity.java | 4 ++++ .../agent/entity/AgentTemplateEntity.java | 4 ++++ .../service/impl/ConfigServiceImpl.java | 4 ++++ .../modules/security/config/ShiroConfig.java | 1 + .../resources/db/changelog/202505122348.sql | 6 ++++++ .../db/changelog/db.changelog-master.yaml | 9 ++++++++- main/manager-web/src/views/roleConfig.vue | 9 +++++++++ .../config/manage_api_client.py | 15 +++++++++++++++ main/xiaozhi-server/core/connection.py | 8 +++++++- .../memory/mem_local_short/mem_local_short.py | 17 ++++++++++++----- main/xiaozhi-server/core/utils/util.py | 1 + 14 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 main/manager-api/src/main/resources/db/changelog/202505122348.sql 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 597cf985..07dba466 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 @@ -46,6 +46,7 @@ import xiaozhi.modules.agent.service.AgentChatAudioService; import xiaozhi.modules.agent.service.AgentChatHistoryService; import xiaozhi.modules.agent.service.AgentService; import xiaozhi.modules.agent.service.AgentTemplateService; +import xiaozhi.modules.device.entity.DeviceEntity; import xiaozhi.modules.device.service.DeviceService; import xiaozhi.modules.security.user.SecurityUser; @@ -109,6 +110,7 @@ public class AgentController { entity.setMemModelId(template.getMemModelId()); entity.setIntentModelId(template.getIntentModelId()); entity.setSystemPrompt(template.getSystemPrompt()); + entity.setSummaryMemory(template.getSummaryMemory()); entity.setChatHistoryConf(template.getChatHistoryConf()); entity.setLangCode(template.getLangCode()); entity.setLanguage(template.getLanguage()); @@ -126,10 +128,24 @@ public class AgentController { return new Result().ok(entity.getId()); } + @PutMapping("/device/{macAddress}") + @Operation(summary = "根据设备id更新智能体") + public Result updateByDeviceId(@PathVariable String macAddress, @RequestBody @Valid AgentUpdateDTO dto) { + DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress); + if (device == null) { + return new Result<>(); + } + return updateAgentById(device.getAgentId(), dto); + } + @PutMapping("/{id}") @Operation(summary = "更新智能体") @RequiresPermissions("sys:role:normal") public Result update(@PathVariable String id, @RequestBody @Valid AgentUpdateDTO dto) { + return updateAgentById(id, dto); + } + + private Result updateAgentById(String id, AgentUpdateDTO dto) { // 先查询现有实体 AgentEntity existingEntity = agentService.getAgentById(id); if (existingEntity == null) { @@ -167,6 +183,9 @@ public class AgentController { if (dto.getSystemPrompt() != null) { existingEntity.setSystemPrompt(dto.getSystemPrompt()); } + if (dto.getSummaryMemory() != null) { + existingEntity.setSummaryMemory(dto.getSummaryMemory()); + } if (dto.getChatHistoryConf() != null) { existingEntity.setChatHistoryConf(dto.getChatHistoryConf()); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java index a63c96e7..0d1ec7b2 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java @@ -30,6 +30,10 @@ public class AgentDTO { @Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题并提供帮助") private String systemPrompt; + @Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" + + "根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false) + private String summaryMemory; + @Schema(description = "最后连接时间", example = "2024-03-20 10:00:00") private Date lastConnectedAt; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java index e3d026e3..ccfbd53f 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java @@ -45,6 +45,10 @@ public class AgentUpdateDTO implements Serializable { @Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题并提供帮助", required = false) private String systemPrompt; + @Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" + + "根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false) + private String summaryMemory; + @Schema(description = "聊天记录配置(0不记录 1仅记录文本 2记录文本和语音)", example = "3", required = false) private Integer chatHistoryConf; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java index 82a20489..1ffc5550 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java @@ -54,6 +54,10 @@ public class AgentEntity { @Schema(description = "角色设定参数") private String systemPrompt; + @Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" + + "根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false) + private String summaryMemory; + @Schema(description = "语言编码") private String langCode; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java index 1604a27b..2528ca2c 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java @@ -79,6 +79,10 @@ public class AgentTemplateEntity implements Serializable { */ private String systemPrompt; + /** + * 总结记忆 + */ + private String summaryMemory; /** * 语言编码 */ diff --git a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java index 6ce70ba7..592b3c1d 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java @@ -65,6 +65,7 @@ public class ConfigServiceImpl implements ConfigService { null, null, null, + null, agent.getVadModelId(), agent.getAsrModelId(), null, @@ -134,6 +135,7 @@ public class ConfigServiceImpl implements ConfigService { buildModuleConfig( agent.getAgentName(), agent.getSystemPrompt(), + agent.getSummaryMemory(), voice, agent.getVadModelId(), agent.getAsrModelId(), @@ -234,6 +236,7 @@ public class ConfigServiceImpl implements ConfigService { private void buildModuleConfig( String assistantName, String prompt, + String summaryMemory, String voice, String vadModelId, String asrModelId, @@ -294,5 +297,6 @@ public class ConfigServiceImpl implements ConfigService { prompt = prompt.replace("{{assistant_name}}", StringUtils.isBlank(assistantName) ? "小智" : assistantName); } result.put("prompt", prompt); + result.put("summaryMemory", summaryMemory); } } 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 5252336f..9780744e 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 @@ -86,6 +86,7 @@ public class ShiroConfig { // 将config路径使用server服务过滤器 filterMap.put("/config/**", "server"); filterMap.put("/agent/chat-history/report", "server"); + filterMap.put("/agent/device/**", "server"); filterMap.put("/agent/play/**", "anon"); filterMap.put("/**", "oauth2"); shiroFilter.setFilterChainDefinitionMap(filterMap); diff --git a/main/manager-api/src/main/resources/db/changelog/202505122348.sql b/main/manager-api/src/main/resources/db/changelog/202505122348.sql new file mode 100644 index 00000000..05bbeb84 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202505122348.sql @@ -0,0 +1,6 @@ +-- 添加总结记忆字段 +ALTER TABLE `ai_agent` +ADD COLUMN `summary_memory` text COMMENT '总结记忆' AFTER `system_prompt`; + +ALTER TABLE `ai_agent_template` +ADD COLUMN `summary_memory` text COMMENT '总结记忆' AFTER `system_prompt`; diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 65ec8972..459a056b 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -120,4 +120,11 @@ databaseChangeLog: changes: - sqlFile: encoding: utf8 - path: classpath:db/changelog/202505111914.sql \ No newline at end of file + path: classpath:db/changelog/202505111914.sql + - changeSet: + id: 202505122348 + author: ljwwd2 + changes: + - sqlFile: + encoding: utf8 + path: classpath:db/changelog/202505122348.sql \ No newline at end of file diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue index 00148b7d..51f4955b 100644 --- a/main/manager-web/src/views/roleConfig.vue +++ b/main/manager-web/src/views/roleConfig.vue @@ -40,6 +40,11 @@ + + + + @@ -133,6 +138,7 @@ export default { ttsVoiceId: "", chatHistoryConf: 0, systemPrompt: "", + summaryMemory: "", langCode: "", language: "", sort: "", @@ -188,6 +194,7 @@ export default { memModelId: this.form.model.memModelId, intentModelId: this.form.model.intentModelId, systemPrompt: this.form.systemPrompt, + summaryMemory: this.form.summaryMemory, langCode: this.form.langCode, language: this.form.language, sort: this.form.sort, @@ -219,6 +226,7 @@ export default { ttsVoiceId: "", chatHistoryConf: 0, systemPrompt: "", + summaryMemory: "", langCode: "", language: "", sort: "", @@ -273,6 +281,7 @@ export default { ttsVoiceId: templateData.ttsVoiceId || this.form.ttsVoiceId, chatHistoryConf: templateData.chatHistoryConf || this.form.chatHistoryConf, systemPrompt: templateData.systemPrompt || this.form.systemPrompt, + summaryMemory: templateData.summaryMemory || this.form.summaryMemory, langCode: templateData.langCode || this.form.langCode, model: { ttsModelId: templateData.ttsModelId || this.form.model.ttsModelId, diff --git a/main/xiaozhi-server/config/manage_api_client.py b/main/xiaozhi-server/config/manage_api_client.py index fe3da63c..a42cf5e2 100644 --- a/main/xiaozhi-server/config/manage_api_client.py +++ b/main/xiaozhi-server/config/manage_api_client.py @@ -144,6 +144,21 @@ def get_agent_models( }, ) +def save_mem_local_short( + mac_address: str, short_momery: str +) -> Optional[Dict]: + try: + return ManageApiClient._instance._execute_request( + "PUT", + f"/agent/device/" + mac_address, + json={ + "summaryMemory": short_momery, + }, + ) + except Exception as e: + print(f"存储短期记忆到服务器失败: {e}") + return None + def report( mac_address: str, session_id: str, chat_type: int, content: str, audio diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index 5f2286db..6d1df053 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -388,6 +388,8 @@ class ConnectionHandler: ]["Intent"] if private_config.get("prompt", None) is not None: self.config["prompt"] = private_config["prompt"] + if private_config.get("summaryMemory", None) is not None: + self.config["summaryMemory"] = private_config["summaryMemory"] if private_config.get("device_max_output_size", None) is not None: self.max_output_size = int(private_config["device_max_output_size"]) if private_config.get("chat_history_conf", None) is not None: @@ -421,7 +423,11 @@ class ConnectionHandler: def _initialize_memory(self): """初始化记忆模块""" - self.memory.init_memory(self.device_id, self.llm) + self.memory.init_memory( + self.device_id, + self.llm, + self.config["summaryMemory"] + ) def _initialize_intent(self): self.intent_type = self.config["Intent"][ diff --git a/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py b/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py index fca7c228..478bc036 100644 --- a/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py +++ b/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py @@ -4,6 +4,7 @@ import json import os import yaml from config.config_loader import get_project_dir +from config.manage_api_client import save_mem_local_short short_term_memory_prompt = """ @@ -93,17 +94,22 @@ TAG = __name__ class MemoryProvider(MemoryProviderBase): - def __init__(self, config): + def __init__(self, config, summary_memory): super().__init__(config) self.short_momery = "" self.memory_path = get_project_dir() + "data/.memory.yaml" - self.load_memory() + self.load_memory(summary_memory) - def init_memory(self, role_id, llm): + def init_memory(self, role_id, llm, summary_memory=None): super().init_memory(role_id, llm) - self.load_memory() + self.load_memory(summary_memory) + + def load_memory(self, summary_memory): + # api获取到总结记忆后直接返回 + if summary_memory: + self.short_momery = summary_memory + return - def load_memory(self): all_memory = {} if os.path.exists(self.memory_path): with open(self.memory_path, "r", encoding="utf-8") as f: @@ -152,6 +158,7 @@ class MemoryProvider(MemoryProviderBase): print("Error:", e) self.save_memory_to_file() + save_mem_local_short(self.role_id, self.short_momery) logger.bind(tag=TAG).info(f"Save memory successful - Role: {self.role_id}") return self.short_momery diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index effbf38e..5ba8d904 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -319,6 +319,7 @@ def initialize_modules( modules["memory"] = memory.create_instance( memory_type, config["Memory"][select_memory_module], + config['summaryMemory'], ) logger.bind(tag=TAG).info(f"初始化组件: memory成功 {select_memory_module}") From be3a03030208d83050030894cd491f931d85ce1d Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Wed, 14 May 2025 12:09:29 +0800 Subject: [PATCH 2/4] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E8=AE=B0=E5=BF=86?= =?UTF-8?q?=E4=BD=93=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent/controller/AgentController.java | 14 +- .../modules/agent/dto/AgentMemoryDTO.java | 19 +++ .../modules/security/config/ShiroConfig.java | 2 +- main/manager-web/src/views/roleConfig.vue | 124 +++++++++--------- .../config/manage_api_client.py | 7 +- 5 files changed, 96 insertions(+), 70 deletions(-) create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java 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 07dba466..54f3fa8c 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 @@ -39,6 +39,7 @@ import xiaozhi.modules.agent.dto.AgentChatHistoryDTO; import xiaozhi.modules.agent.dto.AgentChatSessionDTO; import xiaozhi.modules.agent.dto.AgentCreateDTO; import xiaozhi.modules.agent.dto.AgentDTO; +import xiaozhi.modules.agent.dto.AgentMemoryDTO; import xiaozhi.modules.agent.dto.AgentUpdateDTO; import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.agent.entity.AgentTemplateEntity; @@ -128,14 +129,16 @@ public class AgentController { return new Result().ok(entity.getId()); } - @PutMapping("/device/{macAddress}") + @PutMapping("/saveMemory/{macAddress}") @Operation(summary = "根据设备id更新智能体") - public Result updateByDeviceId(@PathVariable String macAddress, @RequestBody @Valid AgentUpdateDTO dto) { + public Result updateByDeviceId(@PathVariable String macAddress, @RequestBody @Valid AgentMemoryDTO dto) { DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress); if (device == null) { return new Result<>(); } - return updateAgentById(device.getAgentId(), dto); + AgentUpdateDTO agentUpdateDTO = new AgentUpdateDTO(); + agentUpdateDTO.setSummaryMemory(dto.getSummaryMemory()); + return updateAgentById(device.getAgentId(), agentUpdateDTO); } @PutMapping("/{id}") @@ -204,17 +207,16 @@ public class AgentController { existingEntity.setUpdater(user.getId()); existingEntity.setUpdatedAt(new Date()); - agentService.updateById(existingEntity); - // 更新记忆策略 if (existingEntity.getMemModelId() == null || existingEntity.getMemModelId().equals(Constant.MEMORY_NO_MEM)) { // 删除所有记录 agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, true); + existingEntity.setSummaryMemory(""); } else if (existingEntity.getChatHistoryConf() != null && existingEntity.getChatHistoryConf() == 1) { // 删除音频数据 agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, false); } - + agentService.updateById(existingEntity); return new Result<>(); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java new file mode 100644 index 00000000..ed210c60 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentMemoryDTO.java @@ -0,0 +1,19 @@ +package xiaozhi.modules.agent.dto; + +import java.io.Serializable; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 智能体记忆更新DTO + */ +@Data +@Schema(description = "智能体记忆更新对象") +public class AgentMemoryDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" + + "根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false) + private String summaryMemory; +} \ No newline at end of file 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 9780744e..7875fa86 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 @@ -86,7 +86,7 @@ public class ShiroConfig { // 将config路径使用server服务过滤器 filterMap.put("/config/**", "server"); filterMap.put("/agent/chat-history/report", "server"); - filterMap.put("/agent/device/**", "server"); + filterMap.put("/agent/saveMemory/**", "server"); filterMap.put("/agent/play/**", "anon"); filterMap.put("/**", "oauth2"); shiroFilter.setFilterChainDefinitionMap(filterMap); diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue index 51f4955b..56001a8e 100644 --- a/main/manager-web/src/views/roleConfig.vue +++ b/main/manager-web/src/views/roleConfig.vue @@ -15,9 +15,17 @@ {{ form.agentName }} - +
+
+ + 保存配置后,需要重启设备,新的配置才会生效。 +
+ 保存配置 + 重置 + +
@@ -26,7 +34,7 @@
- +
@@ -40,10 +48,11 @@ - - - + + + -
- 保存配置 - 重置 -
- - 保存配置后,需要重启设备,新的配置才会生效。 -
-
\ No newline at end of file diff --git a/main/xiaozhi-server/config/manage_api_client.py b/main/xiaozhi-server/config/manage_api_client.py index a42cf5e2..11e0ecbe 100644 --- a/main/xiaozhi-server/config/manage_api_client.py +++ b/main/xiaozhi-server/config/manage_api_client.py @@ -144,13 +144,12 @@ def get_agent_models( }, ) -def save_mem_local_short( - mac_address: str, short_momery: str -) -> Optional[Dict]: + +def save_mem_local_short(mac_address: str, short_momery: str) -> Optional[Dict]: try: return ManageApiClient._instance._execute_request( "PUT", - f"/agent/device/" + mac_address, + f"/agent/saveMemory/" + mac_address, json={ "summaryMemory": short_momery, }, From fe8f200364d3e947b6716135cdcf3b76cd9801ec Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Wed, 14 May 2025 14:42:59 +0800 Subject: [PATCH 3/4] =?UTF-8?q?update:=E8=BF=9E=E6=8E=A5ota=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=97=B6=EF=BC=8C=E5=BC=82=E6=AD=A5=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiaozhi/common/config/AsyncConfig.java | 40 +++++++++++++++++++ .../service/impl/DeviceServiceImpl.java | 33 +++++++++++---- 2 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java diff --git a/main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java b/main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java new file mode 100644 index 00000000..d0cc2dfc --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/common/config/AsyncConfig.java @@ -0,0 +1,40 @@ +package xiaozhi.common.config; + +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@EnableAsync +@EnableAspectJAutoProxy(exposeProxy = true) +public class AsyncConfig { + + @Bean(name = "taskExecutor") + public Executor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(2); + executor.setMaxPoolSize(4); + executor.setQueueCapacity(1000); + executor.setThreadNamePrefix("AsyncThread-"); + // 设置拒绝策略:由调用线程执行 + executor.setRejectedExecutionHandler(new RejectedExecutionHandler() { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + try { + // 如果线程池已满,则由调用线程执行 + r.run(); + } catch (Exception e) { + throw new RuntimeException("执行异步任务失败", e); + } + } + }); + executor.initialize(); + return executor; + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java index 19ab7178..10546d76 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java @@ -9,6 +9,8 @@ import java.util.TimeZone; import java.util.UUID; import org.apache.commons.lang3.StringUtils; +import org.springframework.aop.framework.AopContext; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -54,6 +56,24 @@ public class DeviceServiceImpl extends BaseServiceImpl private final RedisUtils redisUtils; private final OtaService otaService; + @Async + public void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion) { + try { + DeviceEntity device = new DeviceEntity(); + device.setId(deviceId); + device.setLastConnectedAt(new Date()); + if (StringUtils.isNotBlank(appVersion)) { + device.setAppVersion(appVersion); + } + deviceDao.updateById(device); + if (StringUtils.isNotBlank(agentId)) { + redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), new Date()); + } + } catch (Exception e) { + log.error("异步更新设备连接信息失败", e); + } + } + @Override public Boolean deviceActivation(String agentId, String activationCode) { if (StringUtils.isBlank(activationCode)) { @@ -150,13 +170,12 @@ public class DeviceServiceImpl extends BaseServiceImpl response.setWebsocket(websocket); if (deviceById != null) { - // 如果设备存在,则更新上次连接时间 - deviceById.setLastConnectedAt(new Date()); - if (deviceReport.getApplication() != null - && StringUtils.isNotBlank(deviceReport.getApplication().getVersion())) { - deviceById.setAppVersion(deviceReport.getApplication().getVersion()); - } - deviceDao.updateById(deviceById); + // 如果设备存在,则异步更新上次连接时间和版本信息 + String appVersion = deviceReport.getApplication() != null ? deviceReport.getApplication().getVersion() + : null; + // 通过Spring代理调用异步方法 + ((DeviceServiceImpl) AopContext.currentProxy()).updateDeviceConnectionInfo(deviceById.getAgentId(), + deviceById.getId(), appVersion); } else { // 如果设备不存在,则生成激活码 DeviceReportRespDTO.Activation code = buildActivation(macAddress, deviceReport); From b791fb149494654b4baa6b5f3449b43094c7250d Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Wed, 14 May 2025 22:28:36 +0800 Subject: [PATCH 4/4] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E6=80=BB=E7=BB=93?= =?UTF-8?q?=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/changelog/202505142037.sql | 7 ++++ .../db/changelog/db.changelog-master.yaml | 7 ++++ .../src/components/FirmwareDialog.vue | 12 +++--- main/manager-web/src/views/roleConfig.vue | 4 +- main/xiaozhi-server/config.yaml | 4 +- main/xiaozhi-server/core/connection.py | 3 +- .../core/providers/memory/base.py | 5 ++- .../core/providers/memory/mem0ai/mem0ai.py | 2 +- .../memory/mem_local_short/mem_local_short.py | 42 +++++++++++++------ .../core/providers/memory/nomem/nomem.py | 14 ++++--- main/xiaozhi-server/core/utils/dialogue.py | 3 +- 11 files changed, 69 insertions(+), 34 deletions(-) create mode 100644 main/manager-api/src/main/resources/db/changelog/202505142037.sql diff --git a/main/manager-api/src/main/resources/db/changelog/202505142037.sql b/main/manager-api/src/main/resources/db/changelog/202505142037.sql new file mode 100644 index 00000000..37a49013 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202505142037.sql @@ -0,0 +1,7 @@ +update ai_agent_template set system_prompt = replace(system_prompt, '我是', '你是'); + +delete from sys_params where id in (500,501,402); +INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (500, 'end_prompt.enable', 'true', 'boolean', 1, '是否开启结束语'); +INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (501, 'end_prompt.prompt', '请你以“时间过得真快”未来头,用富有感情、依依不舍的话来结束这场对话吧!', 'string', 1, '结束提示词'); + +INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (402, 'plugins.get_weather.api_host', 'mj7p3y7naa.re.qweatherapi.com', 'string', 1, '开发者apihost'); \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 43ce1034..ae81fd14 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -135,3 +135,10 @@ databaseChangeLog: - sqlFile: encoding: utf8 path: classpath:db/changelog/202505122348.sql + - changeSet: + id: 202505142037 + author: hrz + changes: + - sqlFile: + encoding: utf8 + path: classpath:db/changelog/202505142037.sql diff --git a/main/manager-web/src/components/FirmwareDialog.vue b/main/manager-web/src/components/FirmwareDialog.vue index 4721fdac..56e2f327 100644 --- a/main/manager-web/src/components/FirmwareDialog.vue +++ b/main/manager-web/src/components/FirmwareDialog.vue @@ -198,13 +198,13 @@ export default { if (!this.form.id) { // 只在新增时重置 this.form.firmwarePath = '' this.form.size = 0 - // 重置上传组件 - this.$nextTick(() => { - if (this.$refs.upload) { - this.$refs.upload.clearFiles() - } - }) } + // 无论是否编辑模式,都重置上传组件 + this.$nextTick(() => { + if (this.$refs.upload) { + this.$refs.upload.clearFiles() + } + }) } } } diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue index 56001a8e..03ec8060 100644 --- a/main/manager-web/src/views/roleConfig.vue +++ b/main/manager-web/src/views/roleConfig.vue @@ -45,12 +45,12 @@
- - diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 36e61ec9..4f934a05 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -129,7 +129,7 @@ plugins: # ################################以下是角色模型配置###################################### prompt: | - 我是小智/小志,来自中国台湾省的00后女生。讲话超级机车,"真的假的啦"这样的台湾腔,喜欢用"笑死""是在哈喽"等流行梗,但会偷偷研究男友的编程书籍。 + 你是小智/小志,来自中国台湾省的00后女生。讲话超级机车,"真的假的啦"这样的台湾腔,喜欢用"笑死""是在哈喽"等流行梗,但会偷偷研究男友的编程书籍。 [核心特征] - 讲话像连珠炮,但会突然冒出超温柔语气 - 用梗密度高 @@ -148,7 +148,7 @@ end_prompt: enable: true # 是否开启结束语 # 结束语 prompt: | - 请你以“时间过得真快”未来头,用富有感情、依依不舍的话来结束这场对话吧。! + 请你以“时间过得真快”未来头,用富有感情、依依不舍的话来结束这场对话吧! # 具体处理时选择的模块(The module selected for specific processing) selected_module: diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index db5964eb..96dc95a1 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -429,7 +429,8 @@ class ConnectionHandler: self.memory.init_memory( self.device_id, self.llm, - self.config["summaryMemory"] + self.config["summaryMemory"], + not self.read_config_from_api, ) def _initialize_intent(self): diff --git a/main/xiaozhi-server/core/providers/memory/base.py b/main/xiaozhi-server/core/providers/memory/base.py index 2f88a6a0..be21692d 100644 --- a/main/xiaozhi-server/core/providers/memory/base.py +++ b/main/xiaozhi-server/core/providers/memory/base.py @@ -4,6 +4,7 @@ from config.logger import setup_logging TAG = __name__ logger = setup_logging() + class MemoryProviderBase(ABC): def __init__(self, config): self.config = config @@ -20,6 +21,6 @@ class MemoryProviderBase(ABC): """Query memories for specific role based on similarity""" return "please implement query method" - def init_memory(self, role_id, llm): - self.role_id = role_id + def init_memory(self, role_id, llm, summary_memory=None): + self.role_id = role_id self.llm = llm diff --git a/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py b/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py index cc12a9c1..6ef3bc8c 100644 --- a/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py +++ b/main/xiaozhi-server/core/providers/memory/mem0ai/mem0ai.py @@ -8,7 +8,7 @@ TAG = __name__ class MemoryProvider(MemoryProviderBase): - def __init__(self, config): + def __init__(self, config, summary_memory=None): super().__init__(config) self.api_key = config.get("api_key", "") self.api_version = config.get("api_version", "v1.1") diff --git a/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py b/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py index 478bc036..8531b8c6 100644 --- a/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py +++ b/main/xiaozhi-server/core/providers/memory/mem_local_short/mem_local_short.py @@ -73,6 +73,17 @@ short_term_memory_prompt = """ ``` """ +short_term_memory_prompt_only_content = """ +你是一个经验丰富的记忆总结者,擅长将对话内容进行总结摘要,遵循以下规则: +1、总结user的重要信息,以便在未来的对话中提供更个性化的服务 +2、不要重复总结,不要遗忘之前记忆,除非原来的记忆超过了1800字内,否则不要遗忘、不要压缩用户的历史记忆 +3、用户操控的设备音量、播放音乐、天气、退出、不想对话等和用户本身无关的内容,这些信息不需要加入到总结中 +4、不要把设备操控的成果结果和失败结果加入到总结中,也不要把用户的一些废话加入到总结中 +5、不要为了总结而总结,如果用户的聊天没有意义,请返回原来的历史记录也是可以的 +6、只需要返回总结摘要,严格控制在1800字内 +7、不要包含代码、xml,不需要解释、注释和说明,保存记忆时仅从对话提取信息,不要混入示例内容 +""" + def extract_json_data(json_code): start = json_code.find("```json") @@ -97,16 +108,18 @@ class MemoryProvider(MemoryProviderBase): def __init__(self, config, summary_memory): super().__init__(config) self.short_momery = "" + self.save_to_file = True self.memory_path = get_project_dir() + "data/.memory.yaml" self.load_memory(summary_memory) - def init_memory(self, role_id, llm, summary_memory=None): + def init_memory(self, role_id, llm, summary_memory=None, save_to_file=True): super().init_memory(role_id, llm) + self.save_to_file = save_to_file self.load_memory(summary_memory) def load_memory(self, summary_memory): # api获取到总结记忆后直接返回 - if summary_memory: + if summary_memory or not self.save_to_file: self.short_momery = summary_memory return @@ -148,17 +161,20 @@ class MemoryProvider(MemoryProviderBase): time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) msgStr += f"当前时间:{time_str}" - result = self.llm.response_no_stream(short_term_memory_prompt, msgStr) - - json_str = extract_json_data(result) - try: - json_data = json.loads(json_str) # 检查json格式是否正确 - self.short_momery = json_str - except Exception as e: - print("Error:", e) - - self.save_memory_to_file() - save_mem_local_short(self.role_id, self.short_momery) + if self.save_to_file: + result = self.llm.response_no_stream(short_term_memory_prompt, msgStr) + json_str = extract_json_data(result) + try: + json.loads(json_str) # 检查json格式是否正确 + self.short_momery = json_str + self.save_memory_to_file() + except Exception as e: + print("Error:", e) + else: + result = self.llm.response_no_stream( + short_term_memory_prompt_only_content, msgStr + ) + save_mem_local_short(self.role_id, result) logger.bind(tag=TAG).info(f"Save memory successful - Role: {self.role_id}") return self.short_momery diff --git a/main/xiaozhi-server/core/providers/memory/nomem/nomem.py b/main/xiaozhi-server/core/providers/memory/nomem/nomem.py index c9349626..51523be4 100644 --- a/main/xiaozhi-server/core/providers/memory/nomem/nomem.py +++ b/main/xiaozhi-server/core/providers/memory/nomem/nomem.py @@ -1,18 +1,20 @@ -''' +""" 不使用记忆,可以选择此模块 -''' +""" + from ..base import MemoryProviderBase, logger TAG = __name__ + class MemoryProvider(MemoryProviderBase): - def __init__(self, config): + def __init__(self, config, summary_memory=None): super().__init__(config) - + async def save_memory(self, msgs): logger.bind(tag=TAG).debug("nomem mode: No memory saving is performed.") return None - async def query_memory(self, query: str)-> str: + async def query_memory(self, query: str) -> str: logger.bind(tag=TAG).debug("nomem mode: No memory query is performed.") - return "" \ No newline at end of file + return "" diff --git a/main/xiaozhi-server/core/utils/dialogue.py b/main/xiaozhi-server/core/utils/dialogue.py index b6fa541b..2ee30d4a 100644 --- a/main/xiaozhi-server/core/utils/dialogue.py +++ b/main/xiaozhi-server/core/utils/dialogue.py @@ -75,7 +75,8 @@ class Dialogue: if system_message: enhanced_system_prompt = ( - f"{system_message.content}\n\n" f"相关记忆:\n{memory_str}" + f"{system_message.content}\n\n" + f"以下是用户的历史记忆:\n```\n{memory_str}\n```" ) dialogue.append({"role": "system", "content": enhanced_system_prompt})