用python实现聊天机器人

最近,要做个迎宾机器人,其中得实现语音交互功能,就记录一下创作过程。

首先,我是调用阿里云的大模型接口的,所以得在阿里云百炼上注册账号。

阿里云百炼官网:阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

如果是学生的话,可以在阿里云官网进行学生认证,有300元的代金券,用来做研究绰绰有余。

 阿里云官网:阿里云-计算,为了无法计算的价值

登录进去后,创建一个API-KEY,为了后续能够调用各种AI大模型

接着,回到创作部分,语音交互,可以简单分解为三个模块:语音识别模块、文字对话模块、语音合成模块。先来看语音识别模块。

在阿里云百炼的模型广场上找到语音识别的大模型 

里面有很多模型,我做的语音交互模块要求实时听到语音并输出,所以选的是Paraformer实时语音识别-v2。 点击API调用示例,可以查看如何编写调用代码。

 同样的道理,在文本生成、语音合成那里分别选择一个模型,就可完成大模型的配置。

文本生成我选的是通义千问-Plus,语音合成我选的是语音合成CosyVoice大模型。

在理解了各个模型的调用方法后,便可根据自己的项目要求编写代码了。

这里给上我写的代码

为了使用户能够看到聊天界面和控制聊天的开关,我用pygame显示了语音识别和文字生成的的文字,增加效果显示。将dashcope.api_key替换成自己在阿里云百炼上创建的API-KEY即可运行代码。

from openai import OpenAI
import dashscope
from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse
from dashscope.audio.tts_v2 import *
import pygame
import sys
import time
from dashscope.audio.asr import (Recognition, RecognitionCallback,RecognitionResult)
import pyaudio
import math

#语音识别的回调函数
class Rn_Callback(RecognitionCallback):
    def __init__(self):
        self.sentence_end=False
        self.mic = None
        self.stream = None
        self.content=''
    def on_open(self) -> None:
        print('RecognitionCallback open.')
        self.mic = pyaudio.PyAudio()
        self.stream = self.mic.open(format=pyaudio.paInt16,
                          channels=1,
                          rate=16000,
                          input=True)

    def on_close(self) -> None:
        print('RecognitionCallback close.')
        self.stream.stop_stream()
        self.stream.close()
        self.mic.terminate()
        self.stream = None
        self.mic = None

    def on_event(self, result: RecognitionResult) -> None:
        if result.get_sentence().get('sentence_end')==True:
            print('This is the end',result.get_sentence().get('text'))
            self.sentence_end=True
            self.content+=result.get_sentence().get('text')

#语音合成的回调函数
class Rs_Callback(ResultCallback):
    _player = None
    _stream = None
    def __init__(self):
        self.boastcast=1
        self.websocket=1
    def on_open(self):
        print("websocket is open.")
        self._player = pyaudio.PyAudio()
        self._stream = self._player.open(
            format=pyaudio.paInt16, channels=1, rate=22050, output=True
        )

    def on_complete(self):
        print("speech synthesis task complete successfully.")

    def on_error(self, message: str):
        print(f"speech synthesis task failed, {message}")

    def on_close(self):
        print("websocket is closed.")
        # 停止播放器
        self.websocket=0
        self._stream.stop_stream()
        self._stream.close()
        self._player.terminate()

    def on_event(self, message):

        #print(f"recv speech synthsis message {message}")
        return 1
    def on_data(self, data: bytes) -> None:
        #print("audio result length:", len(data))
        self._stream.write(data)

class voice_interaction:
    def __init__(self):
        pygame.init()
        dashscope.api_key = 'your_key'#这里使用自己在阿里云创建的API-KEY
        self.model = "cosyvoice-v1"  # 语音播放模型
        self.voice = "longwan"  # 语音声音人物
        self.screen = pygame.display.set_mode((1000, 800))
        pygame.display.set_caption('语音聊天界面')
        self.interval = 20  # 字与字之间的间隔
        # 设置楷体字体
        self.font = pygame.font.SysFont('KaiTi', self.interval, bold=False, italic=False)
        #如果是在树莓派系统上部署项目,使用pygame中自带的中文字体会出现乱码的问题,可在网上下载一个中文字体,放在项目文件夹中,使用下面这行代码显示字体
        #self.font = pygame.font.Font('china.ttf', self.interval, bold=False, italic=False)
        self.messages=[]
        self.Rs_callback = Rs_Callback()
        self.input_word=14#用户框的一行字数
        self.output_word=25#助手框的一行字数
        self.background=(232,238,242)#聊天界面背景颜色
    def voice_recognition(self):
        Rn_callback = Rn_Callback()
        recognition = Recognition(model='paraformer-realtime-v2',
                                  format='pcm',
                                  sample_rate=16000,
                                  callback=Rn_callback)
        self.screen.fill(self.background)
        self.y = 2
        print('按下enter键开始对话')
        begin_words=self.font.render('按下任意键开始对话',True,(128,128,128))
        self.screen.blit(begin_words,(410,350))
        pygame.display.update()

        audio = 0
        while audio == 0:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:  # 如果收到退出事件,则退出循环
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    self.screen.fill(self.background)
                    recog_words=self.font.render('正在识别语音......',True,(128,128,128))
                    self.screen.blit(recog_words,(410,350))
                    pygame.display.update()
                    recognition.start()
                    start_time=time.time()
                    while Rn_callback.sentence_end == False:
                        data = Rn_callback.stream.read(3200, exception_on_overflow=False)
                        recognition.send_audio_frame(data)
                        run_time=time.time()-start_time
                        if run_time>=10:
                            break
                    recognition.stop()
                    audio=1
        self.screen.fill(self.background)
        speak_words=self.font.render('对话生成中......',True,(128,128,128))
        self.screen.blit(speak_words,(410,350))
        pygame.display.update()
        self.text = Rn_callback.content#识别语音输入的结果
        if self.text=='':
            self.text='。'

        self.input=[]
        self.row_number=math.ceil(len(self.text)/self.input_word)
        green = pygame.Color('#7fffd4')
        for i in range(self.row_number):
            self.input.append(self.text[self.input_word*i:self.input_word*(i+1)])
            if len(self.text)>=self.input_word:
                first_position=890-self.input_word*self.interval#用户输入框文字的起始位置
                pos = first_position - 5, self.interval * self.y - 5,self.input_word*self.interval+10,self.interval*self.row_number+10#矩形框的位置
            else:
                first_position=890-len(self.input[0])*20
                pos=first_position-5,self.interval*self.y-5,self.interval*len(self.text)+5,self.interval+10

        pygame.draw.rect(self.screen, green, pos, 0)
        #如果想要显示用户头像,解除下面两行注释,并准备一个80*80像素的图片,放在项目文件夹中,更名为user.xxx,xxx为图片格式
        #user_image = pygame.image.load('user.jpg')  # 用户头像
        #self.screen.blit(user_image, [900, 20])  # 显示用户头像
        for word in self.input:
            text_surface = self.font.render(word, True, (0, 0, 0))
            self.screen.blit(text_surface, (first_position, self.interval*self.y))

            self.y+=1
        pygame.display.update()
    def chat(self):
        client = OpenAI(
            # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
            api_key=dashscope.api_key,
            base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
        )
        self.messages.append ({"role": "user", "content": self.text})
        self.response = client.chat.completions.create(
            model="qwen-plus",
            messages=self.messages,
            stream=True,
            extra_body={
                "enable_search": True
            }
        )

    def voice_broadcast(self):
        # 流式输出

        null = 0
        self.title_answers = []
        self.synthesizer = SpeechSynthesizer(model=self.model, voice=self.voice,
                                             format=AudioFormat.PCM_22050HZ_MONO_16BIT,
                                             callback=self.Rs_callback, )
        for chunk in self.response:
            temp = eval(chunk.model_dump_json())
            self.answer = temp['choices'][0]['delta']['content']
            self.title_answers.append(self.answer)
            print(self.answer)
            result = self.synthesizer.streaming_call(self.answer)

        self.title_answers=''.join(self.title_answers)
        self.title_answers=self.title_answers.replace('*','')
        self.title_answers=self.title_answers.replace('\n','')
        self.title_answers=self.title_answers.replace('😊','')
        self.title_answers=self.title_answers.replace('#','')
        print(self.title_answers)
        self.output=[]
        yellow = pygame.Color('#FFF8DC')
        output_row_number=math.ceil(len(self.title_answers)/self.output_word)
        for i in range(output_row_number):
            self.output.append(self.title_answers[self.output_word*i:self.output_word*(i+1)])
            print(self.output[i])
        if len(self.output[0])>=self.output_word:
            width=self.interval*self.output_word+10
            height=self.interval*output_row_number+10
        else:
            width=self.interval*len(self.output[0])+10
            height=self.interval+10
        pos_output=90-5,self.interval*self.y-5,width,height#输出端矩形框的位置

        self.screen.fill(self.background,rect=(410,350,200,200))
        skip_words = self.font.render('按下任意键进行下一轮对话', True, (128, 128, 128))
        self.screen.blit(skip_words, (710, 350))
        pygame.display.update()

        pygame.draw.rect(self.screen, yellow, pos_output, 0)
        #如果想显示助手头像,解除下面两行注释,并准备一张80*80像素的图片,放在项目文件夹中,更名为assistant.xxx,xxx为图片格式。
        #img = pygame.image.load('assistant.png') #显示助手头像
        #self.screen.blit(img, [0, 20*(self.y-1)])
        for output_word in self.output:
            text_surface = self.font.render(output_word, True, (0, 0, 0),yellow)
            self.screen.blit(text_surface, (90, 20*self.y))
            self.y+=1

        pygame.display.update()
        self.messages.append({"role": "assistant", "content": self.title_answers})

        boastcast=1
        while boastcast==1:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:  # 如果收到退出事件,则退出循环
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if self.Rs_callback.websocket==1:
                        self.synthesizer.streaming_cancel()
                    boastcast=0

        print('下一轮对话')

if __name__ == '__main__':
    bot=voice_interaction()
    while 1:
        bot.voice_recognition()
        bot.chat()
        bot.voice_broadcast()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值