基于ChatTTS与zhipuai虚拟聊天助手(demo)

     填了上篇文章文章埋下的坑基于ChatTTS与zhipuai虚拟聊天助手(baseline)_chattts api key-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/2301_81587902/article/details/141643805?spm=1001.2014.3001.5502

        应用背景:很多人都是没有一个能够陪伴聊天的人,而且目前大环境下压力很大,很多人都是一个人在大城市打拼,下班回家都没有一个可以陪伴说话的人。当下也存在着大量的空巢老人,为此制作了虚拟聊天助手。解决了年轻人的内心的空虚,也可以陪伴独居的老人,可以进行情感的交流,释放内心的压力。减少心理问题。

        有了希望解决的问题,那么我们就需要思考大框架,首先必须要能够普通定制,每个人都能拥有自己想要的角色,所以收集需要扮演的角色,例如:男朋友,女朋友等不同的角色。另外也要给予一个角色的姓名和特点,这样才能使得ai有比较好的表现,同时也增加了带入感。

        所以就需要有能够收集信息的ai助手。收集到信息之后就要构建prompt,构建了prompt就确定了system。之后就可以利用ChatTTS将输出的文字用语音输出。

        (刚刚的是普通定制就只是利用ai原本的能力,那么高级定制就可以利用微调技术,生成具有更符合用户特点的输出)以下都是普通定制内容,这里只是提供微调的想法,但是未实现。

        ChatTTS是输入文字,输出语音,AI能生成逼真的中英文语音和语气。作为数字人、大模型、人机对话、具身智能的语音交互基座。

        首先安装ChatTTS,由于自己的电脑跑不动,所有用了魔塔社区(也可以用其他的云平台)。具体的安装流程可以参考

ChatTTS_Tutorials/zihao_chattts_20240613_4/【B2】安装配置ChatTTS环境-备选.ipynb at main · TommyZihao/ChatTTS_Tutorials · GitHubStep-by-step Jupyter notebook tutorials for ChatTTS - ChatTTS_Tutorials/zihao_chattts_20240613_4/【B2】安装配置ChatTTS环境-备选.ipynb at main · TommyZihao/ChatTTS_Tutorialsicon-default.png?t=N7T8https://github.com/TommyZihao/ChatTTS_Tutorials/blob/main/zihao_chattts_20240613_4/%E3%80%90B2%E3%80%91%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AEChatTTS%E7%8E%AF%E5%A2%83-%E5%A4%87%E9%80%89.ipynb        首先建立TTs.py调用ChatTTS 

import torchaudio
import torch
from ChatTTS import ChatTTS
import soundfile
from IPython.display import Audio
chat = ChatTTS.Chat()
chat.load_models(compile=False)


class Tts():
    def __init__(self):
        pass

    # 语音模型
    def chat_sound(self, texts, infer_code):
        # refine_text = chat.infer(texts, refine_text_only=True)
        wavs = chat.infer(texts, params_infer_code=infer_code)
        return wavs

    # 输出与下载
    def tts_response(self, answer, infer_code):
        wavs = self.chat_sound(answer, infer_code)
        print("___"*10)
        torchaudio.save("output/output_d1.wav", torch.from_numpy(wavs[0]), 24000)
        return wavs

         然后建立获取collect的函数命名为collect_role.py因为采用streamlit的text_input作为输入所以可以直接输入目标角色和角色关系以及特点性格,所以这里不采用ai进行收集。

        也可以采用ai收集,上篇文章就是用任务完成型ai进行的收集,但是个人认为繁琐了点。

import streamlit as st
import os
from dataclasses import dataclass, asdict
from sqlalchemy import insert
from sqlalchemy import Table, Column, Integer, String, DateTime, Text, MetaData, SmallInteger
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import sqlite3

# 收集角色信息
@dataclass
class ChatSession:
    role: str
    role_name: str
    role_personality: str

class role_collect():
    def __init__(self):
        pass

    def role_prompt(self, role, role_name, role_personality):
        all_role = ChatSession(role, role_name, role_personality)
        print("角色信息:", role_name)
        self.store(all_role)


    def store(self,all_role: ChatSession):

        with SessionLocal.begin() as sess:
            q = insert(
                chat_session_table
            ).values(
                [asdict(all_role)]
            )
            sess.execute(q)


db_file = "chatbot.db"

if os.path.exists(db_file):
    os.remove(db_file)
engine = create_engine(f"sqlite:///{db_file}")
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
metadata_obj = MetaData()
chat_session_table = Table(
    "chat_session_table",
    metadata_obj,
    Column("role", String(16)),
    Column("role_name", String(32)),
    Column("role_personality", String(32)),

)

metadata_obj.create_all(engine, checkfirst=True)
print("数据库创建成功!")

        之后就是获取保存好sql数据并且命名为chatbot.db,之后就是打开保存好的数据,并且将整个文件命名为use_sql.py。

# 将collect收集的信息导出
import sqlite3


def query_table(table: str):
    con = sqlite3.connect("chatbot.db")
    cur = con.cursor()
    q = cur.execute(f"SELECT * FROM {table}")
    return q.fetchall()


def query_chat_session():
    sql = query_table("chat_session_table")
    print("finish query chat session")
    return sql


if __name__ == ' __main__':
    
    sql = query_chat_session()

        接下来就是将收集的内容构建成prompt,并且根据角色的关系放回需要下载的语音包(目前只有设计男女两种不同的语音),所以将包命名成get_prompt.py

import streamlit as st
from TTs_down import download_speaker
from decide import ManWoman

manwoman = ManWoman()
# 调用模型


def get_sql(inputs_text=""):
    # 默认为空
    if inputs_text == "":
        inputs = "no"
    else:
        inputs = inputs_text
    # 输入no时直接对信息
    if inputs == "no":
        from use_sql import query_chat_session
        query_chat = query_chat_session()
        print("no")
        return query_chat
    # 输入为yes时重新读信息
    elif inputs == "yes":
        from use_sql import query_chat_session
        query_chat = query_chat_session()
        print("yes")
        return query_chat
    else:
        st.error("输入错误,请重新输入")


def get_prompt():
    query_chat = get_sql()
    req = []
    for i in range(len(query_chat[0])):
        if i <= 2:
            inx = -1 * (i + 1)
            # print(query_chat[0][inx])
            req.append(query_chat[0][inx])
        else:
            break
    role = req[2]  # 男/女角色
    role_name = req[1]
    role_personality = req[0]
    prompt = f'''我与你的【任何对话】都必须严格执行以下角色要求。你叫{role_name},
    我与你的对话必须严格遵守以下角色要求,你将扮演我的【{role}】,我的【{role}】必须严格遵守以下角色要求:
    姓名:{role_name}。性格:{role_personality}。
    记住你不是AI,请你完全沉浸在{role}的角色里,我们的任何对话都要用{role_name}的口吻回答我,
            回答不能超过50字,根据聊天话题延展你自己的想法。
            不能有解释类型的逻辑,表达要带有角色的性格特点。
    '''
    print("finish prompt")
    w_m = manwoman.similarity(role)
    print(w_m)
    info = download_speaker(w_m)
    return prompt, info

        因此需要有判断是男性或者是女性的包,这里采用的是embedding之后进行相似度对比,相似度高的就是所需要的音色,命名为decide.py

from zhipuai import ZhipuAI
import os
from dotenv import load_dotenv, find_dotenv
from sklearn.metrics.pairwise import cosine_similarity
_ = load_dotenv(find_dotenv())
client = ZhipuAI(api_key=os.getenv("ZhipuAI_API_KEY"))


class ManWoman:
    def __init__(self):
        pass

    def embedding_man(self, text):
        emb = client.embeddings.create(
            model="embedding-2",
            input=text,
        )
        return emb.data[0].embedding

    # 通过相似度来对比角色是男性还是女性
    def similarity(self, role):
        text = self.embedding_man(role)
        man = self.embedding_man("男性")
        woman = self.embedding_man("女性")
        if cosine_similarity([text], [man])[0][0] > cosine_similarity([text], [woman])[0][0]:
            return "男性"
        else:
            return "女性"


if __name__ == '__main__':
    WM = ManWoman()
    print(WM.similarity("汪星人"))

        既然判断完男女就可以下载对应的语音信息,并且将整个封装好命名为TTs_down.py

import torch


def download_speaker(text):
    if text == "男性":
        speaker = torch.load('speakers/b1hou.pth')
    elif text == "女性":
        speaker = torch.load('speakers/g1.pth')
    else:
        print("err")
    infer_code = {
        "spk_emb": speaker,
        # 'prompt': '[speed_10]',
        'temperature': 0.1,
        'top_P': 0.7,
        'top_K': 20,
        # "custom_voice": 3000,
        }
    print("finish download")
    return infer_code

        所有的内容都构建完了就到main.py,就是采用zhipuai的glm-4进行回复,将大部分的内容串起来,有一部分需要用demo.py才能连接

import os
import streamlit as st
from dotenv import load_dotenv, find_dotenv
from zhipuai import ZhipuAI
from get_prompt import get_prompt
from TTs import Tts
from decide import ManWoman
# 调用模型
tts = Tts()
manwoman = ManWoman()

_ = load_dotenv(find_dotenv())
client = ZhipuAI(api_key=os.getenv("ZhipuAI_API_KEY"))


class ChatGlm():
    def __init__(self):

        self.prompt, self.info = get_prompt()
        self.msg = [{"role": "user", "content": self.prompt}]

    def reponse(self, msg):
        response = client.chat.completions.create(
            model="glm-4",
            messages=msg,
            temperature=0.7,
        )
        return response.choices[0].message.content

    def check_over(self, inpt):
        if "再见" in inpt or "拜拜" in inpt or "结束" in inpt:
            return True

    def chat(self, ):
        while True:
            outp = self.reponse(self.msg)
            inpt = input()
            self.msg += [
                {"role": "assistant", "content": outp},
                {"role": "user", "content": inpt},
            ]
            answer = self.reponse(self.msg)
            if self.check_over(inpt):
                break
            print(answer)

        最后demo.py,包括了st.text_input输入人物信息,并且采用按钮的方式确认人物形象的输入,若人物信息未输入完成既按下按钮也不会有任何的影响,也防止别人误输入后直接就运行了。也有chat_input输入对话信息,以及对话信息的记录。

from main import ChatGlm
from TTs import Tts
import streamlit as st
from get_prompt import get_sql

tts = Tts()
# # 创建一个标题和一个副标题
st.title("💬 Zhipu AI聊天(支持语音输出)")
st.text("是否需要重新生成prompt?yes/no\n")
if 'text' not in st.session_state:
    st.session_state['text'] = ''
inputs_text = st.text_input("请输入yes/no",key='text')
st.write("当前内容:", st.session_state['text'])

# 当输入yes时重新创建一次角色信息(每次启动只能重新创建一次目前没有解决这个问题)
if inputs_text == "yes":
    st.text("角色\n")
    role = st.text_input('请输入角色与你的关系')
    st.write('角色与你的关系', role)

    st.text("角色名称\n")
    role_name = st.text_input('请输入角色的姓名(例如:李华)')
    st.write('角色的姓名', role_name)

    st.text("角色性格\n")
    role_personality = st.text_input('角色的特色(例如:幽默,乐观)')
    st.write('角色性格', role_personality)

    # 创建一个按钮,当点击按钮时,将点击事件标记为已点击,利用按钮传输送角色信息
    if 'clicked' not in st.session_state:
        st.session_state.clicked = False

    def click():
        st.session_state.clicked = True
        # 将点击事件标记为已点击
    st.button('完成输入', on_click=click)

    if st.session_state.clicked:
        if role and role_name and role_personality != "":
            from collect_role import role_collect
            role_collect().role_prompt(role, role_name, role_personality)
            get_sql(inputs_text)
            if "messages" not in st.session_state:
                st.session_state["messages"] = []
            del st.session_state["messages"]
            print("完成输入")
            st.session_state.clicked = False


chat_glm = ChatGlm()


if "messages" not in st.session_state:
    st.session_state["messages"] = []
    st.session_state.messages.append({"role": "system", "content": chat_glm.prompt})

for i in st.session_state.messages[1:]:
    st.chat_message(i["role"]).write(i["content"])

if inpt := st.chat_input():
    st.session_state.messages.append({"role": "user", "content": inpt})
    # 将用户的输入添加到session_state中的messages列表中
    st.chat_message("user").write(inpt)
    # 在聊天界面上显示用户的输入
    ans = chat_glm.reponse(st.session_state.messages)
    print(ans)
    tts.tts_response(ans, chat_glm.info)
    # 将模型的输出添加到session_state中的messages列表中
    st.session_state.messages.append({"role": "assistant", "content": ans})
    # 在聊天界面上显示模型的输出
    st.chat_message("assistant").write(ans)
    # 保存语音
    audio_file = open('output/output_d1.wav', 'rb')
    audio_bytes = audio_file.read()
    st.audio(audio_bytes, format='audio/wav')

        streamlit run demo.py运行的效果,chattts的语音生成过程有点慢可能需要5-10s生成语音,更具电脑性能决定。

        如果你想改人物只需要在修改demo输入yes就可以更改了 (目前每次打开只能重新生成一次,不能重复改),想要再次修改只能重启demo

 

        修改过程如下 输入完成点击完成输入即可

        大概流程就是这样,目前任在优化中,待续...

        已经上传GitHub:deng-mumu/-ai (github.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值