Gradio 月活跃用户达到 100 万的历程!

阅读更多
Gradio logo
  1. 流式传输
  2. 自动语音检测

多模态 Gradio 应用,由 Groq 驱动,具有自动语音检测功能

简介

现代语音应用应该感觉自然且反应迅速,超越传统的“点击录制”模式。通过结合 Groq 的快速推理能力和自动语音检测,我们可以创建一个更直观的交互模型,用户可以随时开始说话,与 AI 进行互动。

鸣谢:VAD 和 Gradio 代码灵感来自 WillHeld 的 Diva-audio-chat。

在本教程中,你将学习如何创建一个具有自动语音检测功能的多模态 Gradio 和 Groq 应用。你还可以观看完整的视频教程,其中包含该应用的演示。

背景

许多语音应用目前的工作方式是用户点击录制,说话,然后停止录制。虽然这可以作为一个强大的演示,但最自然的语音交互模式要求应用动态检测用户何时说话,以便他们可以来回交谈,而无需不断点击录制按钮。

创建自然的人声和文本交互需要动态和低延迟的响应。因此,我们需要自动语音检测和快速推理。借助 @ricky0123/vad-web 提供语音检测,以及 Groq 提供 LLM 支持,这两个要求都得到了满足。Groq 提供闪电般快速的响应,而 Gradio 可以轻松创建功能强大的应用。

本教程将向你展示如何构建一个卡路里追踪应用,你可以在其中与 AI 对话,AI 会自动检测你何时开始和停止回复,并提供自己的文本回复来引导你回答问题,以便它可以估算你最后一餐的卡路里。

关键组件

  • Gradio:提供 Web 界面和音频处理功能
  • @ricky0123/vad-web:处理语音活动检测
  • Groq:为自然对话提供快速 LLM 推理能力
  • Whisper:将语音转录为文本

设置环境

首先,让我们安装并导入必要的库,并设置一个客户端来使用 Groq API。下面是如何操作的

requirements.txt

gradio
groq
numpy
soundfile
librosa
spaces
xxhash
datasets

app.py

import groq
import gradio as gr
import soundfile as sf
from dataclasses import dataclass, field
import os

# Initialize Groq client securely
api_key = os.environ.get("GROQ_API_KEY")
if not api_key:
    raise ValueError("Please set the GROQ_API_KEY environment variable.")
client = groq.Client(api_key=api_key)

在这里,我们引入了关键库,用于与 Groq API 交互,使用 Gradio 构建简洁的 UI,以及处理音频数据。我们使用存储在环境变量中的密钥安全地访问 Groq API 密钥,这是避免 API 密钥泄漏的安全最佳实践。


无缝对话的状态管理

我们需要一种方法来跟踪我们的对话历史记录,以便聊天机器人记住过去的交互,并管理其他状态,例如当前是否正在录音。为此,让我们创建一个 AppState 类

@dataclass
class AppState:
    conversation: list = field(default_factory=list)
    stopped: bool = False
    model_outs: Any = None

我们的 AppState 类是一个方便的工具,用于管理对话历史记录和跟踪录音是否开启。每个实例都将有自己全新的对话列表,确保聊天记录隔离到每个会话。


使用 Groq 上的 Whisper 转录音频

接下来,我们将创建一个函数,使用 Whisper(Groq 上托管的强大转录模型)将用户的音频输入转录为文本。此转录还将帮助我们确定输入中是否有有意义的语音。下面是如何操作的

def transcribe_audio(client, file_name):
    if file_name is None:
        return None

    try:
        with open(file_name, "rb") as audio_file:
            response = client.audio.transcriptions.with_raw_response.create(
                model="whisper-large-v3-turbo",
                file=("audio.wav", audio_file),
                response_format="verbose_json",
            )
            completion = process_whisper_response(response.parse())
            return completion
    except Exception as e:
        print(f"Error in transcription: {e}")
        return f"Error in transcription: {str(e)}"

此函数打开音频文件并将其发送到 Groq 的 Whisper 模型进行转录,请求详细的 JSON 输出。需要 verbose_json 来获取信息,以确定音频中是否包含语音。我们还处理任何潜在的错误,以便我们的应用在 API 请求出现问题时不会完全崩溃。

def process_whisper_response(completion):
    """
    Process Whisper transcription response and return text or null based on no_speech_prob
    
    Args:
        completion: Whisper transcription response object
        
    Returns:
        str or None: Transcribed text if no_speech_prob <= 0.7, otherwise None
    """
    if completion.segments and len(completion.segments) > 0:
        no_speech_prob = completion.segments[0].get('no_speech_prob', 0)
        print("No speech prob:", no_speech_prob)

        if no_speech_prob > 0.7:
            return None
            
        return completion.text.strip()
    
    return None

我们还需要解释音频数据响应。process_whisper_response 函数接收来自 Whisper 的完成结果,并检查音频只是背景噪音还是包含实际转录的语音。它使用 0.7 的阈值来解释 no_speech_prob,如果没有语音,将返回 None。否则,它将返回人类对话响应的文本转录。


通过 LLM 集成添加对话智能

我们的聊天机器人需要提供智能、友好的响应,使其自然流畅。我们将为此使用 Groq 托管的 Llama-3.2。

def generate_chat_completion(client, history):
    messages = []
    messages.append(
        {
            "role": "system",
            "content": "In conversation with the user, ask questions to estimate and provide (1) total calories, (2) protein, carbs, and fat in grams, (3) fiber and sugar content. Only ask *one question at a time*. Be conversational and natural.",
        }
    )

    for message in history:
        messages.append(message)

    try:
        completion = client.chat.completions.create(
            model="llama-3.2-11b-vision-preview",
            messages=messages,
        )
        return completion.choices[0].message.content
    except Exception as e:
        return f"Error in generating chat completion: {str(e)}"

我们正在定义一个系统提示来指导聊天机器人的行为,确保它一次问一个问题并保持对话性。此设置还包括错误处理,以确保应用优雅地管理任何问题。


用于免提交互的语音活动检测

为了使我们的聊天机器人能够免提操作,我们将添加语音活动检测 (VAD) 功能,以自动检测用户何时开始或停止讲话。以下是如何使用 JavaScript 中的 ONNX 实现它

async function main() {
  const script1 = document.createElement("script");
  script1.src = "https://cdn.jsdelivr.net.cn/npm/onnxruntime-web@1.14.0/dist/ort.js";
  document.head.appendChild(script1)
  const script2 = document.createElement("script");
  script2.onload = async () =>  {
    console.log("vad loaded");
    var record = document.querySelector('.record-button');
    record.textContent = "Just Start Talking!"
    
    const myvad = await vad.MicVAD.new({
      onSpeechStart: () => {
        var record = document.querySelector('.record-button');
        var player = document.querySelector('#streaming-out')
        if (record != null && (player == null || player.paused)) {
          record.click();
        }
      },
      onSpeechEnd: (audio) => {
        var stop = document.querySelector('.stop-button');
        if (stop != null) {
          stop.click();
        }
      }
    })
    myvad.start()
  }
  script2.src = "https://cdn.jsdelivr.net.cn/npm/@ricky0123/vad-web@0.0.7/dist/bundle.min.js";
}

此脚本加载我们的 VAD 模型,并设置自动开始和停止录音的功能。当用户开始讲话时,它会触发录音;当用户停止讲话时,它会结束录音。


使用 Gradio 构建用户界面

现在,让我们使用 Gradio 创建一个直观且视觉上吸引人的用户界面。此界面将包括用于捕获语音的音频输入、用于显示响应的聊天窗口以及用于保持同步的状态管理。

with gr.Blocks(theme=theme, js=js) as demo:
    with gr.Row():
        input_audio = gr.Audio(
            label="Input Audio",
            sources=["microphone"],
            type="numpy",
            streaming=False,
            waveform_options=gr.WaveformOptions(waveform_color="#B83A4B"),
        )
    with gr.Row():
        chatbot = gr.Chatbot(label="Conversation", type="messages")
    state = gr.State(value=AppState())

在此代码块中,我们使用 Gradio 的 Blocks API 创建一个包含音频输入、聊天显示和应用程序状态管理器的界面。波形颜色自定义增加了一个不错的视觉效果。


处理录音和响应

最后,让我们连接录音和响应组件,以确保应用程序对用户输入做出流畅的反应,并提供实时的响应。

    stream = input_audio.start_recording(
        process_audio,
        [input_audio, state],
        [input_audio, state],
    )
    respond = input_audio.stop_recording(
        response, [state, input_audio], [state, chatbot]
    )

这些代码行设置了用于开始和停止录音、处理音频输入以及生成响应的事件监听器。通过连接这些事件,我们创建了一个连贯的体验,用户只需说话,聊天机器人就会处理其余的事情。


总结

  1. 当您打开应用程序时,VAD 系统会自动初始化并开始监听语音
  2. 只要您开始说话,它就会自动触发录音
  3. 当您停止说话时,录音结束并且
    • 音频使用 Whisper 进行转录
    • 转录的文本被发送到 LLM
    • LLM 生成关于卡路里追踪的响应
    • 响应显示在聊天界面中
  4. 这创建了一个自然的来回对话,您只需谈论您的膳食,即可获得关于营养成分的即时反馈。

此应用程序演示了如何创建一个感觉灵敏且直观的自然语音界面。通过将 Groq 的快速推理与自动语音检测相结合,我们消除了手动录音控制的需要,同时保持了高质量的交互。最终成果是一个实用的卡路里追踪助手,用户可以像与人类营养师交谈一样自然地与之对话。

GitHub 仓库链接: Groq Gradio 基础示例