mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-21 22:53:56 +08:00
update:加入测试速度功能
This commit is contained in:
@@ -333,22 +333,14 @@ https://espressif.github.io/esp-launchpad/
|
||||
|
||||
## 1、TTS 经常失败,经常超时
|
||||
|
||||
建议:如果`EdgeTTS`慢或经常失败,可以更换成`火山引擎的豆包TTS`,如果两个都慢,可能所处的网络环境需要优化一下。
|
||||
有部分网友反馈,使用`EdgeTTS`时,最好不要使用梯子,容易失败。
|
||||
建议:如果`EdgeTTS`经常失败,先检查一下是否用了梯子。
|
||||
如果用的是`火山引擎的豆包TTS`经常失败,最好使用付费版本,因为他们的测试版本只有2个并发。
|
||||
|
||||
## 2、大模型回复有点慢
|
||||
|
||||
建议:大模型和TTS都是依赖接口,如果网络环境不佳,可以考虑换成本地模型。或多尝试切换不同的接口模型。
|
||||
|
||||
## 3、为啥我的ChatGLMLLM回复有点问题,明明它才是小智,却把我当小智。
|
||||
|
||||
建议: 可以先修改`配置文件`里的提示词。也可以把免费的`glm-4-flash`,换成`智谱`其他收费版本的模型。
|
||||
|
||||
## 4、我想通过小智控制电灯、空调、远程开关机等操作。
|
||||
## 2、我想通过小智控制电灯、空调、远程开关机等操作。
|
||||
|
||||
建议:在配置文件里,将`LLM`设置成`DifyLLM`,然后通过`Dify`编排智能体实现。
|
||||
|
||||
## 5、我说话很慢,我停顿一下,小智老是抢我的话,咋办。
|
||||
## 3、我说话很慢,我停顿一下,小智老是抢我的话,咋办。
|
||||
|
||||
建议:在配置文件里,找到这一段,将`min_silence_duration_ms`值改大一点,比如改成`1000`。
|
||||
|
||||
@@ -360,7 +352,51 @@ VAD:
|
||||
min_silence_duration_ms: 700 # 如果说话停顿比较长,可以把这个值设置大一些
|
||||
```
|
||||
|
||||
## 6、更多问题,可联系我们反馈
|
||||
## 3、如何才能提高小智对话响应速度?
|
||||
|
||||
本项目的默认配置,是成本最低的配置。建议刚上手的童鞋,使用默认的免费模型,先解决了“跑得动”的问题,再解决“跑得快”的问题。
|
||||
如果要提高响应速度,需要更换各组件来解决。以下是本项目各组件的响应速度测试Tip。
|
||||
|
||||
以下内容和结论仅供参考,不构成任何形式的承诺或保证。
|
||||
|
||||
### LLM 类组件
|
||||
|
||||
| 排名 | 组件名称 | 响应速度(ms) |
|
||||
|:---|:------------|:--------:|
|
||||
| 1 | AliLLM | 630 |
|
||||
| 2 | ChatGLMLLM | 2000 |
|
||||
| 3 | DeepSeekLLM | 6800 |
|
||||
|
||||
```
|
||||
测试地点:广东省佛山市禅城区
|
||||
测试时间:2025年2月9日 16:12
|
||||
宽带运营商:中国移动
|
||||
测试方法:更换配置后,执行core/utils/llm.py文件
|
||||
```
|
||||
|
||||
### TTS 类组件
|
||||
|
||||
| 排名 | 组件名称 | 响应速度(ms) |
|
||||
|:---|:----------|:--------:|
|
||||
| 1 | DoubaoTTS | 645 |
|
||||
| 2 | EdgeTTS | 1019 |
|
||||
|
||||
```
|
||||
测试地点:广东省佛山市禅城区
|
||||
测试时间:2025年2月9日 16:12
|
||||
宽带运营商:中国移动
|
||||
测试方法:更换配置后,执行core/utils/tts.py文件
|
||||
|
||||
```
|
||||
|
||||
### 结论
|
||||
|
||||
`2025年2月9日`,如果我的电脑在`广东省佛山市禅城区`,且使用的是`中国移动`网络,我会优先使用:
|
||||
|
||||
- LLM:`AliLLM`
|
||||
- TTS:`DoubaoTTS`
|
||||
|
||||
## 4、更多问题,可联系我们反馈
|
||||
|
||||

|
||||
|
||||
|
||||
+10
-3
@@ -42,6 +42,11 @@ VAD:
|
||||
min_silence_duration_ms: 700 # 如果说话停顿比较长,可以把这个值设置大一些
|
||||
|
||||
LLM:
|
||||
AliLLM:
|
||||
# 可在这里找到你的 api_key https://bailian.console.aliyun.com/?apiKey=1#/api-key
|
||||
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
model_name: qwen-turbo
|
||||
api_key: 你的阿里云dashscope api key
|
||||
DeepSeekLLM:
|
||||
# 可在这里找到你的api key https://platform.deepseek.com/
|
||||
model_name: deepseek-chat
|
||||
@@ -63,9 +68,11 @@ TTS:
|
||||
output_file: tmp/
|
||||
DoubaoTTS:
|
||||
# 火山引擎语音合成服务,需要先在火山引擎控制台创建应用并获取appid和access_token
|
||||
# 山引擎语音一定要购买花钱,起步价30元,就有100并发了。如果用免费的只有2个并发,会经常报tts错误
|
||||
# 购买服务后,购买免费的音色后,可能要等半小时左右,才能使用。
|
||||
# 地址:https://console.volcengine.com/speech/service/8
|
||||
voice: BV407_V2_streaming
|
||||
voice: BV001_streaming
|
||||
output_file: tmp/
|
||||
appid: 你的火山引擎appid
|
||||
access_token: 你的火山引擎access_token
|
||||
appid: 你的火山引擎语音合成服务appid
|
||||
access_token: 你的火山引擎语音合成服务access_token
|
||||
cluster: volcano_tts
|
||||
|
||||
+2
-1
@@ -111,7 +111,7 @@ class ConnectionHandler:
|
||||
# 提交 LLM 任务
|
||||
try:
|
||||
start_time = time.time() # 记录开始时间
|
||||
llm_responses = self.llm.response(self, self.dialogue.get_llm_dialogue())
|
||||
llm_responses = self.llm.response(self.session_id, self.dialogue.get_llm_dialogue())
|
||||
except Exception as e:
|
||||
self.logger.error(f"LLM 处理出错 {query}: {e}")
|
||||
return None
|
||||
@@ -203,6 +203,7 @@ class ConnectionHandler:
|
||||
|
||||
def recode_first_last_text(self, text):
|
||||
if not self.tts_first_text:
|
||||
self.logger.info(f"大模型说出第一句话: {text}")
|
||||
self.tts_first_text = text
|
||||
self.tts_last_text = text
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ async def sendAudioMessage(conn, audios, duration, text):
|
||||
base_delay = conn.tts_duration
|
||||
|
||||
if text == conn.tts_first_text:
|
||||
logger.info(f"发送第一段语音: {text}")
|
||||
conn.tts_start_speak_time = time.time()
|
||||
await conn.websocket.send(json.dumps({
|
||||
"type": "tts",
|
||||
|
||||
@@ -12,6 +12,9 @@ async def handleTextMessage(conn, message):
|
||||
logger.info(f"收到文本消息:{message}")
|
||||
try:
|
||||
msg_json = json.loads(message)
|
||||
if isinstance(msg_json, int):
|
||||
await conn.websocket.send(message)
|
||||
return
|
||||
if msg_json["type"] == "hello":
|
||||
await handleHelloMessage(conn)
|
||||
elif msg_json["type"] == "abort":
|
||||
@@ -33,4 +36,4 @@ async def handleTextMessage(conn, message):
|
||||
if "text" in msg_json:
|
||||
await startToChat(conn, msg_json["text"])
|
||||
except json.JSONDecodeError:
|
||||
await conn.websocket.send(message)
|
||||
await conn.websocket.send(message)
|
||||
|
||||
+68
-5
@@ -2,6 +2,10 @@ import json
|
||||
import logging
|
||||
import openai
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from core.utils.util import is_segment
|
||||
from core.utils.util import get_string_no_punctuation_or_emoji
|
||||
from core.utils.util import read_config, get_project_dir
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -9,7 +13,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class LLM(ABC):
|
||||
@abstractmethod
|
||||
def response(self, conn, dialogue):
|
||||
def response(self, session_id, dialogue):
|
||||
"""LLM response generator"""
|
||||
pass
|
||||
|
||||
@@ -21,7 +25,7 @@ class DeepSeekLLM(LLM):
|
||||
self.base_url = config.get("url")
|
||||
self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url)
|
||||
|
||||
def response(self, conn, dialogue):
|
||||
def response(self, session_id, dialogue):
|
||||
logger.info(f"Generating response using {dialogue}")
|
||||
try:
|
||||
responses = self.client.chat.completions.create(
|
||||
@@ -47,7 +51,7 @@ class ChatGLMLLM(LLM):
|
||||
self.base_url = config.get("url")
|
||||
self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url)
|
||||
|
||||
def response(self, conn, dialogue):
|
||||
def response(self, session_id, dialogue):
|
||||
try:
|
||||
responses = self.client.chat.completions.create(
|
||||
model=self.model_name,
|
||||
@@ -64,13 +68,36 @@ class ChatGLMLLM(LLM):
|
||||
except Exception as e:
|
||||
logger.error(f"Error in response generation: {e}")
|
||||
|
||||
class AliLLM(LLM):
|
||||
def __init__(self, config):
|
||||
self.model_name = config.get("model_name")
|
||||
self.api_key = config.get("api_key")
|
||||
self.base_url = config.get("base_url")
|
||||
self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url)
|
||||
|
||||
def response(self, session_id, dialogue):
|
||||
try:
|
||||
responses = self.client.chat.completions.create(
|
||||
model=self.model_name,
|
||||
messages=dialogue,
|
||||
stream=True
|
||||
)
|
||||
for chunk in responses:
|
||||
# 检查是否存在有效的choice且content不为空
|
||||
if chunk.choices and len(chunk.choices) > 0:
|
||||
delta = chunk.choices[0].delta
|
||||
content = getattr(delta, 'content', '')
|
||||
if content: # 仅在content非空时生成
|
||||
yield content
|
||||
except Exception as e:
|
||||
logger.error(f"Error in response generation: {e}")
|
||||
|
||||
class DifyLLM(LLM):
|
||||
def __init__(self, config):
|
||||
self.api_key = config["api_key"]
|
||||
self.base_url = config.get("base_url", "https://api.dify.ai/v1").rstrip('/')
|
||||
|
||||
def response(self, conn,dialogue):
|
||||
def response(self, session_id, dialogue):
|
||||
try:
|
||||
# 取最后一条用户消息
|
||||
last_msg = next(m for m in reversed(dialogue) if m["role"] == "user")
|
||||
@@ -82,7 +109,7 @@ class DifyLLM(LLM):
|
||||
json={
|
||||
"query": last_msg["content"],
|
||||
"response_mode": "streaming",
|
||||
"user": conn.session_id,
|
||||
"user": session_id,
|
||||
"inputs": {}
|
||||
},
|
||||
stream=True
|
||||
@@ -103,9 +130,45 @@ def create_instance(class_name, *args, **kwargs):
|
||||
"DeepSeekLLM": DeepSeekLLM,
|
||||
"ChatGLMLLM": ChatGLMLLM,
|
||||
"DifyLLM": DifyLLM,
|
||||
"AliLLM": AliLLM,
|
||||
# 可扩展其他LLM实现
|
||||
}
|
||||
|
||||
if cls := cls_map.get(class_name):
|
||||
return cls(*args, **kwargs)
|
||||
raise ValueError(f"不支持的LLM类型: {class_name}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
响应速度测试
|
||||
"""
|
||||
config = read_config(get_project_dir() + "config.yaml")
|
||||
llm = create_instance(
|
||||
config["selected_module"]["LLM"],
|
||||
config["LLM"][config["selected_module"]["LLM"]]
|
||||
)
|
||||
|
||||
start_time = datetime.now()
|
||||
|
||||
dialogue = []
|
||||
dialogue.append({"role": "system", "content": config.get("prompt")})
|
||||
dialogue.append({"role": "user", "content": "你好小智"})
|
||||
llm_responses = llm.response("test", dialogue)
|
||||
response_message = []
|
||||
first_text = None
|
||||
start = 0
|
||||
|
||||
for content in llm_responses:
|
||||
response_message.append(content)
|
||||
|
||||
if is_segment(response_message):
|
||||
segment_text = "".join(response_message[start:])
|
||||
segment_text = get_string_no_punctuation_or_emoji(segment_text)
|
||||
if len(segment_text) > 0:
|
||||
if first_text is None:
|
||||
first_text = segment_text
|
||||
print("大模型首次返回耗时:" + str(datetime.now() - start_time))
|
||||
start = len(response_message)
|
||||
|
||||
print("大模型返回总耗时:" + str(datetime.now() - start_time))
|
||||
|
||||
+9
-4
@@ -20,7 +20,6 @@ class TTS(ABC):
|
||||
def __init__(self, config, delete_audio_file):
|
||||
self.delete_audio_file = delete_audio_file
|
||||
self.output_file = config.get("output_file")
|
||||
self.delete_audio_file = delete_audio_file
|
||||
|
||||
@abstractmethod
|
||||
def generate_filename(self):
|
||||
@@ -164,6 +163,9 @@ def create_instance(class_name, *args, **kwargs):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
响应速度测试
|
||||
"""
|
||||
config = read_config(get_project_dir() + "config.yaml")
|
||||
tts = create_instance(
|
||||
config["selected_module"]["TTS"],
|
||||
@@ -171,6 +173,9 @@ if __name__ == "__main__":
|
||||
config["delete_audio"]
|
||||
)
|
||||
tts.output_file = get_project_dir() + tts.output_file
|
||||
file_path = tts.to_tts("你好,测试")
|
||||
print(file_path)
|
||||
print(tts.wav_to_opus_data(file_path))
|
||||
start = datetime.now()
|
||||
file_path = tts.to_tts("你好,测试,我是人工智能小智")
|
||||
print("语音合成耗时:" + str(datetime.now() - start))
|
||||
start = datetime.now()
|
||||
tts.wav_to_opus_data(file_path)
|
||||
print("语音opus耗时:" + str(datetime.now() - start))
|
||||
|
||||
Reference in New Issue
Block a user