2024 / 11 / 15
一个能够快速响应你的回答的东西是不可缺少的,过多的等待时间会让用户产生急躁、不耐烦感。因此,一个能够快速响应的AI是十分有必要的。
首先,错误示例:使用 OpenAI 全家桶进行开发。 (这里的全家桶指的是 Whisper + GPT + TTS,而不是那个realtime)
因为OpenAI的Whisper和TTS非常非常慢。如果你用过OpenAI老版本的语言对话你就会知道,他的响应速度奇慢无比,整整11秒多。(点击查看前车之鉴 )
所以,我们需要改进一下。
虽然Whisper可信度高,TTS的音频较为真实,但这并不意味着我们需要为了少几个错别字多等几秒(并且有的时候他可能还会识别错误)。而且现在的大模型普遍可以免受错别字干扰。因此,我们只需使用 speech_recognition
库即可。
我们使用gTTS来做TTS,虽然他不是很会说中文 (让我们说中文)。
以下是我们实现的初步代码
import speech_recognition as sr from gtts import gTTS import os from openai import OpenAI # 文本处理函数 def process_text(text): client = OpenAI( base_url='https://api.openai-proxy.org/v1', api_key='sk-马赛克', ) chat_completion = client.chat.completions.create( messages=[ { "role": "user", "content": text, } ], model="gpt-4o-mini", ) print(chat_completion) response = chat_completion.choices[0].message.content return response # 语音识别(STT) def speech_to_text(): recognizer = sr.Recognizer() with sr.Microphone() as source: print("请说话...") audio = recognizer.listen(source) try: text = recognizer.recognize_google(audio, language="zh-CN") # 支持中文 print("识别的文本: ", text) return text except sr.UnknownValueError: print("抱歉,我没听懂") return None except sr.RequestError: print("网络错误,无法获取语音识别服务") return None # 文本转语音(TTS) def text_to_speech(text): tts = gTTS(text, lang='zh') tts.save("output.mp3") os.system("start output.mp3") if __name__ == "__main__": # 获取语音并转换为文本 text = speech_to_text() if text: # 处理文本 processed_text = process_text(text) # 将处理后的文本转换为语音 text_to_speech(processed_text)
最终用时:4.81秒
这已经是个比较能看的速度了(至少比全家桶的11秒快多了)。但,这还不够。
首先 把os.system换成playsound(记得先在开头导入它):
def text_to_speech(text): global starttime tts = gTTS(text, lang='zh') tts.save("output.mp3") print(time.time() - starttime) playsound.playsound("output.mp3")
实测5.61s。
然后开始断句。
这里简单短句,以“,”、“。”、“?”、“!”、“……”做分句,在播放第一个分句时开始生成第二个分句,以此类推。
例如:“你好,我是你的AI助手,有什么可以帮你的吗?”,这句话就有三个分句:
"你好"
"我是你的AI助手"
"有什么可以帮你的吗"
当TTS进程生成完“你好”时会立马开始生成下一句“我是你的AI助手”,此时主线程念出“你好”。播放完毕后等TTS生成完第二句话,好了就开始播放第二句话。
代码:
import speech_recognition as sr from gtts import gTTS from openai import OpenAI import re import playsound import threading starttime = 0 # Debug 用的,记录从开始识别到播放第一段音频前所耗费的时间。 sentences = 0 # 目前TTS已经生成了多少段音频 allsentences = 0 # TTS总共需要生成多少段音频 class TTSing(threading.Thread): #继承父类threading.Thread def __init__(self, text): threading.Thread.__init__(self) self.text = text # 将要生成的文本 def run(self): global sentences, allsentences # 先分句 # 定义分句的标点符号 punctuation = r'[,。?!……]' # 使用正则表达式按照标点符号分割句子 fj = re.split(punctuation, self.text) # 去掉空白字符 fj = [fj.strip() for fj in fj if fj.strip()] allsentences = len(fj) print("分句结果:", fj) # 生成音频 for i in fj: print("开始") tts = gTTS(i, lang='zh') tts.save(f"output{str(sentences+1)}.mp3") sentences += 1 if sentences == allsentences: break print(f"生成了 output{str(sentences)}.mp3,这是第{str(sentences)}段音频,共{str(allsentences)}段。") # 文本处理函数 def process_text(text): client = OpenAI( base_url='https://api.openai-proxy.org/v1', api_key='sk-马赛克', ) chat_completion = client.chat.completions.create( messages=[ { "role": "user", "content": text, } ], model="gpt-4o-mini", ) print(chat_completion) response = chat_completion.choices[0].message.content return response # 语音识别(STT) def speech_to_text(): global starttime recognizer = sr.Recognizer() with sr.Microphone() as source: print("请说话...") audio = recognizer.listen(source) try: text = recognizer.recognize_google(audio, language="zh-CN") # 支持中文 print("识别的文本: ", text) return text except sr.UnknownValueError: print("抱歉,我没听懂") return None except sr.RequestError: print("网络错误,无法获取语音识别服务") return None # 文本转语音(TTS) def text_to_speech(text): global starttime, sentences, allsentences alreadyplay = 0 # 已经播放了多少段音频 # 激活TTS线程 tts_thread = TTSing(text) tts_thread.start() while alreadyplay < allsentences: if alreadyplay + 1 <= sentences: try: playsound.playsound(f"output{str(alreadyplay+1)}.mp3") alreadyplay += 1 except: pass if __name__ == "__main__": # 获取语音并转换为文本 text = speech_to_text() if text: # 处理文本 processed_text = process_text(text) # 将处理后的文本转换为语音 text_to_speech(processed_text)
这里我们做两次测试。
这里,AI只回答了“早上好,有什么可以帮到你的吗”,测试一下两段代码的耗时:
第一段代码响应时间:5.45秒
第二段代码响应时间:5.25秒
第一段代码完整播放时间:8.23秒
第二段代码完整播放时间:9.32秒
这里,AI回答了“哈哈,这个问题好有趣!泡面拌42号混凝土的话,估计吃下去就不止胃疼了,可能连肠胃也会被砸一下。混凝土不是拿来吃的,毕竟它的主要功能是做建筑材料。泡面的正确打开方式还是按包里的调料包来,简单又美味。不过,如果是玩笑话的话,那就是极限挑战了,敢不敢来试试混凝土泡面?”,测试一下两段代码的耗时:
第一段代码响应时间:19.10秒
第二段代码响应时间:4.63秒
第一段代码完整播放时间:56.60秒
第二段代码完整播放时间:44.73秒
可以看到,在正常情况下,第二段代码的响应时间都是较稳定的,介于4~6秒之间。而由于是先把整句话TTS出来后再播放,第一段代码的响应时间相对会受回复文本长短的影响而变化。而且,第一段代码在生成TTS时会空闲一段时间,造成时间的浪费。
*暂时就这么多 如果你有更多好想法的话欢迎与我联系(howardjones1919810@gmail.com )!*
© Howard Jones 版权所有