Compare commits

...
367 Commits
Author SHA1 Message Date
欣南科技andGitHub bffed8bb8a Merge pull request #2141 from xinnan-tech/fix-tool-input-bg
update:修复mcp input样式污染其他input的bug
2025-08-31 00:00:30 +08:00
hrz ff3c0ab7ca update:修复mcp input样式污染其他input的bug 2025-08-30 23:58:11 +08:00
欣南科技andGitHub 6d0addf005 Merge pull request #2140 from xinnan-tech/rollback
update:流式优化暂不稳定,先回滚到早期代码
2025-08-30 23:03:18 +08:00
hrz 1520874432 update:升级版本号 2025-08-30 23:02:50 +08:00
hrz eaade698fb update:流式优化暂不稳定,先回滚到早期代码 2025-08-30 22:59:21 +08:00
欣南科技andGitHub b4f4995ff9 Merge pull request #2139 from xinnan-tech/py_wakeup_audio
Py wakeup audio
2025-08-30 18:15:40 +08:00
hrzandGitHub 76a7ac04c9 Merge branch 'main' into py_wakeup_audio 2025-08-30 18:15:09 +08:00
欣南科技andGitHub 3e1765a26d Merge pull request #2138 from xinnan-tech/update-doc
update:更换Demo视频连接
2025-08-29 13:48:42 +08:00
hrz 95b80f6bb1 update:更换Demo视频连接 2025-08-29 13:47:40 +08:00
hrzandGitHub caf5f3f04c Merge pull request #2134 from xinnan-tech/py_test_tts
update:AliAppLLM流式接口改造
2025-08-28 19:19:39 +08:00
Sakura-RanChen 41b8fac3aa update: 增加唤醒时声纹处理 ,1秒内发送至大模型 【需优化唤醒锁机制,中途会遭受打断(偶发),考虑忽略检测】 2025-08-28 17:58:23 +08:00
3030332422 f6f240bc4b update:AliAppLLM流式接口改造 2025-08-28 16:32:00 +08:00
欣南科技andGitHub a79d8d61e3 Merge pull request #2128 from xinnan-tech/ha-timeout
update:优化ha插件超时方法
2025-08-28 00:40:19 +08:00
hrz a5e748f775 update:优化ha插件超时方法 2025-08-28 00:39:43 +08:00
hrzandGitHub 0fc4edc431 Merge pull request #2114 from xinnan-tech/paddlespeech
update:更新paddlespeechtts供应器
2025-08-27 23:22:04 +08:00
gitjianyuandGitHub 3378d2cd8f Merge pull request #2102 from Minamiyama/refactor/extract-streaming-context
refactor(audio): 将流式音频处理逻辑提取为独立类
2025-08-27 10:33:07 +08:00
hrzandGitHub e004a85f53 Merge pull request #2115 from xinnan-tech/py_fix_mcp
fix:修复“服务端mcp 未添加到 function 列表中”问题
2025-08-27 10:16:59 +08:00
Sakura-RanChen 91cd843cfe update: 恢复唤醒播放机制,待优化和改造(目前只有linkerai拥有正常保存唤醒音频to_tts) 2025-08-27 10:04:27 +08:00
FAN-yeB 880b669877 还原配置文件 2025-08-26 11:49:08 +08:00
FAN-yeB e53ba28c65 更新paddlespeechtts供应器 2025-08-26 11:47:52 +08:00
3030332422 bdb6d3c093 fix:修复“服务端mcp 未添加到 function 列表中” 2025-08-26 11:45:52 +08:00
FAN-yeB 9af77d0f47 更新paddlespeechtts供应器 2025-08-26 11:24:58 +08:00
FAN-yeB 15291bfcf2 更新paddlespeechtts供应器 2025-08-26 11:02:57 +08:00
FAN-yeB ae228f3b86 update:更新paddlespeechtts供应器 2025-08-26 10:33:38 +08:00
欣南科技andGitHub b8803f92e7 Merge pull request #2111 from xinnan-tech/duplicate-model
Duplicate model
2025-08-25 22:28:36 +08:00
hrzandGitHub 1515784700 Merge pull request #2104 from Minamiyama/feat/model-management/duplicate-model
feat(模型管理): 添加模型副本创建功能
2025-08-25 22:23:34 +08:00
hrzandGitHub 06e2909fda Merge pull request #2110 from myifeng/main
移除未使用的变量
2025-08-25 22:15:06 +08:00
hrzandGitHub 9012deeae7 Update connection.py 2025-08-25 22:14:41 +08:00
Chingfeng Li 31657e1dc0 移除未使用的变量 2025-08-25 18:26:55 +08:00
欣南科技andGitHub 5a08a41cc8 Merge pull request #2105 from xinnan-tech/update_doc
update:增加移动端贡献者信息
2025-08-25 09:22:20 +08:00
hrz 3e48d7e331 update:增加移动端贡献者信息 2025-08-25 09:21:48 +08:00
Minamiyama 5993470d3f feat(模型管理): 添加模型副本创建功能
在模型编辑对话框中增加副本创建功能,当选择创建副本时自动在原模型名称和代码后添加'_副本'后缀,并通过新增API接口保存为新模型
2025-08-24 23:40:54 +08:00
Minamiyama 18acec1a81 refactor(audio): 将流式音频处理逻辑提取为独立类
将原本内联在test_page.html中的流式音频处理逻辑重构为独立的StreamingContext类,提高代码可维护性和复用性
2025-08-24 11:31:30 +08:00
欣南科技andGitHub 701936326d Merge pull request #2100 from xinnan-tech/py_fix_audio
update:显示天气接口错误信息
2025-08-23 23:25:52 +08:00
hrz 5b7d613a35 update:显示天气接口错误信息 2025-08-23 23:25:19 +08:00
hrzandGitHub 0ce55ff7a4 Merge pull request #2099 from Minamiyama/refactor/audio-context-singleton
refactor(音频处理): 提取音频上下文创建逻辑到独立函数
2025-08-23 22:25:11 +08:00
欣南科技andGitHub 595edb6f80 Merge pull request #2094 from xinnan-tech/py_fix_audio
fix: 优化流控音频播放 wechat聊天模式错误STT消息发送
2025-08-23 22:23:59 +08:00
Minamiyama d4b75c5d99 refactor(音频处理): 提取音频上下文创建逻辑到独立函数
将重复的音频上下文创建逻辑提取到独立的getAudioContextInstance函数中,减少代码重复并统一音频上下文配置
2025-08-23 07:47:40 +08:00
Sakura-RanChen 1988bced60 fix: 优化流控音频播放 wechat聊天模式错误STT消息发送 2025-08-22 17:10:36 +08:00
hrzandGitHub 60f8dbf418 Merge pull request #2086 from xinnan-tech/ASRPerformanceTester
update:更新流式ASR首词等待时间测试工具
2025-08-21 11:58:31 +08:00
FAN-yeB a708382cfd update:更新流式ASR首词等待时间测试工具 2025-08-21 11:54:06 +08:00
hrzandGitHub 1d95e1420a Merge pull request #2080 from VanillaNahida/main
fix: 修复脚本获取不到ws和视觉分析接口地址的bug,更新README
2025-08-20 22:50:18 +08:00
hrzandGitHub e1bcff82e2 Merge pull request #2084 from xinnan-tech/py_fix_audio
Py fix audio
2025-08-20 16:28:54 +08:00
hrzandGitHub 216f1b701f Merge pull request #2082 from xinnan-tech/fix_mobile
fix:修复mcp地址显示慢,增加服务端地址校验
2025-08-20 16:27:06 +08:00
Sakura-RanChen 0ea18d87a3 fix: asr识别缺字现象,去除无用的变量 2025-08-20 16:22:32 +08:00
hrzandGitHub 6a79d57a93 Update docker-setup.sh 2025-08-20 14:42:46 +08:00
hrzandGitHub 31f596f96c Update docker-setup.sh
全模块部署,OTA地址是在8002端口
2025-08-20 14:41:22 +08:00
hrzandGitHub 7f99895116 Update Deployment_all.md 2025-08-20 14:38:51 +08:00
rainv123 789d756ce1 fix:修复mcp地址显示慢,增加服务端地址校验 2025-08-20 14:36:42 +08:00
hrzandGitHub 570fb29d3c Merge pull request #2081 from xinnan-tech/performance_tester
update:更新流式TTS首词耗时测试工具
2025-08-20 14:36:36 +08:00
FAN-yeB d7dc636d3f update:更新流式TTS测试工具 2025-08-20 14:30:47 +08:00
香草味的纳西妲喵andGitHub 67d3a8f94f Update Deployment_all.md 2025-08-20 14:07:46 +08:00
VanillaNahida 7d7b8ddfb1 fix: 修复脚本获取不到ws和视觉分析接口地址的bug
docs: 更新README说明
2025-08-20 14:00:53 +08:00
Sakura-RanChen 74826c1c59 fix 2025-08-20 09:30:43 +08:00
hrzandGitHub d138e1dcef Merge pull request #2073 from xinnan-tech/web_fix_DeviceManagement
fix:修复设备管理页面全选功能失效问题
2025-08-19 17:24:43 +08:00
3030332422 372dc8ecb0 fix:修复设备管理页面全选功能失效问题 2025-08-19 14:45:14 +08:00
欣南科技andGitHub e8bfe9850e Merge pull request #2071 from xinnan-tech/fix-ota
fix: #2067 #2070 OTA和流控的音频bug
2025-08-19 00:20:46 +08:00
hrz ad50fbe3c9 fix: #2067 #2070 OTA和流控的音频bug 2025-08-19 00:19:41 +08:00
hrzandGitHub 61636dd085 Merge pull request #2068 from rainv123/fix-mobile-server
update:修复切换服务端地址无法生效,登陆界面右上角添加可跳转至设置页的按钮
2025-08-18 16:38:39 +08:00
rainv123andGitHub 5ea669be5a Update 删除无用代码行 2025-08-18 15:56:31 +08:00
rainv123 5260b62287 update:修复切换服务端地址无法生效,登陆界面右上角添加可跳转至设置页的按钮 2025-08-18 14:54:54 +08:00
欣南科技andGitHub 984a84edc1 Merge pull request #2061 from xinnan-tech/llm-tester
update:纠正教程错误描述
2025-08-17 14:15:31 +08:00
hrz 32548878e4 update:纠正教程错误描述 2025-08-17 14:14:25 +08:00
欣南科技andGitHub 093db56c98 Merge pull request #2058 from xinnan-tech/llm-tester
Llm tester
2025-08-17 11:13:47 +08:00
hrz a17f8518d6 update:优化推荐配置 2025-08-17 11:13:04 +08:00
hrz e9e9c5355d update:增加性能测试报告链接 2025-08-17 10:44:13 +08:00
hrz 61ee21d627 update:给LLM性能测试工具增加系统提示词,更符合本项目的实际效果 2025-08-17 09:35:06 +08:00
欣南科技andGitHub a86707f1ff Merge pull request #2056 from xinnan-tech/build-apk
update:回滚取消自动编译apk
2025-08-16 19:58:06 +08:00
hrz be42d80061 update:回滚取消自动编译apk 2025-08-16 19:57:34 +08:00
欣南科技andGitHub 16da6f9696 Merge pull request #2054 from xinnan-tech/dev-ops
update:优化自动部署脚本
2025-08-16 16:36:26 +08:00
hrz 427b0aa197 update:优化自动部署脚本 2025-08-16 16:35:26 +08:00
欣南科技andGitHub a2432d45cc Merge pull request #2052 from xinnan-tech/hot-fix
Hot fix
2025-08-16 15:07:53 +08:00
hrz f62b5d4f22 update:优化常见问题链接 2025-08-16 15:07:13 +08:00
hrz 001225c27b update:优化常见问题 2025-08-16 15:03:02 +08:00
欣南科技andGitHub 3e68f188fa Merge pull request #2051 from xinnan-tech/hot-fix
update:引导测试页面启动
2025-08-16 14:37:04 +08:00
hrz 2d75eb76f1 update:引导测试页面启动 2025-08-16 14:36:23 +08:00
欣南科技andGitHub bcc3ebb6bb Merge pull request #2050 from xinnan-tech/hot-fix
update:增加移动版智控台的说明
2025-08-16 11:39:04 +08:00
hrz 52c4e65686 update:增加移动版智控台的说明 2025-08-16 11:37:23 +08:00
欣南科技andGitHub 44a5afb8a2 Merge pull request #2049 from xinnan-tech/hot-fix
update:优化常见问题,梳理链接教程
2025-08-16 10:34:26 +08:00
hrz 3057d35111 update:优化常见问题,梳理链接教程 2025-08-16 10:33:28 +08:00
欣南科技andGitHub bdf99bc554 Merge pull request #2048 from xinnan-tech/hot-fix
update:优化测试工具目录结构
2025-08-16 10:10:17 +08:00
hrz e70415690c update:优化测试工具目录结构 2025-08-16 10:09:36 +08:00
hrzandGitHub 9b5e4eb9b7 Merge pull request #2039 from xinnan-tech/py_voiceprint_add_health
update:优化声纹识别,添加服务器健康检查机制避免运行时超时等待
2025-08-15 18:27:44 +08:00
hrzandGitHub e2324d3c37 Merge pull request #2044 from xinnan-tech/py_test_audio
Py test audio
2025-08-15 17:59:21 +08:00
hrzandGitHub c395c31e46 Merge pull request #2046 from xinnan-tech/xiaozhi-sercer-testPage
修复测试页面 打断对话多次可能导致无法发出声音的bug
2025-08-15 17:58:48 +08:00
JianYu Zheng 94662e17ba 优化:设置播放最短是0.3秒 2025-08-15 17:33:02 +08:00
3030332422 0bfa5cf723 update:feat: 为声纹识别添加缓存机制 2025-08-15 16:58:56 +08:00
Sakura-RanChen d2f29f335e update: 音频流式优化 2025-08-15 16:33:46 +08:00
JianYu Zheng 4423cb85c2 优化:优化播放逻辑去除多个方法共享属性导致的逻辑错误 2025-08-15 11:26:14 +08:00
CGDandGitHub 46fb997b52 Merge pull request #2035 from xinnan-tech/performance_text
update:更新各组件性能测试功能
2025-08-14 17:58:37 +08:00
3030332422 dcf8c134a2 update:优化声纹识别,添加服务器健康检查机制避免运行时超时等待 2025-08-14 17:57:46 +08:00
JianYu Zheng 7c0df908e9 优化:添加阻塞队列,优化缓存禁止 2025-08-14 14:44:52 +08:00
FAN-yeB 7bb76d7ced 更新 performance_test_tool.py 2025-08-14 10:39:53 +08:00
FAN-yeB 1cfb8de3a0 up:修改性能测试工具读取配置的目录 2025-08-14 10:31:31 +08:00
FAN-yeB 768d2b82d6 update:更新性能测试工具 2025-08-14 10:01:43 +08:00
rainv123andGitHub 7a4431d80c Merge pull request #2031 from xinnan-tech/update-paddlespeech
Update: paddlespeech配置
2025-08-13 16:02:44 +08:00
FAN-yeB 7e63e1d607 update:更新paddlespeech.py 2025-08-13 16:00:33 +08:00
FAN-yeB bda6c1e3ed update:paddlespeech配置 2025-08-13 15:59:48 +08:00
JianYu Zheng a9d116df2f 优化:拆分test_page.html一部分代码 2025-08-13 14:25:04 +08:00
欣南科技andGitHub e7c6c32d40 Merge pull request #2025 from xinnan-tech/openrz-patch-1
Update build-apk.yml
2025-08-12 13:55:04 +08:00
hrzandGitHub cdfe24fcdb Update build-apk.yml 2025-08-12 13:54:47 +08:00
欣南科技andGitHub a9199171df Merge pull request #2024 from xinnan-tech/openrz-patch-1
update:修改版本号
2025-08-12 13:41:17 +08:00
hrz 76b9542507 update:修改版本号 2025-08-12 13:40:40 +08:00
欣南科技andGitHub d30b6a8dcf Merge pull request #2023 from xinnan-tech/openrz-patch-1
Update build-apk.yml
2025-08-12 13:35:45 +08:00
hrzandGitHub 76b5ad1c99 Update build-apk.yml 2025-08-12 13:35:26 +08:00
欣南科技andGitHub 7840e83ada Merge pull request #2022 from xinnan-tech/build-apk
update:优化编译
2025-08-12 13:33:17 +08:00
hrz c4ac9da3e5 update:优化编译 2025-08-12 13:32:27 +08:00
Sakura-RanChenandGitHub 311e5d5cfd Merge pull request #1988 from myifeng/async-handler
调整OPUS函数式处理,不再以数组方式收集再遍历
2025-08-12 10:14:43 +08:00
JianYu Zheng a1f6795d44 优化:删除test_page.html无用的代码 2025-08-12 10:06:41 +08:00
欣南科技andGitHub f8a30ce261 Merge pull request #2021 from xinnan-tech/build-apk
update:测试自动编译apk
2025-08-12 08:52:41 +08:00
hrz ccfd15bda0 update:测试自动编译apk 2025-08-12 08:50:31 +08:00
hrzandGitHub a9610e6ee7 Merge pull request #2016 from xinnan-tech/py_huoshanTTS_AddConfig
update: huoshanTTS add config
2025-08-11 21:46:18 +08:00
hrz 733b36b869 update:补充参数说明 2025-08-11 21:45:32 +08:00
欣南科技andGitHub 70c3f9f929 Merge pull request #2020 from xinnan-tech/hot-fix
fix: #2014  Invalid bound statement (not found)
2025-08-11 21:27:07 +08:00
hrz 210bda15b8 update:更新版本号 2025-08-11 21:24:43 +08:00
hrz 93be1b3443 fix: #2014 Invalid bound statement (not found) 2025-08-11 21:23:00 +08:00
欣南科技andGitHub 533a001aeb Merge pull request #2018 from xinnan-tech/feature/mobile-app
Feature/mobile app
2025-08-11 20:49:04 +08:00
hrz 0b57d78732 update:增加h5的启动方法 2025-08-11 20:47:39 +08:00
hrzandGitHub dce9d1a1e2 Merge pull request #2015 from huangjunsen0406/feature/mobile-app
fix: 修复移动端app json文件丢失
2025-08-11 20:39:35 +08:00
JianYu Zheng 1cff39cec4 优化:拆分test_page.html,把一部分opus和document操作的代码提取出来,减少单个文件大小 2025-08-11 18:04:01 +08:00
JianYu Zheng c7392dcf5a 优化:拆分test_page.html,把一部分document操作的代码提取出来,减少单个文件大小,且把logger.js和xiaoZhiConnect.js也提取出来 2025-08-11 17:44:01 +08:00
Sakura-RanChen 3eb39ebef2 update: huoshanTTS add config 2025-08-11 17:38:17 +08:00
Junsen 041d8d5643 feat: 更新README文档,删除不再使用的请求配置文件,添加TypeScript配置文件,优化设置页面逻辑 2025-08-11 17:26:52 +08:00
JianYu Zheng 1c19046442 优化:拆分test_page.html,把一部分小智连接的代码提取出来,减少单个文件大小 2025-08-11 17:14:00 +08:00
Junsen 3d696aaa64 chore: 更新.gitignore以允许跟踪manager-mobile中的env和json文件;修改LICENSE版权信息;添加环境配置文件;新增manifest.json和pages.json以支持移动应用结构 2025-08-11 16:39:53 +08:00
JianYu Zheng 76516336af 优化:拆分test_page.html,把日志函数提取出来,减少单个文件大小 2025-08-11 16:20:46 +08:00
Junsen fc18677409 chore(gitignore): 允许跟踪 package.json 文件 2025-08-11 16:09:13 +08:00
Junsen 029d3730a8 chore(gitignore): allow tracking of package.json; add manager-mobile/package.json 2025-08-11 16:06:54 +08:00
hrz 8509e62114 update:调整文件夹 2025-08-11 15:48:28 +08:00
hrzandGitHub 6e8f9ca22e Merge pull request #2005 from huangjunsen0406/feature/mobile-app
feat: add complete mobile app project initial commit
2025-08-11 15:42:08 +08:00
Junsen dbc1a515d2 feat: 添加自定义标签组件,更新聊天记录和设备管理页面,优化请求配置 2025-08-11 15:14:23 +08:00
hrzandJunsen 70221e7f62 fix:保存记忆时出错 2025-08-11 15:14:23 +08:00
Sakura-RanChenandJunsen 28f921c38d update: 文档示例补充 2025-08-11 15:14:23 +08:00
hrzandJunsen 30a773601f 升级版本 2025-08-11 15:14:23 +08:00
Sakura-RanChenandJunsen 6cddd5c704 update: 补充安装文档 2025-08-11 15:14:23 +08:00
JianYu Zheng 709bc12b1c 优化:拆分test_page.html,css提取出来,减少单个文件大小 2025-08-11 14:24:06 +08:00
欣南科技andGitHub 171892a894 Merge pull request #2012 from xinnan-tech/py_fix_memory
fix:保存记忆时出错
2025-08-11 14:16:12 +08:00
hrz 535ab315d9 fix:保存记忆时出错 2025-08-11 14:15:34 +08:00
hrzandGitHub de28247b7e Merge pull request #2009 from xinnan-tech/py_IndexStreamTTS
update: 文档示例补充
2025-08-10 17:23:46 +08:00
Sakura-RanChen 6186eb753a update: 文档示例补充 2025-08-09 14:59:02 +08:00
欣南科技andGitHub a18bc85a9b Merge pull request #2008 from xinnan-tech/openrz-patch-1
升级版本
2025-08-09 11:44:03 +08:00
hrzandGitHub 806e4c398d 升级版本 2025-08-09 11:43:33 +08:00
hrzandGitHub a4f1abfa42 Merge pull request #2007 from xinnan-tech/py_IndexStreamTTS
update: 补充安装文档
2025-08-08 22:44:35 +08:00
Sakura-RanChen f2efd9718e update: 补充安装文档 2025-08-08 18:09:56 +08:00
Junsen 031c56e30b feat: 将配置中的名称更改为动态获取的环境变量VITE_APP_TITLE 2025-08-08 17:35:35 +08:00
Junsen 994715d9eb style: 优化移动端页面样式,调整组件布局,增强用户体验 2025-08-08 17:35:35 +08:00
Junsen 90f7af7afa feat: 初始化移动端项目 2025-08-08 17:35:35 +08:00
欣南科技andGitHub 2f3c466833 Merge pull request #2004 from xinnan-tech/py_IndexStreamTTS
Py index stream tts
2025-08-08 17:33:14 +08:00
hrz d742bca205 update:添加indextts 流式语音合成 2025-08-08 17:32:41 +08:00
Sakura-RanChen 8e51d52155 update: 增加单模块IndexTTS 2025-08-08 16:55:17 +08:00
hrzandGitHub a1c4f72470 Merge pull request #2002 from xinnan-tech/manager-api-springdoc
修复api接口文档页面,接口文档和点击按钮的说明不一致,串标题的bug
2025-08-08 14:46:30 +08:00
欣南科技andGitHub 2c11bba25e Merge pull request #2003 from xinnan-tech/update-doc
update:添加新场景
2025-08-08 13:08:26 +08:00
hrz 42a5b4fae7 update:添加新场景 2025-08-08 13:07:21 +08:00
JianYu Zheng a61aedc906 修复api接口文档页面,接口文档和点击按钮的说明不一致,串标题的bug
--pom.xml
1.升级了springdoc 2.8.8, knife4j4.6 与 doc2.7.7版本存在兼容问题,按照官方建议升级到2.8.8 解决了此bug
2.升级了 commons-lang3 3.18.0,springdoc 2.8.8 依赖commons-lang低版本存在CVE-2025-48924漏洞,升级版本解决
2025-08-08 11:55:08 +08:00
FAN-yeBandGitHub 7b72d517ab Merge pull request #1997 from xinnan-tech/rainv123-patch-1
feat:paddlespeech tts服务实现
2025-08-08 11:08:22 +08:00
FAN-yeB 1c43295fbc Merge branch 'rainv123-patch-1' of https://github.com/xinnan-tech/xiaozhi-esp32-server into rainv123-patch-1 2025-08-08 11:05:39 +08:00
FAN-yeB 747c432ea3 update:paddlespeech tts服务配置教程文档 2025-08-08 11:00:31 +08:00
rainv123andGitHub 235bdba42a paddlespeech-deploy.md
PaddleSpeechTTS服务集成xiaozhi服务
2025-08-08 10:59:07 +08:00
hrzandGitHub b3f603732e Merge pull request #1915 from YourKnight1/main
调整PaddleSpeechTTS流式传输
2025-08-07 23:42:16 +08:00
rainv123andGitHub 6091eae7fc paddlespeech-deploy.md
本地部署xiaozhi调用paddlespeechTTS服务
2025-08-07 17:07:52 +08:00
rainv123andGitHub ef36ad1ec1 paddlespeech-deploy.md 2025-08-07 16:50:10 +08:00
rainv123andGitHub f832611d41 Create paddlespeech-deploy.md
PaddleSpeechTTS流式传输集成到xiaozhi服务
2025-08-07 16:19:06 +08:00
Chingfeng Li 4d50eeb830 修正self错误 2025-08-06 14:53:59 +08:00
Chingfeng Li abf1fa0ef4 调整子类 2025-08-06 11:34:57 +08:00
Chingfeng Li 903c0e51ee 修正剩余文本 2025-08-06 10:13:42 +08:00
Chingfeng Li ce6a711c8e 调整流控参数优化 2025-08-06 10:09:28 +08:00
Chingfeng Li 095fe72db0 待完善流控方式 2025-08-05 18:35:37 +08:00
Chingfeng Li 1b6cd01eaf 增加流式处理方法 2025-08-05 14:53:52 +08:00
Chingfeng Li ac1e19f621 调整流式处理opus 2025-08-05 14:10:55 +08:00
Chingfeng Li faf2890695 增加流式处理 2025-08-04 17:04:46 +08:00
Chingfeng Li 75f0773247 增加流式处理Opus 2025-08-04 14:00:41 +08:00
Chingfeng Li 8b81b918cc 移除未使用的total_duration返回值 2025-08-04 13:49:12 +08:00
欣南科技andGitHub 5fe91a80fa Merge pull request #1976 from xinnan-tech/docker-install
Docker install
2025-08-03 16:20:49 +08:00
hrz 6f3dee74be update:优化自动docker全模块懒人脚本说明 2025-08-03 16:09:32 +08:00
hrzandGitHub 9391e8cc1a Merge pull request #1949 from VanillaNahida/main
refact: 重写了Docker全量部署的脚本的实现,支持一键安装一键配置一键更新镜像
2025-08-02 08:56:19 +08:00
hrzandGitHub 8aa5537029 Merge pull request #1963 from myifeng/fix-tts-message
移除无用的TTS消息
2025-08-02 08:55:12 +08:00
Chingfeng Li f40c3ec0f6 恢复sentence_start判断 2025-07-31 16:59:52 +08:00
hrzandGitHub 408143e55e Merge pull request #1962 from zebbingo/main
fix: bug state.type -> state['type']
2025-07-31 16:30:13 +08:00
Chingfeng Li 71eef4693d 仅在首句发送TTS消息 2025-07-31 14:15:39 +08:00
Han WuandGitHub 0b7814882d Merge pull request #1 from zebbingo/codex/find-and-fix-important-codebase-bug
Fix bug in hass_set_state plugin
2025-07-31 13:50:14 +08:00
Han Wu 99cf26ee9e Fix Home Assistant state handler 2025-07-31 13:38:46 +08:00
hrzandGitHub 775f754ff3 Merge pull request #1953 from yaotutu/feature/sherpa-paraformer-support
feat: 添加 Sherpa-ONNX Paraformer 模型支持
2025-07-31 13:35:03 +08:00
hrzandGitHub b6ee2dee68 Delete docs/sherpa-paraformer-guide.md 2025-07-31 13:34:17 +08:00
Chingfeng Li ef25e82544 移除无用的tts消息sentence_end 2025-07-31 13:21:29 +08:00
hrzandGitHub 14b7631dd6 Merge pull request #1958 from pursue-wind/fix_qwen_functioncall_err
fix: 修复qwen模型返回toolcall的arguments为None时导致后续请求400
2025-07-30 20:31:36 +08:00
hrzandGitHub ef0099b3c9 Update connection.py 2025-07-30 20:31:17 +08:00
VanillaNahida 38d60affce Docker安装镜像改为国内镜像源 2025-07-30 18:55:54 +08:00
chan c8a2c9bbd4 fix: 修复qwen模型返回toolcall的arguments为None时导致后续请求400 2025-07-30 18:53:48 +08:00
hrzandGitHub 9c2084b62e Merge pull request #1955 from xinnan-tech/py_fix_time
fix: 时间被固定
2025-07-30 17:33:01 +08:00
yaotutu 9edd083411 feat: 添加 Sherpa-ONNX Paraformer 模型支持
- 在 sherpa_onnx_local.py 中添加 model_type 参数,支持 paraformer 和 sense_voice 两种模型类型
- 在 config.yaml 中添加 SherpaParaformerASR 配置示例
- 添加详细的 Paraformer 使用文档 (docs/sherpa-paraformer-guide.md)
- 保持向后兼容,默认使用 sense_voice 模型

这个改动允许用户在低性能设备(如 RK3566)上使用更轻量的 Paraformer 模型,
相比 SenseVoice (894MB),Paraformer-small (78MB) 可以提供 4-6 倍的识别速度提升。
2025-07-30 10:41:27 +08:00
VanillaNahida 80e8ecc4f4 refact: 重写了Docker全量部署的脚本的实现,支持一键安装一键配置一键更新镜像 2025-07-30 03:00:37 +08:00
Sakura-RanChen e5d3048fb2 fix: 时间被固定 2025-07-29 09:55:32 +08:00
hrzandGitHub 67c4622ca7 Merge pull request #1935 from myifeng/aliyun-internal
如果配置的是ECS内网地址,则使用ws协议,默认是wss协议
2025-07-29 08:59:34 +08:00
欣南科技andGitHub 121f1c4698 Merge pull request #1936 from xinnan-tech/hot-fix
update:优化最后活动时间
2025-07-28 22:40:21 +08:00
hrz ff9fb9eb1b update:优化最后活动时间 2025-07-28 22:38:48 +08:00
Chingfeng Li b07a8796ff 阿里云ECS内网访问协议设置 2025-07-28 17:06:53 +08:00
hrzandGitHub 8ad5ff457e Merge pull request #1932 from xingxinonline/fix/manager-api-voice-print
fix: 增加时间排序,修复增加声纹注册获取对话记录不是最新对话的问题
2025-07-28 15:14:04 +08:00
gitjianyuandGitHub 99f6209c57 Update AgentChatHistoryServiceImpl.java
添加降序排序,
1.改用id的原因:数据形式,id越大的创建时间就越晚,所以使用id的结果和创建时间降序排列结果一样
 2.id作为降序排列的优势,性能高,有主键索引,不用在排序的时候重新进行排除扫描比较
2025-07-28 14:12:42 +08:00
gitjianyuandGitHub 1b3a55b105 Update VoicePrintDialog.vue
删除监听智能体id的,从原先的打开声纹页面获取一次对话,改为每次打开增加修改页面,从新获取一次新的对话信息
2025-07-28 13:58:06 +08:00
醒心onlineandGitHub 8b59a94324 Merge branch 'xinnan-tech:main' into fix/manager-api-voice-print 2025-07-28 12:01:41 +08:00
xingxinonline b17f20eece feat: 在对话框可见性变化时获取最近的50条记录 2025-07-28 11:51:45 +08:00
xingxinonline d9062a0bb0 fix: 增加时间排序,修复增加声纹注册获取对话记录不是最新对话的问题
https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/1922
2025-07-28 11:20:39 +08:00
欣南科技andGitHub 6066c20676 Merge pull request #1923 from xinnan-tech/update-version
update:升级版本号
2025-07-26 11:47:15 +08:00
hrz 1c7ba50def update:升级版本号 2025-07-26 11:46:11 +08:00
hrzandGitHub e53b24ef47 Merge pull request #1901 from xinnan-tech/py_test_tts
Py test tts
2025-07-25 22:17:59 +08:00
hrz 1a978abcc1 update: MinimaxTTSHTTPStream和MinimaxTTSWebSocketStream还在测试,测试完再开放 2025-07-25 22:11:30 +08:00
hrzandGitHub b91f4e4281 Merge pull request #1911 from xinnan-tech/manager-api-agent-optimize
优化
2025-07-25 21:47:54 +08:00
hrz 86978329eb update:优化逻辑 2025-07-25 21:47:21 +08:00
欣南科技andGitHub 337ecf0efe Merge pull request #1918 from xinnan-tech/update-doc
update:调整位置
2025-07-25 11:38:06 +08:00
hrz 4c3eb90bfc update:调整位置 2025-07-25 11:37:29 +08:00
欣南科技andGitHub 018a0422b7 Merge pull request #1916 from xinnan-tech/update-doc
update:更新全模块关系图
2025-07-25 11:29:56 +08:00
hrz f18ac169fa update:更新全模块关系图 2025-07-25 11:29:23 +08:00
root 04746961f2 调整PaddleSpeechTTS流式传输 2025-07-25 10:09:04 +08:00
JianYu Zheng 3939c81044 添加:删除模型的验证
--ModelConfigServiceImpl.java 删除视觉模型时,如果引用了,就不能删除
2025-07-24 17:13:05 +08:00
JianYu Zheng fdbe5fa556 添加了修改模型配置验证
--ModelConfigServiceImpl.java 修改模型,jsonConfig 里包括llm,校验一下llm是否时在LLM模型里,并且这个模型的供应器类型只能是openai和ollama类型
2025-07-24 17:01:43 +08:00
hrz dff8b8ccec Merge branch 'main' into py_test_tts 2025-07-24 16:18:39 +08:00
JianYu Zheng 8d11b47241 添加了新请求接口,大语言模型和意图参数关系的选择判断
--model.js 添加新接口方法
--roleConfig.vue 添加智能体配置,当选择的LLM不是openai、ollama类型时,意图模型不能选择“无意图识别“
2025-07-24 16:17:27 +08:00
hrz 78e5c52932 Merge branch 'main' into py_test_tts 2025-07-24 15:15:44 +08:00
CGDandGitHub 46c7759718 Merge pull request #1909 from xinnan-tech/py_fix_emoji
fix: 补充None值判断
2025-07-24 15:09:14 +08:00
Sakura-RanChen 2f5e8c2019 fix: 补充None值判断 2025-07-24 15:07:35 +08:00
hrz 2508d3f965 update:获取nginx转发的ip地址 2025-07-24 14:38:56 +08:00
hrzandGitHub 3699d28dd0 Merge pull request #1893 from Hmmrrr/main
feat:添加minimax单向双向流式
2025-07-24 14:18:19 +08:00
JianYu Zheng be7146fa89 添加新接口,获取llm模型基础模型信息
--LlmModelBasicInfoDTO.java 新的llm模型基础模型信息返回对象
--ModelConfigService.java
--ModelConfigServiceImpl.java
--ModelController.java
定义,实现,添加了接口。一个获取获取LLM模型信息的:信息内容包括id,名称,type
2025-07-24 11:50:15 +08:00
CGDandGitHub 66f4ea0a84 Merge pull request #1905 from xinnan-tech/py_fix_emoji
fix: LLM携带空文本时错误判断
2025-07-24 09:33:19 +08:00
Sakura-RanChen 84ff897b46 fix: LLM携带空文本时错误判断 2025-07-24 09:31:44 +08:00
JianYu Zheng 29c7b2a920 修改:保存智能体配置方法
--AgentServiceImpl.java 保存智能体配置方法,添加`大语言模型和意图识别是否符合匹配`的参数验证
2025-07-23 15:59:22 +08:00
Sakura-RanChen 83ded8458a Merge branch 'py_test_tts' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_test_tts 2025-07-23 14:40:14 +08:00
Sakura-RanChen e93053d412 fix: 文本重复发送 2025-07-23 14:35:59 +08:00
欣南科技andGitHub 2370936dfd Merge pull request #1899 from xinnan-tech/manager-api-mcp
优化获取请求mcp工具方法
2025-07-23 14:15:44 +08:00
JianYu Zheng 06bd7aed36 优化获取请求mcp工具方法
--JsonRpcTwo.java 添加JSON-RPC2.0 格式规范对象
--XiaoZhiMcpJsonRpcJson.java 添加小智mcp JSON-RPC 2.0 请求内容json 常量
--McpJsonRpcRequest.java 删除之前的mcp JSON-RPC2.0 构造对象
--AgentMcpAccessPointServiceImpl.java
优化思路,每次发送请求都要构造2次对象和转换2次json请求内容,且每次内容都一样,现在把最终转成的json请求内容存储为常量,所有请求共用这些常量,减少每次请求构造和转换
2025-07-23 11:56:36 +08:00
欣南科技andGitHub ff560a317c Merge pull request #1894 from xinnan-tech/update-doc
update:添加阿里云流式推荐
2025-07-22 18:22:05 +08:00
hrz 6892befa15 update:添加阿里云流式推荐 2025-07-22 18:21:09 +08:00
Hmmrrr fadf18b7fc feat:添加minimax单向双向流式 2025-07-22 18:13:05 +08:00
hrzandGitHub d9b632c031 Merge pull request #1892 from xinnan-tech/py_fix_mcp
update:添加Home Assistant插件超时处理和错误日志
2025-07-22 17:43:33 +08:00
hrzandGitHub 35fd4d2770 Merge pull request #1890 from xinnan-tech/manager-api-mcp
修复mcp工具太多会获取不到内容的bug
2025-07-22 16:45:05 +08:00
3030332422 1b8963f311 update:添加Home Assistant插件超时处理和错误日志 2025-07-22 16:28:12 +08:00
欣南科技andGitHub 710218b5b5 Merge pull request #1716 from xinnan-tech/py_test_tts
update:添加阿里云流式asr
2025-07-22 13:27:41 +08:00
hrz 60cbe1571c update:修复cosyvoice非流式合成方法 2025-07-22 13:25:54 +08:00
hrzandGitHub 6740d6a723 Merge pull request #1885 from xinnan-tech/py_prompt_emoji
Py prompt emoji
2025-07-21 23:18:30 +08:00
hrzandGitHub eb14e50941 Merge pull request #1882 from pupa880/main
添加最简化配置时配置声纹的教程
2025-07-21 23:14:08 +08:00
hrzandGitHub 37ac778ff4 Update voiceprint-integration.md 2025-07-21 23:13:45 +08:00
Sakura-RanChen bc5586a077 update: HuoshanTTS服务器资源释放 2025-07-21 17:46:22 +08:00
JianYu Zheng eb7ac93e72 修复mcp工具太多会获取不到内容的bug
--WebSocketClientManager.java
1.构建websocket对象的时候,添加可以设置缓冲区大小
2.断开连接的日志添加断开原因的内容
--AgentMcpAccessPointServiceImpl.java
1.设置每个websocket的缓冲区为1m,bug原因:缓存区太小,数据量太大,导致直接断开连接
2025-07-21 16:07:56 +08:00
pupa bf8a8bddf1 添加最简化配置时配置声纹的教程 2025-07-21 14:13:54 +08:00
hrz d1badcb28c update:修改版本号 2025-07-21 13:29:17 +08:00
hrz f6e79e17b7 update:更正cosyvoice大模型节点 2025-07-21 12:03:53 +08:00
Sakura-RanChen eead126f7a update: 表情由llm发送,长文本进行约束 2025-07-21 09:30:23 +08:00
hrzandGitHub e5ef61dfd3 Merge pull request #1873 from xinnan-tech/py_test
修复声纹识别场景下的意图识别问题、日志文件的补充
2025-07-20 20:23:29 +08:00
hrz eb130aa57f Merge branch 'py_test_tts' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_test_tts 2025-07-20 00:29:25 +08:00
hrz c9b10494d8 update:优化配置文件 2025-07-20 00:29:00 +08:00
3030332422 b8136ac1dc fix:修复阿里云流式ASR音频传递问题 2025-07-19 18:17:21 +08:00
3030332422 6d5b53fcd5 fix: 修复阿里云流式ASR终止协议缺失问题 2025-07-19 12:16:12 +08:00
hrz 5cf8eb5e71 update:优化OpenaiASR、GroqASR配置 2025-07-19 01:03:36 +08:00
3030332422 f037f4c7de fix: 修复声纹识别场景下的意图识别问题 2025-07-18 11:34:14 +08:00
3030332422 618be6d108 update:日志文件的补充 2025-07-17 16:08:13 +08:00
hrz 7fb028205f Merge branch 'main' into py_test_tts
# Conflicts:
#	main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml
2025-07-17 14:32:50 +08:00
Sakura-RanChen a09e56bbfd Merge branch 'py_test_tts' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_test_tts 2025-07-17 11:43:49 +08:00
Sakura-RanChen c0c01a0285 数据集回滚 2025-07-17 11:42:51 +08:00
hrzandGitHub af0854a840 Merge pull request #1812 from xinnan-tech/openai-asr
OpenAI asr
2025-07-17 11:31:54 +08:00
hrzandGitHub f70652f165 Merge branch 'py_test_tts' into openai-asr 2025-07-17 11:31:44 +08:00
hrzandGitHub 7c440f47b6 Merge pull request #1852 from xinnan-tech/py_fix_DoubaoStreamASR
修复关于声纹识别的若干问题
2025-07-17 11:22:59 +08:00
hrzandGitHub 6967cc2293 Merge pull request #1855 from pursue-wind/fix_mcp_proxy_err
fix: 修复mcp-proxy命令不存在
2025-07-17 11:22:28 +08:00
hrzandGitHub 97ad1f6f22 Merge pull request #1856 from xinnan-tech/manager-api-mcp
修改智控台删除声纹,声纹服务不在线,无法删除的业务逻辑错误
2025-07-17 11:07:49 +08:00
JianYu Zheng f62f530f97 优化
--AgentVoicePrintServiceImpl.java
 1.重写了构造器引入系统配置线程池对象
 2.删除的时候异步处理删除声纹数据的方法,加快删除接口的返回
2025-07-17 10:49:51 +08:00
JianYu Zheng 815647ae02 补充日志
--AgentVoicePrintServiceImpl.java
2025-07-17 09:49:05 +08:00
JianYu Zheng ce1e3fb04d 修改业务逻辑
--AgentVoicePrintServiceImpl.java 删除声纹的时间,声纹服务不在线或者删除失败,都允许删除
2025-07-17 09:45:27 +08:00
chan 1a86828ae6 fix: 修复mcp-proxy命令不存在 2025-07-17 09:23:41 +08:00
3030332422 8831ea22b7 update:删去一些重复、不用的代码 2025-07-16 16:43:49 +08:00
3030332422 1a411da133 fix:修复FunASR多设备连接时声纹识别配置相互覆盖的问题 2025-07-16 15:42:21 +08:00
Sakura-RanChen e2a7534aa5 update: 优化相关处理 2025-07-16 15:03:53 +08:00
欣南科技andGitHub f8a92e1710 Merge pull request #1845 from xinnan-tech/manager-api-mcp
优化mcp错误提示
2025-07-15 18:36:45 +08:00
3030332422 953a232654 fix: 修复使用豆包流式ASR时,不能使用声纹识别功能的问题。 2025-07-15 18:13:01 +08:00
JianYu Zheng 4afa29554f 优化map错误提示
--AgentMcpAccessPointServiceImpl.java 优化提示
2025-07-15 15:44:43 +08:00
3030332422 dc78057823 fix:修复多模块部署时远程ASR声纹识别功能未初始化的问题 2025-07-15 14:52:56 +08:00
欣南科技andGitHub 53ca586847 Merge pull request #1826 from xinnan-tech/py_fix_log
fix:日志优化的补充
2025-07-15 14:40:54 +08:00
欣南科技andGitHub 4f7e96ed7d Merge pull request #1838 from xinnan-tech/update-doc
update:更新文档
2025-07-15 14:39:47 +08:00
hrz 0397ef3fad update:更新文档 2025-07-15 13:09:04 +08:00
欣南科技andGitHub aa38591d90 Merge pull request #1837 from xinnan-tech/update-doc
update:增加开发团队信息
2025-07-15 12:00:10 +08:00
hrz b87b82bec9 update:增加开发团队信息 2025-07-15 11:59:13 +08:00
hrzandGitHub 2e719e2ccc Merge pull request #1830 from hsycc/main
fix: 修复配置声纹识别地址为域名不带端口, 请求url错误的问题
2025-07-14 23:16:54 +08:00
hsycc 909b41174b fix: 修复配置声纹识别地址为域名不带端口, 请求url错误的问题 2025-07-14 20:13:48 +08:00
Sakura-RanChen 2c33ee5b32 update: 优化文本处理 2025-07-14 17:34:42 +08:00
3030332422 82beae0e59 fix:日志优化的补充 2025-07-14 07:28:05 +08:00
欣南科技andGitHub 7f979e2f52 Merge pull request #1820 from xinnan-tech/update-doc
update:补充声纹识别部署细节
2025-07-12 17:43:59 +08:00
hrz e94eb302a8 update:补充声纹识别部署细节 2025-07-12 17:43:27 +08:00
欣南科技andGitHub 8ffbb614f7 Merge pull request #1819 from xinnan-tech/update-doc
update:补充声纹服务启动的细节
2025-07-12 16:50:10 +08:00
hrz 7a589f4a7b update:补充声纹服务启动的细节 2025-07-12 16:49:32 +08:00
Sakura-RanChen 04ed5ed980 update: 同步非流式处理 10秒超时链接不复用 2025-07-11 18:04:00 +08:00
欣南科技andGitHub e37b094d1e Merge pull request #1813 from xinnan-tech/py_fix_log
Py fix log
2025-07-11 17:29:49 +08:00
欣南科技andGitHub a491ce96cb Merge branch 'main' into py_fix_log 2025-07-11 17:29:37 +08:00
hrz 7be79519b1 update:增加声纹识别演示视频 2025-07-11 17:23:13 +08:00
3030332422 47e4c0f190 fix:日志文件的优化 2025-07-11 16:44:50 +08:00
Sakura-RanChen 8e5d933745 update: 优化音频播放 文本发送 2025-07-11 16:36:47 +08:00
欣南科技andGitHub 6b24844172 Merge pull request #1810 from xinnan-tech/hot-fix
update:优化总结
2025-07-11 15:43:39 +08:00
hrz c6225f3ab8 update:优化总结 2025-07-11 15:42:46 +08:00
欣南科技andGitHub 60c4aa943b Merge pull request #1807 from xinnan-tech/hot-fix
add:声纹识别开启教程
2025-07-11 14:14:32 +08:00
hrz 81d0adf73c add:声纹识别开启教程 2025-07-11 14:13:39 +08:00
欣南科技andGitHub 7dc3929f0e Merge pull request #1806 from xinnan-tech/hot-fix
update:优化声纹识别有结果时内容的展示
2025-07-11 12:15:05 +08:00
hrz 09be3a4de3 update:优化声纹识别有结果时内容的展示 2025-07-11 12:14:25 +08:00
欣南科技andGitHub f7407e46c5 Merge pull request #1805 from xinnan-tech/hot-fix
update:修复二次声纹注册bug
2025-07-11 11:57:25 +08:00
hrz 8f685967d3 update:修复二次声纹注册bug 2025-07-11 11:49:53 +08:00
Sakura-RanChen 23cd453af8 update: aliyun双流改造 待优化长连接机制和文本生成反馈为空 2025-07-11 11:36:58 +08:00
欣南科技andGitHub 8f6dd22a6d Merge pull request #1804 from xinnan-tech/mangger-api-voice-print
声纹识别
2025-07-11 10:19:15 +08:00
hrz 108c87f395 update:优化提示词 2025-07-11 10:15:14 +08:00
hrz cd58b3a39a update:发行新版本 2025-07-11 09:14:34 +08:00
Sakura-RanChen 26a3b51162 fix: 补充文档 2025-07-10 16:07:06 +08:00
3030332422 2fbee33185 update:python对接声纹识别优化 2025-07-10 15:03:49 +08:00
hrz 6da3138814 Merge branch 'main' into py_test_tts 2025-07-10 13:47:53 +08:00
luruxian b2e6156bbb chore(db): 添加新的数据库变更集202507101201
添加新的数据库变更脚本202507101201.sql到变更日志中
2025-07-10 11:44:49 +08:00
luruxian fbd4a22e3e feat(asr): 添加OpenAI和Groq语音识别支持
新增GPT语音识别模型供应器和配置,实现语音转文本功能
更新相关文档说明,包含API申请步骤和使用注意事项
2025-07-10 11:41:20 +08:00
Sakura-RanChen dede73e9a5 fix: id一致 方法流程 2025-07-10 11:34:31 +08:00
JianYu Zheng f499040955 Merge remote-tracking branch 'origin/mangger-api-voice-print' into mangger-api-voice-print 2025-07-10 11:33:28 +08:00
JianYu Zheng 008c446966 添加判断
--AgentVoicePrintServiceImpl.java
 1.声纹音频保存,增加了保存前识别一下声纹是否有相似的声音,有则不保存
 2.声纹音频修改,增加了保存前识别一下声纹是否有相似的声音且相似声纹id和本次修改id不一样的,则不修改
 3.添加了识别声纹http的请求的方法
 --IdentifyVoicePrintResponse.java 识别声纹响应类型
2025-07-10 11:20:32 +08:00
Sakura-RanChen ce358dcd65 fix: 音频播放方法跟随主分支 2025-07-10 10:17:07 +08:00
hrz 01158e67e0 add:智控台返回声纹接口信息 2025-07-10 09:54:16 +08:00
Sakura-RanChen d533fb83eb fix: 插入报错修复,跟随主分支新增列 2025-07-10 09:52:54 +08:00
f4cfa04954 update: TTS复用链接,VAD双阈值判断 (#1742)
* update: TTS复用链接,VAD双阈值判断

* fix: 播放音乐时,引导词卡顿

* update:优化引导词

* update: 优化chat函数流程 优化huoshan处理 会话保持一致性

* fix: 等待时可能已经完成(设置为None),后续对None错误访问

* update:增加书名号

* fix: 打断状态未重置 监听未完成时服务端可能还在发送数据 此时复用链接会接收上个语音的残余 需要两者一同关闭

---------

Co-authored-by: hrz <1710360675@qq.com>
2025-07-09 18:34:04 +08:00
JianYu Zheng ae4387ce3f Merge remote-tracking branch 'origin/mangger-api-voice-print' into mangger-api-voice-print 2025-07-09 18:12:27 +08:00
JianYu Zheng a64b495083 增加判断
--AgentVoicePrintServiceImpl.java 添加,修改声纹音频的是增加了验证这个音频是否属于这个智能体
2025-07-09 17:31:34 +08:00
JianYu Zheng 81cdd0a211 添加新接口和2个新方法
--AgentChatHistoryService.java 添加‘根据音频数据ID获取聊天内容’和‘查询此音频id是否属于此智能体’的方法定义
--AgentChatHistoryServiceImpl.java 实现2个新方法
--AgentController.java 增加一个获取音频内容的接口
2025-07-09 17:29:36 +08:00
Sakura-RanChen 6bfb5f1340 Merge branch 'py_link_huoshan' of https://github.com/xinnan-tech/xiaozhi-esp32-server into py_link_huoshan 2025-07-09 17:05:02 +08:00
Sakura-RanChen 2625133f40 fix: 打断状态未重置 监听未完成时服务端可能还在发送数据 此时复用链接会接收上个语音的残余 需要两者一同关闭 2025-07-09 17:04:42 +08:00
hrzandGitHub 38409b4200 Merge pull request #1755 from xinnan-tech/py_test
update:python单模块部署声纹识别对接
2025-07-09 16:51:26 +08:00
hrzandGitHub 29b4d0daeb Merge branch 'mangger-api-voice-print' into py_test 2025-07-09 16:51:10 +08:00
hrz 8f4f9fe19a update:优化提示词 2025-07-09 16:30:10 +08:00
hrz 7f67459c6e update:增加书名号 2025-07-09 16:26:26 +08:00
JianYu Zheng f47a7b4dbc 修复添加和修改声纹选择的时候,编辑的声纹不在里面,导致无法显示内容的问题
--agent.js 增加查询音频内容的接口
--VoicePrintDialog.vue 修复添加和修改声纹选择的时候,编辑的声纹不在里面,导致无法显示内容的问题
2025-07-09 15:56:07 +08:00
hrz 0bf6926506 update:优化提示词 2025-07-09 15:35:25 +08:00
3030332422 d13fb73c67 update:优化 2025-07-09 14:23:47 +08:00
JianYu Zheng 5e18a46e3f 添加新功能,声纹选择的时候可以听取音频文件
--VoicePrintDialog.vue 添加听取音频的功能
2025-07-09 11:48:02 +08:00
Sakura-RanChen 62e39dd0e2 fix: 等待时可能已经完成(设置为None),后续对None错误访问 2025-07-09 11:18:26 +08:00
hrzandGitHub 203ea89c6d Merge pull request #1788 from xinnan-tech/py_intent_fix
fix: delete未定义的方法报错
2025-07-08 17:54:17 +08:00
Sakura-RanChen 9ddd967776 fix: delete未定义的方法报错 2025-07-08 17:52:51 +08:00
Sakura-RanChen 44593ff3f3 update: 优化chat函数流程 优化huoshan处理 会话保持一致性 2025-07-08 17:48:50 +08:00
JianYu Zheng 3ed13b3ff5 修复漏洞
--AgentChatHistoryServiceImpl.java 用户聊天记录不一定都带有音频,查询时要排除不带音频的最近50条聊天记录
--AgentChatHistoryService.java 修改方法描述
2025-07-08 17:32:44 +08:00
JianYu Zheng 53cfdcc240 Merge remote-tracking branch 'origin/mangger-api-voice-print' into mangger-api-voice-print 2025-07-08 17:15:10 +08:00
JianYu Zheng 0636b95b87 声纹表添加音频字段
--202507081646.sql 添加字段sql
--AgentVoicePrintEntity.java 对应实体更新
--db.changelog-master.yaml 执行添加字段sql的配置
2025-07-08 17:10:57 +08:00
hrzandGitHub cf449a9b60 Merge pull request #1787 from xinnan-tech/system-prompt
强化小智提示词
2025-07-08 17:00:46 +08:00
hrz ab890ada37 update:强化小智提示词 2025-07-08 16:59:38 +08:00
JianYu Zheng b9c136c6ae 修复错误
--VoicePrint.vue
 1.方法调用错误,修改方法名
 2.删除无用的多选框
2025-07-08 16:55:02 +08:00
JianYu Zheng f81a7e8273 智能体设置卡片添加设置声纹的按钮
--DeviceItem.vue
 1.添加声纹设置按钮
 2.添加跳转到声纹页面方法
--home.vue 修复因为添加新按钮导致卡片按钮文字换行样式问题
2025-07-08 16:18:29 +08:00
JianYu Zheng d40d4a08b0 添加声纹管理页面
--VoicePrint.vue 声纹展示页面
--VoicePrintDialog.vue 声纹增改组件
--index.js 注册声纹页面路径
2025-07-08 16:15:52 +08:00
JianYu Zheng 61907d2270 添加新接口方法
--agent.js 添加关于声纹和获取用户列表的接口
 1.添加智能体的声纹
 2.获取指定智能体声纹列表
 3.删除智能体声纹
 4.更新智能体声纹
 5.获取指定智能体用户类型聊天记录
2025-07-08 16:13:13 +08:00
JianYu Zheng 601a90981e 修复错误
--AgentVoicePrintServiceImpl.java
 1.修复事务处理过程出错,返回错误无法被前端页面处理
 2.修复更新声纹方法,更正错误sql
2025-07-08 15:48:20 +08:00
JianYu Zheng 10b7259b13 添加新接口:查看指定智能体最近50条用户聊天记录
--AgentChatHistoryService.java 指定智能体最近50条用户聊天记录定义
--AgentChatHistoryServiceImpl.javaAgentChatHistoryService.java 实现指定智能体最近50条用户聊天记录方法
--AgentChatHistoryType.java 添加智能体聊天记录类型枚举
--AgentChatHistoryUserVO.java 展示用户聊天记录vo
--AgentController.java 添加新接口
2025-07-08 15:44:43 +08:00
CGD 5c83d63fe2 update:声纹识别对接的优化 2025-07-08 11:25:54 +08:00
hrzandGitHub beb7a191c1 Merge pull request #1777 from jlau-ice/feat/add-password-visibility
feat: 登录注册等页面密码输入框添加可见属性
2025-07-07 21:29:10 +08:00
ganbin ee72a328ca feat: 登录注册等页面密码输入框添加可见属性 2025-07-07 18:50:03 +08:00
JianYu Zheng c85419bf06 添加添加声纹增删改查接口
--AgentVoicePrintController.java 添加接口
2025-07-07 15:33:16 +08:00
CGD 155ba92ba6 update:增加了智控台的sql语句,优化了部分配置 2025-07-07 15:29:13 +08:00
JianYu Zheng b201c3cca8 添加数据认证,需要登录用户是此数据所有者才有权限修改,查询,删除
--AgentVoicePrintService.java 修改方法定义
--AgentVoicePrintServiceImpl.java
 1.需要登录用户是此数据所有者才有权限修改,查询,删除
 2.修改发送注册声纹http请求和发送注销声纹的请求的错误说明和添加日志记录
2025-07-07 15:11:37 +08:00
hrzandGitHub c45f47faa0 Update:优化mcp工具读取顺序 (#1775)
* update:发布0.6.3版本

* update:升级sherpa_onnx版本

* update:补充自动更新教程

* update:优化mcp工具读取顺序

* update:优化
2025-07-07 15:04:32 +08:00
JianYu Zheng ee7ea7fca6 添加编程事务,保证数据的统一,提取公共方法
--AgentVoicePrintServiceImpl.java
 1.为保存,修复,删除声纹添加事务
 2.提取发送注销声纹的请求为公共方法
2025-07-07 14:12:28 +08:00
hrzandGitHub 5ee7830f1b Merge pull request #1770 from jlau-ice/fix/vllm-reply-in-English
fix: 修正VLLM本地调用,question参数为空会用英文回复的问题
2025-07-07 11:58:58 +08:00
hrz 8da9dc8a3f update:优化引导词 2025-07-07 11:45:37 +08:00
JianYu Zheng 61ecfd2d92 实现智能体声纹服务层定义的‘获取指定智能体的所有声纹数据’和‘更新智能体的指的声纹数据’定义(未测试)
--AgentVoicePrintServiceImpl.java 实现方法
 1.获取指定智能体的所有声纹数据
 2.更新智能体的指的声纹数据
 3.提取共用方法(获取声纹音频资源数据,发送注册声纹http请求)
2025-07-07 10:58:35 +08:00
ganbin 2535ff6c90 fix: 修正VLLM本地调用,question参数为空会用英文回复的问题 2025-07-07 09:02:59 +08:00
hrzandGitHub 880bc6326a update:补充自动更新教程 (#1766)
* update:发布0.6.3版本

* update:升级sherpa_onnx版本

* update:补充自动更新教程
2025-07-06 15:40:02 +08:00
JianYu Zheng 77527d8823 实现智能体声纹服务层定义的‘添加智能体新的声纹’和‘删除智能体的指的声纹’定义
--AgentVoicePrintServiceImpl.java 实现方法
 1.添加智能体新的声纹
 2.删除智能体的指的声纹
 3.共用方法(获取生纹接口URI对象,获取声纹地址基础路径,获取验证Authorization)
2025-07-04 18:04:39 +08:00
JianYu Zheng 5e5ec98995 添加智能体声纹实体,vo,dto,dao,service类
--AgentVoicePrintVO.java 智能体声纹展示vo
--AgentVoicePrintService.java 智能体声纹服务层定义
 1.添加智能体新的声纹
 2.删除智能体的指的声纹
 3.获取指定智能体的所有声纹数据
 4.更新智能体的指的声纹数据
--AgentVoicePrintSaveDTO.java 智能体声纹保存dto
--AgentVoicePrintUpdateDTO.java 智能体声纹修改dto
--AgentVoicePrintEntity.java 智能体声纹实体
--AgentVoicePrintDao.java 智能体声纹持久层
2025-07-04 18:02:19 +08:00
JianYu Zheng 1c2d4f9045 添加新表:智能体声纹表
--202507041018.sql 建表sql语句
--db.changelog-master.yaml 配置文件里添加执行新sql的配置
2025-07-04 17:54:41 +08:00
CGD 3d64152dba update:python单模块部署声纹识别对接 2025-07-04 17:11:44 +08:00
Sakura-RanChen 7e5a2b4549 fix: 播放音乐时,引导词卡顿 2025-07-04 11:27:49 +08:00
JianYu Zheng 036dde5bb0 添加新的参数:声纹接口地址
--202507031602.sql 添加声纹接口地址参数sql
--Constant.java 对应新参数的常量
--SysParamsController.java 验证声纹接口地址参数是否符合规则
--db.changelog-master.yaml 添加执行新的sql文件
2025-07-03 16:34:50 +08:00
Sakura-RanChen 69cac9d40a update: TTS复用链接,VAD双阈值判断 2025-07-03 09:16:06 +08:00
CGD 039badd265 update:添加了阿里云CosyVoice流式TTS 2025-07-01 18:14:36 +08:00
CGD 90a9631450 update:添加阿里云流式asr 2025-06-30 14:41:53 +08:00
292 changed files with 37609 additions and 3201 deletions
+9
View File
@@ -174,3 +174,12 @@ main/xiaozhi-server/mysql
uploadfile
*.json
.vscode
.cursor
!package.json
!**/package.json
# Do not ignore env and json files inside manager-mobile
!main/manager-mobile/**/env/
!main/manager-mobile/**/.env*
!main/manager-mobile/**/*.json
+1
View File
@@ -21,6 +21,7 @@ RUN apt-get update && \
# 从构建阶段复制Python包和前端构建产物
COPY --from=builder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
COPY --from=builder /usr/local/bin/mcp-proxy /usr/local/bin/mcp-proxy
# 复制应用代码
COPY main/xiaozhi-server .
+47 -21
View File
@@ -3,10 +3,10 @@
<h1 align="center">小智后端服务xiaozhi-esp32-server</h1>
<p align="center">
本项目为开源智能硬件项目
本项目基于人机共生智能理论和技术研发智能终端软硬件体系<br/>为开源智能硬件项目
<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a>提供后端服务<br/>
根据<a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">小智通信协议</a>使用Python、Java、Vue实现<br/>
帮助您快速搭建小智服务器
支持MCP接入点和声纹识别
</p>
<p align="center">
@@ -37,6 +37,14 @@
</a>
</p>
<p align="center">
Spearheaded by Professor Siyuan Liu's Team (South China University of Technology)
</br>
刘思源教授团队主导研发(华南理工大学)
</br>
<img src="./docs/images/hnlg.jpg" alt="华南理工大学" width="50%">
</p>
---
## 适用人群 👥
@@ -49,9 +57,9 @@
<table>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
<a href="https://www.bilibili.com/video/BV1C1tCzUEZh" target="_blank">
<picture>
<img alt="小智esp32连接自己的后台模型" src="docs/images/demo1.png" />
<img alt="小智医疗救护车场景" src="docs/images/demo1.png" />
</picture>
</a>
</td>
@@ -86,7 +94,7 @@
</tr>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1Vy96YCE3R" target="_blank">
<a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
<picture>
<img alt="自定义音色" src="docs/images/demo6.png" />
</picture>
@@ -144,13 +152,18 @@
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
<a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
<picture>
<img alt="MCP接入点" src="docs/images/demo13.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
<picture>
<img alt="声纹识别" src="docs/images/demo14.png" />
</picture>
</a>
</td>
</tr>
</table>
@@ -176,13 +189,15 @@
| 部署方式 | 特点 | 适用场景 | 部署文档 | 配置要求 | 视频教程 |
|---------|------|---------|---------|---------|---------|
| **最简化安装** | 智能对话、IOT、MCP、视觉感知 | 低配置环境,数据存储在配置文件,无需数据库 | [①Docker版](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②源码部署](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 如果使用`FunASR`要2核4G,如果全API,要2核2G | - |
| **全模块安装** | 智能对话、IOT、MCP接入点、视觉感知、OTA、智控台 | 完整功能体验,数据存储在数据库 |[①Docker版](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [②源码部署](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③源码部署自动更新教程](./docs/dev-ops-integration.md) | 如果使用`FunASR`要4核8G,如果全API,要2核4G| [本地源码启动视频教程](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
| **全模块安装** | 智能对话、IOT、MCP接入点、声纹识别、视觉感知、OTA、智控台 | 完整功能体验,数据存储在数据库 |[①Docker版](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [②源码部署](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③源码部署自动更新教程](./docs/dev-ops-integration.md) | 如果使用`FunASR`要4核8G,如果全API,要2核4G| [本地源码启动视频教程](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
常见问题及相关教程,可参考[这个链接](./docs/FAQ.md)
> 💡 提示:以下是按最新代码部署后的测试平台,有需要可烧录测试,并发为6个,每天会清空数据
> 💡 提示:以下是按最新代码部署后的测试平台,有需要可烧录测试,并发为6个,每天会清空数据
```
智控台地址: https://2662r3426b.vicp.fun
智控台(h5版): https://2662r3426b.vicp.fun/h5/index.html
服务测试工具: https://2662r3426b.vicp.fun/test/
OTA接口地址: https://2662r3426b.vicp.fun/xiaozhi/ota/
@@ -201,35 +216,37 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| 模块名称 | 入门全免费设置 | 流式配置 |
|:---:|:---:|:---:|
| ASR(语音识别) | FunASR(本地) | 👍FunASRServer 或 👍DoubaoStreamASR |
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍DoubaoLLM(火山doubao-1-5-pro-32k-250115) |
| ASR(语音识别) | FunASR(本地) | 👍FunASR(本地GPU模式) |
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍AliLLM(qwen3-235b-a22b-instruct-2507) 或 👍DoubaoLLM(doubao-1-5-pro-32k-250115) |
| VLLM(视觉大模型) | ChatGLMVLLM(智谱glm-4v-flash) | 👍QwenVLVLLM(千问qwen2.5-vl-3b-instructh) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) 或 👍AliyunStreamTTS(阿里云流式语音合成) |
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
如果您关心各组件的耗时,请查阅[小智各组件性能测试报告](https://github.com/xinnan-tech/xiaozhi-performance-research),可按报告中的测试方法在您的环境中实际测试。
#### 🔧 测试工具
本项目提供以下测试工具,帮助您验证系统和选择合适的模型:
| 工具名称 | 位置 | 使用方法 | 功能说明 |
|:---:|:---|:---:|:---:|
| 音频交互测试工具 | main》xiaozhi-server》test》test_page.html | 使用谷歌浏览器直接打开 | 测试音频播放和接收功能,验证Python端音频处理是否正常 |
| 模型响应测试工具1 | main》xiaozhi-server》performance_tester.py | 执行 `python performance_tester.py` | 测试ASR(语音识别)、LLM(大模型)、TTS(语音合成)三个核心模块的响应速度 |
| 模型响应测试工具2 | main》xiaozhi-server》performance_tester_vllm.py | 执行 `python performance_tester_vllm.py` | 测试VLLM(视觉模型)的响应速度 |
| 模型响应测试工具 | main》xiaozhi-server》performance_tester.py | 执行 `python performance_tester.py` | 测试ASR(语音识别)、LLM(大模型)、VLLM(视觉模型)、TTS(语音合成)三个核心模块的响应速度 |
> 💡 提示:测试模型速度时,只会测试配置了密钥的模型。
---
## 功能清单 ✨
### 已实现 ✅
![请参考-全模块安装架构图](docs/images/deploy2.png)
| 功能模块 | 描述 |
|:---:|:---|
| 核心服务架构 | 基于WebSocket和HTTP服务器,提供完整的控制台管理和认证系统 |
| 语音交互系统 | 支持流式ASR(语音识别)、流式TTS(语音合成)、VAD(语音活动检测),支持多语言识别和语音处理 |
| 智能对话系统 | 支持多种LLM(大语言模型),实现智能对话 |
| 视觉感知系统 | 支持多种VLLM(视觉大模型),实现多模态交互 |
| 意图识别系统 | 支持LLM意图识别、Function Call函数调用,提供插件化意图处理机制 |
| 核心架构 | 基于WebSocket和HTTP服务器,提供完整的控制台管理和认证系统 |
| 语音交互 | 支持流式ASR(语音识别)、流式TTS(语音合成)、VAD(语音活动检测),支持多语言识别和语音处理 |
| 声纹识别 | 支持多用户声纹注册、管理和识别,与ASR并行处理,实时识别说话人身份并传递给LLM进行个性化回应 |
| 智能对话 | 支持多种LLM(大语言模型),实现智能对话 |
| 视觉感知 | 支持多种VLLM(视觉大模型),实现多模态交互 |
| 意图识别 | 支持LLM意图识别、Function Call函数调用,提供插件化意图处理机制 |
| 记忆系统 | 支持本地短期记忆、mem0ai接口记忆,具备记忆总结功能 |
| 工具调用 | 支持客户端IOT协议、客户MCP协议、服务端MCP协议、MCP接入点协议、自定义工具函数 |
| 管理后台 | 提供Web管理界面,支持用户管理、系统配置和设备管理 |
@@ -239,7 +256,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
### 正在开发 🚧
想了解具体开发计划进度,[请点击这里](https://github.com/users/xinnan-tech/projects/3)
想了解具体开发计划进度,[请点击这里](https://github.com/users/xinnan-tech/projects/3)。常见问题及相关教程,可参考[这个链接](./docs/FAQ.md)
如果你是一名软件开发者,这里有一份[《致开发者的公开信》](docs/contributor_open_letter.md),欢迎加入!
@@ -257,7 +274,6 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
---
## 本项目支持的平台/组件列表 📋
### LLM 语言模型
| 使用方式 | 支持平台 | 免费平台 |
@@ -308,6 +324,14 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
---
### Voiceprint 声纹识别
| 使用方式 | 支持平台 | 免费平台 |
|:---:|:---:|:---:|
| 本地使用 | 3D-Speaker | 3D-Speaker |
---
### Memory 记忆存储
| 类型 | 平台名称 | 使用方式 | 收费模式 | 备注 |
@@ -333,8 +357,10 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| <img src="./docs/images/logo_bailing.png" width="160"> | [百聆语音对话机器人](https://github.com/wwbin2017/bailing) | 本项目受[百聆语音对话机器人](https://github.com/wwbin2017/bailing)启发,并在其基础上实现 |
| <img src="./docs/images/logo_tenclass.png" width="160"> | [十方融海](https://www.tenclass.com/) | 感谢[十方融海](https://www.tenclass.com/)为小智生态制定了标准的通讯协议、多设备兼容性方案及高并发场景实践示范;为本项目提供了全链路技术文档支持 |
| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [玄凤科技](https://github.com/Eric0308) | 感谢[玄凤科技](https://github.com/Eric0308)贡献函数调用框架、MCP通信协议及插件化调用机制的实现代码,通过标准化的指令调度体系与动态扩展能力,显著提升了前端设备(IoT)的交互效率和功能延展性 |
| <img src="./docs/images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | 感谢[huangjunsen](https://github.com/huangjunsen0406) 贡献`智控台移动端`模块,实现了跨平台移动设备的高效控制与实时交互,大幅提升了系统在移动场景下的操作便捷性和管理效率 |
| <img src="./docs/images/logo_huiyuan.png" width="160"> | [汇远设计](http://ui.kwd988.net/) | 感谢[汇远设计](http://ui.kwd988.net/)为本项目提供专业视觉解决方案,用其服务超千家企业的设计实战经验,赋能本项目产品用户体验 |
| <img src="./docs/images/logo_qinren.png" width="160"> | [西安勤人信息科技](https://www.029app.com/) | 感谢[西安勤人信息科技](https://www.029app.com/)深化本项目视觉体系,确保整体设计风格在多场景应用中的一致性和扩展性 |
| <img src="./docs/images/logo_contributors.png" width="160"> | [代码贡献者](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | 感谢[所有代码贡献者](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors)贡献者,你们的付出让项目更加健壮和强大。 |
<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+131 -73
View File
@@ -3,17 +3,17 @@
<h1 align="center">Xiaozhi Backend Service xiaozhi-esp32-server</h1>
<p align="center">
This project provides backend services for the open-source smart hardware project
This project is based on human-machine symbiotic intelligence theory and technology to develop intelligent terminal hardware and software systems<br/>providing backend services for the open-source intelligent hardware project
<a href="https://github.com/78/xiaozhi-esp32">xiaozhi-esp32</a><br/>
Implemented using Python, Java, and Vue according to the <a href="https://ccnphfhqs21z.feishu.cn/wiki/M0XiwldO9iJwHikpXD5cEx71nKh">Xiaozhi Communication Protocol</a><br/>
Helps you quickly set up your Xiaozhi server
Supports MCP endpoints and voiceprint recognition
</p>
<p align="center">
<a href="./README.md">中文</a>
· <a href="./docs/FAQ.md">FAQ</a>
· <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/issues">Report Issues</a>
· <a href="./README_en.md#deployment-documentation">Deployment Guide</a>
· <a href="./README.md#%E9%83%A8%E7%BD%B2%E6%96%87%E6%A1%A3">Deployment Docs</a>
· <a href="https://github.com/xinnan-tech/xiaozhi-esp32-server/releases">Release Notes</a>
</p>
<p align="center">
@@ -37,41 +37,49 @@ Helps you quickly set up your Xiaozhi server
</a>
</p>
<p align="center">
Spearheaded by Professor Siyuan Liu's Team (South China University of Technology)
</br>
刘思源教授团队主导研发(华南理工大学)
</br>
<img src="./docs/images/hnlg.jpg" alt="South China University of Technology" width="50%">
</p>
---
## Target Users 👥
This project requires ESP32 hardware devices. If you have purchased ESP32-related hardware, successfully connected to Brother Xia's backend service, and want to set up your own `xiaozhi-esp32` backend service, then this project is perfect for you.
This project requires ESP32 hardware devices to work. If you have purchased ESP32-related hardware, successfully connected to Brother Xia's deployed backend service, and want to build your own `xiaozhi-esp32` backend service independently, then this project is perfect for you.
Want to see it in action? Check out these videos 🎥
Want to see the usage effects? Click the videos below 🎥
<table>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1FMFyejExX" target="_blank">
<picture>
<img alt="Xiaozhi esp32 connecting to own backend model" src="docs/images/demo1.png" />
<img alt="Xiaozhi ESP32 connecting to own backend model" src="docs/images/demo1.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1CDKWemEU6" target="_blank">
<picture>
<img alt="Custom voice" src="docs/images/demo2.png" />
<img alt="Custom voice timbre" src="docs/images/demo2.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV12yA2egEaC" target="_blank">
<picture>
<img alt="Using Cantonese" src="docs/images/demo3.png" />
<img alt="Using Cantonese for communication" src="docs/images/demo3.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1pNXWYGEx1" target="_blank">
<picture>
<img alt="Control home appliances" src="docs/images/demo5.png" />
<img alt="Controlling home appliances" src="docs/images/demo5.png" />
</picture>
</a>
</td>
@@ -85,16 +93,16 @@ Want to see it in action? Check out these videos 🎥
</tr>
<tr>
<td>
<a href="https://www.bilibili.com/video/BV1Vy96YCE3R" target="_blank">
<a href="https://www.bilibili.com/video/BV1vchQzaEse" target="_blank">
<picture>
<img alt="Custom voice" src="docs/images/demo6.png" />
<img alt="Custom voice timbre" src="docs/images/demo6.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1VC96Y5EMH" target="_blank">
<picture>
<img alt="Play music" src="docs/images/demo7.png" />
<img alt="Playing music" src="docs/images/demo7.png" />
</picture>
</a>
</td>
@@ -108,14 +116,14 @@ Want to see it in action? Check out these videos 🎥
<td>
<a href="https://www.bilibili.com/video/BV178XuYfEpi" target="_blank">
<picture>
<img alt="IOT command control" src="docs/images/demo9.png" />
<img alt="IOT command control devices" src="docs/images/demo9.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV17LXWYvENb" target="_blank">
<picture>
<img alt="News broadcast" src="docs/images/demo0.png" />
<img alt="News broadcasting" src="docs/images/demo0.png" />
</picture>
</a>
</td>
@@ -131,7 +139,7 @@ Want to see it in action? Check out these videos 🎥
<td>
<a href="https://www.bilibili.com/video/BV1Co76z7EvK" target="_blank">
<picture>
<img alt="Photo recognition" src="docs/images/demo12.png" />
<img alt="Photo recognition of objects" src="docs/images/demo12.png" />
</picture>
</a>
</td>
@@ -143,20 +151,29 @@ Want to see it in action? Check out these videos 🎥
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1ZQKUzYExM" target="_blank">
<picture>
<img alt="MCP endpoint" src="docs/images/demo13.png" />
</picture>
</a>
</td>
<td>
<a href="https://www.bilibili.com/video/BV1Exu3zqEDe" target="_blank">
<picture>
<img alt="Voiceprint recognition" src="docs/images/demo14.png" />
</picture>
</a>
</td>
</tr>
</table>
---
## Warning ⚠️
## Warnings ⚠️
1. This project is open-source software. This software has no commercial relationship with any third-party API service providers (including but not limited to speech recognition, large models, speech synthesis, and other platforms) and does not provide any form of guarantee for their service quality or financial security.
It is recommended that users prioritize service providers with relevant business licenses and carefully read their service agreements and privacy policies. This software does not host any account keys, does not participate in fund transfers, and does not bear the risk of recharge fund losses.
1. This project is open-source software. This software has no commercial partnership with any third-party API service providers (including but not limited to speech recognition, large models, speech synthesis, and other platforms) that it interfaces with, and does not provide any form of guarantee for their service quality or financial security. It is recommended that users prioritize service providers with relevant business licenses and carefully read their service agreements and privacy policies. This software does not host any account keys, does not participate in fund flows, and does not bear the risk of recharge fund losses.
2. This project's functionality is not complete and has not passed network security testing. Please do not use it in production environments. If you deploy this project for learning in a public network environment, please ensure necessary protection measures are in place.
2. The functionality of this project is not complete and has not passed network security assessment. Please do not use it in production environments. If you deploy this project for learning purposes in a public network environment, please ensure necessary protection measures are in place.
---
@@ -164,18 +181,20 @@ It is recommended that users prioritize service providers with relevant business
![Banners](docs/images/banner2.png)
This project provides two deployment methods. Please choose according to your specific needs:
This project provides two deployment methods. Please choose based on your specific needs:
#### 🚀 Deployment Method Selection
| Deployment Method | Features | Suitable Scenarios | Deployment Guide | Requirements | Video Tutorial |
| Deployment Method | Features | Applicable Scenarios | Deployment Docs | Configuration Requirements | Video Tutorials |
|---------|------|---------|---------|---------|---------|
| **Simplified Installation** | Smart dialogue, IOT functionality, data stored in configuration files | Low-configuration environment, no database needed | [Docker Version](./docs/Deployment.md#method-1-docker-server-only) / [Source Code Deployment](./docs/Deployment.md#method-2-local-source-code-server-only) | 2 cores 4G if using `FunASR`, 2 cores 2G if using all APIs | - |
| **Full Module Installation** | Smart dialogue, IOT, OTA, Control Panel, data stored in database | Complete functionality experience | [Docker Version](./docs/Deployment_all.md#method-1-docker-full-modules) / [Source Code Deployment](./docs/Deployment_all.md#method-2-local-source-code-full-modules) | 4 cores 8G if using `FunASR`, 2 cores 4G if using all APIs | [Local Source Code Startup Video Tutorial](https://www.bilibili.com/video/BV1wBJhz4Ewe) / [Local Source Code Auto-Update Tutorial](./docs/dev-ops-integration.md) |
| **Simplified Installation** | Intelligent dialogue, IOT, MCP, visual perception | Low-configuration environments, data stored in config files, no database required | [Docker Version](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [Source Code Deployment](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 2 cores 4GB if using `FunASR`, 2 cores 2GB if all APIs | - |
| **Full Module Installation** | Intelligent dialogue, IOT, MCP endpoints, voiceprint recognition, visual perception, OTA, intelligent control console | Complete functionality experience, data stored in database |[Docker Version](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [Source Code Deployment](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③Source Code Deployment Auto-Update Tutorial](./docs/dev-ops-integration.md) | 4 cores 8GB if using `FunASR`, 2 cores 4GB if all APIs| [Local Source Code Startup Video Tutorial](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
> 💡 Note: Below are the test platforms deployed with the latest code. You can flash and test if needed. Concurrent users: 6, data will be cleared daily
> 💡 Note: Below is a test platform deployed with the latest code. You can burn and test if needed. Concurrent users: 6, data will be cleared daily.
```
Control Panel Address: https://2662r3426b.vicp.fun
Intelligent Control Console Address: https://2662r3426b.vicp.fun
Intelligent Control Console Address (H5): https://2662r3426b.vicp.fun/h5/index.html
Service Test Tool: https://2662r3426b.vicp.fun/test/
OTA Interface Address: https://2662r3426b.vicp.fun/xiaozhi/ota/
@@ -184,74 +203,103 @@ Websocket Interface Address: wss://2662r3426b.vicp.fun/xiaozhi/v1/
#### 🚩 Configuration Description and Recommendations
> [!Note]
> The default configuration of this project is `Entry Level Free` settings. For better results, we recommend using `Full Streaming Configuration`.
> This project provides two configuration schemes:
>
> Since version `0.5.2`, this project supports full streaming throughout the entire lifecycle. Compared to versions before `0.5`, response speed has improved by approximately `2.5 seconds`
> 1. `Entry Level Free Settings`: Suitable for personal and home use, all components use free solutions, no additional payment required.
>
> 2. `Streaming Configuration`: Suitable for demonstrations, training, scenarios with more than 2 concurrent users, etc. Uses streaming processing technology for faster response speed and better experience.
>
> Starting from version `0.5.2`, the project supports streaming configuration. Compared to earlier versions, response speed is improved by approximately `2.5 seconds`, significantly improving user experience.
| Module Name | Entry Level Free Settings | Full Streaming Configuration |
|---------|---------|------|
| ASR(Speech Recognition) | FunASR(Local) | ✅DoubaoASR(Volcano Streaming Speech Recognition) |
| LLM(Large Language Model) | ChatGLMLLM(Zhipu glm-4-flash) | DoubaoLLM(Volcano doubao-1-5-pro-32k-250115) |
| VLLM(Vision Large Model) | ChatGLMVLLM(Zhipu glm-4v-flash) | ✅ChatGLMVLLM(Zhipu glm-4v-flash) |
| TTS(Speech Synthesis) | EdgeTTS(Microsoft Speech) | HuoshanDoubleStreamTTS(Volcano Double Streaming Speech Synthesis) |
| Intent(Intent Recognition) | function_call(Function Call) | function_call(Function Call) |
| Memory(Memory Function) | mem_local_short(Local Short-term Memory) | mem_local_short(Local Short-term Memory) |
| Module Name | Entry Level Free Settings | Streaming Configuration |
|:---:|:---:|:---:|
| ASR(Speech Recognition) | FunASR(Local) | 👍FunASRServer or 👍DoubaoStreamASR |
| LLM(Large Model) | ChatGLMLLM(Zhipu glm-4-flash) | 👍DoubaoLLM(Volcano doubao-1-5-pro-32k-250115) |
| VLLM(Vision Large Model) | ChatGLMVLLM(Zhipu glm-4v-flash) | 👍QwenVLVLLM(Qwen qwen2.5-vl-3b-instructh) |
| TTS(Speech Synthesis) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano dual-stream speech synthesis) |
| Intent(Intent Recognition) | function_call(Function calling) | function_call(Function calling) |
| Memory(Memory function) | mem_local_short(Local short-term memory) | mem_local_short(Local short-term memory) |
#### 🔧 Testing Tools
This project provides the following testing tools to help you verify the system and choose suitable models:
| Tool Name | Location | Usage Method | Function Description |
|:---:|:---|:---:|:---:|
| Audio Interaction Test Tool | main》xiaozhi-server》test》test_page.html | Open directly with Google Chrome | Tests audio playback and reception functions, verifies if Python-side audio processing is normal |
| Model Response Test Tool 1 | main》xiaozhi-server》performance_tester.py | Execute `python performance_tester.py` | Tests response speed of three core modules: ASR(speech recognition), LLM(large model), TTS(speech synthesis) |
| Model Response Test Tool 2 | main》xiaozhi-server》performance_tester_vllm.py | Execute `python performance_tester_vllm.py` | Tests VLLM(vision model) response speed |
> 💡 Note: When testing model speed, only models with configured keys will be tested.
---
## Feature List ✨
### Implemented ✅
![请参考-全模块安装架构图](docs/images/deploy2.png)
| Feature Module | Description |
|---------|------|
| Communication Protocol | Based on `xiaozhi-esp32` protocol, implements data interaction through WebSocket |
| Dialogue Interaction | Supports wake-up dialogue, manual dialogue, and real-time interruption. Auto-sleep after long periods of no dialogue |
| Intent Recognition | Supports LLM intent recognition, function call, reducing hard-coded intent judgment |
| Multi-language Recognition | Supports Mandarin, Cantonese, English, Japanese, Korean (default using FunASR) |
| LLM Module | Supports flexible LLM module switching, default using ChatGLMLLM, can also use Ali Bailian, DeepSeek, Ollama, etc. |
| TTS Module | Supports EdgeTTS (default), Volcano Engine Doubao TTS, and other TTS interfaces |
| Memory Function | Supports ultra-long memory, local summary memory, and no memory modes |
| IOT Function | Supports managing registered device IOT functionality, supports smart IoT control based on dialogue context |
| Control Panel | Provides Web management interface, supports agent management, user management, system configuration, etc. |
|:---:|:---|
| Core Architecture | Based on WebSocket and HTTP servers, provides complete console management and authentication system |
| Voice Interaction | Supports streaming ASR(speech recognition), streaming TTS(speech synthesis), VAD(voice activity detection), supports multi-language recognition and voice processing |
| Voiceprint Recognition | Supports multi-user voiceprint registration, management, and recognition, processes in parallel with ASR, real-time speaker identity recognition and passes to LLM for personalized responses |
| Intelligent Dialogue | Supports multiple LLM(large language models), implements intelligent dialogue |
| Visual Perception | Supports multiple VLLM(vision large models), implements multimodal interaction |
| Intent Recognition | Supports LLM intent recognition, Function Call function calling, provides plugin-based intent processing mechanism |
| Memory System | Supports local short-term memory, mem0ai interface memory, with memory summarization functionality |
| Tool Calling | Supports client IOT protocol, client MCP protocol, server MCP protocol, MCP endpoint protocol, custom tool functions |
| Management Backend | Provides Web management interface, supports user management, system configuration, and device management |
| Testing Tools | Provides performance testing tools, vision model testing tools, and audio interaction testing tools |
| Deployment Support | Supports Docker deployment and local deployment, provides complete configuration file management |
| Plugin System | Supports functional plugin extensions, custom plugin development, and plugin hot-loading |
### In Development 🚧
### Under Development 🚧
To learn about specific development progress, [click here](https://github.com/users/xinnan-tech/projects/3)
To learn about specific development plan progress, [click here](https://github.com/users/xinnan-tech/projects/3)
If you are a software developer, here is an [Open Letter to Developers](docs/contributor_open_letter.md). Welcome to join!
---
## Product Ecosystem 👬
Xiaozhi is an ecosystem. When using this product, you might also want to check out other excellent projects in this ecosystem
Xiaozhi is an ecosystem. When using this product, you can also check out other excellent projects in this ecosystem
| Project Name | Project Address | Project Description |
|:---------------------|:--------|:--------|
| Xiaozhi Android Client | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | A Flutter-based Android and iOS voice dialogue application supporting real-time voice interaction and text dialogue. |
| Xiaozhi PC Client | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | This project provides a Python-based Xiaozhi AI client, allowing you to experience Xiaozhi AI's functionality through code even without physical hardware. |
| Xiaozhi Java Server | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | The Java version of Xiaozhi open-source backend service is a Java-based open-source project.<br/>It includes both frontend and backend services, aiming to provide users with a complete backend service solution. |
| Xiaozhi Android Client | [xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) | An Android and iOS voice dialogue application based on xiaozhi-server, supporting real-time voice interaction and text dialogue.<br/>Currently a Flutter version, connecting iOS and Android platforms. |
| Xiaozhi Desktop Client | [py-xiaozhi](https://github.com/Huang-junsen/py-xiaozhi) | This project provides a Python-based AI client for beginners, allowing users to experience Xiaozhi AI functionality through code even without physical hardware conditions. |
| Xiaozhi Java Server | [xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) | Xiaozhi open-source backend service Java version is a Java-based open-source project.<br/>It includes frontend and backend services, aiming to provide users with a complete backend service solution. |
---
## Supported Platforms/Components List 📋
### LLM Language Models
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| openai interface call | Ali Bailian, Volcano Engine Doubao, DeepSeek, Zhipu ChatGLM, Gemini | Zhipu ChatGLM, Gemini |
| ollama interface call | Ollama | - |
| dify interface call | Dify | - |
| fastgpt interface call | Fastgpt | - |
| coze interface call | Coze | - |
| OpenAI interface calls | Alibaba Bailian, Volcano Engine Doubao, DeepSeek, Zhipu ChatGLM, Gemini | Zhipu ChatGLM, Gemini |
| Ollama interface calls | Ollama | - |
| Dify interface calls | Dify | - |
| FastGPT interface calls | FastGPT | - |
| Coze interface calls | Coze | - |
In fact, any LLM that supports openai interface calls can be integrated and used.
In fact, any LLM that supports OpenAI interface calls can be integrated and used.
---
### VLLM Vision Models
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| OpenAI interface calls | Alibaba Bailian, Zhipu ChatGLMVLLM | Zhipu ChatGLMVLLM |
In fact, any VLLM that supports OpenAI interface calls can be integrated and used.
---
### TTS Speech Synthesis
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| API Call | EdgeTTS, Volcano Engine Doubao TTS, Tencent Cloud, Alibaba Cloud TTS, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS | EdgeTTS, CosyVoiceSiliconflow(partial) |
| Local Service | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS |
| Interface calls | EdgeTTS, Volcano Engine Doubao TTS, Tencent Cloud, Alibaba Cloud TTS, CosyVoiceSiliconflow, TTS302AI, CozeCnTTS, GizwitsTTS, ACGNTTS, OpenAITTS, Lingxi Streaming TTS | Lingxi Streaming TTS, EdgeTTS, CosyVoiceSiliconflow(partial) |
| Local services | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS | FishSpeech, GPT_SOVITS_V2, GPT_SOVITS_V3, MinimaxTTS |
---
@@ -259,7 +307,7 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Type | Platform Name | Usage Method | Pricing Model | Notes |
|:---:|:---------:|:----:|:----:|:--:|
| VAD | SileroVAD | Local Usage | Free | |
| VAD | SileroVAD | Local use | Free | |
---
@@ -267,8 +315,16 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| Local Usage | FunASR, SherpaASR | FunASR, SherpaASR |
| API Call | DoubaoASR, FunASRServer, TencentASR, AliyunASR | FunASRServer |
| Local use | FunASR, SherpaASR | FunASR, SherpaASR |
| Interface calls | DoubaoASR, FunASRServer, TencentASR, AliyunASR | FunASRServer |
---
### Voiceprint Recognition
| Usage Method | Supported Platforms | Free Platforms |
|:---:|:---:|:---:|
| Local use | 3D-Speaker | 3D-Speaker |
---
@@ -276,8 +332,8 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Type | Platform Name | Usage Method | Pricing Model | Notes |
|:------:|:---------------:|:----:|:---------:|:--:|
| Memory | mem0ai | API Call | 1000 calls/month quota | |
| Memory | mem_local_short | Local Summary | Free | |
| Memory | mem0ai | Interface calls | 1000 times/month quota | |
| Memory | mem_local_short | Local summarization | Free | |
---
@@ -285,8 +341,8 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Type | Platform Name | Usage Method | Pricing Model | Notes |
|:------:|:-------------:|:----:|:-------:|:---------------------:|
| Intent | intent_llm | API Call | Based on LLM pricing | Uses large model for intent recognition, highly versatile |
| Intent | function_call | API Call | Based on LLM pricing | Uses large model function calls for intent, fast and effective |
| Intent | intent_llm | Interface calls | Based on LLM pricing | Recognizes intent through large models, strong generalization |
| Intent | function_call | Interface calls | Based on LLM pricing | Completes intent through large model function calling, fast speed, good effect |
---
@@ -294,11 +350,13 @@ In fact, any LLM that supports openai interface calls can be integrated and used
| Logo | Project/Company | Description |
|:---:|:---:|:---|
| <img src="./docs/images/logo_bailing.png" width="160"> | [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) | This project was inspired by [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) and implemented based on it |
| <img src="./docs/images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Thanks to [Tenclass](https://www.tenclass.com/) for establishing standard communication protocols, multi-device compatibility solutions, and high-concurrency scenario practices for the Xiaozhi ecosystem; providing full-chain technical documentation support for this project |
| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology](https://github.com/Eric0308) | Thanks to [Xuanfeng Technology](https://github.com/Eric0308) for contributing the function call framework, MCP communication protocol, and plugin call mechanism implementation code, significantly improving front-end device (IoT) interaction efficiency and functional extensibility through standardized instruction scheduling system and dynamic expansion capabilities |
| <img src="./docs/images/logo_huiyuan.png" width="160"> | [Huiyuan Design](http://ui.kwd988.net/) | Thanks to [Huiyuan Design](http://ui.kwd988.net/) for providing professional visual solutions for this project, empowering the product user experience with their design experience serving over a thousand enterprises |
| <img src="./docs/images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology](https://www.029app.com/) | Thanks to [Xi'an Qinren Information Technology](https://www.029app.com/) for deepening the visual system of this project, ensuring consistency and extensibility of the overall design style in multi-scenario applications |
| <img src="./docs/images/logo_bailing.png" width="160"> | [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) | This project is inspired by [Bailing Voice Dialogue Robot](https://github.com/wwbin2017/bailing) and implemented on its basis |
| <img src="./docs/images/logo_tenclass.png" width="160"> | [Tenclass](https://www.tenclass.com/) | Thanks to [Tenclass](https://www.tenclass.com/) for formulating standard communication protocols, multi-device compatibility solutions, and high-concurrency scenario practice demonstrations for the Xiaozhi ecosystem; providing full-link technical documentation support for this project |
| <img src="./docs/images/logo_xuanfeng.png" width="160"> | [Xuanfeng Technology](https://github.com/Eric0308) | Thanks to [Xuanfeng Technology](https://github.com/Eric0308) for contributing function calling framework, MCP communication protocol, and plugin-based calling mechanism implementation code. Through standardized instruction scheduling system and dynamic expansion capabilities, it significantly improves the interaction efficiency and functional extensibility of frontend devices (IoT) |
| <img src="./docs/images/logo_junsen.png" width="160"> | [huangjunsen](https://github.com/huangjunsen0406) | Thanks to [huangjunsen](https://github.com/huangjunsen0406) for contributing the `Smart Control Console Mobile` module, which enables efficient control and real-time interaction across mobile devices, significantly enhancing the system's operational convenience and management efficiency in mobile scenarios. |
| <img src="./docs/images/logo_huiyuan.png" width="160"> | [Huiyuan Design](http://ui.kwd988.net/) | Thanks to [Huiyuan Design](http://ui.kwd988.net/) for providing professional visual solutions for this project, using their design practical experience serving over a thousand enterprises to empower this project's product user experience |
| <img src="./docs/images/logo_qinren.png" width="160"> | [Xi'an Qinren Information Technology](https://www.029app.com/) | Thanks to [Xi'an Qinren Information Technology](https://www.029app.com/) for deepening this project's visual system, ensuring consistency and extensibility of overall design style in multi-scenario applications |
| <img src="./docs/images/logo_contributors.png" width="160"> | [Code Contributors](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors) | Thanks to [all code contributors](https://github.com/xinnan-tech/xiaozhi-esp32-server/graphs/contributors), your efforts have made the project more robust and powerful. |
<a href="https://star-history.com/#xinnan-tech/xiaozhi-esp32-server&Date">
+405 -97
View File
@@ -1,105 +1,413 @@
#!/bin/sh
# 脚本作者@VanillaNahida
# 本文件是用于一键自动下载本项目所需文件,自动创建好目录
# 所需条件(否则无法使用):
# 1、请确保你的环境可以正常访问 GitHub 否则无法下载脚本
#
# 检测操作系统类型
case "$(uname -s)" in
Linux*) OS=Linux;;
Darwin*) OS=Mac;;
CYGWIN*) OS=Windows;;
MINGW*) OS=Windows;;
MSYS*) OS=Windows;;
*) OS=UNKNOWN;;
# 暂且只支持X86版本的Ubuntu系统,其他系统未测试
# 定义中断处理函数
handle_interrupt() {
echo ""
echo "安装已被用户中断(Ctrl+C或Esc)"
echo "如需重新安装,请再次运行脚本"
exit 1
}
# 设置信号捕获,处理Ctrl+C
trap handle_interrupt SIGINT
# 处理Esc键
# 保存终端设置
old_stty_settings=$(stty -g)
# 设置终端立即响应,不回显
stty -icanon -echo min 1 time 0
# 后台进程检测Esc键
(while true; do
read -r key
if [[ $key == $'\e' ]]; then
# 检测到Esc键,触发中断处理
kill -SIGINT $$
break
fi
done) &
# 脚本结束时恢复终端设置
trap 'stty "$old_stty_settings"' EXIT
# 打印彩色字符画
echo -e "\e[1;32m" # 设置颜色为亮绿色
cat << "EOF"
脚本作者:@Bilibili 香草味的纳西妲喵
__ __ _ _ _ _ _ _ _ _
\ \ / / (_)| || | | \ | | | | (_) | |
\ \ / /__ _ _ __ _ | || | __ _ | \| | __ _ | |__ _ __| | __ _
\ \/ // _` || '_ \ | || || | / _` | | . ` | / _` || '_ \ | | / _` | / _` |
\ /| (_| || | | || || || || (_| | | |\ || (_| || | | || || (_| || (_| |
\/ \__,_||_| |_||_||_||_| \__,_| |_| \_| \__,_||_| |_||_| \__,_| \__,_|
EOF
echo -e "\e[0m" # 重置颜色
echo -e "\e[1;36m 小智服务端全量部署一键安装脚本 Ver 0.2 2025年8月20日更新 \e[0m\n"
sleep 1
# 检查并安装whiptail
check_whiptail() {
if ! command -v whiptail &> /dev/null; then
echo "正在安装whiptail..."
apt update
apt install -y whiptail
fi
}
check_whiptail
# 创建确认对话框
whiptail --title "安装确认" --yesno "即将安装小智服务端,是否继续?" \
--yes-button "继续" --no-button "退出" 10 50
# 根据用户选择执行操作
case $? in
0)
;;
1)
exit 1
;;
esac
# 设置颜色(Windows CMD 不支持,但不影响使用)
if [ "$OS" = "Windows" ]; then
GREEN=""
RED=""
NC=""
else
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
fi
echo "${GREEN}开始安装小智服务端...${NC}"
# 创建必要的目录
echo "创建目录结构..."
mkdir -p xiaozhi-server/data xiaozhi-server/models/SenseVoiceSmall
cd xiaozhi-server || exit
# 根据操作系统选择下载命令
if [ "$OS" = "Windows" ]; then
DOWNLOAD_CMD="curl -L -o"
if ! command -v curl >/dev/null 2>&1; then
DOWNLOAD_CMD="powershell -Command Invoke-WebRequest -Uri"
DOWNLOAD_CMD_SUFFIX="-OutFile"
fi
else
if command -v curl >/dev/null 2>&1; then
DOWNLOAD_CMD="curl -L -o"
elif command -v wget >/dev/null 2>&1; then
DOWNLOAD_CMD="wget -O"
else
echo "${RED}错误: 需要安装 curl 或 wget${NC}"
exit 1
fi
fi
# 下载语音识别模型
echo "下载语音识别模型..."
if [ "$DOWNLOAD_CMD" = "powershell -Command Invoke-WebRequest -Uri" ]; then
$DOWNLOAD_CMD "https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt" $DOWNLOAD_CMD_SUFFIX "models/SenseVoiceSmall/model.pt"
else
$DOWNLOAD_CMD "models/SenseVoiceSmall/model.pt" "https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt"
fi
if [ $? -ne 0 ]; then
echo "${RED}模型下载失败。请手动从以下地址下载:${NC}"
echo "1. https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt"
echo "2. 百度网盘: https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg (提取码: qvna)"
echo "下载后请将文件放置在 models/SenseVoiceSmall/model.pt"
fi
# 下载配置文件
echo "下载配置文件..."
if [ "$DOWNLOAD_CMD" = "powershell -Command Invoke-WebRequest -Uri" ]; then
$DOWNLOAD_CMD "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/docker-compose.yml" $DOWNLOAD_CMD_SUFFIX "docker-compose.yml"
$DOWNLOAD_CMD "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/config.yaml" $DOWNLOAD_CMD_SUFFIX "data/.config.yaml"
else
$DOWNLOAD_CMD "docker-compose.yml" "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/docker-compose.yml"
$DOWNLOAD_CMD "data/.config.yaml" "https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/main/xiaozhi-server/config.yaml"
fi
# 检查文件是否存在
echo "检查文件完整性..."
FILES_TO_CHECK="docker-compose.yml data/.config.yaml models/SenseVoiceSmall/model.pt"
ALL_FILES_EXIST=true
for FILE in $FILES_TO_CHECK; do
if [ ! -f "$FILE" ]; then
echo "${RED}错误: $FILE 不存在${NC}"
ALL_FILES_EXIST=false
fi
done
if [ "$ALL_FILES_EXIST" = false ]; then
echo "${RED}某些文件下载失败,请检查上述错误信息并手动下载缺失的文件。${NC}"
# 检查root权限
if [ $EUID -ne 0 ]; then
whiptail --title "权限错误" --msgbox "请使用root权限运行本脚本" 10 50
exit 1
fi
echo "${GREEN}文件下载完成!${NC}"
echo "请编辑 data/.config.yaml 文件配置你的API密钥。"
echo "配置完成后,运行以下命令启动服务:"
echo "${GREEN}docker-compose up -d${NC}"
echo "查看日志请运行:"
echo "${GREEN}docker logs -f xiaozhi-esp32-server${NC}"
# 检查系统版本
if [ -f /etc/os-release ]; then
. /etc/os-release
if [ "$ID" != "debian" ] && [ "$ID" != "ubuntu" ]; then
whiptail --title "系统错误" --msgbox "该脚本只支持Debian/Ubuntu系统执行" 10 60
exit 1
fi
else
whiptail --title "系统错误" --msgbox "无法确定系统版本,该脚本只支持Debian/Ubuntu系统执行" 10 60
exit 1
fi
# 提示用户编辑配置文件
echo "\n${RED}重要提示:${NC}"
echo "1. 请确保编辑 data/.config.yaml 文件,配置必要的API密钥"
echo "2. 特别是 ChatGLM 和 mem0ai 的密钥必须配置"
echo "3. 配置完成后再启动 docker 服务"
# 下载配置文件函数
check_and_download() {
local filepath=$1
local url=$2
if [ ! -f "$filepath" ]; then
if ! curl -fL --progress-bar "$url" -o "$filepath"; then
whiptail --title "错误" --msgbox "${filepath}文件下载失败" 10 50
exit 1
fi
else
echo "${filepath}文件已存在,跳过下载"
fi
}
# 检查是否已安装
check_installed() {
# 检查目录是否存在且非空
if [ -d "/opt/xiaozhi-server/" ] && [ "$(ls -A /opt/xiaozhi-server/)" ]; then
DIR_CHECK=1
else
DIR_CHECK=0
fi
# 检查容器是否存在
if docker inspect xiaozhi-esp32-server > /dev/null 2>&1; then
CONTAINER_CHECK=1
else
CONTAINER_CHECK=0
fi
# 两次检查都通过
if [ $DIR_CHECK -eq 1 ] && [ $CONTAINER_CHECK -eq 1 ]; then
return 0 # 已安装
else
return 1 # 未安装
fi
}
# 更新相关
if check_installed; then
if whiptail --title "已安装检测" --yesno "检测到小智服务端已安装,是否进行升级?" 10 60; then
# 用户选择升级,执行清理操作
echo "开始升级操作..."
# 停止并移除所有docker-compose服务
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml down
# 停止并删除特定容器(考虑容器可能不存在的情况)
containers=(
"xiaozhi-esp32-server"
"xiaozhi-esp32-server-web"
"xiaozhi-esp32-server-db"
"xiaozhi-esp32-server-redis"
)
for container in "${containers[@]}"; do
if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then
docker stop "$container" >/dev/null 2>&1 && \
docker rm "$container" >/dev/null 2>&1 && \
echo "成功移除容器: $container"
else
echo "容器不存在,跳过: $container"
fi
done
# 删除特定镜像(考虑镜像可能不存在的情况)
images=(
"ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest"
"ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:web_latest"
)
for image in "${images[@]}"; do
if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${image}$"; then
docker rmi "$image" >/dev/null 2>&1 && \
echo "成功删除镜像: $image"
else
echo "镜像不存在,跳过: $image"
fi
done
echo "所有清理操作完成"
# 备份原有配置文件
mkdir -p /opt/xiaozhi-server/backup/
if [ -f /opt/xiaozhi-server/data/.config.yaml ]; then
cp /opt/xiaozhi-server/data/.config.yaml /opt/xiaozhi-server/backup/.config.yaml
echo "已备份原有配置文件到 /opt/xiaozhi-server/backup/.config.yaml"
fi
# 下载最新版配置文件
check_and_download "/opt/xiaozhi-server/docker-compose_all.yml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml"
check_and_download "/opt/xiaozhi-server/data/.config.yaml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml"
# 启动Docker服务
echo "开始启动最新版本服务..."
# 升级完成后标记,跳过后续下载步骤
UPGRADE_COMPLETED=1
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
else
whiptail --title "跳过升级" --msgbox "已取消升级,将继续使用当前版本。" 10 50
# 跳过升级,继续执行后续安装流程
fi
fi
# 检查curl安装
if ! command -v curl &> /dev/null; then
echo "------------------------------------------------------------"
echo "未检测到curl,正在安装..."
apt update
apt install -y curl
else
echo "------------------------------------------------------------"
echo "curl已安装,跳过安装步骤"
fi
# 检查Docker安装
if ! command -v docker &> /dev/null; then
echo "------------------------------------------------------------"
echo "未检测到Docker,正在安装..."
# 使用国内镜像源替代官方源
DISTRO=$(lsb_release -cs)
MIRROR_URL="https://mirrors.aliyun.com/docker-ce/linux/ubuntu"
GPG_URL="https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg"
# 安装基础依赖
apt update
apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg
# 创建密钥目录并添加国内镜像源密钥
mkdir -p /etc/apt/keyrings
curl -fsSL "$GPG_URL" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 添加国内镜像源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $MIRROR_URL $DISTRO stable" \
> /etc/apt/sources.list.d/docker.list
# 添加备用官方源密钥(避免国内源密钥验证失败)
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7EA0A9C3F273FCD8 2>/dev/null || \
echo "警告:部分密钥添加失败,继续尝试安装..."
# 安装Docker
apt update
apt install -y docker-ce docker-ce-cli containerd.io
# 启动服务
systemctl start docker
systemctl enable docker
# 检查是否安装成功
if docker --version; then
echo "------------------------------------------------------------"
echo "Docker安装完成!"
else
whiptail --title "错误" --msgbox "Docker安装失败,请检查日志。" 10 50
exit 1
fi
else
echo "Docker已安装,跳过安装步骤"
fi
# Docker镜像源配置
MIRROR_OPTIONS=(
"1" "轩辕镜像 (推荐)"
"2" "腾讯云镜像源"
"3" "中科大镜像源"
"4" "网易163镜像源"
"5" "华为云镜像源"
"6" "阿里云镜像源"
"7" "自定义镜像源"
"8" "跳过配置"
)
MIRROR_CHOICE=$(whiptail --title "选择Docker镜像源" --menu "请选择要使用的Docker镜像源" 20 60 10 \
"${MIRROR_OPTIONS[@]}" 3>&1 1>&2 2>&3) || {
echo "用户取消选择,退出脚本"
exit 1
}
case $MIRROR_CHOICE in
1) MIRROR_URL="https://docker.xuanyuan.me" ;;
2) MIRROR_URL="https://mirror.ccs.tencentyun.com" ;;
3) MIRROR_URL="https://docker.mirrors.ustc.edu.cn" ;;
4) MIRROR_URL="https://hub-mirror.c.163.com" ;;
5) MIRROR_URL="https://05f073ad3c0010ea0f4bc00b7105ec20.mirror.swr.myhuaweicloud.com" ;;
6) MIRROR_URL="https://registry.aliyuncs.com" ;;
7) MIRROR_URL=$(whiptail --title "自定义镜像源" --inputbox "请输入完整的镜像源URL:" 10 60 3>&1 1>&2 2>&3) ;;
8) MIRROR_URL="" ;;
esac
if [ -n "$MIRROR_URL" ]; then
mkdir -p /etc/docker
if [ -f /etc/docker/daemon.json ]; then
cp /etc/docker/daemon.json /etc/docker/daemon.json.bak
fi
cat > /etc/docker/daemon.json <<EOF
{
"dns": ["8.8.8.8", "114.114.114.114"],
"registry-mirrors": ["$MIRROR_URL"]
}
EOF
whiptail --title "配置成功" --msgbox "已成功添加镜像源: $MIRROR_URL\n请按Enter键重启Docker服务并继续..." 12 60
echo "------------------------------------------------------------"
echo "开始重启Docker服务..."
systemctl restart docker.service
fi
# 创建安装目录
echo "------------------------------------------------------------"
echo "开始创建安装目录..."
# 检查并创建数据目录
if [ ! -d /opt/xiaozhi-server/data ]; then
mkdir -p /opt/xiaozhi-server/data
echo "已创建数据目录: /opt/xiaozhi-server/data"
else
echo "目录xiaozhi-server/data已存在,跳过创建"
fi
# 检查并创建模型目录
if [ ! -d /opt/xiaozhi-server/models/SenseVoiceSmall ]; then
mkdir -p /opt/xiaozhi-server/models/SenseVoiceSmall
echo "已创建模型目录: /opt/xiaozhi-server/models/SenseVoiceSmall"
else
echo "目录xiaozhi-server/models/SenseVoiceSmall已存在,跳过创建"
fi
echo "------------------------------------------------------------"
echo "开始下载语音识别模型"
# 下载模型文件
MODEL_PATH="/opt/xiaozhi-server/models/SenseVoiceSmall/model.pt"
if [ ! -f "$MODEL_PATH" ]; then
(
for i in {1..20}; do
echo $((i*5))
sleep 0.5
done
) | whiptail --title "下载中" --gauge "开始下载语音识别模型..." 10 60 0
curl -fL --progress-bar https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt -o "$MODEL_PATH" || {
whiptail --title "错误" --msgbox "model.pt文件下载失败" 10 50
exit 1
}
else
echo "model.pt文件已存在,跳过下载"
fi
# 如果不是升级完成,才执行下载
if [ -z "$UPGRADE_COMPLETED" ]; then
check_and_download "/opt/xiaozhi-server/docker-compose_all.yml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml"
check_and_download "/opt/xiaozhi-server/data/.config.yaml" "https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/config_from_api.yaml"
fi
# 启动Docker服务
(
echo "------------------------------------------------------------"
echo "正在拉取Docker镜像..."
echo "这可能需要几分钟时间,请耐心等待"
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
if [ $? -ne 0 ]; then
whiptail --title "错误" --msgbox "Docker服务启动失败,请尝试更换镜像源后重新执行本脚本" 10 60
exit 1
fi
echo "------------------------------------------------------------"
echo "正在检查服务启动状态..."
TIMEOUT=300
START_TIME=$(date +%s)
while true; do
CURRENT_TIME=$(date +%s)
if [ $((CURRENT_TIME - START_TIME)) -gt $TIMEOUT ]; then
whiptail --title "错误" --msgbox "服务启动超时,未在指定时间内找到预期日志内容" 10 60
exit 1
fi
if docker logs xiaozhi-esp32-server-web 2>&1 | grep -q "Started AdminApplication in"; then
break
fi
sleep 1
done
echo "服务端启动成功!正在完成配置..."
echo "正在启动服务..."
docker compose -f /opt/xiaozhi-server/docker-compose_all.yml up -d
echo "服务启动完成!"
)
# 密钥配置
# 获取服务器公网地址
PUBLIC_IP=$(hostname -I | awk '{print $1}')
whiptail --title "配置服务器密钥" --msgbox "请使用浏览器,访问下方链接,打开智控台并注册账号: \n\n内网地址:http://127.0.0.1:8002/\n公网地址:http://$PUBLIC_IP:8002/ (若是云服务器请在服务器安全组放行端口 8000 8001 8002)。\n\n注册的第一个用户即是超级管理员,以后注册的用户都是普通用户。普通用户只能绑定设备和配置智能体; 超级管理员可以进行模型管理、用户管理、参数配置等功能。\n\n注册好后请按Enter键继续" 18 70
SECRET_KEY=$(whiptail --title "配置服务器密钥" --inputbox "请使用超级管理员账号登录智控台\n内网地址:http://127.0.0.1:8002/\n公网地址:http://$PUBLIC_IP:8002/\n在顶部菜单 参数字典 → 参数管理 找到参数编码: server.secret (服务器密钥) \n复制该参数值并输入到下面输入框\n\n请输入密钥(留空则跳过配置):" 15 60 3>&1 1>&2 2>&3)
if [ -n "$SECRET_KEY" ]; then
python3 -c "
import sys, yaml;
config_path = '/opt/xiaozhi-server/data/.config.yaml';
with open(config_path, 'r') as f:
config = yaml.safe_load(f) or {};
config['manager-api'] = {'url': 'http://xiaozhi-esp32-server-web:8002/xiaozhi', 'secret': '$SECRET_KEY'};
with open(config_path, 'w') as f:
yaml.dump(config, f);
"
docker restart xiaozhi-esp32-server
fi
# 获取并显示地址信息
LOCAL_IP=$(hostname -I | awk '{print $1}')
# 修复日志文件获取不到ws的问题,改为硬编码
whiptail --title "安装完成!" --msgbox "\
服务端相关地址如下:\n\
管理后台访问地址: http://$LOCAL_IP:8002\n\
OTA 地址: http://$LOCAL_IP:8002/xiaozhi/ota/\n\
视觉分析接口地址: http://$LOCAL_IP:8003/mcp/vision/explain\n\
WebSocket 地址: ws://$LOCAL_IP:8000/xiaozhi/v1/\n\
\n安装完毕!感谢您的使用!\n按Enter键退出..." 16 70
+34 -57
View File
@@ -8,47 +8,13 @@ docker镜像已支持x86架构、arm64架构的CPU,支持在国产操作系统
如果您的电脑还没安装docker,可以按照这里的教程安装:[docker安装](https://www.runoob.com/docker/ubuntu-docker-install.html)
如果你已经安装好docker,你可以[1.1使用懒人脚本](#11-懒人脚本)自动帮你下载所需的文件和配置文件,你可以使用docker[1.2手动部署](#12-手动部署)
安装好docker后,进继续
### 1.1 懒人脚本
### 1.1 手动部署
你可以使用以下命令一键下载并执行部署脚本:
请确保你的环境可以正常访问 GitHub 否则无法下载脚本。
```bash
curl -L -o docker-setup.sh https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/docker-setup.sh
```
#### 1.1.1 创建目录
如果您的电脑是windows系统,请使用使用 Git Bash、WSL、PowerShell 或 CMD 运行以下命令:
```bash
# Git Bash 或 WSL
sh docker-setup.sh
# PowerShell 或 CMD
.\docker-setup.sh
```
如果您的电脑是linux 或者 macos 系统,请使用终端运行以下命令:
```bash
chmod +x docker-setup.sh
./docker-setup.sh
```
脚本会自动完成以下操作:
> 1. 创建必要的目录结构
> 2. 下载语音识别模型
> 3. 下载配置文件
> 4. 检查文件完整性
>
> 执行完成后,请按照提示配置 API 密钥。
当你一切顺利完成以上操作后,继续操作[配置项目文件](#2-配置项目文件)
### 1.2 手动部署
如果懒人脚本无法正常运行,请按本章节1.2进行手动部署。
#### 1.2.1 创建目录
安装完后,你需要为这个项目找一个安放配置文件的目录,例如我们可以新建一个文件夹叫`xiaozhi-server`
安装完docker后,你需要为这个项目找一个安放配置文件的目录,例如我们可以新建一个文件夹叫`xiaozhi-server`
创建好目录后,你需要在`xiaozhi-server`下面创建`data`文件夹和`models`文件夹,`models`下面还要再创建`SenseVoiceSmall`文件夹。
@@ -61,18 +27,18 @@ xiaozhi-server
├─ SenseVoiceSmall
```
#### 1.2.2 下载语音识别模型文件
#### 1.1.2 下载语音识别模型文件
你需要下载语音识别的模型文件,因为本项目的默认语音识别用的是本地离线语音识别方案。可通过这个方式下载
[跳转到下载语音识别模型文件](#模型文件)
下载完后,回到本教程。
#### 1.2.3 下载配置文件
#### 1.1.3 下载配置文件
你需要下载两个配置文件:`docker-compose.yaml``config.yaml`。需要从项目仓库下载这两个文件。
##### 1.2.3.1 下载 docker-compose.yaml
##### 1.1.3.1 下载 docker-compose.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/docker-compose.yml)。
@@ -81,7 +47,7 @@ xiaozhi-server
下载完后,回到本教程继续往下。
##### 1.2.3.2 创建 config.yaml
##### 1.1.3.2 创建 config.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/config.yaml)。
@@ -260,7 +226,7 @@ LLM:
文件放在`models/SenseVoiceSmall`
目录下。下面两个下载路线任选一个。
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路二:百度网盘下载[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 提取码:
`qvna`
@@ -290,19 +256,30 @@ LLM:
2、 [基于虾哥编译好的固件配置自定义服务器](firmware-setting.md)了。
# 常见问题
以下是一些常见问题,供参考:
[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)
[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)
[3、TTS 经常失败,经常超时](./FAQ.md)
[4、使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)
[5、如何提高小智对话响应速度?](./FAQ.md)
[6、我说话很慢,停顿时小智老是抢话](./FAQ.md)
[7、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md)
1、[为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)<br/>
2、[为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)<br/>
3、[TTS 经常失败,经常超时](./FAQ.md)<br/>
4、[使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)<br/>
5、[如何提高小智对话响应速度?](./FAQ.md)<br/>
6、[我说话很慢,停顿时小智老是抢话](./FAQ.md)<br/>
## 部署相关教程
1、[如何自动拉取本项目最新代码自动编译和启动](./dev-ops-integration.md)<br/>
2、[如何与Nginx集成](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
## 拓展相关教程
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
2、[如何集成HomeAssistant实现智能家居控制](./homeassistant-integration.md)<br/>
3、[如何开启视觉模型实现拍照识物](./mcp-vision-integration.md)<br/>
4、[如何部署MCP接入点](./mcp-endpoint-enable.md)<br/>
5、[如何接入MCP接入点](./mcp-endpoint-integration.md)<br/>
6、[如何开启声纹识别](./voiceprint-integration.md)<br/>
10、[新闻插件源配置指南](./newsnow_plugin_config.md)<br/>
## 语音克隆、本地语音部署相关教程
1、[如何部署集成index-tts本地语音](./index-stream-integration.md)<br/>
2、[如何部署集成fish-speech本地语音](./fish-speech-integration.md)<br/>
3、[如何部署集成PaddleSpeech本地语音](./paddlespeech-deploy.md)<br/>
## 性能测试教程
1、[各组件速度测试指南](./performance_tester.md)<br/>
2、[定期公开测试结果](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
+60 -23
View File
@@ -7,7 +7,34 @@ docker镜像已支持x86架构、arm64架构的CPU,支持在国产操作系统
如果您的电脑还没安装docker,可以按照这里的教程安装:[docker安装](https://www.runoob.com/docker/ubuntu-docker-install.html)
#### 1.1 创建目录
docker 安装全模块有两种方式,你可以[使用懒人脚本](./Deployment_all.md#11-懒人脚本)(作者[@VanillaNahida](https://github.com/VanillaNahida)
脚本会自动帮你下载所需的文件和配置文件,你也可以使用[手动部署](./Deployment_all.md#12-手动部署)从零搭建。
### 1.1 懒人脚本
部署简便,可以参考[视频教程](https://www.bilibili.com/video/BV17bbvzHExd/) ,文字版教程如下:
> [!NOTE]
> 暂且只支持Ubuntu服务器一键部署,其他系统未尝试,可能会有一些奇怪的bug
使用SSH工具连接到服务器,以root权限执行如下脚本
```bash
sudo bash -c "$(wget -qO- https://ghfast.top/https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/main/docker-setup.sh)"
```
脚本会自动完成以下操作:
> 1. 安装Docker
> 2. 配置镜像源
> 3. 下载/拉取镜像
> 4. 下载语音识别模型文件
> 5. 引导配置服务端
>
执行完成后简单配置后,再参照[4. 运行程序](#4. 运行程序)和[5.重启xiaozhi-esp32-server](#5.重启xiaozhi-esp32-server)里提到的最重要的3件事情,完成3这三项配置后即可使用。
### 1.2 手动部署
#### 1.2.1 创建目录
安装完后,你需要为这个项目找一个安放配置文件的目录,例如我们可以新建一个文件夹叫`xiaozhi-server`
@@ -22,22 +49,22 @@ xiaozhi-server
├─ SenseVoiceSmall
```
#### 1.2 下载语音识别模型文件
#### 1.2.2 下载语音识别模型文件
本项目语音识别模型,默认使用`SenseVoiceSmall`模型,进行语音转文字。因为模型较大,需要独立下载,下载后把`model.pt`
文件放在`models/SenseVoiceSmall`
目录下。下面两个下载路线任选一个。
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路二:百度网盘下载[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 提取码:
`qvna`
#### 1.3 下载配置文件
#### 1.2.3 下载配置文件
你需要下载两个配置文件:`docker-compose_all.yaml``config_from_api.yaml`。需要从项目仓库下载这两个文件。
##### 1.3.1 下载 docker-compose_all.yaml
##### 1.2.3.1 下载 docker-compose_all.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/docker-compose_all.yml)。
@@ -48,7 +75,7 @@ xiaozhi-server
下载完后,回到本教程继续往下。
##### 1.3.2 下载 config_from_api.yaml
##### 1.2.3.2 下载 config_from_api.yaml
用浏览器打开[这个链接](../main/xiaozhi-server/config_from_api.yaml)。
@@ -179,12 +206,12 @@ docker logs -f xiaozhi-esp32-server
OTA接口:
```
http://你电脑局域网的ip:8002/xiaozhi/ota/
http://你宿主机局域网的ip:8002/xiaozhi/ota/
```
Websocket接口:
```
ws://你电脑局域网的ip:8000/xiaozhi/v1/
ws://你宿主机的ip:8000/xiaozhi/v1/
```
### 第三件重要的事情
@@ -358,7 +385,7 @@ pip install -r requirements.txt
文件放在`models/SenseVoiceSmall`
目录下。下面两个下载路线任选一个。
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路一:阿里魔下载[SenseVoiceSmall](https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt)
- 线路二:百度网盘下载[SenseVoiceSmall](https://pan.baidu.com/share/init?surl=QlgM58FHhYv1tFnUT_A8Sg&pwd=qvna) 提取码:
`qvna`
@@ -431,19 +458,29 @@ ws://你电脑局域网的ip:8000/xiaozhi/v1/
2、 [基于虾哥编译好的固件配置自定义服务器](firmware-setting.md)了。
# 常见问题
以下是一些常见问题,供参考:
[1、为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)
[2、为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)
[3、TTS 经常失败,经常超时](./FAQ.md)
[4、使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)
[5、如何提高小智对话响应速度?](./FAQ.md)
[6、我说话很慢,停顿时小智老是抢话](./FAQ.md)
[7、我想通过小智控制电灯、空调、远程开关机等操作](./FAQ.md)
1、[为什么我说的话,小智识别出来很多韩文、日文、英文](./FAQ.md)<br/>
2、[为什么会出现“TTS 任务出错 文件不存在”?](./FAQ.md)<br/>
3、[TTS 经常失败,经常超时](./FAQ.md)<br/>
4、[使用Wifi能连接自建服务器,但是4G模式却接不上](./FAQ.md)<br/>
5、[如何提高小智对话响应速度?](./FAQ.md)<br/>
6、[我说话很慢,停顿时小智老是抢话](./FAQ.md)<br/>
## 部署相关教程
1、[如何自动拉取本项目最新代码自动编译和启动](./dev-ops-integration.md)<br/>
2、[如何与Nginx集成](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
## 拓展相关教程
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
2、[如何集成HomeAssistant实现智能家居控制](./homeassistant-integration.md)<br/>
3、[如何开启视觉模型实现拍照识物](./mcp-vision-integration.md)<br/>
4、[如何部署MCP接入点](./mcp-endpoint-enable.md)<br/>
5、[如何接入MCP接入点](./mcp-endpoint-integration.md)<br/>
6、[如何开启声纹识别](./voiceprint-integration.md)<br/>
10、[新闻插件源配置指南](./newsnow_plugin_config.md)<br/>
## 语音克隆、本地语音部署相关教程
1、[如何部署集成index-tts本地语音](./index-stream-integration.md)<br/>
2、[如何部署集成fish-speech本地语音](./fish-speech-integration.md)<br/>
3、[如何部署集成PaddleSpeech本地语音](./paddlespeech-deploy.md)<br/>
## 性能测试教程
1、[各组件速度测试指南](./performance_tester.md)<br/>
2、[定期公开测试结果](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
+35 -62
View File
@@ -34,59 +34,18 @@ conda install conda-forge::ffmpeg
### 5、如何提高小智对话响应速度? ⚡
本项目默认配置为低成本方案,建议初学者先使用默认免费模型,解决"跑得动"的问题,再优化"跑得快"。
如需提升响应速度,可尝试更换各组件。以下为各组件的响应速度测试数据(仅供参考,不构成承诺):
如需提升响应速度,可尝试更换各组件。`0.5.2`版本起,项目支持流式配置,相比早期版本,响应速度提升约`2.5秒`,显著改善用户体验。
| 影响因素 | 因素值 |
|:-----:|:----------------:|
| 测试地点 | 广东省广州市海珠区 |
| 测试时间 | 2025年2月19日 12:52 |
| 宽带运营商 | 中国联通 |
| 模块名称 | 入门全免费设置 | 流式配置 |
|:---:|:---:|:---:|
| ASR(语音识别) | FunASR(本地) | 👍FunASR(本地GPU模式) |
| LLM(大模型) | ChatGLMLLM(智谱glm-4-flash) | 👍AliLLM(qwen3-235b-a22b-instruct-2507) 或 👍DoubaoLLM(doubao-1-5-pro-32k-250115) |
| VLLM(视觉大模型) | ChatGLMVLLM(智谱glm-4v-flash) | 👍QwenVLVLLM(千问qwen2.5-vl-3b-instructh) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山双流式语音合成) 或 👍AliyunStreamTTS(阿里云流式语音合成) |
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
测试方法:
1、把各组件的密钥配置上去,只有配置了密钥的组件才参与测试。
2、配置完密钥后,执行以下方法
```
# 进入项目根目录,执行以下命令:
conda activate xiaozhi-esp32-server
python performance_tester.py
```
生成报告如下
LLM 性能排行:
| 模块名称 | 平均首Token时间 | 平均总响应时间 |
|:-----------|:-----------|:--------|
| AliLLM | 0.547s | 1.485s |
| ChatGLMLLM | 0.677s | 3.057s |
TTS 性能排行:
| 模块名称 | 平均合成时间 |
|----------------------|--------|
| EdgeTTS | 1.019s |
| DoubaoTTS | 0.503s |
| CosyVoiceSiliconflow | 3.732s |
推荐配置组合 (综合响应速度):
| 组合方案 | 综合得分 | LLM首Token | TTS合成 |
|-------------------------------|-------|-----------|--------|
| AliLLM + DoubaoTTS | 0.539 | 0.547s | 0.503s |
| AliLLM + EdgeTTS | 0.642 | 0.547s | 1.019s |
| ChatGLMLLM + DoubaoTTS | 0.642 | 0.677s | 0.503s |
| ChatGLMLLM + EdgeTTS | 0.745 | 0.677s | 1.019s |
| AliLLM + CosyVoiceSiliconflow | 1.184 | 0.547s | 3.732s |
### 结论 🔍
`2025年2月19日`,如果我的电脑在`广东省广州市海珠区`,且使用的是`中国联通`网络,我会优先使用:
- LLM`AliLLM`
- TTS`DoubaoTTS`
如果您关心各组件的耗时,请查阅[小智各组件性能测试报告](https://github.com/xinnan-tech/xiaozhi-performance-research),可按报告中的测试方法在您的环境中实际测试。
### 6、我说话很慢,停顿时小智老是抢话 🗣️
@@ -100,20 +59,34 @@ VAD:
min_silence_duration_ms: 700 # 如果说话停顿较长,可将此值调大
```
### 7、我想通过小智控制电灯、空调、远程开关机等操作 💡
### 7、部署相关教程
1、[如何进行最简化部署](./Deployment.md)<br/>
2、[如何进行全模块部署](./Deployment_all.md)<br/>
3、[如何自动拉取本项目最新代码自动编译和启动](./dev-ops-integration.md)<br/>
4、[如何与Nginx集成](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)<br/>
参考教程[ESP32设备与HomeAssistant集成指南](./homeassistant-integration.md)
### 8、编译固件相关教程
1、[如何自己编译小智固件](./firmware-build.md)<br/>
2、[如何基于虾哥编译好的固件修改OTA地址](./firmware-setting.md)<br/>
### 8、如何开启手机注册智控台 📱
### 8、拓展相关教程
1、[如何开启手机号码注册智控台](./ali-sms-integration.md)<br/>
2、[如何集成HomeAssistant实现智能家居控制](./homeassistant-integration.md)<br/>
3、[如何开启视觉模型实现拍照识物](./mcp-vision-integration.md)<br/>
4、[如何部署MCP接入点](./mcp-endpoint-enable.md)<br/>
5、[如何接入MCP接入点](./mcp-endpoint-integration.md)<br/>
6、[如何开启声纹识别](./voiceprint-integration.md)<br/>
10、[新闻插件源配置指南](./newsnow_plugin_config.md)<br/>
参考教程[阿里云短信集成指南](./ali-sms-integration.md)
### 9、语音克隆、本地语音部署相关教程
1、[如何部署集成index-tts本地语音](./index-stream-integration.md)<br/>
2、[如何部署集成fish-speech本地语音](./fish-speech-integration.md)<br/>
3、[如何部署集成PaddleSpeech本地语音](./paddlespeech-deploy.md)<br/>
### 9、如何开启视觉模型实现拍照识物 📷
### 10、性能测试教程
1、[各组件速度测试指南](./performance_tester.md)<br/>
2、[定期公开测试结果](https://github.com/xinnan-tech/xiaozhi-performance-research)<br/>
参考教程[视觉模型使用指南](./mcp-vision-integration.md)
### 13、更多问题,可联系我们反馈 💬
### 10、更多问题,可联系我们反馈 💬
可以在[issues](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues)提交您的问题。
也可以发邮件我们取得联系:huangrongzhuang@xin-nan.com
可以在[issues](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues)提交您的问题。
+23 -4
View File
@@ -4,6 +4,8 @@
本项目的测试平台`https://2662r3426b.vicp.fun`,从开放以来就使用了该方法,效果良好。
教程可参考B站博主`毕乐labs`发布的视频教程:[《开源小智服务器xiaozhi-server自动更新以及最新版本MCP接入点配置保姆教程》](https://www.bilibili.com/video/BV15H37zHE7Q)
# 开始条件
- 你的电脑/服务器是linux操作系统
- 你已经跑通了整个流程
@@ -40,6 +42,9 @@ git clone https://ghproxy.net/https://github.com/xinnan-tech/xiaozhi-esp32-serve
此刻你需要把`model.pt`文件复制到新的目录去,你可以这样
```
# 创建需要的目录
mkdir -p /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/
cp 你原来的.config.yaml完整路径 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/.config.yaml
cp 你原来的model.pt完整路径 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/model.pt
```
@@ -100,6 +105,8 @@ else
fi
nohup java -jar xiaozhi-esp32-api.jar --spring.profiles.active=dev &
tail tail -f nohup.out
```
保存好后执行赋权命令
@@ -129,8 +136,12 @@ else
echo "已杀掉进程 $PID"
fi
cd main/xiaozhi-server
# 初始化conda环境
source ~/.bashrc
conda activate xiaozhi-esp32-server
pip install -r requirements.txt
nohup python app.py >/dev/null &
tail -f /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/tmp/server.log
```
保存好后执行赋权命令
@@ -144,8 +155,6 @@ chmod 777 update_8000.sh
以上的脚本都建立好后,日常更新,我们只要依次执行以下命令就可以做到自动更新和启动
```
# 进入pyhton环境
conda activate xiaozhi-esp32-server
cd /home/system/xiaozhi
# 更新并启动Java程序
./update_8001.sh
@@ -153,11 +162,21 @@ cd /home/system/xiaozhi
./update_8002.sh
# 更新并启动python程序
./update_8000.sh
# 查看Java日志
# 后期想查看java日志,执行以下命令
tail -f nohup.out
# 查看Python日志
# 后期想查看python日志,执行以下命令
tail -f /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/tmp/server.log
```
# 注意事项
测试平台`https://2662r3426b.vicp.fun`,是使用nginx做了反向代理。nginx.conf详细配置可以[参考这里](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)
## 常见问题
### 1、为什么没有见到8001端口?
回答:8001是开发环境使用的,用于运行前端的端口。如果你是服务器部署,不建议使用`npm run serve`启动8001端口运行前端,而是像本教程一样编译成html文件,然后使用nginx来管理访问。
### 2、每次更新需要更新手动SQL语句吗?
回答:不需要,因为项目使用**Liquibase**管理数据库版本,会自动执行新的sql脚本。
Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

+165
View File
@@ -0,0 +1,165 @@
# IndexStreamTTS 使用指南
## 环境准备
### 1. 克隆项目 (这里使用的为VLLM的版本)
```bash
git clone https://github.com/Ksuriuri/index-tts-vllm.git
cd index-tts-vllm
```
### 2. 创建并激活 conda 环境
```bash
conda create -n index-tts-vllm python=3.12
conda activate index-tts-vllm
```
### 3. 安装PyTorch
#### 查看显卡最高支持的版本和实际安装的版本
```bash
nvidia-smi
nvcc --version
```
#### 驱动支持的最高 CUDA 版本
```bash
CUDA Version: 12.8
```
#### 实际安装的 CUDA 编译器版本
```bash
Cuda compilation tools, release 12.8, V12.8.89
```
#### 那么对应的安装命令 (请注意不要横跨大版本!!!)
```bash
pip install torch==2.7.0 torchvision==0.22.0 torchaudio==2.7.0 --index-url https://download.pytorch.org/whl/cu128
```
优先建议安装 pytorch 2.7.0(对应 vllm 0.9.0),具体安装指令请参考:[pytorch 官网](https://pytorch.org/get-started/locally/]\)
若显卡不支持,请安装 pytorch 2.5.1(对应 vllm 0.7.3),并将 requirements.txt 中 vllm==0.9.0 修改为 vllm==0.7.3
### 4. 安装依赖
```bash
pip install -r requirements.txt
```
### 5. 下载模型权重
此为官方权重文件,下载到本地任意路径即可,支持 IndexTTS-1.5 的权重
| HuggingFace | ModelScope |
|---------------------------------------------------------------|---------------------------------------------------------------------|
| [IndexTTS](https://huggingface.co/IndexTeam/Index-TTS) | [IndexTTS](https://modelscope.cn/models/IndexTeam/Index-TTS) |
| [IndexTTS-1.5](https://huggingface.co/IndexTeam/IndexTTS-1.5) | [IndexTTS-1.5](https://modelscope.cn/models/IndexTeam/IndexTTS-1.5) |
下面以ModelScope的安装方法为例
### 请注意:git需要安装并初始化启用lfs(如已安装可以跳过)
```bash
sudo apt-get install git-lfs
git lfs install
```
创建模型目录,并拉取模型
```bash
mkkdir model_dir
cd model_dir
git clone https://www.modelscope.cn/IndexTeam/IndexTTS-1.5.git
```
### 5. 模型权重转换
```bash
bash convert_hf_format.sh /path/to/your/model_dir
```
例如:你下载的IndexTTS-1.5模型存放在model_dir目录下,则执行以下命令
```bash
bash convert_hf_format.sh model_dir/IndexTTS-1.5
```
此操作会将官方的模型权重转换为 transformers 库兼容的版本,保存在模型权重路径下的 vllm 文件夹中,方便后续 vllm 库加载模型权重
### 6. 更改接口适配一下项目
接口返回数据与项目不适配需要调整一下,使其直接返回音频数据
```bash
@app.post("/tts", responses={
200: {"content": {"application/octet-stream": {}}},
500: {"content": {"application/json": {}}}
})
async def tts_api(request: Request):
try:
data = await request.json()
text = data["text"]
character = data["character"]
global tts
sr, wav = await tts.infer_with_ref_audio_embed(character, text)
return Response(content=wav.tobytes(), media_type="application/octet-stream")
except Exception as ex:
tb_str = ''.join(traceback.format_exception(type(ex), ex, ex.__traceback__))
print(tb_str)
return JSONResponse(
status_code=500,
content={
"status": "error",
"error": str(tb_str)
}
)
```
### 7.编写sh启动脚本(请注意要在相应的conda环境下运行)
```bash
vi start_api.sh
```
### 将下面内容粘贴进去并按:输入wq保存
#### 脚本中的/home/system/indexTTS/index-tts-vllm/model_dir/IndexTTS-1.5 请自行修改为实际路径
```bash
# 激活conda环境
conda activate index-tts-vllm
echo "激活项目conda环境"
sleep 2
# 查找占用11996端口的进程号
PID_VLLM=$(sudo netstat -tulnp | grep 11996 | awk '{print $7}' | cut -d'/' -f1)
# 检查是否找到进程号
if [ -z "$PID_VLLM" ]; then
echo "没有找到占用11996端口的进程"
else
echo "找到占用11996端口的进程,进程号为: $PID_VLLM"
# 先尝试普通kill,等待2秒
kill $PID_VLLM
sleep 2
# 检查进程是否还在
if ps -p $PID_VLLM > /dev/null; then
echo "进程仍在运行,强制终止..."
kill -9 $PID_VLLM
fi
echo "已终止进程 $PID_VLLM"
fi
# 创建tmp目录(如果不存在)
mkdir -p tmp
# 后台运行api_server.py,日志重定向到tmp/server.log
export VLLM_USE_V1=0
nohup python api_server.py --model_dir /home/system/indexTTS/index-tts-vllm/model_dir/IndexTTS-1.5 --port 11996 > tmp/server.log 2>&1 &
echo "api_server.py 已在后台运行,日志请查看 tmp/server.log"
```
给脚本执行权限并运行脚本
```bash
chmod +x tmp
./start_api.sh
```
日志会在tmp/server.log中输出,可以通过以下命令查看日志情况
```bash
tail -f tmp/server.log
```
## 音色配置
index-tts-vllm支持通过配置文件注册自定义音色,支持单音色和混合音色配置。
在项目根目录下的assets/speaker.json文件中配置自定义音色
### 配置格式说明
```bash
{
"说话人名称1": [
"音频文件路径1.wav",
"音频文件路径2.wav"
],
"说话人名称2": [
"音频文件路径3.wav"
]
}
```
### 注意
添加后需在智控台中添加相应的说话人(单模块则更换相应的voice)
+3 -3
View File
@@ -1,9 +1,9 @@
# MCP 接入点部署使用指南
本教程包含2个部分
本教程包含3个部分
- 1、如何部署MCP接入点这个服务
- 1、全模块部署时,怎么配置MCP接入点
- 2、单模块部署时,怎么配置MCP接入点
- 2、全模块部署时,怎么配置MCP接入点
- 3、单模块部署时,怎么配置MCP接入点
# 1、如何部署MCP接入点这个服务
+99
View File
@@ -0,0 +1,99 @@
# PaddleSpeechTTS集成xiaozhi服务
## 一、基础环境要求
操作系统:Windows / Linux / WSL 2
Python 版本:3.9以上(请根据Paddle官方教程调整)
Paddle 版本:官方最新版本 ```https://www.paddlepaddle.org.cn/install```
依赖管理工具:conda 或 venv
## 二、启动paddlespeech服务
### 1.从paddlespeech官方仓库拉取源码
```bash
git clone https://github.com/PaddlePaddle/PaddleSpeech.git
```
### 2.建立虚拟环境
```bash
#请根据Paddle官方支持的python版本建立环境 ```https://www.paddlepaddle.org.cn/install```
conda create -n paddle_env python=3.10 -y
conda activate paddle_env
```
### 3.进入paddlespeech目录
```bash
cd PaddleSpeech
```
### 4.安装paddlespeech
```bash
pip install pytest-runner -i https://pypi.tuna.tsinghua.edu.cn/simple
#以下命令使用任意一个
pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
pip install paddlespeech -i https://pypi.tuna.tsinghua.edu.cn/simple
```
### 5.使用命令自动下载语音模型
```bash
paddlespeech tts --input "你好,这是一次测试"
```
此步骤会自动下载模型缓存至本地 .paddlespeech/models 目录
### 6.修改tts_online_application.yaml配置
参考目录 ```"PaddleSpeech\demos\streaming_tts_server\conf\tts_online_application.yaml"```
选择```tts_online_application.yaml```文件用编辑器打开,设置```protocol```为```websocket```
### 7.启动服务
```yaml
paddlespeech_server start --config_file ./demos/streaming_tts_server/conf/tts_online_application.yaml
#官方默认启动命令:
paddlespeech_server start --config_file ./conf/tts_online_application.yaml
```
请根据你的```tts_online_application.yaml```的实际目录来启动命令,看到如下日志即启动成功
```
Prefix dict has been built successfully.
[2025-08-07 10:03:11,312] [ DEBUG] __init__.py:166 - Prefix dict has been built successfully.
INFO: Started server process [2298]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8092 (Press CTRL+C to quit)
```
## 三、修改小智的配置文件
### 1.```main/xiaozhi-server/core/providers/tts/paddle_speech.py```
### 2.```main/xiaozhi-server/data/.config.yaml```
使用单模块部署
```yaml
selected_module:
TTS: PaddleSpeechTTS
TTS:
PaddleSpeechTTS:
type: paddle_speech
protocol: websocket
url: ws://127.0.0.1:8092/paddlespeech/tts/streaming # TTS 服务的 URL 地址,指向本地服务器 [websocket默认ws://127.0.0.1:8092/paddlespeech/tts/streaming]
spk_id: 0 # 发音人 ID,0 通常表示默认的发音人
sample_rate: 24000 # 采样率 [websocket默认24000http默认0 自动选择]
speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢
volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小
save_path: # 保存路径
```
### 3.启动xiaozhi服务
```py
python app.py
```
打开test目录下的test_page.html,测试连接和发送消息时paddlespeech端是否有输出日志
输出日志参考:
```
INFO: 127.0.0.1:44312 - "WebSocket /paddlespeech/tts/streaming" [accepted]
INFO: connection open
[2025-08-07 11:16:33,355] [ INFO] - sentence: 哈哈,怎么突然找我聊天啦?
[2025-08-07 11:16:33,356] [ INFO] - The durations of audio is: 2.4625 s
[2025-08-07 11:16:33,356] [ INFO] - first response time: 0.1143045425415039 s
[2025-08-07 11:16:33,356] [ INFO] - final response time: 0.4777836799621582 s
[2025-08-07 11:16:33,356] [ INFO] - RTF: 0.19402382942625715
[2025-08-07 11:16:33,356] [ INFO] - Other info: front time: 0.06514096260070801 s, first am infer time: 0.008037090301513672 s, first voc infer time: 0.04112648963928223 s,
[2025-08-07 11:16:33,356] [ INFO] - Complete the synthesis of the audio streams
INFO: connection closed
```
+27
View File
@@ -0,0 +1,27 @@
# 语音识别、大语言模型、非流式语音合成、流式语音合成、视觉模型的性能测试工具使用指南
1.在main/xiaozhi-server目录下创建data目录
2.在data目录下创建.config.yaml文件
3.在.data/config.yaml中,写入你的语音识别、大语言模型、流式语音合成、视觉模型的参数
例如:
```
LLM:
ChatGLMLLM:
# 定义LLM API类型
type: openai
# glm-4-flash 是免费的,但是还是需要注册填写api_key的
# 可在这里找到你的api key https://bigmodel.cn/usercenter/proj-mgmt/apikeys
model_name: glm-4-flash
url: https://open.bigmodel.cn/api/paas/v4/
api_key: 你的chat-glm web key
TTS:
VLLM:
ASR:
```
4.在main/xiaozhi-server目录下运行performance_tester.py:
```
python performance_tester.py
```
+233
View File
@@ -0,0 +1,233 @@
# 声纹识别启用指南
本教程包含3个部分
- 1、如何部署声纹识别这个服务
- 2、全模块部署时,怎么配置声纹识别接口
- 3、最简化部署时,怎么配置声纹识别
# 1、如何部署声纹识别这个服务
## 第一步,下载声纹识别项目源码
浏览器打开[声纹识别项目地址](https://github.com/xinnan-tech/voiceprint-api)
打开完,找到页面中一个绿色的按钮,写着`Code`的按钮,点开它,然后你就看到`Download ZIP`的按钮。
点击它,下载本项目源码压缩包。下载到你电脑后,解压它,此时它的名字可能叫`voiceprint-api-main`
你需要把它重命名成`voiceprint-api`
## 第二步, 创建数据库和表
声纹识别需要依赖`mysql`数据库。如果你之前已经部署`智控台`,说明你已经安装了`mysql`。你可以共用它。
你可以你试一下在宿主机使用`telnet`命令,看看能不能正常访问`mysql``3306`端口。
```
telnet 127.0.0.1 3306
```
如果能访问到3306端口,请忽略以下的内容,直接进入第三步。
如果不能访问,你需要回忆一下,你的`mysql`是怎么安装的。
如果你的mysql是通过自己使用安装包安装的,说明你的`mysql`做了网络隔离。你可能先解访问`mysql``3306`端口这个问题。
如果你`mysql`是通过本项目的`docker-compose_all.yml`安装的。你需要找一下你当时创建数据库的`docker-compose_all.yml`文件,修改以下的内容
修改前
```
xiaozhi-esp32-server-db:
...
networks:
- default
expose:
- "3306:3306"
```
修改后
```
xiaozhi-esp32-server-db:
...
networks:
- default
ports:
- "3306:3306"
```
注意是将`xiaozhi-esp32-server-db`下面的`expose`改成`ports`。改完后,需要重新启动。以下是重启mysql的命令:
```
# 进入你docker-compose_all.yml所在的文件夹,例如我的是xiaozhi-server
cd xiaozhi-server
docker compose -f docker-compose_all.yml down
docker compose -f docker-compose.yml up -d
```
启动完后,在宿主机再使用`telnet`命令,看看能不能正常访问`mysql``3306`端口。
```
telnet 127.0.0.1 3306
```
正常来说这样就可以访问的了。
## 第三步, 创建数据库和表
如果你的宿主机,能正常访问mysql数据库,那就在mysql上创建一个名字为`voiceprint_db`的数据库和`voiceprints`表。
```
CREATE DATABASE voiceprint_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE voiceprint_db;
CREATE TABLE voiceprints (
id INT AUTO_INCREMENT PRIMARY KEY,
speaker_id VARCHAR(255) NOT NULL UNIQUE,
feature_vector LONGBLOB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_speaker_id (speaker_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
## 第四步, 配置数据库连接
进入`voiceprint-api`文件夹,创建名字为`data`的文件夹。
`voiceprint-api`根目录里的`voiceprint.yaml`,复制到`data`的文件夹,将它重命名为`.voiceprint.yaml`
接下来,你需要重点配置一下`.voiceprint.yaml`里的数据库连接。
```
mysql:
host: "127.0.0.1"
port: 3306
user: "root"
password: "your_password"
database: "voiceprint_db"
```
注意!由于你的声纹识别服务是使用docker部署,`host`需要填写成你`mysql所在机器的局域网ip`
注意!由于你的声纹识别服务是使用docker部署,`host`需要填写成你`mysql所在机器的局域网ip`
注意!由于你的声纹识别服务是使用docker部署,`host`需要填写成你`mysql所在机器的局域网ip`
## 第五步,启动程序
这个项目是一个很简单的项目,建议使用docker运行。不过如果你不想使用docker运行,你可以参考[这个页面](https://github.com/xinnan-tech/voiceprint-api/blob/main/README.md)使用源码运行。以下是docker运行的方法
```
# 进入本项目源码根目录
cd voiceprint-api
# 清除缓存
docker compose -f docker-compose.yml down
docker stop voiceprint-api
docker rm voiceprint-api
docker rmi ghcr.nju.edu.cn/xinnan-tech/voiceprint-api:latest
# 启动docker容器
docker compose -f docker-compose.yml up -d
# 查看日志
docker logs -f voiceprint-api
```
此时,日志里会输出类似以下的日志
```
250711 INFO-🚀 开始: 生产环境服务启动(Uvicorn),监听地址: 0.0.0.0:8005
250711 INFO-============================================================
250711 INFO-声纹接口地址: http://127.0.0.1:8005/voiceprint/health?key=abcd
250711 INFO-============================================================
```
请你把声纹接口地址复制出来:
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
由于你是docker部署,切不可直接使用上面的地址!
你先把地址复制出来,放在一个草稿里,你要知道你的电脑的局域网ip是什么,例如我的电脑局域网ip是`192.168.1.25`,那么
原来我的接口地址
```
http://127.0.0.1:8005/voiceprint/health?key=abcd
```
就要改成
```
http://192.168.1.25:8005/voiceprint/health?key=abcd
```
改好后,请使用浏览器直接访问`声纹接口地址`。当浏览器出现类似这样的代码,说明是成功了。
```
{"total_voiceprints":0,"status":"healthy"}
```
请你保留好修改后的`声纹接口地址`,下一步要用到。
# 2、全模块部署时,怎么配置声纹识别
## 第一步 配置接口
如果你是全模块部署,使用管理员账号,登录智控台,点击顶部`参数字典`,选择`参数管理`功能。
然后搜索参数`server.voice_print`,此时,它的值应该是`null`值。
点击修改按钮,把上一步得来的`声纹接口地址`粘贴到`参数值`里。然后保存。
如果能保存成功,说明一切顺利,你可以去智能体查看效果了。如果不成功,说明智控台无法访问声纹识别,很大概率是网络防火墙,或者没有填写正确的局域网ip。
## 第二步 设置智能体记忆模式
进入你的智能体的角色配置里,将记忆设置成`本地短期记忆`,一定要开启`上报文字+语音`
## 第三步 和你的智能体聊天
将你的设备通电,然后和他用正常的语速和音调聊天。
## 第四步 设置声纹
在智控台,`智能体管理`页面,在智能体的面板里,有一个`声纹识别`按钮,点击它。在底部有一个`新增按钮`。就可以对某个人说的话进行声纹注册。
在弹出的框里,`描述`这个属性建议填写上,可以是这个人的职业、性格、爱好。方便智能体对说话人进行分析和了解。
## 第三步 和你的智能体聊天
将你的设备通电,问它,你知道我是谁吗?如果他能回答得出,说明声纹识别功能正常。
# 3、最简化部署时,怎么配置声纹识别
## 第一步 配置接口
打开 `xiaozhi-server/data/.config.yaml` 文件(如果没有需要创建),然后添加/修改以下内容:
```
# 声纹识别配置
voiceprint:
# 声纹接口地址
url: 你的声纹接口地址
# 说话人配置:speaker_id,名称,描述
speakers:
- "test1,张三,张三是一个程序员"
- "test2,李四,李四是一个产品经理"
- "test3,王五,王五是一个设计师"
```
把上一步得来的 `声纹接口地址` 粘贴到 `url` 里。然后保存。
`speakers` 参数依据需求添加。这里需要注意这个 `speaker_id` 参数,后面注册声纹会用到。
## 第二步 注册声纹
如果你已经启动了声纹服务,本地浏览器里访问 `http://localhost:8005/voiceprint/docs` 即可查看 API 文档,这里只说明注册声纹的 API 如何使用。
注册声纹的 API 地址为 `http://localhost:8005/voiceprint/register`,请求方式为 POST。
请求头需要包含 Bearer Token 认证,token 为 `声纹接口地址``?key=` 后的部分,比如如果我的声纹注册地址为 `http://127.0.0.1:8005/voiceprint/health?key=abcd`,那么我的 token 就是`abcd`
请求体包含说话人 IDspeaker_id),和 WAV 音频文件(file),请求示例如下:
```
curl -X POST \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "speaker_id=your_speaker_id_here" \
-F "file=@/path/to/your/file" \
http://localhost:8005/voiceprint/register
```
这里的 `file` 是要注册的说话人说话的音频文件, `speaker_id` 需要和第一步配置接口的 `speaker_id` 保持一致。比如说我需要注册张三的声纹,在 `.config.yaml` 中填的张三的 `speaker_id``test1`,那么我注册张三声纹的时候,请求体里填的 `speaker_id` 就是 `test1` `file` 填的就是张三说一段话的音频文件。
## 第三步 启动服务
启动小智服务器和声纹服务,即可正常使用。
+72
View File
@@ -8,6 +8,7 @@
* [3.1. `xiaozhi-server` (核心AI引擎 - Python实现)](#31-xiaozhi-server-核心ai引擎---python实现)
* [3.2. `manager-api` (管理后端 - Java Spring Boot实现)](#32-manager-api-管理后端---java-spring-boot实现)
* [3.3. `manager-web` (Web管理前端 - Vue.js实现)](#33-manager-web-web管理前端---vuejs实现)
* [3.4. `manager-mobile` (移动管理端 - uni-app+Vue3实现)](#34-manager-mobile-移动管理端---uni-appvue3实现)
4. [数据流与交互机制](#4-数据流与交互机制)
5. [核心功能概要](#5-核心功能概要)
6. [部署与配置概述](#6-部署与配置概述)
@@ -58,6 +59,14 @@
* (潜在功能)监控系统运行状态、查看日志、进行故障排查等。
*`manager-api` 提供的所有后端管理功能进行全面的交互。
5. **`manager-mobile` (智控台移动版 - uni-app实现):**
这是一个基于uni-app v3 + Vue 3 + Vite的跨端移动管理端,支持AppAndroid & iOS)和微信小程序。其主要能力包括:
* 提供移动设备上的便捷管理界面,与manager-web功能类似但针对移动端进行了优化。
* 支持用户登录、设备管理、AI服务配置等核心功能。
* 跨平台适配,一套代码可同时运行在iOS、Android和微信小程序上。
* 基于alova + @alova/adapter-uniapp实现网络请求,与manager-api无缝集成。
* 使用pinia进行状态管理,确保数据一致性。
**高层交互流程概述:**
* **语音交互主线:** **ESP32设备**捕捉到用户语音后,通过**WebSocket**将音频数据实时传输给**`xiaozhi-server`**。`xiaozhi-server`完成一系列AI处理(VAD、ASR、LLM交互、TTS)后,再通过WebSocket将合成的语音回复发送回ESP32设备进行播放。所有与语音直接相关的实时交互均在此链路完成。
@@ -71,6 +80,7 @@ xiaozhi-esp32-server
├─ xiaozhi-server 8000 端口 Python语言开发 负责与esp32通信
├─ manager-web 8001 端口 Node.js+Vue开发 负责提供控制台的web界面
├─ manager-api 8002 端口 Java语言开发 负责提供控制台的api
└─ manager-mobile 跨平台移动应用 uni-app+Vue3开发 负责提供移动版智控台管理
```
---
@@ -294,6 +304,68 @@ xiaozhi-esp32-server
---
### 3.4. `manager-mobile` (智控台移动版 - uni-app实现)
`manager-mobile` 组件是一个基于uni-app v3 + Vue 3 + Vite的跨端移动管理端,支持AppAndroid & iOS)和微信小程序。它为系统管理员提供了移动端的管理界面,使得管理操作更加便捷。
* **核心目标:**
* 提供移动设备上的便捷管理界面,与manager-web功能类似但针对移动端进行了优化。
* 支持用户登录、设备管理、AI服务配置等核心功能。
* 跨平台适配,一套代码可同时运行在iOS、Android和微信小程序上。
* 为移动用户提供流畅、高效的管理体验。
* **平台兼容性:**
| H5 | iOS | Android | 微信小程序 |
| -- | --- | ------- | ---------- |
| √ | √ | √ | √ |
* **核心技术栈:**
* **uni-app v3:** 一个使用Vue.js开发所有前端应用的框架,支持iOS、Android、H5、以及各种小程序。
* **Vue 3:** 用于构建用户界面的渐进式框架,提供了更好的性能和新特性。
* **Vite:** 下一代前端开发与构建工具,提供极速的开发体验。
* **pnpm:** 快速、节省磁盘空间的包管理器。
* **alova:** 轻量级、灵活的请求策略库,搭配@alova/adapter-uniapp适配uni-app环境。
* **pinia:** Vue的状态管理库,替代Vuex,提供更简洁的API和更好的TypeScript支持。
* **UnoCSS:** 具有高性能且极具灵活性的即时原子化CSS引擎。
* **TypeScript:** 提供类型安全的开发体验。
* **关键实现细节:**
1. **跨平台架构:**
* 基于uni-app框架,实现了一套代码多端运行的目标,大幅减少了开发和维护成本。
* 针对不同平台的特性和限制,通过条件编译进行平台特定的代码处理。
2. **项目结构:**
* **`src/App.vue`:** 应用的根组件,定义了全局的样式和配置。
* **`src/main.ts`:** 应用的入口文件,负责初始化Vue实例、注册插件和路由拦截器。
* **`src/pages/`:** 存放应用的页面组件,如登录页、设备管理页等。
* **`src/layouts/`:** 定义应用的布局组件,如默认布局、带tabbar的布局等。
* **`src/api/`:** 封装与后端API的通信逻辑。
* **`src/store/`:** 使用pinia进行状态管理。
* **`src/components/`:** 存放可复用的组件。
* **`src/utils/`:** 提供通用的工具函数。
3. **网络请求:**
* 基于alova + @alova/adapter-uniapp实现网络请求,统一处理请求头、认证、错误等。
* 请求地址和环境配置通过.env文件管理,支持不同环境的切换。
4. **路由与鉴权:**
* 使用uni-app的路由系统,结合路由拦截器实现页面的登录验证和权限控制。
* 未登录用户访问需要认证的页面时,会被重定向到登录页。
5. **状态管理:**
* 使用pinia管理应用状态,如用户信息、设备列表等。
* 通过pinia-plugin-persistedstate插件实现状态的持久化存储。
6. **构建与发布:**
* 支持多种构建命令,如构建微信小程序、Android和iOS App等。
* 使用HBuilderX进行App的云打包,简化了打包流程。
`manager-mobile` 通过这些技术的应用,为用户提供了一个功能完备、体验流畅的移动端管理工具,使得管理员可以随时随地进行系统管理和配置。
---
## 4. 数据流与交互机制
`xiaozhi-esp32-server` 系统通过各组件间定义清晰的数据流和交互协议来协同工作。主要的通信方式依赖于针对实时交互优化的WebSocket协议和适用于客户端-服务器请求的RESTful API。
+66 -1
View File
@@ -8,6 +8,7 @@
* [3.1. `xiaozhi-server` (Core AI Engine - Python Implementation)](#31-xiaozhi-server-core-ai-engine---python-implementation)
* [3.2. `manager-api` (Management Backend - Java Spring Boot Implementation)](#32-manager-api-management-backend---java-spring-boot-implementation)
* [3.3. `manager-web` (Web Management Frontend - Vue.js Implementation)](#33-manager-web-web-management-frontend---vuejs-implementation)
* [3.4. `manager-mobile` (Mobile Management Console - uni-app Implementation)](#34-manager-mobile-mobile-management-console---uniapp-implementation)
4. [Data Flow and Interaction Mechanisms](#4-data-flow-and-interaction-mechanisms)
5. [Key Features Summary](#5-key-features-summary)
6. [Deployment and Configuration Overview](#6-deployment-and-configuration-overview)
@@ -72,6 +73,7 @@ xiaozhi-esp32-server
├─ xiaozhi-server Port 8000 Python development Responsible for ESP32 communication
├─ manager-web Port 8001 Node.js+Vue development Responsible for providing web interface for console
├─ manager-api Port 8002 Java development Responsible for providing console API
└─ manager-mobile Cross-platform mobile application uni-app+Vue3 development Responsible for providing mobile console management
```
---
@@ -190,7 +192,70 @@ The `manager-web` is a Single Page Application (SPA) providing the administrativ
4. **State Management (`src/store/index.js`):** Vuex manages global state (user info, device lists, etc.) via state, getters, mutations, and actions (often involving API calls).
5. **API Communication (`src/apis/`):** Modularized API service files make asynchronous calls to `manager-api`.
6. **Build Process & PWA Features:** Vue CLI (Webpack) bundles assets. Workbox enables PWA features like caching.
7. **Environment Configuration (`.env` files):** Manages settings like the `manager-api` base URL for different environments.
7. **Environment Configuration (`.env` files):**
* The `.env` (and `.env.development`, `.env.production`, etc.) files in the project root directory are used to define environment variables. These variables (such as `VUE_APP_API_BASE_URL` to specify the base URL of `manager-api`) can be accessed in the application code through `process.env.VUE_APP_XXX`, allowing configuration of different parameters for different build environments (development, testing, production).
`manager-web` constructs a powerful, maintainable, and user-friendly management interface through the comprehensive application of these technologies, providing solid frontend support for the configuration and monitoring of the `xiaozhi-esp32-server` system.
### 3.4. `manager-mobile` (Mobile Management Console - uni-app Implementation)
The `manager-mobile` component is a cross-platform mobile management application based on uni-app v3 + Vue 3 + Vite, supporting App (Android & iOS) and WeChat Mini Program. It provides system administrators with a mobile management interface, making management operations more convenient.
* **Core Objectives:**
* Provide a convenient management interface on mobile devices, similar in functionality to manager-web but optimized for mobile platforms.
* Support core functions such as user login, device management, and AI service configuration.
* Cross-platform adaptation, allowing a single codebase to run on iOS, Android, and WeChat Mini Programs.
* Provide mobile users with a smooth and efficient management experience.
* **Platform Compatibility:**
| H5 | iOS | Android | WeChat Mini Program |
| -- | --- | ------- | ------------------ |
| × | √ | √ | √ |
* **Core Technologies:**
* **uni-app v3:** A framework for developing all frontend applications using Vue.js, supporting iOS, Android, H5, and various mini-programs.
* **Vue 3:** A progressive framework for building user interfaces, providing better performance and new features.
* **Vite:** The next generation frontend development and build tool, offering an extremely fast development experience.
* **pnpm:** A fast, disk space-efficient package manager.
* **alova:** A lightweight, flexible request strategy library, paired with @alova/adapter-uniapp to adapt to the uni-app environment.
* **pinia:** State management library for Vue, replacing Vuex, providing a simpler API and better TypeScript support.
* **UnoCSS:** A high-performance and extremely flexible instant atomic CSS engine.
* **TypeScript:** Provides a type-safe development experience.
* **Key Implementation Details:**
1. **Cross-Platform Architecture:**
* Based on the uni-app framework, achieving the goal of "write once, run anywhere," significantly reducing development and maintenance costs.
* Handling platform-specific code through conditional compilation to address the characteristics and limitations of different platforms.
2. **Project Structure:**
* **`src/App.vue`:** The root component of the application, defining global styles and configurations.
* **`src/main.ts`:** The entry file of the application, responsible for initializing the Vue instance, registering plugins, and setting up route interceptors.
* **`src/pages/`:** Stores application page components, such as login pages, device management pages, etc.
* **`src/layouts/`:** Defines application layout components, such as default layouts, layouts with tabbar, etc.
* **`src/api/`:** Encapsulates communication logic with backend APIs.
* **`src/store/`:** Uses pinia for state management.
* **`src/components/`:** Stores reusable components.
* **`src/utils/`:** Provides common utility functions.
3. **Network Requests:**
* Implements network requests based on alova + @alova/adapter-uniapp, unified handling of request headers, authentication, errors, etc.
* Request addresses and environment configurations are managed through .env files, supporting switching between different environments.
4. **Routing and Authentication:**
* Uses uni-app's routing system, combined with route interceptors to implement page login verification and permission control.
* When unlogged users access pages requiring authentication, they are redirected to the login page.
5. **State Management:**
* Uses pinia to manage application state, such as user information, device lists, etc.
* Implements persistent storage of state through the pinia-plugin-persistedstate plugin.
6. **Build and Release:**
* Supports multiple build commands, such as building WeChat Mini Programs, Android, and iOS Apps.
* Uses HBuilderX for cloud packaging of Apps, simplifying the packaging process.
`manager-mobile` provides users with a fully functional, smooth mobile management tool through the application of these technologies, allowing administrators to perform system management and configuration anytime, anywhere.
---
+16
View File
@@ -24,6 +24,8 @@
<hutool.version>5.8.24</hutool.version>
<jsoup.version>1.19.1</jsoup.version>
<knife4j.version>4.6.0</knife4j.version>
<springdoc.version>2.8.8</springdoc.version>
<commons-lang3.version>3.18.0</commons-lang3.version>
<shiro.version>2.0.2</shiro.version>
<captcha.version>1.6.2</captcha.version>
<guava.version>33.0.0-jre</guava.version>
@@ -75,6 +77,7 @@
<version>${captcha.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
@@ -190,11 +193,24 @@
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<!--knife4j -->
<dependency>
<groupId>com.github.xingfudeshi</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- springdoc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- 日常工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@@ -116,6 +116,11 @@ public interface Constant {
*/
String SERVER_MCP_ENDPOINT = "server.mcp_endpoint";
/**
* mcp接入点路径
*/
String SERVER_VOICE_PRINT = "server.voice_print";
/**
* 无记忆
*/
@@ -232,7 +237,7 @@ public interface Constant {
/**
* 版本号
*/
public static final String VERSION = "0.6.3";
public static final String VERSION = "0.7.6";
/**
* 无效固件URL
@@ -0,0 +1,21 @@
package xiaozhi.common.utils;
import lombok.Data;
/**
* JSON-RPC2.0 格式规范对象
*/
@Data
public class JsonRpcTwo {
private String jsonrpc = "2.0";
private String method;
private Object params;
private Integer id;
public JsonRpcTwo(String method, Object params, Integer id) {
this.method = method;
this.params = params;
this.id = id;
}
}
@@ -0,0 +1,21 @@
package xiaozhi.modules.agent.Enums;
import lombok.Getter;
/**
* 智能体聊天记录类型
*/
@Getter
public enum AgentChatHistoryType {
USER((byte) 1),
AGENT((byte) 2);
private final byte value;
AgentChatHistoryType(byte i) {
this.value = i;
}
}
@@ -0,0 +1,44 @@
package xiaozhi.modules.agent.Enums;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.JsonRpcTwo;
import java.util.Map;
/**
* 小智MCP JSON-RPC 请求json
*/
public class XiaoZhiMcpJsonRpcJson {
//小智初始化mcp请求json
private static final String INITIALIZE_JSON;
//小智mcp初始化成功,返回通知请求json
private static final String NOTIFICATIONS_INITIALIZED_JSON;
//小智mcp获取mcp工具集合请求json
private static final String TOOLS_LIST_REQUEST;
// 延迟加载
static {
INITIALIZE_JSON = JsonUtils.toJsonString(new JsonRpcTwo("initialize",
Map.of(
"protocolVersion", "2024-11-05",
"capabilities", Map.of(
"roots", Map.of("listChanged", false),
"sampling", Map.of()),
"clientInfo", Map.of(
"name", "xz-mcp-broker",
"version", "0.0.1")),
1));
NOTIFICATIONS_INITIALIZED_JSON = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
TOOLS_LIST_REQUEST = JsonUtils.toJsonString(new JsonRpcTwo("tools/list", null, 2));
}
public static String getInitializeJson(){
return INITIALIZE_JSON;
}
public static String getNotificationsInitializedJson(){
return NOTIFICATIONS_INITIALIZED_JSON;
}
public static String getToolsListJson(){
return TOOLS_LIST_REQUEST;
}
}
@@ -47,6 +47,7 @@ import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
@@ -181,6 +182,33 @@ public class AgentController {
List<AgentChatHistoryDTO> result = agentChatHistoryService.getChatHistoryBySessionId(id, sessionId);
return new Result<List<AgentChatHistoryDTO>>().ok(result);
}
@GetMapping("/{id}/chat-history/user")
@Operation(summary = "获取智能体聊天记录(用户)")
@RequiresPermissions("sys:role:normal")
public Result<List<AgentChatHistoryUserVO>> getRecentlyFiftyByAgentId(
@PathVariable("id") String id) {
// 获取当前用户
UserDetail user = SecurityUser.getUser();
// 检查权限
if (!agentService.checkAgentPermission(id, user.getId())) {
return new Result<List<AgentChatHistoryUserVO>>().error("没有权限查看该智能体的聊天记录");
}
// 查询聊天记录
List<AgentChatHistoryUserVO> data = agentChatHistoryService.getRecentlyFiftyByAgentId(id);
return new Result<List<AgentChatHistoryUserVO>>().ok(data);
}
@GetMapping("/{id}/chat-history/audio")
@Operation(summary = "获取音频内容")
@RequiresPermissions("sys:role:normal")
public Result<String> getContentByAudioId(
@PathVariable("id") String id) {
// 查询聊天记录
String data = agentChatHistoryService.getContentByAudioId(id);
return new Result<String>().ok(data);
}
@PostMapping("/audio/{audioId}")
@Operation(summary = "获取音频下载ID")
@@ -0,0 +1,86 @@
package xiaozhi.modules.agent.controller;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
import xiaozhi.modules.agent.service.AgentVoicePrintService;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
import xiaozhi.modules.security.user.SecurityUser;
import xiaozhi.modules.sys.service.SysParamsService;
@Tag(name = "智能体声纹管理")
@AllArgsConstructor
@RestController
@RequestMapping("/agent/voice-print")
public class AgentVoicePrintController {
private final AgentVoicePrintService agentVoicePrintService;
private final SysParamsService sysParamsService;
@PostMapping
@Operation(summary = "创建智能体的声纹")
@RequiresPermissions("sys:role:normal")
public Result<Void> save(@RequestBody @Valid AgentVoicePrintSaveDTO dto) {
boolean b = agentVoicePrintService.insert(dto);
if (b) {
return new Result<>();
}
return new Result<Void>().error("智能体的声纹创建失败");
}
@PutMapping
@Operation(summary = "更新智能体的对应声纹")
@RequiresPermissions("sys:role:normal")
public Result<Void> update(@RequestBody @Valid AgentVoicePrintUpdateDTO dto) {
Long userId = SecurityUser.getUserId();
boolean b = agentVoicePrintService.update(userId, dto);
if (b) {
return new Result<>();
}
return new Result<Void>().error("智能体的对应声纹更新失败");
}
@DeleteMapping("/{id}")
@Operation(summary = "删除智能体对应声纹")
@RequiresPermissions("sys:role:normal")
public Result<Void> delete(@PathVariable String id) {
Long userId = SecurityUser.getUserId();
// 先删除关联的设备
boolean delete = agentVoicePrintService.delete(userId, id);
if (delete) {
return new Result<>();
}
return new Result<Void>().error("智能体的对应声纹删除失败");
}
@GetMapping("/list/{id}")
@Operation(summary = "获取用户指定智能体声纹列表")
@RequiresPermissions("sys:role:normal")
public Result<List<AgentVoicePrintVO>> list(@PathVariable String id) {
String voiceprintUrl = sysParamsService.getValue("server.voice_print", true);
if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
throw new RenException("声纹接口未配置,请先在参数配置中配置声纹接口地址(server.voice_print)");
}
Long userId = SecurityUser.getUserId();
List<AgentVoicePrintVO> list = agentVoicePrintService.list(userId, id);
return new Result<List<AgentVoicePrintVO>>().ok(list);
}
}
@@ -0,0 +1,20 @@
package xiaozhi.modules.agent.dao;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
/**
* {@link AgentChatHistoryEntity} 智能体聊天历史记录Dao对象
*
* @author Goody
* @version 1.0, 2025/4/30
* @since 1.0.0
*/
@Mapper
public interface AgentVoicePrintDao extends BaseMapper<AgentVoicePrintEntity> {
}
@@ -0,0 +1,28 @@
package xiaozhi.modules.agent.dto;
import lombok.Data;
/**
* 保存智能体声纹的dto
*
* @author zjy
*/
@Data
public class AgentVoicePrintSaveDTO {
/**
* 关联的智能体id
*/
private String agentId;
/**
* 音频文件id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
}
@@ -0,0 +1,28 @@
package xiaozhi.modules.agent.dto;
import lombok.Data;
/**
* 修改智能体声纹的dto
*
* @author zjy
*/
@Data
public class AgentVoicePrintUpdateDTO {
/**
* 智能体声纹id
*/
private String id;
/**
* 音频文件id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
}
@@ -0,0 +1,21 @@
package xiaozhi.modules.agent.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 声纹识别接口返回的对象
*/
@Data
public class IdentifyVoicePrintResponse {
/**
* 最匹配的声纹id
*/
@JsonProperty("speaker_id")
private String speakerId;
/**
* 声纹的分数
*/
private Double score;
}
@@ -1,32 +0,0 @@
package xiaozhi.modules.agent.dto;
import lombok.Data;
/**
* MCP JSON-RPC 请求 DTO
*/
@Data
public class McpJsonRpcRequest {
private String jsonrpc = "2.0";
private String method;
private Object params;
private Integer id;
public McpJsonRpcRequest() {
}
public McpJsonRpcRequest(String method) {
this.method = method;
}
public McpJsonRpcRequest(String method, Object params, Integer id) {
this.method = method;
this.params = params;
this.id = id;
}
public McpJsonRpcRequest(String method, Object params) {
this.method = method;
this.params = params;
}
}
@@ -0,0 +1,64 @@
package xiaozhi.modules.agent.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 智能体声纹表
*
* @author zjy
*/
@TableName(value = "ai_agent_voice_print")
@Data
public class AgentVoicePrintEntity {
/**
* 主键id
*/
@TableId(type = IdType.ASSIGN_UUID)
private String id;
/**
* 关联的智能体id
*/
private String agentId;
/**
* 关联的音频id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long creator;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createDate;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updater;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateDate;
}
@@ -9,6 +9,7 @@ import xiaozhi.common.page.PageData;
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
/**
* 智能体聊天记录表处理service
@@ -44,4 +45,30 @@ public interface AgentChatHistoryService extends IService<AgentChatHistoryEntity
* @param deleteText 是否删除文本
*/
void deleteByAgentId(String agentId, Boolean deleteAudio, Boolean deleteText);
/**
* 根据智能体ID获取最近50条用户的聊天记录数据(带音频数据)
*
* @param agentId 智能体id
* @return 聊天记录列表(只有用户)
*/
List<AgentChatHistoryUserVO> getRecentlyFiftyByAgentId(String agentId);
/**
* 根据音频数据ID获取聊天内容
*
* @param audioId 音频id
* @return 聊天内容
*/
String getContentByAudioId(String audioId);
/**
* 查询此音频id是否属于此智能体
*
* @param audioId 音频id
* @param agentId 音频id
* @return T:属于 F:不属于
*/
boolean isAudioOwnedByAgent(String audioId,String agentId);
}
@@ -0,0 +1,50 @@
package xiaozhi.modules.agent.service;
import java.util.List;
import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
/**
* 智能体声纹处理service
*
* @author zjy
*/
public interface AgentVoicePrintService {
/**
* 添加智能体新的声纹
*
* @param dto 保存智能体声纹的数据
* @return T:成功 F:失败
*/
boolean insert(AgentVoicePrintSaveDTO dto);
/**
* 删除智能体的指的声纹
*
* @param userId 当前登录的用户id
* @param voicePrintId 声纹id
* @return 是否成功 T:成功 F:失败
*/
boolean delete(Long userId, String voicePrintId);
/**
* 获取指定智能体的所有声纹数据
*
* @param userId 当前登录的用户id
* @param agentId 智能体id
* @return 声纹数据集合
*/
List<AgentVoicePrintVO> list(Long userId, String agentId);
/**
* 更新智能体的指的声纹数据
*
* @param userId 当前登录的用户id
* @param dto 修改的声纹的数据
* @return 是否成功 T:成功 F:失败
*/
boolean update(Long userId, AgentVoicePrintUpdateDTO dto);
}
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -16,11 +17,14 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.page.PageData;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
/**
* 智能体聊天记录表处理service {@link AgentChatHistoryService} impl
@@ -90,4 +94,78 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryD
}
}
@Override
public List<AgentChatHistoryUserVO> getRecentlyFiftyByAgentId(String agentId) {
// 构建查询条件(不添加按照创建时间排序,数据本来就是主键越大创建时间越大
// 不添加这样可以减少排序全部数据在分页的全盘扫描消耗)
LambdaQueryWrapper<AgentChatHistoryEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.select(AgentChatHistoryEntity::getContent, AgentChatHistoryEntity::getAudioId)
.eq(AgentChatHistoryEntity::getAgentId, agentId)
.eq(AgentChatHistoryEntity::getChatType, AgentChatHistoryType.USER.getValue())
.isNotNull(AgentChatHistoryEntity::getAudioId)
// 添加此行,确保查询结果按照创建时间降序排列
// 使用id的原因:数据形式,id越大的创建时间就越晚,所以使用id的结果和创建时间降序排列结果一样
// id作为降序排列的优势,性能高,有主键索引,不用在排序的时候重新进行排除扫描比较
.orderByDesc(AgentChatHistoryEntity::getId);
// 构建分页查询,查询前50页数据
Page<AgentChatHistoryEntity> pageParam = new Page<>(0, 50);
IPage<AgentChatHistoryEntity> result = this.baseMapper.selectPage(pageParam, wrapper);
return result.getRecords().stream().map(item -> {
AgentChatHistoryUserVO vo = ConvertUtils.sourceToTarget(item, AgentChatHistoryUserVO.class);
// 处理 content 字段,确保只返回聊天内容
if (vo != null && vo.getContent() != null) {
vo.setContent(extractContentFromString(vo.getContent()));
}
return vo;
}).toList();
}
/**
* 从 content 字段中提取聊天内容
* 如果 content 是 JSON 格式(如 {"speaker": "未知说话人", "content": "现在几点了。"}),则提取 content
* 字段
* 如果 content 是普通字符串,则直接返回
*
* @param content 原始内容
* @return 提取的聊天内容
*/
private String extractContentFromString(String content) {
if (content == null || content.trim().isEmpty()) {
return content;
}
// 尝试解析为 JSON
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(content, Map.class);
if (jsonMap != null && jsonMap.containsKey("content")) {
Object contentObj = jsonMap.get("content");
return contentObj != null ? contentObj.toString() : content;
}
} catch (Exception e) {
// 如果不是有效的 JSON,直接返回原内容
}
// 如果不是 JSON 格式或没有 content 字段,直接返回原内容
return content;
}
@Override
public String getContentByAudioId(String audioId) {
AgentChatHistoryEntity agentChatHistoryEntity = baseMapper
.selectOne(new LambdaQueryWrapper<AgentChatHistoryEntity>()
.select(AgentChatHistoryEntity::getContent)
.eq(AgentChatHistoryEntity::getAudioId, audioId));
return agentChatHistoryEntity == null ? null : agentChatHistoryEntity.getContent();
}
@Override
public boolean isAudioOwnedByAgent(String audioId, String agentId) {
// 查询是否有指定音频id和智能体id的数据,如果有且只有一条说明此数据属性此智能体
Long row = baseMapper.selectCount(new LambdaQueryWrapper<AgentChatHistoryEntity>()
.eq(AgentChatHistoryEntity::getAudioId, audioId)
.eq(AgentChatHistoryEntity::getAgentId, agentId));
return row == 1;
}
}
@@ -18,7 +18,7 @@ import xiaozhi.common.constant.Constant;
import xiaozhi.common.utils.AESUtils;
import xiaozhi.common.utils.HashEncryptionUtil;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.dto.McpJsonRpcRequest;
import xiaozhi.modules.agent.Enums.XiaoZhiMcpJsonRpcJson;
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
import xiaozhi.modules.sys.service.SysParamsService;
import xiaozhi.modules.sys.utils.WebSocketClientManager;
@@ -61,57 +61,79 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
wsUrl = wsUrl.replace("/mcp/", "/call/");
try {
// 创建 WebSocket 连接
// 创建 WebSocket 连接,增加超时时间到15秒
try (WebSocketClientManager client = WebSocketClientManager.build(
new WebSocketClientManager.Builder()
.uri(wsUrl)
.connectTimeout(5, TimeUnit.SECONDS)
.maxSessionDuration(9, TimeUnit.SECONDS))) {
.bufferSize(1024 * 1024)
.connectTimeout(8, TimeUnit.SECONDS)
.maxSessionDuration(10, TimeUnit.SECONDS))) {
// 发送初始化消息
McpJsonRpcRequest initializeRequest = new McpJsonRpcRequest("initialize",
Map.of(
"protocolVersion", "2024-11-05",
"capabilities", Map.of(
"roots", Map.of("listChanged", false),
"sampling", Map.of()),
"clientInfo", Map.of(
"name", "xz-mcp-broker",
"version", "0.0.1")),
1);
client.sendJson(initializeRequest);
// 步骤1: 发送初始化消息并等待响应
log.info("发送MCP初始化消息,智能体ID: {}", id);
client.sendText(XiaoZhiMcpJsonRpcJson.getInitializeJson());
// 等待初始化响应
Thread.sleep(200);
// 发送初始化完成通知
// 对于通知类型的消息,手动构建JSON以避免包含null字段
String notificationJson = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
client.sendText(notificationJson);
// 等待 0.2 秒
Thread.sleep(200);
// 发送工具列表请求
McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 2);
client.sendJson(toolsRequest);
// 监听响应,直到收到包含 id=2 的响应(tools/list响应)
List<String> responses = client.listener(response -> {
// 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
List<String> initResponses = client.listenerWithoutClose(response -> {
try {
// 先尝试解析为通用JSON对象来获取id
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
// 检查是否有result字段,表示初始化成功
return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
}
return false;
} catch (Exception e) {
log.warn("解析响应失败: {}", response, e);
log.warn("解析初始化响应失败: {}", response, e);
return false;
}
});
// 处理响应
for (String response : responses) {
// 验证初始化响应
boolean initSucceeded = false;
for (String response : initResponses) {
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
if (jsonMap.containsKey("result")) {
log.info("MCP初始化成功,智能体ID: {}", id);
initSucceeded = true;
break;
} else if (jsonMap.containsKey("error")) {
log.error("MCP初始化失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
return List.of();
}
}
} catch (Exception e) {
log.warn("处理初始化响应失败: {}", response, e);
}
}
if (!initSucceeded) {
log.error("未收到有效的MCP初始化响应,智能体ID: {}", id);
return List.of();
}
// 步骤2: 发送初始化完成通知 - 只有在收到initialize响应后才发送
log.info("发送MCP初始化完成通知,智能体ID: {}", id);
client.sendText(XiaoZhiMcpJsonRpcJson.getNotificationsInitializedJson());
// 步骤3: 发送工具列表请求 - 立即发送,无需额外延迟
log.info("发送MCP工具列表请求,智能体ID: {}", id);
client.sendText(XiaoZhiMcpJsonRpcJson.getToolsListJson());
// 等待工具列表响应 (id=2)
List<String> toolsResponses = client.listener(response -> {
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
} catch (Exception e) {
log.warn("解析工具列表响应失败: {}", response, e);
return false;
}
});
// 处理工具列表响应
for (String response : toolsResponses) {
try {
// 先解析为通用JSON对象
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
// 检查是否有result字段
@@ -122,11 +144,16 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
if (toolsObj instanceof List) {
List<Map<String, Object>> toolsList = (List<Map<String, Object>>) toolsObj;
// 提取工具名称列表
return toolsList.stream()
List<String> result = toolsList.stream()
.map(tool -> (String) tool.get("name"))
.filter(name -> name != null)
.collect(Collectors.toList());
log.info("成功获取MCP工具列表,智能体ID: {}, 工具数量: {}", id, result.size());
return result;
}
} else if (jsonMap.containsKey("error")) {
log.error("获取工具列表失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
return List.of();
}
}
} catch (Exception e) {
@@ -134,12 +161,12 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
}
}
log.warn("未找到有效的工具列表响应");
log.warn("未找到有效的工具列表响应,智能体ID: {}", id);
return List.of();
}
} catch (Exception e) {
log.error("获取智能体 MCP 工具列表失败,智能体ID: {}", id, e);
log.error("获取智能体 MCP 工具列表失败,智能体ID: {},错误原因:{}", id, e.getMessage());
return List.of();
}
}
@@ -204,4 +231,4 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
// 加密后成token值
return AESUtils.encrypt(key, json);
}
}
}
@@ -41,6 +41,7 @@ import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.service.DeviceService;
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.security.user.SecurityUser;
@@ -324,9 +325,35 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
// 删除音频数据
agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, false);
}
boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId());
if (!b) {
throw new RenException("LLM大模型和Intent意图识别,选择参数不匹配");
}
this.updateById(existingEntity);
}
/**
* 验证大语言模型和意图识别的参数是否符合匹配
*
* @param llmModelId 大语言模型id
* @param intentModelId 意图识别id
* @return T 匹配 : F 不匹配
*/
private boolean validateLLMIntentParams(String llmModelId, String intentModelId) {
if (StringUtils.isBlank(llmModelId)) {
return true;
}
ModelConfigEntity llmModelData = modelConfigService.selectById(llmModelId);
String type = llmModelData.getConfigJson().get("type").toString();
// 如果查询大语言模型是openai或者ollama,意图识别选参数都可以
if ("openai".equals(type) || "ollama".equals(type)) {
return true;
}
// 除了openai和ollama的类型,不可以选择id为Intent_function_call(函数调用)的意图识别
return !"Intent_function_call".equals(intentModelId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createAgent(AgentCreateDTO dto) {
@@ -0,0 +1,410 @@
package xiaozhi.modules.agent.service.impl;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
import xiaozhi.modules.agent.dto.AgentVoicePrintSaveDTO;
import xiaozhi.modules.agent.dto.AgentVoicePrintUpdateDTO;
import xiaozhi.modules.agent.dto.IdentifyVoicePrintResponse;
import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
import xiaozhi.modules.agent.service.AgentChatAudioService;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentVoicePrintService;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
import xiaozhi.modules.sys.service.SysParamsService;
/**
* @author zjy
*/
@Service
@Slf4j
public class AgentVoicePrintServiceImpl extends ServiceImpl<AgentVoicePrintDao, AgentVoicePrintEntity>
implements AgentVoicePrintService {
private final AgentChatAudioService agentChatAudioService;
private final RestTemplate restTemplate;
private final SysParamsService sysParamsService;
private final AgentChatHistoryService agentChatHistoryService;
// Springboot提供的编程事务类
private final TransactionTemplate transactionTemplate;
// 识别度
private final Double RECOGNITION = 0.5;
private final Executor taskExecutor;
public AgentVoicePrintServiceImpl(AgentChatAudioService agentChatAudioService, RestTemplate restTemplate,
SysParamsService sysParamsService, AgentChatHistoryService agentChatHistoryService,
TransactionTemplate transactionTemplate, @Qualifier("taskExecutor") Executor taskExecutor) {
this.agentChatAudioService = agentChatAudioService;
this.restTemplate = restTemplate;
this.sysParamsService = sysParamsService;
this.agentChatHistoryService = agentChatHistoryService;
this.transactionTemplate = transactionTemplate;
this.taskExecutor = taskExecutor;
}
@Override
public boolean insert(AgentVoicePrintSaveDTO dto) {
// 获取音频数据
ByteArrayResource resource = getVoicePrintAudioWAV(dto.getAgentId(), dto.getAudioId());
// 识别一下此声音是否注册过
IdentifyVoicePrintResponse response = identifyVoicePrint(dto.getAgentId(), resource);
if (response != null && response.getScore() > RECOGNITION) {
// 根据识别出的声纹ID查询对应的用户信息
AgentVoicePrintEntity existingVoicePrint = baseMapper.selectById(response.getSpeakerId());
String existingUserName = existingVoicePrint != null ? existingVoicePrint.getSourceName() : "未知用户";
throw new RenException("此声音声纹对应的人(" + existingUserName + ")已经注册,请选择其他声音注册");
}
AgentVoicePrintEntity entity = ConvertUtils.sourceToTarget(dto, AgentVoicePrintEntity.class);
// 开启事务
return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
try {
// 保存声纹信息
int row = baseMapper.insert(entity);
// 插入一条数据,影响的数据不等于1说明出现了,保存问题回滚
if (row != 1) {
status.setRollbackOnly(); // 标记事务回滚
return false;
}
// 发送注册声纹请求
registerVoicePrint(entity.getId(), resource);
return true;
} catch (RenException e) {
status.setRollbackOnly(); // 标记事务回滚
throw e;
} catch (Exception e) {
status.setRollbackOnly(); // 标记事务回滚
log.error("保存声纹错误原因:{}", e.getMessage());
throw new RenException("保存声纹错误,请联系管理员");
}
}));
}
@Override
public boolean delete(Long userId, String voicePrintId) {
// 开启事务
boolean b = Boolean.TRUE.equals(transactionTemplate.execute(status -> {
try {
// 删除声纹,按照指定当前登录用户和智能体
int row = baseMapper.delete(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.eq(AgentVoicePrintEntity::getId, voicePrintId)
.eq(AgentVoicePrintEntity::getCreator, userId));
if (row != 1) {
status.setRollbackOnly(); // 标记事务回滚
return false;
}
return true;
} catch (Exception e) {
status.setRollbackOnly(); // 标记事务回滚
log.error("删除声纹存在错误原因:{}", e.getMessage());
throw new RenException("删除声纹出现了错误");
}
}));
// 数据库声纹数据删除成功才继续执行删除声纹服务的数据
if(b){
taskExecutor.execute(()-> {
try {
cancelVoicePrint(voicePrintId);
}catch (RuntimeException e) {
log.error("删除声纹存在运行时错误原因:{},id:{}", e.getMessage(),voicePrintId);
}
});
}
return b;
}
@Override
public List<AgentVoicePrintVO> list(Long userId, String agentId) {
// 按照指定当前登录用户和智能体查找数据
List<AgentVoicePrintEntity> list = baseMapper.selectList(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.eq(AgentVoicePrintEntity::getAgentId, agentId)
.eq(AgentVoicePrintEntity::getCreator, userId));
return list.stream().map(entity -> {
// 遍历转换成AgentVoicePrintVO类型
return ConvertUtils.sourceToTarget(entity, AgentVoicePrintVO.class);
}).toList();
}
@Override
public boolean update(Long userId, AgentVoicePrintUpdateDTO dto) {
AgentVoicePrintEntity agentVoicePrintEntity = baseMapper
.selectOne(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.eq(AgentVoicePrintEntity::getId, dto.getId())
.eq(AgentVoicePrintEntity::getCreator, userId));
if (agentVoicePrintEntity == null) {
return false;
}
// 获取音频Id
String audioId = dto.getAudioId();
// 获取智能体id
String agentId = agentVoicePrintEntity.getAgentId();
ByteArrayResource resource;
// audioId不等于空,且audioId和之前的保存的音频id不一样,则需要重新获取音频数据生成声纹
if (!StringUtils.isEmpty(audioId) && !audioId.equals(agentVoicePrintEntity.getAudioId())) {
resource = getVoicePrintAudioWAV(agentId, audioId);
// 识别一下此声音是否注册过
IdentifyVoicePrintResponse response = identifyVoicePrint(agentId, resource);
// 返回分数高于RECOGNITION说明这个声纹已经有了
if (response != null && response.getScore() > RECOGNITION) {
// 判断返回的id如果不是要修改的声纹id,说明这个声纹id,现在要注册的声音已经存在且不是原来的声纹,不允许修改
if (!response.getSpeakerId().equals(dto.getId())) {
// 根据识别出的声纹ID查询对应的用户信息
AgentVoicePrintEntity existingVoicePrint = baseMapper.selectById(response.getSpeakerId());
String existingUserName = existingVoicePrint != null ? existingVoicePrint.getSourceName() : "未知用户";
throw new RenException("此次修改不允许,此声音已经注册为声纹了(" + existingUserName + "");
}
}
} else {
resource = null;
}
// 开启事务
return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
try {
AgentVoicePrintEntity entity = ConvertUtils.sourceToTarget(dto, AgentVoicePrintEntity.class);
int row = baseMapper.updateById(entity);
if (row != 1) {
status.setRollbackOnly(); // 标记事务回滚
return false;
}
if (resource != null) {
String id = entity.getId();
// 先注销之前这个声纹id上的声纹向量
cancelVoicePrint(id);
// 发送注册声纹请求
registerVoicePrint(id, resource);
}
return true;
} catch (RenException e) {
status.setRollbackOnly(); // 标记事务回滚
throw e;
} catch (Exception e) {
status.setRollbackOnly(); // 标记事务回滚
log.error("修改声纹错误原因:{}", e.getMessage());
throw new RenException("修改声纹错误,请联系管理员");
}
}));
}
/**
* 获取生纹接口URI对象
*
* @return URI对象
*/
private URI getVoicePrintURI() {
// 获取声纹接口地址
String voicePrint = sysParamsService.getValue(Constant.SERVER_VOICE_PRINT, true);
try {
return new URI(voicePrint);
} catch (URISyntaxException e) {
log.error("路径格式不正确路径:{}\n错误信息:{}", voicePrint, e.getMessage());
throw new RuntimeException("声纹接口的地址存在错误,请进入参数管理修改声纹接口地址");
}
}
/**
* 获取声纹地址基础路径
*
* @param uri 声纹地址uri
* @return 基础路径
*/
private String getBaseUrl(URI uri) {
String protocol = uri.getScheme();
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
return "%s://%s".formatted(protocol, host);
} else {
return "%s://%s:%s".formatted(protocol, host, port);
}
}
/**
* 获取验证Authorization
*
* @param uri 声纹地址uri
* @return Authorization值
*/
private String getAuthorization(URI uri) {
// 获取参数
String query = uri.getQuery();
// 获取aes加密密钥
String str = "key=";
return "Bearer " + query.substring(query.indexOf(str) + str.length());
}
/**
* 获取声纹音频资源数据
*
* @param audioId 音频Id
* @return 声纹音频资源数据
*/
private ByteArrayResource getVoicePrintAudioWAV(String agentId, String audioId) {
// 判断这个音频是否属于当前智能体
boolean b = agentChatHistoryService.isAudioOwnedByAgent(audioId, agentId);
if (!b) {
throw new RenException("音频数据不属于这个智能体");
}
// 获取到音频数据
byte[] audio = agentChatAudioService.getAudio(audioId);
// 如果音频数据为空的直接报错不进行下去
if (audio == null || audio.length == 0) {
throw new RenException("音频数据是空的请检查上传数据");
}
// 将字节数组包装为资源,返回
return new ByteArrayResource(audio) {
@Override
public String getFilename() {
return "VoicePrint.WAV"; // 设置文件名
}
};
}
/**
* 发送注册声纹http请求
*
* @param id 声纹id
* @param resource 声纹音频资源
*/
private void registerVoicePrint(String id, ByteArrayResource resource) {
// 处理声纹接口地址,获取前缀
URI uri = getVoicePrintURI();
String baseUrl = getBaseUrl(uri);
String requestUrl = baseUrl + "/voiceprint/register";
// 创建请求体
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("speaker_id", id);
body.add("file", resource);
// 创建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorization(uri));
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// 创建请求体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
// 发送 POST 请求
ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
if (response.getStatusCode() != HttpStatus.OK) {
log.error("声纹注册失败,请求路径:{}", requestUrl);
throw new RenException("声纹保存失败,请求不成功");
}
// 检查响应内容
String responseBody = response.getBody();
if (responseBody == null || !responseBody.contains("true")) {
log.error("声纹注册失败,请求处理失败内容:{}", responseBody == null ? "空内容" : responseBody);
throw new RenException("声纹保存失败,请求处理失败");
}
}
/**
* 发送注销声纹的请求
*
* @param voicePrintId 声纹id
*/
private void cancelVoicePrint(String voicePrintId) {
URI uri = getVoicePrintURI();
String baseUrl = getBaseUrl(uri);
String requestUrl = baseUrl + "/voiceprint/" + voicePrintId;
// 创建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorization(uri));
// 创建请求体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(headers);
// 发送 POST 请求
ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.DELETE, requestEntity,
String.class);
if (response.getStatusCode() != HttpStatus.OK) {
log.error("声纹注销失败,请求路径:{}", requestUrl);
throw new RenException("声纹注销失败,请求不成功");
}
// 检查响应内容
String responseBody = response.getBody();
if (responseBody == null || !responseBody.contains("true")) {
log.error("声纹注销失败,请求处理失败内容:{}", responseBody == null ? "空内容" : responseBody);
throw new RenException("声纹注销失败,请求处理失败");
}
}
/**
* 发送识别声纹http请求
*
* @param agentId 智能体id
* @param resource 声纹音频资源
* @return 返回识别数据
*/
private IdentifyVoicePrintResponse identifyVoicePrint(String agentId, ByteArrayResource resource) {
// 获取该智能体所有注册的声纹
List<AgentVoicePrintEntity> agentVoicePrintList = baseMapper
.selectList(new LambdaQueryWrapper<AgentVoicePrintEntity>()
.select(AgentVoicePrintEntity::getId)
.eq(AgentVoicePrintEntity::getAgentId, agentId));
// 声纹数量为0,说明还没注册过声纹不需要发生识别请求
if (agentVoicePrintList.isEmpty()) {
return null;
}
// 处理声纹接口地址,获取前缀
URI uri = getVoicePrintURI();
String baseUrl = getBaseUrl(uri);
String requestUrl = baseUrl + "/voiceprint/identify";
// 创建请求体
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
// 创建speaker_id参数
String speakerIds = agentVoicePrintList.stream()
.map(AgentVoicePrintEntity::getId)
.collect(Collectors.joining(","));
body.add("speaker_ids", speakerIds);
body.add("file", resource);
// 创建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorization(uri));
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// 创建请求体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
// 发送 POST 请求
ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
if (response.getStatusCode() != HttpStatus.OK) {
log.error("声纹识别请求失败,请求路径:{}", requestUrl);
throw new RenException("声纹识别失败,请求不成功");
}
// 检查响应内容
String responseBody = response.getBody();
if (responseBody != null) {
return JsonUtils.parseObject(responseBody, IdentifyVoicePrintResponse.class);
}
return null;
}
}
@@ -0,0 +1,16 @@
package xiaozhi.modules.agent.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 智能体用户个人聊天数据的VO
*/
@Data
public class AgentChatHistoryUserVO {
@Schema(description = "聊天内容")
private String content;
@Schema(description = "音频ID")
private String audioId;
}
@@ -0,0 +1,33 @@
package xiaozhi.modules.agent.vo;
import lombok.Data;
import java.util.Date;
/**
* 展示智能体声纹列表VO
*/
@Data
public class AgentVoicePrintVO {
/**
* 主键id
*/
private String id;
/**
* 音频文件id
*/
private String audioId;
/**
* 声纹来源的人姓名
*/
private String sourceName;
/**
* 描述声纹来源的人
*/
private String introduce;
/**
* 创建时间
*/
private Date createDate;
}
@@ -9,20 +9,26 @@ import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.AllArgsConstructor;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.redis.RedisKeys;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.agent.entity.AgentPluginMapping;
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
import xiaozhi.modules.agent.entity.AgentVoicePrintEntity;
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
import xiaozhi.modules.config.service.ConfigService;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
@@ -45,6 +51,7 @@ public class ConfigServiceImpl implements ConfigService {
private final TimbreService timbreService;
private final AgentPluginMappingService agentPluginMappingService;
private final AgentMcpAccessPointService agentMcpAccessPointService;
private final AgentVoicePrintDao agentVoicePrintDao;
@Override
public Object getConfig(Boolean isCache) {
@@ -162,6 +169,8 @@ public class ConfigServiceImpl implements ConfigService {
mcpEndpoint = mcpEndpoint.replace("/mcp/", "/call/");
result.put("mcp_endpoint", mcpEndpoint);
}
// 获取声纹信息
buildVoiceprintConfig(agent.getId(), result);
// 构建模块配置
buildModuleConfig(
@@ -255,6 +264,62 @@ public class ConfigServiceImpl implements ConfigService {
return config;
}
/**
* 构建声纹配置信息
*
* @param agentId 智能体ID
* @param result 结果Map
*/
private void buildVoiceprintConfig(String agentId, Map<String, Object> result) {
try {
// 获取声纹接口地址
String voiceprintUrl = sysParamsService.getValue("server.voice_print", true);
if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
return;
}
// 获取智能体关联的声纹信息(不需要用户权限验证)
List<AgentVoicePrintVO> voiceprints = getVoiceprintsByAgentId(agentId);
if (voiceprints == null || voiceprints.isEmpty()) {
return;
}
// 构建speakers列表
List<String> speakers = new ArrayList<>();
for (AgentVoicePrintVO voiceprint : voiceprints) {
String speakerStr = String.format("%s,%s,%s",
voiceprint.getId(),
voiceprint.getSourceName(),
voiceprint.getIntroduce() != null ? voiceprint.getIntroduce() : "");
speakers.add(speakerStr);
}
// 构建声纹配置
Map<String, Object> voiceprintConfig = new HashMap<>();
voiceprintConfig.put("url", voiceprintUrl);
voiceprintConfig.put("speakers", speakers);
result.put("voiceprint", voiceprintConfig);
} catch (Exception e) {
// 声纹配置获取失败时不影响其他功能
System.err.println("获取声纹配置失败: " + e.getMessage());
}
}
/**
* 获取智能体关联的声纹信息
*
* @param agentId 智能体ID
* @return 声纹信息列表
*/
private List<AgentVoicePrintVO> getVoiceprintsByAgentId(String agentId) {
LambdaQueryWrapper<AgentVoicePrintEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AgentVoicePrintEntity::getAgentId, agentId);
queryWrapper.orderByAsc(AgentVoicePrintEntity::getCreateDate);
List<AgentVoicePrintEntity> entities = agentVoicePrintDao.selectList(queryWrapper);
return ConvertUtils.sourceToTarget(entities, AgentVoicePrintVO.class);
}
/**
* 构建模块配置
*
@@ -21,11 +21,7 @@ import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.config.service.ConfigService;
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
import xiaozhi.modules.model.dto.ModelConfigDTO;
import xiaozhi.modules.model.dto.ModelProviderDTO;
import xiaozhi.modules.model.dto.VoiceDTO;
import xiaozhi.modules.model.dto.*;
import xiaozhi.modules.model.entity.ModelConfigEntity;
import xiaozhi.modules.model.service.ModelConfigService;
import xiaozhi.modules.model.service.ModelProviderService;
@@ -52,6 +48,14 @@ public class ModelController {
return new Result<List<ModelBasicInfoDTO>>().ok(modelList);
}
@GetMapping("/llm/names")
@Operation(summary = "获取LLM模型信息")
@RequiresPermissions("sys:role:normal")
public Result<List<LlmModelBasicInfoDTO>> getLlmModelCodeList(@RequestParam(required = false) String modelName) {
List<LlmModelBasicInfoDTO> llmModelCodeList = modelConfigService.getLlmModelCodeList(modelName);
return new Result<List<LlmModelBasicInfoDTO>>().ok(llmModelCodeList);
}
@GetMapping("/{modelType}/provideTypes")
@Operation(summary = "获取模型供应器列表")
@RequiresPermissions("sys:role:superAdmin")
@@ -0,0 +1,13 @@
package xiaozhi.modules.model.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* LLM的模型的基础展示数据
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class LlmModelBasicInfoDTO extends ModelBasicInfoDTO{
private String type;
}
@@ -4,6 +4,7 @@ import java.util.List;
import xiaozhi.common.page.PageData;
import xiaozhi.common.service.BaseService;
import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
import xiaozhi.modules.model.dto.ModelConfigDTO;
@@ -13,6 +14,8 @@ public interface ModelConfigService extends BaseService<ModelConfigEntity> {
List<ModelBasicInfoDTO> getModelCodeList(String modelType, String modelName);
List<LlmModelBasicInfoDTO> getLlmModelCodeList(String modelName);
PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit);
ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO);
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -23,6 +24,7 @@ import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.modules.agent.dao.AgentDao;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.model.dao.ModelConfigDao;
import xiaozhi.modules.model.dto.LlmModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelBasicInfoDTO;
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
import xiaozhi.modules.model.dto.ModelConfigDTO;
@@ -52,6 +54,25 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
}
@Override
public List<LlmModelBasicInfoDTO> getLlmModelCodeList(String modelName) {
List<ModelConfigEntity> entities = modelConfigDao.selectList(
new QueryWrapper<ModelConfigEntity>()
.eq("model_type", "llm")
.eq("is_enabled", 1)
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%")
.select("id", "model_name", "config_json"));
// 处理获取到的内容
return entities.stream().map(item -> {
LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
dto.setId(item.getId());
dto.setModelName(item.getModelName());
String type = item.getConfigJson().get("type").toString();
dto.setType(type);
return dto;
}).toList();
}
@Override
public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit) {
Map<String, Object> params = new HashMap<String, Object>();
@@ -94,6 +115,21 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
if (CollectionUtil.isEmpty(providerList)) {
throw new RenException("供应器不存在");
}
if (modelConfigBodyDTO.getConfigJson().containsKey("llm")) {
String llm = modelConfigBodyDTO.getConfigJson().get("llm").toString();
ModelConfigEntity modelConfigEntity = modelConfigDao.selectOne(new LambdaQueryWrapper<ModelConfigEntity>()
.eq(ModelConfigEntity::getId, llm));
String selectModelType = (modelConfigEntity == null || modelConfigEntity.getModelType() == null) ? null
: modelConfigEntity.getModelType().toUpperCase();
if (modelConfigEntity == null || !"LLM".equals(selectModelType)) {
throw new RenException("设置的LLM不存在");
}
String type = modelConfigEntity.getConfigJson().get("type").toString();
// 如果查询大语言模型是openai或者ollama,意图识别选参数都可以
if (!"openai".equals(type) && !"ollama".equals(type)) {
throw new RenException("设置的LLM不是openai和ollama");
}
}
// 再更新供应器提供的模型
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
@@ -137,6 +173,8 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
.or()
.eq("mem_model_id", modelId)
.or()
.eq("vllm_model_id", modelId)
.or()
.eq("intent_model_id", modelId));
if (!agents.isEmpty()) {
String agentNames = agents.stream()
@@ -107,6 +107,9 @@ public class SysParamsController {
// 验证MCP地址
validateMcpUrl(dto.getParamCode(), dto.getParamValue());
//
validateVoicePrint(dto.getParamCode(), dto.getParamValue());
sysParamsService.update(dto);
configService.getConfig(false);
return new Result<Void>();
@@ -212,6 +215,7 @@ public class SysParamsController {
if (!url.toLowerCase().contains("key")) {
throw new RenException("不是正确的MCP地址");
}
try {
// 发送GET请求
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
@@ -227,4 +231,37 @@ public class SysParamsController {
throw new RenException("MCP接口验证失败:" + e.getMessage());
}
}
// 验证声纹接口地址是否正常
private void validateVoicePrint(String paramCode, String url) {
if (!paramCode.equals(Constant.SERVER_VOICE_PRINT)) {
return;
}
if (StringUtils.isBlank(url) || url.equals("null")) {
throw new RenException("声纹接口地址不能为空");
}
if (url.contains("localhost") || url.contains("127.0.0.1")) {
throw new RenException("声纹接口地址不能使用localhost或127.0.0.1");
}
if (!url.toLowerCase().contains("key")) {
throw new RenException("不是正确的声纹接口地址");
}
// 验证URL格式
if (!url.toLowerCase().startsWith("http")) {
throw new RenException("声纹接口地址必须以http或https开头");
}
try {
// 发送GET请求
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (response.getStatusCode() != HttpStatus.OK) {
throw new RenException("声纹接口访问失败,状态码:" + response.getStatusCode());
}
// 检查响应内容
String body = response.getBody();
if (body == null || !body.contains("healthy")) {
throw new RenException("声纹接口返回内容格式不正确,可能不是一个真实的MCP接口");
}
} catch (Exception e) {
throw new RenException("声纹接口验证失败:" + e.getMessage());
}
}
}
@@ -86,10 +86,14 @@ public class WebSocketClientManager implements Closeable {
if (sess == null || !sess.isOpen()) {
throw new IOException("握手失败或会话未打开");
}
// 设置缓冲区
sess.setTextMessageSizeLimit(b.bufferSize);
sess.setBinaryMessageSizeLimit(b.bufferSize);
ws.session = sess;
return ws;
}
/**
* 发送 Text
*/
@@ -137,6 +141,37 @@ public class WebSocketClientManager implements Closeable {
return collected;
}
private <T> List<T> listenerCustomWithoutClose(
BlockingQueue<T> queue,
Predicate<T> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
List<T> collected = new ArrayList<>();
long deadline = System.currentTimeMillis() + maxSessionDurationUnit.toMillis(maxSessionDuration);
while (true) {
if (errorFuture.isDone()) {
errorFuture.get();
}
long remaining = deadline - System.currentTimeMillis();
if (remaining <= 0) {
throw new TimeoutException("等待批量消息超时");
}
T msg = queue.poll(remaining, TimeUnit.MILLISECONDS);
if (msg == null) {
throw new TimeoutException("等待批量消息超时");
}
collected.add(msg);
if (predicate.test(msg)) {
break;
}
}
// 不调用 close(),保持连接开放
return collected;
}
/**
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
*
@@ -147,6 +182,17 @@ public class WebSocketClientManager implements Closeable {
return listenerCustom(textMessageQueue, predicate);
}
/**
* 同步接收多条消息,直到 predicate 为 true 或超时抛异常;
* 不自动关闭连接,适用于需要在同一连接上发送多个消息的场景
*
* @return 返回监听期间的所有消息列表
*/
public List<String> listenerWithoutClose(Predicate<String> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
return listenerCustomWithoutClose(textMessageQueue, predicate);
}
public List<byte[]> listenerBinary(Predicate<byte[]> predicate)
throws InterruptedException, TimeoutException, ExecutionException {
return listenerCustom(binaryMessageQueue, predicate);
@@ -266,10 +312,11 @@ public class WebSocketClientManager implements Closeable {
if (stopWatch.isRunning()) {
stopWatch.stop();
}
log.info("ws连接关闭, 目标URI: {}, 关闭时间: {}, 连接总时长: {}s",
log.info("ws连接关闭, 目标URI: {}, 关闭时间: {}, 连接总时长: {}s,断开原因:{}",
targetUri, DateUtils.getDateTimeNow(DateUtils.DATE_TIME_MILLIS_PATTERN),
DateUtils.millsToSecond(stopWatch.getTotalTimeMillis()));
DateUtils.millsToSecond(stopWatch.getTotalTimeMillis()),status);
}
}
public static class Builder {
@@ -279,6 +326,7 @@ public class WebSocketClientManager implements Closeable {
private long maxSessionDuration = 5; // 最大连线时间,默认5秒
private TimeUnit maxSessionDurationUnit = TimeUnit.SECONDS; // 最大连线时间单位
private int queueCapacity = 100; // 消息队列容量
private int bufferSize = 8 * 1024; //默认 8kb
private WebSocketHttpHeaders headers; // 请求头
/**
@@ -310,6 +358,10 @@ public class WebSocketClientManager implements Closeable {
this.queueCapacity = c;
return this;
}
public Builder bufferSize(int c) {
this.bufferSize = c;
return this;
}
public WebSocketClientManager build()
throws InterruptedException, ExecutionException, TimeoutException, IOException {
@@ -0,0 +1,4 @@
-- 添加声纹接口地址参数配置
delete from `sys_params` where id = 114;
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark)
VALUES (114, 'server.voice_print', 'null', 'string', 1, '声纹接口地址');
@@ -0,0 +1,12 @@
DROP TABLE IF EXISTS ai_agent_voice_print;
create table ai_agent_voice_print (
id varchar(32) NOT NULL COMMENT '声纹ID',
agent_id varchar(32) NOT NULL COMMENT '关联的智能体ID',
source_name varchar(50) NOT NULL COMMENT '声纹来源的人的姓名',
introduce varchar(200) COMMENT '描述声纹来源的这个人',
create_date DATETIME COMMENT '创建时间',
creator bigint COMMENT '创建者',
update_date DATETIME COMMENT '修改时间',
updater bigint COMMENT '修改者',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体声纹表'
@@ -0,0 +1,26 @@
-- 添加阿里云流式ASR供应器
delete from `ai_model_provider` where id = 'SYSTEM_ASR_AliyunStreamASR';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_ASR_AliyunStreamASR', 'ASR', 'aliyun_stream', '阿里云语音识别(流式)', '[{"key":"appkey","label":"应用AppKey","type":"string"},{"key":"token","label":"临时Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"host","label":"服务地址","type":"string"},{"key":"max_sentence_silence","label":"断句检测时间","type":"number"},{"key":"output_dir","label":"输出目录","type":"string"}]', 6, 1, NOW(), 1, NOW());
-- 添加阿里云流式ASR模型配置
delete from `ai_model_config` where id = 'ASR_AliyunStreamASR';
INSERT INTO `ai_model_config` VALUES ('ASR_AliyunStreamASR', 'ASR', 'AliyunStreamASR', '阿里云语音识别(流式)', 0, 1, '{\"type\": \"aliyun_stream\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"host\": \"nls-gateway-cn-shanghai.aliyuncs.com\", \"max_sentence_silence\": 800, \"output_dir\": \"tmp/\"}', NULL, NULL, 8, NULL, NULL, NULL, NULL);
-- 更新阿里云流式ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://nls-portal.console.aliyun.com/',
`remark` = '阿里云流式ASR配置说明:
1. 阿里云ASR和阿里云(流式)ASR的区别是:阿里云ASR是一次性识别,阿里云(流式)ASR是实时流式识别
2. 流式ASR具有更低的延迟和更好的实时性,适合语音交互场景
3. 需要在阿里云智能语音交互控制台创建应用并获取认证信息
4. 支持中文实时语音识别,支持标点符号预测和逆文本规范化
5. 需要网络连接,输出文件保存在tmp/目录
申请步骤:
1. 访问 https://nls-portal.console.aliyun.com/ 开通智能语音交互服务
2. 访问 https://nls-portal.console.aliyun.com/applist 创建项目并获取appkey
3. 访问 https://nls-portal.console.aliyun.com/overview 获取临时token(或配置access_key_id和access_key_secret自动获取)
4. 如需动态token管理,建议配置access_key_id和access_key_secret
5. max_sentence_silence参数控制断句检测时间(毫秒),默认800ms
如需了解更多参数配置,请参考:https://help.aliyun.com/zh/isi/developer-reference/real-time-speech-recognition
' WHERE `id` = 'ASR_AliyunStreamASR';
@@ -0,0 +1,56 @@
-- 添加阿里云流式TTS供应器
delete from `ai_model_provider` where id = 'SYSTEM_TTS_AliyunStreamTTS';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_TTS_AliyunStreamTTS', 'TTS', 'aliyun_stream', '阿里云语音合成(流式)', '[{"key":"appkey","label":"应用AppKey","type":"string"},{"key":"token","label":"临时Token","type":"string"},{"key":"access_key_id","label":"AccessKey ID","type":"string"},{"key":"access_key_secret","label":"AccessKey Secret","type":"string"},{"key":"host","label":"服务地址","type":"string"},{"key":"voice","label":"默认音色","type":"string"},{"key":"format","label":"音频格式","type":"string"},{"key":"sample_rate","label":"采样率","type":"number"},{"key":"volume","label":"音量","type":"number"},{"key":"speech_rate","label":"语速","type":"number"},{"key":"pitch_rate","label":"音调","type":"number"},{"key":"output_dir","label":"输出目录","type":"string"}]', 15, 1, NOW(), 1, NOW());
-- 添加阿里云流式TTS模型配置
delete from `ai_model_config` where id = 'TTS_AliyunStreamTTS';
INSERT INTO `ai_model_config` VALUES ('TTS_AliyunStreamTTS', 'TTS', 'AliyunStreamTTS', '阿里云语音合成(流式)', 0, 1, '{\"type\": \"aliyun_stream\", \"appkey\": \"\", \"token\": \"\", \"access_key_id\": \"\", \"access_key_secret\": \"\", \"host\": \"nls-gateway-cn-beijing.aliyuncs.com\", \"voice\": \"longxiaochun\", \"format\": \"pcm\", \"sample_rate\": 16000, \"volume\": 50, \"speech_rate\": 0, \"pitch_rate\": 0, \"output_dir\": \"tmp/\"}', NULL, NULL, 18, NULL, NULL, NULL, NULL);
-- 更新阿里云流式TTS配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://nls-portal.console.aliyun.com/',
`remark` = '阿里云流式TTS配置说明:
1. 阿里云TTS和阿里云(流式)TTS的区别是:阿里云TTS是一次性合成,阿里云(流式)TTS是实时流式合成
2. 流式TTS具有更低的延迟和更好的实时性,适合语音交互场景
3. 需要在阿里云智能语音交互控制台创建应用并获取认证信息
4. 支持CosyVoice大模型音色,音质更加自然
5. 支持实时调节音量、语速、音调等参数
申请步骤:
1. 访问 https://nls-portal.console.aliyun.com/ 开通智能语音交互服务
2. 访问 https://nls-portal.console.aliyun.com/applist 创建项目并获取appkey
3. 访问 https://nls-portal.console.aliyun.com/overview 获取临时token(或配置access_key_id和access_key_secret自动获取)
4. 如需动态token管理,建议配置access_key_id和access_key_secret
5. 可选择北京、上海等不同地域的服务器以优化延迟
6. voice参数支持CosyVoice大模型音色,如longxiaochun、longyueyue等
如需了解更多参数配置,请参考:https://help.aliyun.com/zh/isi/developer-reference/real-time-speech-synthesis
' WHERE `id` = 'TTS_AliyunStreamTTS';
-- 添加阿里云流式TTS音色
delete from `ai_tts_voice` where tts_model_id = 'TTS_AliyunStreamTTS';
-- 温柔女声系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0001', 'TTS_AliyunStreamTTS', '龙小淳-温柔姐姐', 'longxiaochun', '中文及中英文混合', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0002', 'TTS_AliyunStreamTTS', '龙小夏-温柔女声', 'longxiaoxia', '中文及中英文混合', NULL, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0003', 'TTS_AliyunStreamTTS', '龙玫-温柔女声', 'longmei', '中文及中英文混合', NULL, NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0004', 'TTS_AliyunStreamTTS', '龙瑰-温柔女声', 'longgui', '中文及中英文混合', NULL, NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
-- 御姐女声系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0005', 'TTS_AliyunStreamTTS', '龙玉-御姐女声', 'longyu', '中文及中英文混合', NULL, NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0006', 'TTS_AliyunStreamTTS', '龙娇-御姐女声', 'longjiao', '中文及中英文混合', NULL, NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
-- 男声系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0007', 'TTS_AliyunStreamTTS', '龙臣-译制片男声', 'longchen', '中文及中英文混合', NULL, NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0008', 'TTS_AliyunStreamTTS', '龙修-青年男声', 'longxiu', '中文及中英文混合', NULL, NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0009', 'TTS_AliyunStreamTTS', '龙橙-阳光男声', 'longcheng', '中文及中英文混合', NULL, NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0010', 'TTS_AliyunStreamTTS', '龙哲-成熟男声', 'longzhe', '中文及中英文混合', NULL, NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
-- 专业播报系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0011', 'TTS_AliyunStreamTTS', 'Bella2.0-新闻女声', 'loongbella', '中文及中英文混合', NULL, NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0012', 'TTS_AliyunStreamTTS', 'Stella2.0-飒爽女声', 'loongstella', '中文及中英文混合', NULL, NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0013', 'TTS_AliyunStreamTTS', '龙书-新闻男声', 'longshu', '中文及中英文混合', NULL, NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0014', 'TTS_AliyunStreamTTS', '龙婧-严肃女声', 'longjing', '中文及中英文混合', NULL, NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
-- 特色音色系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0015', 'TTS_AliyunStreamTTS', '龙奇-活泼童声', 'longqi', '中文及中英文混合', NULL, NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0016', 'TTS_AliyunStreamTTS', '龙华-活泼女童', 'longhua', '中文及中英文混合', NULL, NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0017', 'TTS_AliyunStreamTTS', '龙无-无厘头男声', 'longwu', '中文及中英文混合', NULL, NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0018', 'TTS_AliyunStreamTTS', '龙大锤-幽默男声', 'longdachui', '中文及中英文混合', NULL, NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
-- 粤语系列
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0019', 'TTS_AliyunStreamTTS', '龙嘉怡-粤语女声', 'longjiayi', '粤语及粤英混合', NULL, NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_AliyunStreamTTS_0020', 'TTS_AliyunStreamTTS', '龙桃-粤语女声', 'longtao', '粤语及粤英混合', NULL, NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
@@ -0,0 +1,3 @@
-- 智能体声纹添加新字段
ALTER TABLE ai_agent_voice_print
ADD COLUMN audio_id VARCHAR(32) NOT NULL COMMENT '音频ID';
@@ -0,0 +1,38 @@
-- OpenAI ASR模型供应器
delete from `ai_model_provider` where id = 'SYSTEM_ASR_OpenaiASR';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_ASR_OpenaiASR', 'ASR', 'openai', 'OpenAI语音识别', '[{"key": "base_url", "type": "string", "label": "基础URL"}, {"key": "model_name", "type": "string", "label": "模型名称"}, {"key": "api_key", "type": "string", "label": "API密钥"}, {"key": "output_dir", "type": "string", "label": "输出目录"}]', 9, 1, NOW(), 1, NOW());
-- OpenAI ASR模型配置
delete from `ai_model_config` where id = 'ASR_OpenaiASR';
INSERT INTO `ai_model_config` VALUES ('ASR_OpenaiASR', 'ASR', 'OpenaiASR', 'OpenAI语音识别', 0, 1, '{\"type\": \"openai\", \"api_key\": \"\", \"base_url\": \"https://api.openai.com/v1/audio/transcriptions\", \"model_name\": \"gpt-4o-mini-transcribe\", \"output_dir\": \"tmp/\"}', NULL, NULL, 9, NULL, NULL, NULL, NULL);
-- groq ASR模型配置
delete from `ai_model_config` where id = 'ASR_GroqASR';
INSERT INTO `ai_model_config` VALUES ('ASR_GroqASR', 'ASR', 'GroqASR', 'Groq语音识别', 0, 1, '{\"type\": \"openai\", \"api_key\": \"\", \"base_url\": \"https://api.groq.com/openai/v1/audio/transcriptions\", \"model_name\": \"whisper-large-v3-turbo\", \"output_dir\": \"tmp/\"}', NULL, NULL, 10, NULL, NULL, NULL, NULL);
-- 更新OpenAI ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://platform.openai.com/docs/api-reference/audio/createTranscription',
`remark` = 'OpenAI ASR配置说明:
1. 需要在OpenAI开放平台创建组织并获取api_key
2. 支持中、英、日、韩等多种语音识别,具体参考文档https://platform.openai.com/docs/guides/speech-to-text
3. 需要网络连接
4. 输出文件保存在tmp/目录
申请步骤:
**OpenAi ASR申请步骤:**
1.登录OpenAI Platform。https://auth.openai.com/log-in
2.创建api-key https://platform.openai.com/settings/organization/api-keys
3.模型可以选择gpt-4o-transcribe或GPT-4o mini Transcribe
' WHERE `id` = 'ASR_OpenaiASR';
-- 更新Groq ASR配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://console.groq.com/docs/speech-to-text',
`remark` = 'Groq ASR配置说明:
1.登录groq Console。https://console.groq.com/home
2.创建api-key https://console.groq.com/keys
3.模型可以选择whisper-large-v3-turbo或whisper-large-v3distil-whisper-large-v3-en仅支持英语转录)
' WHERE `id` = 'ASR_GroqASR';
@@ -0,0 +1,32 @@
-- 添加Index-TTS-vLLM流式TTS供应器
delete from `ai_model_provider` where id = 'SYSTEM_TTS_IndexStreamTTS';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_TTS_IndexStreamTTS', 'TTS', 'index_stream', 'Index-TTS-vLLM流式语音合成', '[{"key":"api_url","label":"API服务地址","type":"string"},{"key":"voice","label":"默认音色","type":"string"},{"key":"audio_format","label":"音频格式","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"}]', 16, 1, NOW(), 1, NOW());
-- 添加Index-TTS-vLLM流式TTS模型配置
delete from `ai_model_config` where id = 'TTS_IndexStreamTTS';
INSERT INTO `ai_model_config` VALUES ('TTS_IndexStreamTTS', 'TTS', 'IndexStreamTTS', 'Index-TTS-vLLM流式语音合成', 0, 1, '{\"type\": \"index_stream\", \"api_url\": \"http://127.0.0.1:11996/tts\", \"voice\": \"jay_klee\", \"audio_format\": \"pcm\", \"output_dir\": \"tmp/\"}', NULL, NULL, 19, NULL, NULL, NULL, NULL);
-- 更新Index-TTS-vLLM流式TTS配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://github.com/Ksuriuri/index-tts-vllm',
`remark` = 'Index-TTS-vLLM流式TTS配置说明:
1. Index-TTS-vLLM是基于Index-TTS项目的vLLM推理服务,提供流式语音合成功能
2. 支持多种音色,音质自然,适合各种语音交互场景
3. 需要先部署Index-TTS-vLLM服务,然后配置API地址
4. 支持实时流式合成,具有较低的延迟
5. 支持自定义音色,可在项目assets文件夹下注册新音色
部署步骤:
1. 克隆项目:git clone https://github.com/Ksuriuri/index-tts-vllm.git
2. 安装依赖:pip install -r requirements.txt
3. 启动服务:python app.py
4. 服务默认运行在 http://127.0.0.1:11996
5. 如需其他音色,可到项目assets文件夹下注册
6. 支持多种音频格式:pcm、wav、mp3等
如需了解更多配置,请参考:https://github.com/Ksuriuri/index-tts-vllm/blob/master/README.md
' WHERE `id` = 'TTS_IndexStreamTTS';
-- 添加Index-TTS-vLLM流式TTS音色
delete from `ai_tts_voice` where tts_model_id = 'TTS_IndexStreamTTS';
-- 默认音色
INSERT INTO `ai_tts_voice` VALUES ('TTS_IndexStreamTTS_0001', 'TTS_IndexStreamTTS', 'Jay Klee', 'jay_klee', '中文及中英文混合', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
@@ -0,0 +1,16 @@
-- 更新HuoshanDoubleStreamTTS供应器增加语速,音调等配置
UPDATE `ai_model_provider`
SET fields = '[{"key": "ws_url", "type": "string", "label": "WebSocket地址"}, {"key": "appid", "type": "string", "label": "应用ID"}, {"key": "access_token", "type": "string", "label": "访问令牌"}, {"key": "resource_id", "type": "string", "label": "资源ID"}, {"key": "speaker", "type": "string", "label": "默认音色"}, {"key": "speech_rate", "type": "number", "label": "语速(-50~100)"}, {"key": "loudness_rate", "type": "number", "label": "音量(-50~100)"}, {"key": "pitch", "type": "number", "label": "音高(-12~12)"}]'
WHERE id = 'SYSTEM_TTS_HSDSTTS';
UPDATE `ai_model_config` SET
`doc_link` = 'https://console.volcengine.com/speech/service/10007',
`remark` = '火山引擎语音合成服务配置说明:
1. 访问 https://www.volcengine.com/ 注册并开通火山引擎账号
2. 访问 https://console.volcengine.com/speech/service/10007 开通语音合成大模型,购买音色
3. 在页面底部获取appid和access_token
5. 资源ID固定为:volc.service_type.10029(大模型语音合成及混音)
6. 语速:-50~100,可不填,正常默认值0,可填-50~100
7. 音量:-50~100,可不填,正常默认值0,可填-50~100
8. 音高:-12~12,可不填,正常默认值0,可填-12~12
9. 填入配置文件中' WHERE `id` = 'TTS_HuoshanDoubleStreamTTS';
@@ -0,0 +1,26 @@
-- 添加 paddle_speech 流式 TTS 供应器
DELETE FROM `ai_model_provider` WHERE id = 'SYSTEM_TTS_PaddleSpeechTTS';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`)
VALUES ('SYSTEM_TTS_PaddleSpeechTTS', 'TTS', 'paddle_speech', 'PaddleSpeechTTS',
'[{"key":"protocol","label":"协议类型","type":"string","options":["websocket","http"]},{"key":"url","label":"服务地址","type":"string"},{"key":"spk_id","label":"音色","type":"int"},{"key":"sample_rate","label":"采样率","type":"float"},{"key":"speed","label":"语速","type":"float"},{"key":"volume","label":"音量","type":"float"},{"key":"save_path","label":"保存路径","type":"string"}]',
17, 1, NOW(), 1, NOW());
-- 添加 paddle_speech 流式 TTS 模型配置
DELETE FROM `ai_model_config` WHERE id = 'TTS_PaddleSpeechTTS';
INSERT INTO `ai_model_config` VALUES ('TTS_PaddleSpeechTTS', 'TTS', 'PaddleSpeechTTS', 'PaddleSpeechTTS', 0, 1,
'{"type": "paddle_speech", "protocol": "websocket", "url": "ws://127.0.0.1:8092/paddlespeech/tts/streaming", "spk_id": "0", "sample_rate": 24000, "speed": 1.0, "volume": 1.0, "save_path": "./streaming_tts.wav"}',
NULL, NULL, 20, NULL, NULL, NULL, NULL);
-- 更新 PaddleSpeechTTS 配置说明
UPDATE `ai_model_config` SET
`doc_link` = 'https://github.com/PaddlePaddle/PaddleSpeech',
`remark` = 'PaddleSpeechTTS 配置说明:
1. PaddleSpeech 是百度飞桨开源的语音合成工具,支持本地离线部署和模型训练。paddlepaddle百度飞浆框架地址:https://www.paddlepaddle.org.cn/
2. 支持 WebSocket 和 HTTP 协议,默认使用 WebSocket 进行流式传输(参考部署文档:https://github.com/xinnan-tech/xiaozhi-esp32-server/blob/main/docs/paddlespeech-deploy.md)。
3. 使用前要在本地部署 paddlespeech 服务,服务默认运行在 ws://127.0.0.1:8092/paddlespeech/tts/streaming
4. 支持自定义发音人、语速、音量和采样率。
' WHERE `id` = 'TTS_PaddleSpeechTTS';
-- 删除旧音色并添加默认音色
DELETE FROM `ai_tts_voice` WHERE tts_model_id = 'TTS_PaddleSpeechTTS';
INSERT INTO `ai_tts_voice` VALUES ('TTS_PaddleSpeechTTS_0000', 'TTS_PaddleSpeechTTS', '默认', '0', '中文', NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
@@ -239,4 +239,67 @@ databaseChangeLog:
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202506261637.sql
path: classpath:db/changelog/202506261637.sql
- changeSet:
id: 202507101203
author: luruxian
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507101203.sql
- changeSet:
id: 202507071130
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507071130.sql
- changeSet:
id: 202507071530
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507071530.sql
- changeSet:
id: 202507031602
author: zjy
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507031602.sql
- changeSet:
id: 202507041018
author: zjy
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507041018.sql
- changeSet:
id: 202507081646
author: zjy
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202507081646.sql
- changeSet:
id: 202508081701
author: hrz
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508081701.sql
- changeSet:
id: 202508111734
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508111734.sql
- changeSet:
id: 202508131557
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508131557.sql
@@ -4,11 +4,11 @@
<mapper namespace="xiaozhi.modules.security.dao.SysUserTokenDao">
<select id="getByToken" resultType="xiaozhi.modules.security.entity.SysUserTokenEntity">
select * from sys_user_token where token = #{value}
select * from sys_user_token where token = #{token}
</select>
<select id="getByUserId" resultType="xiaozhi.modules.security.entity.SysUserTokenEntity">
select * from sys_user_token where user_id = #{value}
select * from sys_user_token where user_id = #{userId}
</select>
<update id="logout">
@@ -5,7 +5,7 @@
<!-- 根据参数编码,查询value -->
<select id="getValueByCode" resultType="String">
select param_value from sys_params where param_code = #{value}
select param_value from sys_params where param_code = #{paramCode}
</select>
<!-- 获取参数编码列表 -->
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
}
+13
View File
@@ -0,0 +1,13 @@
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
+44
View File
@@ -0,0 +1,44 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.hbuilderx
.stylelintcache
.eslintcache
docs/.vitepress/dist
docs/.vitepress/cache
src/types
# lock 文件还是不要了,我主要的版本写死就好了
# pnpm-lock.yaml
# package-lock.json
# TIPS:如果某些文件已经加入了版本管理,现在重新加入 .gitignore 是不生效的,需要执行下面的操作
# `git rm -r --cached .` 然后提交 commit 即可。
# git rm -r --cached file1 file2 ## 针对某些文件
# git rm -r --cached dir1 dir2 ## 针对某些文件夹
# git rm -r --cached . ## 针对所有文件
# 更新 uni-app 官方版本
# npx @dcloudio/uvm@latest
+8
View File
@@ -0,0 +1,8 @@
# registry = https://registry.npmjs.org
registry = https://registry.npmmirror.com
strict-peer-dependencies=false
auto-install-peers=true
shamefully-hoist=true
ignore-workspace-root-check=true
install-workspace-root=true
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Junsen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+170
View File
@@ -0,0 +1,170 @@
## 智控台移动版(manager-mobile
基于 uni-app v3 + Vue 3 + Vite 的跨端移动管理端,支持 AppAndroid & iOS)和微信小程序。
### 平台兼容性
| H5 | iOS | Android | 微信小程序 |
| -- | --- | ------- | ---------- |
| √ | √ | √ | √ |
提示:不同 UI 组件在不同平台的适配度略有差异,请以对应组件库文档为准。
### 开发环境要求
- Node >= 18
- pnpm >= 7.30(建议使用项目中声明的 `pnpm@10.x`
- 可选:HBuilderX(App 调试/打包)、微信开发者工具(微信小程序)
### 快速开始
1) 配置环境变量
- 复制 `env/.env.example``env/.env.development`
- 根据实际情况修改配置项(特别是 `VITE_SERVER_BASEURL``VITE_UNI_APPID``VITE_WX_APPID`
2) 安装依赖
```bash
pnpm i
```
3) 本地开发(热更新)
- h5: `pnpm dev:h5`,然后观察启动日志显示的ip端口号
- 微信小程序:`pnpm dev:mp``pnpm dev:mp-weixin`,然后用微信开发者工具导入 `dist/dev/mp-weixin`
- App:用 HBuilderX 导入 `manager-mobile`,然后参考下面的教程就能运行了
### 环境变量与配置
项目使用自定义 `env` 目录存放环境文件,按 Vite 规范命名:`.env.development``.env.production` 等。
关键变量(部分):
- VITE_APP_TITLE:应用名称(写入 `manifest.config.ts`
- VITE_UNI_APPIDuni-app 应用 appidApp
- VITE_WX_APPID:微信小程序 appidmp-weixin
- VITE_FALLBACK_LOCALE:默认语言,如 `zh-Hans`
- VITE_SERVER_BASEURL:服务端基础地址(HTTP 请求 baseURL
- VITE_DELETE_CONSOLE:构建时是否移除 console`true`/`false`
- VITE_SHOW_SOURCEMAP:是否生成 sourcemap(默认关闭)
- VITE_LOGIN_URL:未登录跳转的登录页路径(路由拦截器使用)
示例(`env/.env.development`):
```env
VITE_APP_TITLE=小智
VITE_FALLBACK_LOCALE=zh-Hans
VITE_UNI_APPID=
VITE_WX_APPID=
VITE_SERVER_BASEURL=http://localhost:8080
VITE_DELETE_CONSOLE=false
VITE_SHOW_SOURCEMAP=false
VITE_LOGIN_URL=/pages/login/index
```
说明:
- `manifest.config.ts` 会从 `env` 读取标题、appid、语言等配置。
### 重要注意事项
⚠️ **部署前必须修改的配置项:**
1. **应用 ID 配置**
- `VITE_UNI_APPID`:需要在 [DCloud 开发者中心](https://dev.dcloud.net.cn/) 创建应用并获取 AppID
- `VITE_WX_APPID`:需要在 [微信公众平台](https://mp.weixin.qq.com/) 注册小程序并获取 AppID
2. **服务端地址**
- `VITE_SERVER_BASEURL`:修改为您的实际服务端地址
3. **应用信息**
- `VITE_APP_TITLE`:修改为您的应用名称
- 更新 `src/static/logo.png` 等图标资源
4. **其他配置**
- 检查 `manifest.config.ts` 中的应用配置信息
- 根据需要修改 `src/layouts/fg-tabbar/tabbarList.ts` 中的 tabbar 配置
### 详细操作指南
#### 1. 获取 uni-app AppID
![生成AppID](../../docs/images/manager-mobile/生成appid.png)
- 复制生成的 AppID 到环境变量 `VITE_UNI_APPID`
#### 2. 本地运行步骤
![本地运行](../../docs/images/manager-mobile/本地运行.png)
**App 本地调试:**
1. 用 HBuilderX 导入 `manager-mobile` 目录
2. 重新识别项目
3. 连接手机或使用模拟器进行真机调试
**项目识别问题解决:**
![重新识别项目](../../docs/images/manager-mobile/重新识别项目.png)
如果 HBuilderX 无法正确识别项目类型:
- 在项目根目录右键选择"重新识别项目类型"
- 确保项目被识别为 "uni-app" 项目
### 路由与鉴权
-`src/main.ts` 中注册了路由拦截插件 `routeInterceptor`
- 黑名单拦截:仅对配置为需要登录的页面进行校验(来源 `@/utils``getNeedLoginPages`)。
- 登录判断:基于用户信息(`pinia``useUserStore`),未登录将跳转到 `VITE_LOGIN_URL`,并附带重定向回原页面的参数。
### 网络请求
- 基于 `alova` + `@alova/adapter-uniapp`,统一在 `src/http/request/alova.ts` 创建实例。
- `baseURL` 读取环境配置(`getEnvBaseUrl`),可通过 `method.config.meta.domain` 动态切换域名。
- 认证:默认从本地 `token``uni.getStorageSync('token')`)注入 `Authorization` 头,缺失则重定向登录。
- 响应:统一处理 `statusCode !== 200` 的 HTTP 错误与业务 `code !== 0` 的错误;`401` 会清除 token 并跳转登录。
### 构建与发布
**微信小程序:**
1. 确保已配置正确的 `VITE_WX_APPID`
2. 运行 `pnpm build:mp`,产物在 `dist/build/mp-weixin`
3. 用微信开发者工具导入项目目录,并上传代码
4. 在微信公众平台提交审核
**Android & iOS App**
#### 3. App 打包发行步骤
**步骤一:准备打包**
![打包发行步骤1](../../docs/images/manager-mobile/打包发行步骤1.png)
1. 确保已配置正确的 `VITE_UNI_APPID`
2. 运行 `pnpm build:app`,产物在 `dist/build/app`
3. 用 HBuilderX 导入项目目录
4. 在 HBuilderX 中点击"发行" → "原生App-云打包"
**步骤二:配置打包参数**
![打包发行步骤2](../../docs/images/manager-mobile/打包发行步骤2.png)
1. **应用图标和启动图**:上传应用图标和启动页面图片
2. **应用版本号**:设置版本号和版本名称
3. **签名证书**
- Android:上传 keystore 证书文件
- iOS:配置开发者证书和描述文件
4. **包名配置**:设置应用的包名(Bundle ID)
5. **打包类型**:选择测试包或正式包
6. 点击"打包"开始云打包流程
**发布到应用商店:**
- **Android**:将生成的 APK 文件上传到各大 Android 应用市场
- **iOS**:将生成的 IPA 文件通过 App Store Connect 上传到 App Store(需要 Apple 开发者账号)
### 约定与工程化
- 页面与分包:由 `@uni-helper/vite-plugin-uni-pages``pages.config.ts` 统一生成;tabbar 配置在 `src/layouts/fg-tabbar/tabbarList.ts`
- 组件与 hooks 自动导入:见 `vite.config.ts``unplugin-auto-import``@uni-helper/vite-plugin-uni-components`
- 样式:使用 UnoCSS 与 `src/style/index.scss`
- 状态管理:`pinia` + `pinia-plugin-persistedstate`
- 代码规范:内置 `eslint``husky``lint-staged`,提交前自动格式化(`lint-staged`)。
### 常用脚本
```bash
# 开发
pnpm dev:mp # 等价 dev:mp-weixin
# 构建
pnpm build:mp # 等价 build:mp-weixin
# 其他
pnpm type-check
pnpm lint && pnpm lint:fix
```
### License
MIT
+19
View File
@@ -0,0 +1,19 @@
VITE_APP_TITLE = '小智'
VITE_APP_PORT = 9000
VITE_UNI_APPID = '__UNI__36A515E'
VITE_WX_APPID = 'wxa2abb91f64032a2b'
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
VITE_APP_PUBLIC_BASE=/
# 登录页面
VITE_LOGIN_URL = '/pages/login/index'
# 第一个请求地址
VITE_SERVER_BASEURL = 'https://2662r3426b.vicp.fun/xiaozhi'
VITE_UPLOAD_BASEURL = '/otaMag/upload'
# h5是否需要配置代理
VITE_APP_PROXY=true
VITE_APP_PROXY_PREFIX = '/xiaozhi'
+6
View File
@@ -0,0 +1,6 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = true
+6
View File
@@ -0,0 +1,6 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = true
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = false
+4
View File
@@ -0,0 +1,4 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
+43
View File
@@ -0,0 +1,43 @@
import uniHelper from '@uni-helper/eslint-config'
export default uniHelper({
unocss: true,
vue: true,
markdown: false,
ignores: [
'src/uni_modules/',
'dist',
// unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
'auto-import.d.ts',
// vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
'uni-pages.d.ts',
// 插件生成的文件
'src/pages.json',
'src/manifest.json',
// 忽略自动生成文件
'src/service/app/**',
],
rules: {
'no-console': 'off',
'no-unused-vars': 'off',
'vue/no-unused-refs': 'off',
'unused-imports/no-unused-vars': 'off',
'eslint-comments/no-unlimited-disable': 'off',
'jsdoc/check-param-names': 'off',
'jsdoc/require-returns-description': 'off',
'ts/no-empty-object-type': 'off',
'no-extend-native': 'off',
},
formatters: {
/**
* Format CSS, LESS, SCSS files, also the `<style>` blocks in Vue
* By default uses Prettier
*/
css: true,
/**
* Format HTML files
* By default uses Prettier
*/
html: true,
},
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+26
View File
@@ -0,0 +1,26 @@
<!doctype html>
<html build-time="%BUILD_TIME%">
<head>
<meta charset="UTF-8" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<script>
var coverSupport =
'CSS' in window &&
typeof CSS.supports === 'function' &&
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') +
'" />',
)
</script>
<title>unibest</title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+155
View File
@@ -0,0 +1,155 @@
import path from 'node:path'
import process from 'node:process'
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
import { loadEnv } from 'vite'
// 手动解析命令行参数获取 mode
function getMode() {
const args = process.argv.slice(2)
const modeFlagIndex = args.findIndex(arg => arg === '--mode')
return modeFlagIndex !== -1 ? args[modeFlagIndex + 1] : args[0] === 'build' ? 'production' : 'development' // 默认 development
}
// 获取环境变量的范例
const env = loadEnv(getMode(), path.resolve(process.cwd(), 'env'))
const {
VITE_APP_TITLE,
VITE_UNI_APPID,
VITE_WX_APPID,
VITE_APP_PUBLIC_BASE,
VITE_FALLBACK_LOCALE,
} = env
export default defineManifestConfig({
'name': VITE_APP_TITLE,
'appid': VITE_UNI_APPID,
'description': '',
'versionName': '1.0.0',
'versionCode': '100',
'transformPx': false,
'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
'h5': {
router: {
// base: VITE_APP_PUBLIC_BASE,
},
},
/* 5+App特有相关 */
'app-plus': {
usingComponents: true,
nvueStyleCompiler: 'uni-app',
compilerVersion: 3,
compatible: {
ignoreVersion: true,
},
splashscreen: {
alwaysShowBeforeRender: true,
waiting: true,
autoclose: true,
delay: 0,
},
/* 模块配置 */
modules: {},
/* 应用发布信息 */
distribute: {
/* android打包配置 */
android: {
minSdkVersion: 30,
targetSdkVersion: 30,
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
permissions: [
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
'<uses-permission android:name="android.permission.VIBRATE"/>',
'<uses-permission android:name="android.permission.READ_LOGS"/>',
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.CAMERA"/>',
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
'<uses-feature android:name="android.hardware.camera"/>',
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
'<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>',
'<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>',
],
},
/* ios打包配置 */
ios: {},
/* SDK配置 */
sdkConfigs: {},
/* 图标配置 */
icons: {
android: {
hdpi: 'unpackage/res/icons/72x72.png',
xhdpi: 'unpackage/res/icons/96x96.png',
xxhdpi: 'unpackage/res/icons/144x144.png',
xxxhdpi: 'unpackage/res/icons/192x192.png',
},
ios: {
appstore: 'unpackage/res/icons/1024x1024.png',
ipad: {
'app': 'unpackage/res/icons/76x76.png',
'app@2x': 'unpackage/res/icons/152x152.png',
'notification': 'unpackage/res/icons/20x20.png',
'notification@2x': 'unpackage/res/icons/40x40.png',
'proapp@2x': 'unpackage/res/icons/167x167.png',
'settings': 'unpackage/res/icons/29x29.png',
'settings@2x': 'unpackage/res/icons/58x58.png',
'spotlight': 'unpackage/res/icons/40x40.png',
'spotlight@2x': 'unpackage/res/icons/80x80.png',
},
iphone: {
'app@2x': 'unpackage/res/icons/120x120.png',
'app@3x': 'unpackage/res/icons/180x180.png',
'notification@2x': 'unpackage/res/icons/40x40.png',
'notification@3x': 'unpackage/res/icons/60x60.png',
'settings@2x': 'unpackage/res/icons/58x58.png',
'settings@3x': 'unpackage/res/icons/87x87.png',
'spotlight@2x': 'unpackage/res/icons/80x80.png',
'spotlight@3x': 'unpackage/res/icons/120x120.png',
},
},
},
},
},
/* 快应用特有相关 */
'quickapp': {},
/* 小程序特有相关 */
'mp-weixin': {
appid: VITE_WX_APPID,
setting: {
urlCheck: false,
// 是否启用 ES6 转 ES5
es6: true,
minified: true,
},
optimization: {
subPackages: true,
},
usingComponents: true,
// __usePrivacyCheck__: true,
permission: {
'scope.userLocation': {
desc: 'WiFi配网功能需要获取位置权限',
},
},
requiredPrivateInfos: ['getLocation'],
},
'mp-alipay': {
usingComponents: true,
styleIsolation: 'shared',
},
'mp-baidu': {
usingComponents: true,
},
'mp-toutiao': {
usingComponents: true,
},
'uniStatistics': {
enable: false,
},
'vueVersion': '3',
})
+161
View File
@@ -0,0 +1,161 @@
{
"name": "xiaozhi-mobile-admin",
"type": "commonjs",
"version": "3.4.0",
"unibest-version": "3.4.0",
"packageManager": "pnpm@10.10.0",
"description": "xiaozhi-esp32-server的移动端管理后台",
"generate-time": "2025-08-04",
"author": {
"name": "huangjunsen",
"zhName": "黄俊森",
"email": "huangjunsen@xiaozhi.com",
"github": "https://github.com/huangjunsen0406",
"gitee": "https://gitee.com/huang-jun-sen"
},
"license": "MIT",
"homepage": "https://github.com/xinnan-tech/xiaozhi-esp32-server",
"repository": "https://github.com/xinnan-tech/xiaozhi-esp32-server",
"bugs": {
"url": "https://github.com/xinnan-tech/xiaozhi-esp32-server/issues"
},
"engines": {
"node": ">=18",
"pnpm": ">=7.30"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
"uvm": "npx @dcloudio/uvm@latest",
"uvm-rm": "node ./scripts/postupgrade.js",
"postuvm": "echo upgrade uni-app success!",
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:custom": "uni -p",
"dev": "uni",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp": "uni -p mp-weixin",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:app-android": "uni build -p app-android",
"build:app-ios": "uni build -p app-ios",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp": "uni build -p mp-weixin",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit",
"openapi-ts-request": "openapi-ts",
"prepare": "git init && husky",
"lint": "eslint",
"lint:fix": "eslint --fix"
},
"dependencies": {
"@alova/adapter-uniapp": "^2.0.14",
"@alova/shared": "^1.3.1",
"@dcloudio/uni-app": "3.0.0-4060620250520001",
"@dcloudio/uni-app-harmony": "3.0.0-4060620250520001",
"@dcloudio/uni-app-plus": "3.0.0-4060620250520001",
"@dcloudio/uni-components": "3.0.0-4060620250520001",
"@dcloudio/uni-h5": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-alipay": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-baidu": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-harmony": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-jd": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-lark": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-qq": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-toutiao": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-weixin": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-xhs": "3.0.0-4060620250520001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4060620250520001",
"@tanstack/vue-query": "^5.62.16",
"abortcontroller-polyfill": "^1.7.8",
"alova": "^3.3.3",
"dayjs": "1.11.10",
"js-cookie": "^3.0.5",
"pinia": "2.0.36",
"pinia-plugin-persistedstate": "3.2.1",
"vue": "^3.4.21",
"wot-design-uni": "^1.9.1",
"z-paging": "2.8.7"
},
"devDependencies": {
"@alova/wormhole": "^1.0.8",
"@antfu/eslint-config": "^4.15.0",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@dcloudio/types": "^3.4.8",
"@dcloudio/uni-automator": "3.0.0-4060620250520001",
"@dcloudio/uni-cli-shared": "3.0.0-4060620250520001",
"@dcloudio/uni-stacktracey": "3.0.0-4060620250520001",
"@dcloudio/uni-uts-v1": "3.0.0-4060620250520001",
"@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@iconify-json/carbon": "^1.2.4",
"@rollup/rollup-darwin-x64": "^4.28.0",
"@types/node": "^20.17.9",
"@types/wechat-miniprogram": "^3.4.8",
"@uni-helper/eslint-config": "^0.4.0",
"@uni-helper/uni-types": "1.0.0-alpha.3",
"@uni-helper/unocss-preset-uni": "^0.2.11",
"@uni-helper/vite-plugin-uni-components": "0.2.0",
"@uni-helper/vite-plugin-uni-layouts": "0.1.10",
"@uni-helper/vite-plugin-uni-manifest": "0.2.8",
"@uni-helper/vite-plugin-uni-pages": "0.2.28",
"@uni-helper/vite-plugin-uni-platform": "0.0.4",
"@uni-ku/bundle-optimizer": "^1.3.3",
"@unocss/eslint-plugin": "^66.2.3",
"@unocss/preset-legacy-compat": "^0.59.4",
"@vue/runtime-core": "^3.4.21",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.20",
"eslint": "^9.29.0",
"eslint-plugin-format": "^1.0.1",
"husky": "^9.1.7",
"lint-staged": "^15.2.10",
"openapi-ts-request": "^1.1.2",
"postcss": "^8.4.49",
"postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "1.77.8",
"typescript": "^5.7.2",
"unocss": "65.4.2",
"unplugin-auto-import": "^0.17.8",
"vite": "5.2.8",
"vite-plugin-restart": "^0.4.2",
"vue-tsc": "^2.2.10"
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china"
},
"lint-staged": {
"*": "eslint --fix"
}
}
+23
View File
@@ -0,0 +1,23 @@
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
import { tabBar } from './src/layouts/fg-tabbar/tabbarList'
export default defineUniPages({
globalStyle: {
navigationStyle: 'default',
navigationBarTitleText: '小智',
navigationBarBackgroundColor: '#f8f8f8',
navigationBarTextStyle: 'black',
backgroundColor: '#FFFFFF',
},
easycom: {
autoscan: true,
custom: {
'^fg-(.*)': '@/components/fg-$1/fg-$1.vue',
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
'z-paging/components/z-paging$1/z-paging$1.vue',
},
},
// tabbar 的配置统一在 "./src/layouts/fg-tabbar/tabbarList.ts" 文件中
tabBar: tabBar as any,
})
@@ -0,0 +1,13 @@
diff --git a/dist/uni-h5.es.js b/dist/uni-h5.es.js
index 7421bad97d94ad34a3d4d94292a9ee9071430662..19c6071ee4036ceb8d1cfa09030e471c002d2cda 100644
--- a/dist/uni-h5.es.js
+++ b/dist/uni-h5.es.js
@@ -23410,7 +23410,7 @@ function useShowTabBar(emit2) {
const tabBar2 = useTabBar();
const showTabBar2 = computed(() => route.meta.isTabBar && tabBar2.shown);
updateCssVar({
- "--tab-bar-height": tabBar2.height
+ "--tab-bar-height": tabBar2?.height || 0
});
return showTabBar2;
}
+14866
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
packages:
- '**'
- '!node_modules'
patchedDependencies:
'@dcloudio/uni-h5': patches/@dcloudio__uni-h5.patch
@@ -0,0 +1,35 @@
// # 执行 `pnpm upgrade` 后会升级 `uniapp` 相关依赖
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
// # 只需要执行下面的命令即可
const { exec } = require('node:child_process')
// 定义要执行的命令
const dependencies = [
'@dcloudio/uni-app-harmony',
// TODO: 如果不需要某个平台的小程序,请手动删除或注释掉
'@dcloudio/uni-mp-alipay',
'@dcloudio/uni-mp-baidu',
'@dcloudio/uni-mp-jd',
'@dcloudio/uni-mp-kuaishou',
'@dcloudio/uni-mp-lark',
'@dcloudio/uni-mp-qq',
'@dcloudio/uni-mp-toutiao',
'@dcloudio/uni-mp-xhs',
'@dcloudio/uni-quickapp-webview',
// i18n模板要注释掉下面的
'vue-i18n',
]
// 使用exec执行命令
exec(`pnpm un ${dependencies.join(' ')}`, (error, stdout, stderr) => {
if (error) {
// 如果有错误,打印错误信息
console.error(`执行出错: ${error}`)
return
}
// 打印正常输出
console.log(`stdout: ${stdout}`)
// 如果有错误输出,也打印出来
console.error(`stderr: ${stderr}`)
})
+39
View File
@@ -0,0 +1,39 @@
<script setup lang="ts">
import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
import { usePageAuth } from '@/hooks/usePageAuth'
import { useConfigStore } from '@/store'
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
usePageAuth()
const configStore = useConfigStore()
onLaunch(() => {
console.log('App Launch')
// 获取公共配置
configStore.fetchPublicConfig().catch((error) => {
console.error('获取公共配置失败:', error)
})
})
onShow(() => {
console.log('App Show')
})
onHide(() => {
console.log('App Hide')
})
</script>
<style lang="scss">
swiper,
scroll-view {
flex: 1;
height: 100%;
overflow: hidden;
}
image {
width: 100%;
height: 100%;
vertical-align: middle;
}
</style>
+185
View File
@@ -0,0 +1,185 @@
import type {
Agent,
AgentCreateData,
AgentDetail,
ModelOption,
RoleTemplate,
} from './types'
import { http } from '@/http/request/alova'
// 获取智能体详情
export function getAgentDetail(id: string) {
return http.Get<AgentDetail>(`/agent/${id}`, {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 获取角色模板列表
export function getRoleTemplates() {
return http.Get<RoleTemplate[]>('/agent/template', {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 获取模型选项
export function getModelOptions(modelType: string, modelName: string = '') {
return http.Get<ModelOption[]>('/models/names', {
params: {
modelType,
modelName,
},
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 获取智能体列表
export function getAgentList() {
return http.Get<Agent[]>('/agent/list', {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 创建智能体
export function createAgent(data: AgentCreateData) {
return http.Post<string>('/agent', data, {
meta: {
ignoreAuth: false,
toast: true,
},
})
}
// 删除智能体
export function deleteAgent(id: string) {
return http.Delete(`/agent/${id}`, {
meta: {
ignoreAuth: false,
toast: true,
},
})
}
// 获取TTS音色列表
export function getTTSVoices(ttsModelId: string, voiceName: string = '') {
return http.Get<{ id: string, name: string }[]>(`/models/${ttsModelId}/voices`, {
params: {
voiceName,
},
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 更新智能体
export function updateAgent(id: string, data: Partial<AgentDetail>) {
return http.Put(`/agent/${id}`, data, {
meta: {
ignoreAuth: false,
toast: true,
},
cacheFor: {
expire: 0,
},
})
}
// 获取插件列表
export function getPluginFunctions() {
return http.Get<any[]>(`/models/provider/plugin/names`, {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 获取mcp接入点
export function getMcpAddress(agentId: string) {
return http.Get<string>(`/agent/mcp/address/${agentId}`, {
meta: {
ignoreAuth: false,
toast: false,
},
})
}
// 获取mcp工具
export function getMcpTools(agentId: string) {
return http.Get<string[]>(`/agent/mcp/tools/${agentId}`, {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 获取声纹列表
export function getVoicePrintList(agentId: string) {
return http.Get<any[]>(`/agent/voice-print/list/${agentId}`, {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 获取语音对话记录
export function getChatHistoryUser(agentId: string) {
return http.Get<any[]>(`/agent/${agentId}/chat-history/user`, {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
// 新增声纹说话人
export function createVoicePrint(data: { agentId: string, audioId: string, sourceName: string, introduce: string }) {
return http.Post('/agent/voice-print', data, {
meta: {
ignoreAuth: false,
toast: true,
},
})
}
+107
View File
@@ -0,0 +1,107 @@
// 智能体列表数据类型
export interface Agent {
id: string
agentName: string
ttsModelName: string
ttsVoiceName: string
llmModelName: string
vllmModelName: string
memModelId: string
systemPrompt: string
summaryMemory: string | null
lastConnectedAt: string | null
deviceCount: number
}
// 智能体创建数据类型
export interface AgentCreateData {
agentName: string
}
// 智能体详情数据类型
export interface AgentDetail {
id: string
userId: string
agentCode: string
agentName: string
asrModelId: string
vadModelId: string
llmModelId: string
vllmModelId: string
ttsModelId: string
ttsVoiceId: string
memModelId: string
intentModelId: string
chatHistoryConf: number
systemPrompt: string
summaryMemory: string
langCode: string
language: string
sort: number
creator: string
createdAt: string
updater: string
updatedAt: string
functions: AgentFunction[]
}
export interface AgentFunction {
id?: string
agentId?: string
pluginId: string
paramInfo: Record<string, string | number | boolean> | null
}
// 角色模板数据类型
export interface RoleTemplate {
id: string
agentCode: string
agentName: string
asrModelId: string
vadModelId: string
llmModelId: string
vllmModelId: string
ttsModelId: string
ttsVoiceId: string
memModelId: string
intentModelId: string
chatHistoryConf: number
systemPrompt: string
summaryMemory: string
langCode: string
language: string
sort: number
creator: string
createdAt: string
updater: string
updatedAt: string
}
// 模型选项数据类型
export interface ModelOption {
id: string
modelName: string
}
export interface PluginField {
key: string
type: string
label: string
default: string
selected?: boolean
editing?: boolean
}
export interface PluginDefinition {
id: string
modelType: string
providerCode: string
name: string
fields: PluginField[] // 注意:原始是字符串,需要先 JSON.parse
sort: number
updater: string
updateDate: string
creator: string
createDate: string
[key: string]: any
}
+127
View File
@@ -0,0 +1,127 @@
import { http } from '@/http/request/alova'
// 登录接口数据类型
export interface LoginData {
username: string
password: string
captcha: string
captchaId: string
areaCode?: string
mobile?: string
}
// 登录响应数据类型
export interface LoginResponse {
token: string
expire: number
clientHash: string
}
// 验证码响应数据类型
export interface CaptchaResponse {
captchaId: string
captchaImage: string
}
// 获取验证码
export function getCaptcha(uuid: string) {
return http.Get<string>('/user/captcha', {
params: { uuid },
meta: {
ignoreAuth: true,
toast: false,
},
})
}
// 用户登录
export function login(data: LoginData) {
return http.Post<LoginResponse>('/user/login', data, {
meta: {
ignoreAuth: true,
toast: true,
},
})
}
// 用户信息响应数据类型
export interface UserInfo {
id: number
username: string
realName: string
email: string
mobile: string
status: number
superAdmin: number
}
// 公共配置响应数据类型
export interface PublicConfig {
enableMobileRegister: boolean
version: string
year: string
allowUserRegister: boolean
mobileAreaList: Array<{
name: string
key: string
}>
beianIcpNum: string
beianGaNum: string
name: string
}
// 获取用户信息
export function getUserInfo() {
return http.Get<UserInfo>('/user/info', {
meta: {
ignoreAuth: false,
toast: false,
},
})
}
// 获取公共配置
export function getPublicConfig() {
return http.Get<PublicConfig>('/user/pub-config', {
meta: {
ignoreAuth: true,
toast: false,
},
})
}
// 注册数据类型
export interface RegisterData {
username: string
password: string
confirmPassword: string
captcha: string
captchaId: string
areaCode: string
mobile: string
mobileCaptcha: string
}
// 发送短信验证码
export function sendSmsCode(data: {
phone: string
captcha: string
captchaId: string
}) {
return http.Post('/user/smsVerification', data, {
meta: {
ignoreAuth: true,
toast: false,
},
})
}
// 用户注册
export function register(data: RegisterData) {
return http.Post('/user/register', data, {
meta: {
ignoreAuth: true,
toast: true,
},
})
}
@@ -0,0 +1,60 @@
import type {
ChatMessage,
ChatSessionsResponse,
GetSessionsParams,
} from './types'
import { http } from '@/http/request/alova'
/**
* 获取聊天会话列表
* @param agentId 智能体ID
* @param params 分页参数
*/
export function getChatSessions(agentId: string, params: GetSessionsParams) {
return http.Get<ChatSessionsResponse>(`/agent/${agentId}/sessions`, {
params,
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
/**
* 获取聊天记录详情
* @param agentId 智能体ID
* @param sessionId 会话ID
*/
export function getChatHistory(agentId: string, sessionId: string) {
return http.Get<ChatMessage[]>(`/agent/${agentId}/chat-history/${sessionId}`, {
meta: {
ignoreAuth: false,
toast: false,
},
})
}
/**
* 获取音频下载ID
* @param audioId 音频ID
*/
export function getAudioId(audioId: string) {
return http.Post<string>(`/agent/audio/${audioId}`, {}, {
meta: {
ignoreAuth: false,
toast: false,
},
})
}
/**
* 获取音频播放地址
* @param downloadId 下载ID
*/
export function getAudioPlayUrl(downloadId: string) {
// 根据需求文档,这个是直接返回二进制的,所以我们直接构造URL
return `/agent/play/${downloadId}`
}
@@ -0,0 +1,2 @@
export * from './chat-history'
export * from './types'
@@ -0,0 +1,38 @@
// 聊天会话列表项
export interface ChatSession {
sessionId: string
createdAt: string
chatCount: number
}
// 聊天会话列表响应
export interface ChatSessionsResponse {
total: number
list: ChatSession[]
}
// 聊天消息
export interface ChatMessage {
createdAt: string
chatType: 1 | 2 // 1是用户,2是AI
content: string
audioId: string | null
macAddress: string
}
// 用户消息内容(需要解析JSON)
export interface UserMessageContent {
speaker: string
content: string
}
// 获取聊天会话列表参数
export interface GetSessionsParams {
page: number
limit: number
}
// 音频播放相关
export interface AudioResponse {
data: string // 音频下载ID
}
@@ -0,0 +1,55 @@
import type { Device, FirmwareType } from './types'
import { http } from '@/http/request/alova'
/**
* 获取设备类型列表
*/
export function getFirmwareTypes() {
return http.Get<FirmwareType[]>('/admin/dict/data/type/FIRMWARE_TYPE')
}
/**
* 获取绑定设备列表
* @param agentId 智能体ID
*/
export function getBindDevices(agentId: string) {
return http.Get<Device[]>(`/device/bind/${agentId}`, {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
/**
* 添加设备
* @param agentId 智能体ID
* @param code 验证码
*/
export function bindDevice(agentId: string, code: string) {
return http.Post(`/device/bind/${agentId}/${code}`, null)
}
/**
* 设置设备OTA升级开关
* @param deviceId 设备ID (MAC地址)
* @param autoUpdate 是否自动升级 0|1
*/
export function updateDeviceAutoUpdate(deviceId: string, autoUpdate: number) {
return http.Put(`/device/update/${deviceId}`, {
autoUpdate,
})
}
/**
* 解绑设备
* @param deviceId 设备ID (MAC地址)
*/
export function unbindDevice(deviceId: string) {
return http.Post('/device/unbind', {
deviceId,
})
}
@@ -0,0 +1,2 @@
export * from './device'
export * from './types'
@@ -0,0 +1,21 @@
export interface FirmwareType {
name: string
key: string
}
export interface Device {
id: string
userId: string
macAddress: string
lastConnectedAt: string
autoUpdate: number
board: string
alias?: string
agentId: string
appVersion: string
sort: number
updater?: string
updateDate: string
creator: string
createDate: string
}
@@ -0,0 +1,2 @@
export * from './types'
export * from './voiceprint'
@@ -0,0 +1,29 @@
// 声纹信息响应类型
export interface VoicePrint {
id: string
audioId: string
sourceName: string
introduce: string
createDate: string
}
// 语音对话记录类型
export interface ChatHistory {
content: string
audioId: string
}
// 创建说话人数据类型
export interface CreateSpeakerData {
agentId: string
audioId: string
sourceName: string
introduce: string
}
// 通用响应类型
export interface ApiResponse<T = any> {
code: number
msg: string
data: T
}

Some files were not shown because too many files have changed in this diff Show More