Skip to content

Commit 67e6a22

Browse files
authored
Merge pull request #1248 from xinnan-tech/manager-local-mem
增加智控台管理【本地记忆】功能
2 parents 9e329bf + b791fb1 commit 67e6a22

File tree

24 files changed

+311
-100
lines changed

24 files changed

+311
-100
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package xiaozhi.common.config;
2+
3+
import java.util.concurrent.Executor;
4+
import java.util.concurrent.RejectedExecutionHandler;
5+
import java.util.concurrent.ThreadPoolExecutor;
6+
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.context.annotation.EnableAspectJAutoProxy;
10+
import org.springframework.scheduling.annotation.EnableAsync;
11+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
12+
13+
@Configuration
14+
@EnableAsync
15+
@EnableAspectJAutoProxy(exposeProxy = true)
16+
public class AsyncConfig {
17+
18+
@Bean(name = "taskExecutor")
19+
public Executor taskExecutor() {
20+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
21+
executor.setCorePoolSize(2);
22+
executor.setMaxPoolSize(4);
23+
executor.setQueueCapacity(1000);
24+
executor.setThreadNamePrefix("AsyncThread-");
25+
// 设置拒绝策略:由调用线程执行
26+
executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
27+
@Override
28+
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
29+
try {
30+
// 如果线程池已满,则由调用线程执行
31+
r.run();
32+
} catch (Exception e) {
33+
throw new RuntimeException("执行异步任务失败", e);
34+
}
35+
}
36+
});
37+
executor.initialize();
38+
return executor;
39+
}
40+
}

main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentController.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@
3939
import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
4040
import xiaozhi.modules.agent.dto.AgentCreateDTO;
4141
import xiaozhi.modules.agent.dto.AgentDTO;
42+
import xiaozhi.modules.agent.dto.AgentMemoryDTO;
4243
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
4344
import xiaozhi.modules.agent.entity.AgentEntity;
4445
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
4546
import xiaozhi.modules.agent.service.AgentChatAudioService;
4647
import xiaozhi.modules.agent.service.AgentChatHistoryService;
4748
import xiaozhi.modules.agent.service.AgentService;
4849
import xiaozhi.modules.agent.service.AgentTemplateService;
50+
import xiaozhi.modules.device.entity.DeviceEntity;
4951
import xiaozhi.modules.device.service.DeviceService;
5052
import xiaozhi.modules.security.user.SecurityUser;
5153

@@ -109,6 +111,7 @@ public Result<String> save(@RequestBody @Valid AgentCreateDTO dto) {
109111
entity.setMemModelId(template.getMemModelId());
110112
entity.setIntentModelId(template.getIntentModelId());
111113
entity.setSystemPrompt(template.getSystemPrompt());
114+
entity.setSummaryMemory(template.getSummaryMemory());
112115
entity.setChatHistoryConf(template.getChatHistoryConf());
113116
entity.setLangCode(template.getLangCode());
114117
entity.setLanguage(template.getLanguage());
@@ -126,10 +129,26 @@ public Result<String> save(@RequestBody @Valid AgentCreateDTO dto) {
126129
return new Result<String>().ok(entity.getId());
127130
}
128131

132+
@PutMapping("/saveMemory/{macAddress}")
133+
@Operation(summary = "根据设备id更新智能体")
134+
public Result<Void> updateByDeviceId(@PathVariable String macAddress, @RequestBody @Valid AgentMemoryDTO dto) {
135+
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
136+
if (device == null) {
137+
return new Result<>();
138+
}
139+
AgentUpdateDTO agentUpdateDTO = new AgentUpdateDTO();
140+
agentUpdateDTO.setSummaryMemory(dto.getSummaryMemory());
141+
return updateAgentById(device.getAgentId(), agentUpdateDTO);
142+
}
143+
129144
@PutMapping("/{id}")
130145
@Operation(summary = "更新智能体")
131146
@RequiresPermissions("sys:role:normal")
132147
public Result<Void> update(@PathVariable String id, @RequestBody @Valid AgentUpdateDTO dto) {
148+
return updateAgentById(id, dto);
149+
}
150+
151+
private Result<Void> updateAgentById(String id, AgentUpdateDTO dto) {
133152
// 先查询现有实体
134153
AgentEntity existingEntity = agentService.getAgentById(id);
135154
if (existingEntity == null) {
@@ -167,6 +186,9 @@ public Result<Void> update(@PathVariable String id, @RequestBody @Valid AgentUpd
167186
if (dto.getSystemPrompt() != null) {
168187
existingEntity.setSystemPrompt(dto.getSystemPrompt());
169188
}
189+
if (dto.getSummaryMemory() != null) {
190+
existingEntity.setSummaryMemory(dto.getSummaryMemory());
191+
}
170192
if (dto.getChatHistoryConf() != null) {
171193
existingEntity.setChatHistoryConf(dto.getChatHistoryConf());
172194
}
@@ -185,17 +207,16 @@ public Result<Void> update(@PathVariable String id, @RequestBody @Valid AgentUpd
185207
existingEntity.setUpdater(user.getId());
186208
existingEntity.setUpdatedAt(new Date());
187209

188-
agentService.updateById(existingEntity);
189-
190210
// 更新记忆策略
191211
if (existingEntity.getMemModelId() == null || existingEntity.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
192212
// 删除所有记录
193213
agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, true);
214+
existingEntity.setSummaryMemory("");
194215
} else if (existingEntity.getChatHistoryConf() != null && existingEntity.getChatHistoryConf() == 1) {
195216
// 删除音频数据
196217
agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, false);
197218
}
198-
219+
agentService.updateById(existingEntity);
199220
return new Result<>();
200221
}
201222

main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public class AgentDTO {
3333
@Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题并提供帮助")
3434
private String systemPrompt;
3535

36+
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
37+
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
38+
private String summaryMemory;
39+
3640
@Schema(description = "最后连接时间", example = "2024-03-20 10:00:00")
3741
private Date lastConnectedAt;
3842

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package xiaozhi.modules.agent.dto;
2+
3+
import java.io.Serializable;
4+
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import lombok.Data;
7+
8+
/**
9+
* 智能体记忆更新DTO
10+
*/
11+
@Data
12+
@Schema(description = "智能体记忆更新对象")
13+
public class AgentMemoryDTO implements Serializable {
14+
private static final long serialVersionUID = 1L;
15+
16+
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
17+
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
18+
private String summaryMemory;
19+
}

main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ public class AgentUpdateDTO implements Serializable {
4545
@Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题并提供帮助", required = false)
4646
private String systemPrompt;
4747

48+
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
49+
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
50+
private String summaryMemory;
51+
4852
@Schema(description = "聊天记录配置(0不记录 1仅记录文本 2记录文本和语音)", example = "3", required = false)
4953
private Integer chatHistoryConf;
5054

main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public class AgentEntity {
5454
@Schema(description = "角色设定参数")
5555
private String systemPrompt;
5656

57+
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
58+
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
59+
private String summaryMemory;
60+
5761
@Schema(description = "语言编码")
5862
private String langCode;
5963

main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentTemplateEntity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ public class AgentTemplateEntity implements Serializable {
7979
*/
8080
private String systemPrompt;
8181

82+
/**
83+
* 总结记忆
84+
*/
85+
private String summaryMemory;
8286
/**
8387
* 语言编码
8488
*/

main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public Object getConfig(Boolean isCache) {
6565
null,
6666
null,
6767
null,
68+
null,
6869
agent.getVadModelId(),
6970
agent.getAsrModelId(),
7071
null,
@@ -134,6 +135,7 @@ public Map<String, Object> getAgentModels(String macAddress, Map<String, String>
134135
buildModuleConfig(
135136
agent.getAgentName(),
136137
agent.getSystemPrompt(),
138+
agent.getSummaryMemory(),
137139
voice,
138140
agent.getVadModelId(),
139141
agent.getAsrModelId(),
@@ -234,6 +236,7 @@ private Object buildConfig(Map<String, Object> config) {
234236
private void buildModuleConfig(
235237
String assistantName,
236238
String prompt,
239+
String summaryMemory,
237240
String voice,
238241
String vadModelId,
239242
String asrModelId,
@@ -294,5 +297,6 @@ private void buildModuleConfig(
294297
prompt = prompt.replace("{{assistant_name}}", StringUtils.isBlank(assistantName) ? "小智" : assistantName);
295298
}
296299
result.put("prompt", prompt);
300+
result.put("summaryMemory", summaryMemory);
297301
}
298302
}

main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import java.util.UUID;
1010

1111
import org.apache.commons.lang3.StringUtils;
12+
import org.springframework.aop.framework.AopContext;
13+
import org.springframework.scheduling.annotation.Async;
1214
import org.springframework.stereotype.Service;
1315
import org.springframework.web.context.request.RequestContextHolder;
1416
import org.springframework.web.context.request.ServletRequestAttributes;
@@ -54,6 +56,24 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
5456
private final RedisUtils redisUtils;
5557
private final OtaService otaService;
5658

59+
@Async
60+
public void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion) {
61+
try {
62+
DeviceEntity device = new DeviceEntity();
63+
device.setId(deviceId);
64+
device.setLastConnectedAt(new Date());
65+
if (StringUtils.isNotBlank(appVersion)) {
66+
device.setAppVersion(appVersion);
67+
}
68+
deviceDao.updateById(device);
69+
if (StringUtils.isNotBlank(agentId)) {
70+
redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), new Date());
71+
}
72+
} catch (Exception e) {
73+
log.error("异步更新设备连接信息失败", e);
74+
}
75+
}
76+
5777
@Override
5878
public Boolean deviceActivation(String agentId, String activationCode) {
5979
if (StringUtils.isBlank(activationCode)) {
@@ -156,13 +176,12 @@ public DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId,
156176
response.setWebsocket(websocket);
157177

158178
if (deviceById != null) {
159-
// 如果设备存在,则更新上次连接时间
160-
deviceById.setLastConnectedAt(new Date());
161-
if (deviceReport.getApplication() != null
162-
&& StringUtils.isNotBlank(deviceReport.getApplication().getVersion())) {
163-
deviceById.setAppVersion(deviceReport.getApplication().getVersion());
164-
}
165-
deviceDao.updateById(deviceById);
179+
// 如果设备存在,则异步更新上次连接时间和版本信息
180+
String appVersion = deviceReport.getApplication() != null ? deviceReport.getApplication().getVersion()
181+
: null;
182+
// 通过Spring代理调用异步方法
183+
((DeviceServiceImpl) AopContext.currentProxy()).updateDeviceConnectionInfo(deviceById.getAgentId(),
184+
deviceById.getId(), appVersion);
166185
} else {
167186
// 如果设备不存在,则生成激活码
168187
DeviceReportRespDTO.Activation code = buildActivation(macAddress, deviceReport);

main/manager-api/src/main/java/xiaozhi/modules/security/config/ShiroConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, SysPar
8686
// 将config路径使用server服务过滤器
8787
filterMap.put("/config/**", "server");
8888
filterMap.put("/agent/chat-history/report", "server");
89+
filterMap.put("/agent/saveMemory/**", "server");
8990
filterMap.put("/agent/play/**", "anon");
9091
filterMap.put("/**", "oauth2");
9192
shiroFilter.setFilterChainDefinitionMap(filterMap);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- 添加总结记忆字段
2+
ALTER TABLE `ai_agent`
3+
ADD COLUMN `summary_memory` text COMMENT '总结记忆' AFTER `system_prompt`;
4+
5+
ALTER TABLE `ai_agent_template`
6+
ADD COLUMN `summary_memory` text COMMENT '总结记忆' AFTER `system_prompt`;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
update ai_agent_template set system_prompt = replace(system_prompt, '我是', '你是');
2+
3+
delete from sys_params where id in (500,501,402);
4+
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (500, 'end_prompt.enable', 'true', 'boolean', 1, '是否开启结束语');
5+
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (501, 'end_prompt.prompt', '请你以“时间过得真快”未来头,用富有感情、依依不舍的话来结束这场对话吧!', 'string', 1, '结束提示词');
6+
7+
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');

main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,17 @@ databaseChangeLog:
128128
- sqlFile:
129129
encoding: utf8
130130
path: classpath:db/changelog/202505111914.sql
131+
- changeSet:
132+
id: 202505122348
133+
author: ljwwd2
134+
changes:
135+
- sqlFile:
136+
encoding: utf8
137+
path: classpath:db/changelog/202505122348.sql
138+
- changeSet:
139+
id: 202505142037
140+
author: hrz
141+
changes:
142+
- sqlFile:
143+
encoding: utf8
144+
path: classpath:db/changelog/202505142037.sql

main/manager-web/src/components/FirmwareDialog.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,13 @@ export default {
198198
if (!this.form.id) { // 只在新增时重置
199199
this.form.firmwarePath = ''
200200
this.form.size = 0
201-
// 重置上传组件
202-
this.$nextTick(() => {
203-
if (this.$refs.upload) {
204-
this.$refs.upload.clearFiles()
205-
}
206-
})
207201
}
202+
// 无论是否编辑模式,都重置上传组件
203+
this.$nextTick(() => {
204+
if (this.$refs.upload) {
205+
this.$refs.upload.clearFiles()
206+
}
207+
})
208208
}
209209
}
210210
}

0 commit comments

Comments
 (0)