1. 流式处理
  2. 自动语音检测

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

介绍

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

致谢:VAD和Gradio代码灵感来源于WillHeld的Diva-audio-chat

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

背景

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

创建与语音和文本的自然交互需要动态且低延迟的响应。因此,我们需要自动语音检测和快速推理。通过@ricky0123/vad-web提供语音检测和Groq提供LLM,这两个需求都得到了满足。Groq提供了闪电般的快速响应,而Gradio则允许轻松创建功能强大的应用程序。

本教程将向您展示如何构建一个卡路里追踪应用程序,您可以通过与AI对话来自动检测您何时开始和停止响应,并提供其自己的文本响应,以通过问题引导您,使其能够估算您最后一餐的卡路里。

关键组件

  • Gradio: 提供网络界面和音频处理功能
  • @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构建一个简洁的用户界面,并处理音频数据。我们通过存储在环境变量中的密钥安全地访问Groq API密钥,这是避免泄露API密钥的安全最佳实践。


无缝对话的状态管理

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

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

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


使用Whisper在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/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/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 Basics