diff --git a/docs/Deployment.md b/docs/Deployment.md index 3ca7b1fc..dfac9dcd 100644 --- a/docs/Deployment.md +++ b/docs/Deployment.md @@ -330,14 +330,16 @@ LLM: 以下是一些常见问题,供参考: -[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87) +[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md) -[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87) +[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md) -[3、TTS 经常失败,经常超时](./FAQ.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87) +[3、TTS 经常失败,经常超时](./FAQ.md) -[4、如何提高小智对话响应速度?](./FAQ.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87) +[4、使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md) -[5、我说话很慢,停顿时小智老是抢话](./FAQ.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87) +[5、如何提高小智对话响应速度?](./FAQ.md) -[6、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87) +[6、我说话很慢,停顿时小智老是抢话](./FAQ.md) + +[7、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md) diff --git a/docs/firmware-build.md b/docs/firmware-build.md index 26352078..9187681e 100644 --- a/docs/firmware-build.md +++ b/docs/firmware-build.md @@ -1,9 +1,8 @@ # 编译esp32固件 -1. 下载`xiaozhi-esp32` - 项目,按照这个教程配置项目环境[《Windows搭建 ESP IDF 5.3.2开发环境以及编译小智》](https://icnynnzcwou8.feishu.cn/wiki/JEYDwTTALi5s2zkGlFGcDiRknXf) +1. 先按照这个教程配置项目环境[《Windows搭建 ESP IDF 5.3.2开发环境以及编译小智》](https://icnynnzcwou8.feishu.cn/wiki/JEYDwTTALi5s2zkGlFGcDiRknXf) -2. 打开`xiaozhi-esp32/main/Kconfig.projbuild`文件,找到`WEBSOCKET_URL`的`default`的内容,把`wss://api.tenclass.net` +2. 配置好编译环境后,下载虾哥[xiaozhi-esp32](https://github.com/78/xiaozhi-esp32)项目源码,进入虾哥项目,打开`xiaozhi-esp32/main/Kconfig.projbuild`文件,找到`WEBSOCKET_URL`的`default`的内容,把`wss://api.tenclass.net` 改成你自己的地址,例如,我的接口地址是`ws://192.168.1.25:8000/xiaozhi/v1/`,就把内容改成这个。 修改前: @@ -92,14 +91,16 @@ https://espressif.github.io/esp-launchpad/ 以下是一些常见问题,供参考: -[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md#1tts-%E7%BB%8F%E5%B8%B8%E5%A4%B1%E8%B4%A5%E7%BB%8F%E5%B8%B8%E8%B6%85%E6%97%B6-) +[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md) -[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md#1tts-%E7%BB%8F%E5%B8%B8%E5%A4%B1%E8%B4%A5%E7%BB%8F%E5%B8%B8%E8%B6%85%E6%97%B6-) +[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md) -[3、TTS 经常失败,经常超时](./FAQ.md#1tts-%E7%BB%8F%E5%B8%B8%E5%A4%B1%E8%B4%A5%E7%BB%8F%E5%B8%B8%E8%B6%85%E6%97%B6-) +[3、TTS 经常失败,经常超时](./FAQ.md) -[4、如何提高小智对话响应速度?](./FAQ.md#1tts-%E7%BB%8F%E5%B8%B8%E5%A4%B1%E8%B4%A5%E7%BB%8F%E5%B8%B8%E8%B6%85%E6%97%B6-) +[4、使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md) -[5、我说话很慢,停顿时小智老是抢话](./FAQ.md#1tts-%E7%BB%8F%E5%B8%B8%E5%A4%B1%E8%B4%A5%E7%BB%8F%E5%B8%B8%E8%B6%85%E6%97%B6-) +[5、如何提高小智对话响应速度?](./FAQ.md) -[6、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md#1tts-%E7%BB%8F%E5%B8%B8%E5%A4%B1%E8%B4%A5%E7%BB%8F%E5%B8%B8%E8%B6%85%E6%97%B6-) +[6、我说话很慢,停顿时小智老是抢话](./FAQ.md) + +[7、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md) diff --git a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java index d71040e0..53aa08dc 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java +++ b/main/manager-api/src/main/java/xiaozhi/common/redis/RedisKeys.java @@ -34,5 +34,24 @@ public class RedisKeys { return "sys:username:id:" + userid; } + /** + * 模型名称的Key + */ + public static String getModelNameById(String id) { + return "sys:model:name:" + id; + } + /** + * 获取音色名称缓存key + */ + public static String getTimbreNameById(String id) { + return "timbre:name:" + id; + } + + /** + * 获取设备数量缓存key + */ + public static String getAgentDeviceCountById(String id) { + return "agent:device:count:" + id; + } } 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 93a741df..1ec2dfbf 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 @@ -29,6 +29,7 @@ import xiaozhi.common.user.UserDetail; import xiaozhi.common.utils.ConvertUtils; import xiaozhi.common.utils.Result; import xiaozhi.modules.agent.dto.AgentCreateDTO; +import xiaozhi.modules.agent.dto.AgentDTO; import xiaozhi.modules.agent.dto.AgentUpdateDTO; import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.agent.entity.AgentTemplateEntity; @@ -47,10 +48,10 @@ public class AgentController { @GetMapping("/list") @Operation(summary = "获取用户智能体列表") @RequiresPermissions("sys:role:normal") - public Result> getUserAgents() { + public Result> getUserAgents() { UserDetail user = SecurityUser.getUser(); - List agents = agentService.getUserAgents(user.getId()); - return new Result>().ok(agents); + List agents = agentService.getUserAgents(user.getId()); + return new Result>().ok(agents); } @GetMapping("/all") diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java index d9aec7d7..8c80d6cc 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentDao.java @@ -1,11 +1,18 @@ package xiaozhi.modules.agent.dao; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import xiaozhi.common.dao.BaseDao; import xiaozhi.modules.agent.entity.AgentEntity; @Mapper public interface AgentDao extends BaseDao { - + /** + * 获取智能体的设备数量 + * + * @param agentId 智能体ID + * @return 设备数量 + */ + Integer getDeviceCountByAgentId(@Param("agentId") String agentId); } \ No newline at end of file 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 new file mode 100644 index 00000000..a63c96e7 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentDTO.java @@ -0,0 +1,38 @@ +package xiaozhi.modules.agent.dto; + +import java.util.Date; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 智能体数据传输对象 + * 用于在服务层和控制器层之间传递智能体相关的数据 + */ +@Data +@Schema(description = "智能体对象") +public class AgentDTO { + @Schema(description = "智能体编码", example = "AGT_1234567890") + private String id; + + @Schema(description = "智能体名称", example = "客服助手") + private String agentName; + + @Schema(description = "语音合成模型名称", example = "tts_model_01") + private String ttsModelName; + + @Schema(description = "音色名称", example = "voice_01") + private String ttsVoiceName; + + @Schema(description = "大语言模型名称", example = "llm_model_01") + private String llmModelName; + + @Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题并提供帮助") + private String systemPrompt; + + @Schema(description = "最后连接时间", example = "2024-03-20 10:00:00") + private Date lastConnectedAt; + + @Schema(description = "设备数量", example = "10") + private Integer deviceCount; +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java index 3282265e..8d0b2959 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentService.java @@ -5,13 +5,10 @@ import java.util.Map; import xiaozhi.common.page.PageData; import xiaozhi.common.service.BaseService; +import xiaozhi.modules.agent.dto.AgentDTO; import xiaozhi.modules.agent.entity.AgentEntity; public interface AgentService extends BaseService { - /** - * 根据用户ID获取智能体列表 - */ - List getUserAgents(Long userId); /** * 管理员获取所有智能体列表(分页) @@ -29,4 +26,20 @@ public interface AgentService extends BaseService { * @param userId */ void deleteAgentByUserId(Long userId); + + /** + * 获取用户智能体列表 + * + * @param userId + * @return + */ + List getUserAgents(Long userId); + + /** + * 获取智能体的设备数量 + * + * @param agentId 智能体ID + * @return 设备数量 + */ + Integer getDeviceCountByAgentId(String agentId); } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java index cc4e1150..bb2a3ed0 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java @@ -3,7 +3,10 @@ package xiaozhi.modules.agent.service.impl; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -11,26 +14,33 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import xiaozhi.common.page.PageData; +import xiaozhi.common.redis.RedisKeys; +import xiaozhi.common.redis.RedisUtils; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.modules.agent.dao.AgentDao; +import xiaozhi.modules.agent.dto.AgentDTO; import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.agent.service.AgentService; +import xiaozhi.modules.model.service.ModelConfigService; +import xiaozhi.modules.timbre.service.TimbreService; @Service public class AgentServiceImpl extends BaseServiceImpl implements AgentService { private final AgentDao agentDao; + @Autowired + private TimbreService timbreModelService; + + @Autowired + private ModelConfigService modelConfigService; + + @Autowired + private RedisUtils redisUtils; + public AgentServiceImpl(AgentDao agentDao) { this.agentDao = agentDao; } - @Override - public List getUserAgents(Long userId) { - QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.eq("user_id", userId); - return agentDao.selectList(wrapper); - } - @Override public PageData adminAgentList(Map params) { IPage page = agentDao.selectPage( @@ -70,4 +80,54 @@ public class AgentServiceImpl extends BaseServiceImpl imp wrapper.eq("user_id", userId); baseDao.delete(wrapper); } + + @Override + public List getUserAgents(Long userId) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("user_id", userId); + List agents = agentDao.selectList(wrapper); + return agents.stream().map(agent -> { + AgentDTO dto = new AgentDTO(); + dto.setId(agent.getId()); + dto.setAgentName(agent.getAgentName()); + dto.setSystemPrompt(agent.getSystemPrompt()); + + // 获取 TTS 模型名称 + dto.setTtsModelName(modelConfigService.getModelNameById(agent.getTtsModelId())); + + // 获取 LLM 模型名称 + dto.setLlmModelName(modelConfigService.getModelNameById(agent.getLlmModelId())); + + // 获取 TTS 音色名称 + dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId())); + + // 获取设备数量 + dto.setDeviceCount(getDeviceCountByAgentId(agent.getId())); + + return dto; + }).collect(Collectors.toList()); + } + + @Override + public Integer getDeviceCountByAgentId(String agentId) { + if (StringUtils.isBlank(agentId)) { + return 0; + } + + // 先从Redis中获取 + Integer cachedCount = (Integer) redisUtils.get(RedisKeys.getAgentDeviceCountById(agentId)); + if (cachedCount != null) { + return cachedCount; + } + + // 如果Redis中没有,则从数据库查询 + Integer deviceCount = agentDao.getDeviceCountByAgentId(agentId); + + // 将结果存入Redis + if (deviceCount != null) { + redisUtils.set(RedisKeys.getAgentDeviceCountById(agentId), deviceCount, 60); + } + + return deviceCount != null ? deviceCount : 0; + } } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java index f6bdb924..b8cc38b1 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/ModelConfigService.java @@ -20,4 +20,12 @@ public interface ModelConfigService extends BaseService { ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO); void delete(String id); + + /** + * 根据ID获取模型名称 + * + * @param id 模型ID + * @return 模型名称 + */ + String getModelNameById(String id); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java index 403d867b..de8cbd4f 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/model/service/impl/ModelConfigServiceImpl.java @@ -15,6 +15,8 @@ import lombok.AllArgsConstructor; import xiaozhi.common.constant.Constant; import xiaozhi.common.exception.RenException; import xiaozhi.common.page.PageData; +import xiaozhi.common.redis.RedisKeys; +import xiaozhi.common.redis.RedisUtils; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.common.utils.ConvertUtils; import xiaozhi.modules.model.dao.ModelConfigDao; @@ -25,7 +27,6 @@ import xiaozhi.modules.model.dto.ModelProviderDTO; import xiaozhi.modules.model.entity.ModelConfigEntity; import xiaozhi.modules.model.service.ModelConfigService; import xiaozhi.modules.model.service.ModelProviderService; -import xiaozhi.modules.timbre.service.TimbreService; @Service @AllArgsConstructor @@ -34,7 +35,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl getModelCodeList(String modelType, String modelName) { @@ -100,4 +101,28 @@ public class ModelConfigServiceImpl extends BaseServiceImpl { void delete(String[] ids); List getVoiceNames(String ttsModelId, String voiceName); + + /** + * 根据ID获取音色名称 + * + * @param id 音色ID + * @return 音色名称 + */ + String getTimbreNameById(String id); } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java index a30104ee..54e5ec04 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/timbre/service/impl/TimbreServiceImpl.java @@ -16,6 +16,8 @@ import cn.hutool.core.collection.CollectionUtil; import lombok.AllArgsConstructor; import xiaozhi.common.constant.Constant; import xiaozhi.common.page.PageData; +import xiaozhi.common.redis.RedisKeys; +import xiaozhi.common.redis.RedisUtils; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.common.utils.ConvertUtils; import xiaozhi.modules.model.dto.VoiceDTO; @@ -37,6 +39,7 @@ import xiaozhi.modules.timbre.vo.TimbreDetailsVO; public class TimbreServiceImpl extends BaseServiceImpl implements TimbreService { private final TimbreDao timbreDao; + private final RedisUtils redisUtils; @Override public PageData page(TimbrePageDTO dto) { @@ -105,4 +108,28 @@ public class TimbreServiceImpl extends BaseServiceImpl private void isTtsModelId(String ttsModelId) { // 等模型配置那边写好调用方法判断 } + + @Override + public String getTimbreNameById(String id) { + if (StringUtils.isBlank(id)) { + return null; + } + + String cachedName = (String) redisUtils.get(RedisKeys.getTimbreNameById(id)); + + if (StringUtils.isNotBlank(cachedName)) { + return cachedName; + } + + TimbreEntity entity = timbreDao.selectById(id); + if (entity != null) { + String name = entity.getName(); + if (StringUtils.isNotBlank(name)) { + redisUtils.set(RedisKeys.getTimbreNameById(id), name); + } + return name; + } + + return null; + } } \ No newline at end of file diff --git a/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml b/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml new file mode 100644 index 00000000..322ca172 --- /dev/null +++ b/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/main/manager-web/src/apis/httpRequest.js b/main/manager-web/src/apis/httpRequest.js index 78e2dbf6..43cdf92d 100755 --- a/main/manager-web/src/apis/httpRequest.js +++ b/main/manager-web/src/apis/httpRequest.js @@ -137,7 +137,7 @@ function reAjaxFun(fn) { } else { showWarning('正在连接服务器(' + ajaxIndex + ')') } - if (fn) { + if (ajaxIndex < 10 && fn) { setTimeout(() => { fn() }, reAjaxSec * 1000) diff --git a/main/manager-web/src/components/DeviceItem.vue b/main/manager-web/src/components/DeviceItem.vue index 729b3d1f..13bf608d 100644 --- a/main/manager-web/src/components/DeviceItem.vue +++ b/main/manager-web/src/components/DeviceItem.vue @@ -2,12 +2,15 @@
- {{ device.agentName }} + {{ device.agentName }}
- - + + + +
@@ -20,14 +23,14 @@
配置角色
- - - - - - -
- 设备管理 + + + + + + +
+ 设备管理({{ device.deviceCount }})
@@ -66,6 +69,7 @@ export default { padding: 22px; box-sizing: border-box; } + .device-name { margin: 7px 0 10px; font-weight: 400; @@ -94,5 +98,11 @@ export default { color: #979db1; font-weight: 400; } + + \ No newline at end of file