为了账号安全,请及时绑定邮箱和手机立即绑定

从零到全掌控:我如何用LangGraph和Whisper重造ElevenLabs的AI语音助手

探索了ElevenLabs的AI代理功能后,我禁不住想,如果从零开始构建会怎样?在我的一篇之前的文章中,《如何使用ElevenLabs、Make.com和Twilio快速构建客户支持语音机器人》,我分享了如何利用ElevenLabs和Make.com创建一个能够轻松安排会议和处理客户互动的语音机器人。这种无代码方法取得了令人印象深刻的结果,但作为一名热爱代码的开发者,我想要更多的控制和自定义选项。

这一次,我选择了一条不同的路线——从零开始使用LangGraph、ElevenLabs的API和Whisper来重建AI语音机器人。我的目标是更好地理解其内部运作机制,优化其性能,并探索现成的解决方案所无法提供的新可能性。在这篇文章中,不仅会带你了解我如何构建自己的ElevenLabs AI代理,还会提供一个清晰的框架来帮助你重建自己的语音机器人。在这个过程中,我会分享我遇到的挑战和做出的决定,以及自定义编码方案的灵活性如何为我解锁了无限的可能性。

AI语音机器人的构建要素

为了使这个项目实现,我依赖了三个关键元素,它们构成了AI语音助手的基础。

积木

语音转文字 小声说话

Whisper,这是由OpenAI开发的,是一种先进的语音转文本模型,以其准确性和多语言处理能力著称。它可以高效地将口语转换为文本,是处理高质量的语音输入的理想解决方案。其深度学习能力使其能够理解各种口音和方言,确保了不同人群的用户的顺畅用户体验。在这个项目中,我使用了OpenAI的Python SDK来处理音频输入:

下面是一个使用OpenAI客户端创建音频转录的示例代码:

    openai_client.audio.transcriptions.create(  
       model="whisper-1",   
       file=audio_bytes,  
    )
聊天AI助手:LangGraph

LangGraph,由LangChain开发,作为AI语音助手的大脑,能够创建具备工具调用和记忆功能的结构化互动对话。它能够处理复杂的对话流程和决策过程,确保助手能够智能地应对各种用户输入。借助LangGraph,我能够设计出高度动态且适应性强的对话体验,能够记住之前的互动并高效利用各种工具。

你知道ElevenLabs的语音合成API吗?

ElevenLabs API 功力于文本转语音组件,提供自然且逼真的语音响应。这一组件通过提供类似人类的语音输出来增强用户体验,使交互更加生动直观。在这个项目中,我利用了 ElevenLabs 的 Python SDK,通过 eleven_turbo_v2_5 模型生成语音。

elevenlabs_client.text_to_speech.convert(  
    voice_id="YUdpWWny7k5yb4QCeweX",  
    output_format="mp3_22050_32",  
    text=cleaned_text,  
    model_id="eleven_turbo_v2_5",   
    voice_settings=VoiceSettings(  
        stability=0.0,  
        similarity_boost=1.0,  
        style=0.0,  
        use_speaker_boost=True,  
    ),  
)
# 将文本转换为语音

通过这些组件,让我从头开始创建了一个高度响应且智能的语音助手。

押着步骤构建AI语音助手

现在我们已经介绍了基本构建模块,让我们开始动手实现这个项目。我们将从使用LangGraph创建一个基本的对话聊天机器人,并且集成记忆功能,以及获取当前日期和时间的工具开始。一旦聊天机器人开始运行正常,我们将通过添加两个额外节点来增强其功能——一个用于使用Whisper将传入的音频转换成文本,另一个用于使用ElevenLabs API将聊天机器人的回答转换成音频。这种循序渐进的方法将为语音机器人的能力构建提供坚实的基础。

第一步:搭建一个简单的聊天机器人

首先,我们将创建一个简单的聊天机器人,它可以记住之前的聊天记录并获取当前的日期和时间信息。这将涉及定义一个状态管理系统,集成了一个从API获取日期和时间的工具,以及使用LangGraph来构建对话流程。

一个使用LangGraph的基本聊天机器人

谈谈状态管理系统的定义

我们首先定义聊天机器人的状态,用来跟踪对话。

    from typing import Annotated  
    from typing_extensions import TypedDict  
    from langgraph.graph.message import add_messages  

    class State(TypedDict):  
        messages: Annotated[list, add_messages]

这段代码定义了一个名为 State 的类型字典,其中包含一个名为 messages 的字段,该字段是一个带有 add_messages 函数注解的列表。

State 类使用一个带注释的列表来存储消息,并在有新消息到达时进行添加。

实现内存方面的功能

接下来,我们来引入记忆功能,存储和检索之前的对话内容,确保对话的连续性。

从langgraph.checkpoint.memory模块导入MemorySaver

# 初始化MemorySaver实例
memory = MemorySaver()

MemorySaver 组件帮助在不同交互间保存对话机器人的状态。

定义这个工具

接下来,我们定义一个工具来从API获取当前的日期和时间。

    from langchain_core.tools import tool  
    import requests  

    @tool  
    def 获取当前日期和时间() -> dict:  
        """  
        从API获取当前的日期和时间。  
        """
        # 此函数从API获取当前的日期和时间。
        try:  
            response = requests.get("https://timeapi.io/api/Time/current/zone?timeZone=Europe/Brussels")  
            response.raise_for_status()  
            data = response.json()  
            return {"date_time": data["dateTime"], "timezone": data["timeZone"]}  
        except requests.RequestException as e:  
            return {"error": str(e)}

这个功能获取当前日期和时间,并给出JSON格式的结果。

搭建语言模型环境

现在开始设置语言模型,并将其与指定的工具集成,这样聊天机器人就可以在回复中调用外部工具了。

    从langchain_openai导入ChatOpenAI模块   

    llm = ChatOpenAI(model='gpt-4o-mini'):
    llm_with_tools = llm.bind_tools([获取日期和时间函数])  # 绑定获取日期和时间的工具

搭建对话流程

现在我们使用LangGraph来构建一个简单的对话流程,仅依赖一个能够访问get_date_and_time工具的“聊天机器人”节点,这个节点。

    from langgraph.graph import StateGraph  
    from langchain_core.runnables import RunnableConfig  

    def chatbot(state: State, config: RunnableConfig):  
        return {"messages": [llm_with_tools.invoke(state["messages"])]}  # 调用llm_with_tools处理消息

    # 初始化图,创建图结构  
    graph_builder = StateGraph(State)  
    graph_builder.add_node("chatbot", chatbot)  # 添加聊天机器人节点  
    graph_builder.add_edge("chatbot", "chatbot")  # 将chatbot节点连接到自身  
    graph = graph_builder.compile(checkpointer=memory)  # 设置检查点为内存

这种配置允许聊天机器人处理消息,将其保存在缓存中,并作出相应的回应。

第二步:给聊天机器人增加语音功能

接下来,我们将为聊天机器人的工作流程添加两个额外的节点——一个用于使用Whisper将传入的音频转录为文本,另一个用于使用ElevenLabs API将文本响应转换成语音。这一改进将使聊天机器人成为一款功能齐全的语音助手。

额外的节点用来进行音频输入和输出的处理。

输入: 录制音频

为了提供无缝且互动的体验,聊天机器人必须能够准确地听取并理解用户的需求。这时,就是 OpenAI的Whisper 模型大显身手的时候了,使聊天机器人能够有效地捕捉和转录口语内容。

将语音转化为文字的过程包括几个关键步骤等。

语音捕获: 使用 sounddevice 库,聊天机器人会开始监听用户的麦克风。一旦开始录音,录音会持续,直到几秒的静默后自动停止。

处理音频输入:设置了一些参数来优化语音识别。

  • SAMPLE_RATE:定义采样率,确保充分捕捉语音。
  • THRESHOLD:设置最低音频水平以检测语音活动。
  • SILENCE_DURATION:确定保持静音的时间长度,以在录音停止前。
  • CHUNK_SIZE:指定音频处理块的大小。

语音识别: 当音频录制完成后,会使用 OpenAI 的 Whisper API 进行处理并转换为文本,该 API 支持高精度的多种语言语音转写。

    import io  
    import threading  
    import numpy as np  
    import sounddevice as sd  
    from scipy.io.wavfile import write  

    from openai import OpenAI  

    from langgraph.graph import  MessagesState, HumanMessage  

    # 初始化OpenAI客户端  
    openai_client = OpenAI()  

    # 音频设置  
    SAMPLE_RATE = 16000  # 适合人类语音频率的采样率  
    THRESHOLD = 500  # 沉默检测阈值(如有需要可以调整)  
    SILENCE_DURATION = 1.5  # 检测到沉默后的持续时间(秒)  
    CHUNK_SIZE = 1024  # 每个音频块的帧数  

    def record_audio_until_silence(state: MessagesState):  
        """等待用户开始说话,录音并在检测到沉默后停止录音。"""  

        audio_data = []  # 用于存储音频块的列表  
        silent_chunks = 0  # 沉默块计数器  
        started_recording = False  # 标记是否已经开始录音  

        def record_audio():  
            """持续录音,等待用户开始说话。"""  
            nonlocal silent_chunks, audio_data, started_recording  

            with sd.InputStream(samplerate=SAMPLE_RATE, channels=1, dtype='int16') as stream:  
                print("等待您开始说话")  

                # 一直等待用户开始说话  
                while not started_recording:  
                    audio_chunk, _ = stream.read(CHUNK_SIZE)  
                    audio_array = np.frombuffer(audio_chunk, dtype=np.int16)  

                    # 检查是否有语音输入  
                    if np.abs(audio_array).max() > THRESHOLD:  
                        started_recording = True  
                        print("语音检测到。开始录音")  
                        audio_data.append(audio_chunk)  
                        break  

                # 一旦检测到语音输入,开始录音  
                while True:  
                    audio_chunk, _ = stream.read(CHUNK_SIZE)  
                    audio_data.append(audio_chunk)  
                    audio_array = np.frombuffer(audio_chunk, dtype=np.int16)  

                    # 检测用户说完话后的沉默  
                    if np.abs(audio_array).max() < THRESHOLD:  
                        silent_chunks += 1  
                    else:  
                        silent_chunks = 0  # 一旦检测到声音,重置沉默计数器为0  

                    # 检测到指定时间内的沉默后停止录音  
                    if silent_chunks > (SILENCE_DURATION * SAMPLE_RATE / CHUNK_SIZE):  
                        print("检测到沉默,停止录音")  
                        break  

        # 在单独的线程中开始录音  
        recording_thread = threading.Thread(target=record_audio)  
        recording_thread.start()  
        recording_thread.join()  

        # 将所有音频块堆叠成一个NumPy数组并写入文件  
        audio_data = np.concatenate(audio_data, axis=0)  

        # 将音频数据转换为WAV格式并保存在内存  
        audio_bytes = io.BytesIO()  
        write(audio_bytes, SAMPLE_RATE, audio_data)  # 使用scipy的write函数将数据保存到BytesIO  
        audio_bytes.seek(0)  # 移动到BytesIO缓冲区的起始位置  
        audio_bytes.name = "audio.wav"  # 设置文件名为内存中的文件  

        # 通过Whisper进行转录  
        transcription = openai_client.audio.transcriptions.create(  
           model="whisper-1",   
           file=audio_bytes,  
           language='nl'  
        )  

        # 打印转录结果  
        print("这里是转录结果:", transcription.text)  

        # 写入消息到消息列表  
        return {"messages": [HumanMessage(content=transcription.text)]}

用语音让聊天机器人变得更有活力

为了让用户体验更加吸引人,聊天机器人需要用更自然、更人性化的交流方式。这时,ElevenLabs的强大的文字转语音(TTS)功能就派上用场了,让我们能够把聊天机器人回复无缝转换成听起来很真实的语音。

这个流程包括几个关键步骤。

  • 初始化ElevenLabs客户端: 使用API密钥设置ElevenLabs API客户端,以实现与文本转语音服务的通信。
  • 处理聊天机器人的响应: 清理聊天机器人的响应,去除可能导致音频干扰的格式化痕迹。
  • 将文本转换为语音: 将清理后的文本发送到ElevenLabs API,利用高级语音设置来控制稳定性、相似度增强和说话人风格等。
  • 播放生成的音频: 在完成文本转语音转换后,播放音频回应,确保对话流畅自然。

下面是如何实现文本转语音的

    import os  
    from elevenlabs import play, VoiceSettings  
    from elevenlabs.client import ElevenLabs  
    from langgraph.graph import  MessagesState  

    # 初始化ElevenLabs客户端  
    elevenlabs_client = ElevenLabs(api_key=os.getenv("ELEVEN_API_KEY"))  

    def play_audio(state: MessagesState):  
        """播放来自远程图的代理的音频回复。"""  

        # 代理的响应  
        response = state['messages'][-1]  

        # 通过将文本中的 ** 替换为空字符串来进行清理  
        cleaned_text = response.content.replace("**", "")  

        # 使用turbo模型调用text_to_speech API来减少延迟  
        response = elevenlabs_client.text_to_speech.convert(  
            voice_id="YUdpWWny7k5yb4QCeweX",  # 预设的Adam音色  
            output_format="mp3_22050_32",  
            text=cleaned_text,  
            model_id="eleven_turbo_v2_5",   
            language_code="nl",  
            voice_settings=VoiceSettings(  
                stability=0.0,  
                similarity_boost=1.0,  
                style=0.0,  
                use_speaker_boost=True,  
            ),  
        )  

        # 播放这段音频  
        play(response)
图形构建:音频处理集成

具备处理文本和语音交互的能力,下一步就是将这些能力无缝地整合到一个统一的工作流程里。通过构建智能对话图谱,我们可以确保语音识别、文本对话和语音回复之间的流畅衔接。

在这个阶段,我们将定义一个新的基于LangGraph的流程,将音频输入和输出功能与我们的聊天机器人连接,从而实现以下操作顺序:

  1. 获取音频输入:
  • 该聊天机器人接收用户输入,并将语音转换成文本。
  • 语音检测完成后,然后将生成的文本交给聊天机器人分析。

2. 处理文本对话:

  • 聊天机器人根据转录的输入和自身的逻辑来生成回复。
  • 它可以利用比如日期和时间查询功能,使交互更加丰富。

3. 生成并播放音频,

  • 聊天机器人的文本回复通过ElevenLabs API转换为语音。
  • 回复播放给用户,完成交互闭环,让用户可以继续回复,从而无缝地继续聊天。

通过这样构建对话流程,我们创建了一个能够高效处理语音和文本互动的动态语音助手机器人,使整体表达更加流畅。

下面是我們如何構建一個完整的圖來集成所有组件。

from langgraph.graph import StateGraph, MessagesState, END, START

# 定义图构建器
builder = StateGraph(MessagesState)

# 将音频输入作为节点直接添加
builder.add_node("audio_input", record_audio_until_silence)
# 将代理作为节点直接添加
builder.add_node("agent", graph)
# 将音频输出作为节点直接添加
builder.add_node("audio_output", play_audio)
builder.add_edge(START, "audio_input")
builder.add_edge("audio_input", "agent")
builder.add_edge("agent", "audio_output")
builder.add_edge("audio_output", "audio_input")
audio_graph = builder.compile(checkpointer=memory)
步骤三:测试语音助手功能

一旦聊天机器人和音频功能完全集成好,就需要测试系统以确保系统运行流畅。我们将通过发送输入命令来启动测试对话,并观察聊天机器人的反应。

    从langchain_core.messages导入convert_to_messages, HumanMessage  

    config = {"configurable": {"thread_id": "1"}}  

    for chunk in audio_graph.stream({"messages": HumanMessage(content="按照用户的指示:")}, stream_mode="values", config=config):  
        chunk["messages"][-1].pretty_print()
结论部分

从零开始构建这个AI语音机器人是一个非常令人满足的经历。使用现成的工具是一回事,但从头开始开发解决方案则能更深入地理解各个部分是如何相互配合的。将语音转换为文本与 Whisper 的集成,到使用 ElevenLabs 生成逼真的回复,每一步都带来了宝贵的洞察力和进一步定制的可能。

如果你是一个喜欢捣鼓人工智能的人,或者想要让你的项目突破无代码平台的局限,深入代码可以为你开拓新的天地。当然,这需要更多的努力,但能够根据你的需求定制解决方案,这一切都是值得的。无论是为了娱乐还是解决实际问题,打造人工智能语音助手的旅程才刚开始。

想看我的完整笔记吗?点击这里订阅,进入我的资源中心 👉 https://www.pairrot.eu/subscribe

跟着我深入了解人工智能吧!

Medium Instagram YouTube Pairrot

感谢你加入我们社区。

在你走之前:

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消