mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
增加智控台管理【本地记忆】功能
This commit is contained in:
@@ -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<String>().ok(entity.getId());
|
||||
}
|
||||
|
||||
@PutMapping("/device/{macAddress}")
|
||||
@Operation(summary = "根据设备id更新智能体")
|
||||
public Result<Void> 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<Void> update(@PathVariable String id, @RequestBody @Valid AgentUpdateDTO dto) {
|
||||
return updateAgentById(id, dto);
|
||||
}
|
||||
|
||||
private Result<Void> 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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -79,6 +79,10 @@ public class AgentTemplateEntity implements Serializable {
|
||||
*/
|
||||
private String systemPrompt;
|
||||
|
||||
/**
|
||||
* 总结记忆
|
||||
*/
|
||||
private String summaryMemory;
|
||||
/**
|
||||
* 语言编码
|
||||
*/
|
||||
|
||||
+4
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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`;
|
||||
@@ -120,4 +120,11 @@ databaseChangeLog:
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202505111914.sql
|
||||
path: classpath:db/changelog/202505111914.sql
|
||||
- changeSet:
|
||||
id: 202505122348
|
||||
author: ljwwd2
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202505122348.sql
|
||||
@@ -40,6 +40,11 @@
|
||||
<el-input type="textarea" rows="12" resize="none" placeholder="请输入内容" v-model="form.systemPrompt"
|
||||
maxlength="2000" show-word-limit class="form-textarea" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="总结记忆:" :style="{ display: form.model.memModelId === 'Memory_mem_local_short' ? 'block' : 'none' }">
|
||||
<el-input type="textarea" rows="12" resize="none" placeholder="请输入内容" v-model="form.summaryMemory"
|
||||
maxlength="2000" show-word-limit class="form-textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="语言编码:" style="display: none;">
|
||||
<el-input v-model="form.langCode" placeholder="请输入语言编码,如:zh_CN" maxlength="10" show-word-limit
|
||||
class="form-input" />
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"][
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user