宣布 LlamaCloud 正式发布(以及我们的 1900 万美元 A 轮融资)!
LlamaIndex

Harshad Suryawanshi 2024-01-14

AI 语音助手:使用 LlamaIndex 和 GPT3.5 增强 AI 的可访问性(已在 Vercel 和 Render 生产环境部署)

介绍

C3 语音助手是我最新的项目,旨在让大型语言模型 (LLM) 和检索增强生成 (RAG) 应用更易于访问。这款语音激活助手服务于广泛的用户群体,包括面临打字困难或可访问性问题的用户。

功能特性

  • 语音激活:通过说“C3”启动。或者,用户可以点击蓝色圆环来激活应用的聆听模式。唤醒词“C3”是可配置的,您可以选择任何其他词。
  • 通用可访问性:对于偏好语音指令或面临打字困难的用户来说是理想的选择。
  • LLM 集成:能够处理通用查询和特定文档查询(例如,英伟达 2023 财年 10K 报告)。
  • 用户友好的界面:AI 语音助手的界面设计简单易用,专注于语音聊天互动。它采用简约友好的 React.js 布局。此外,还有一个方便的侧边栏,以文本格式显示完整的聊天记录,方便用户回顾和反思他们与 AI 的互动。

技术栈

该应用构建于强大且灵活的技术栈之上,确保了流畅、可靠和高效的用户体验。以下是概述

  • 前端:用户界面是使用 React.js 开发的自定义应用。它设计简约但功能强大,优先考虑易用性和可访问性。
  • 后端:服务器端操作由 Python Flask 提供支持。我利用了 LlamaIndex 创新的“create-llama”功能,这显著简化了开发过程。
  • 托管:为了实现流畅的性能,C3 语音助手的前端托管在 Vercel 上。另一方面,后端部署在 Render 上,确保服务器端任务的高效管理和运行。

构建前端

前端使用 React.js 构建,专注于用户交互和可访问性。App.js 脚本包含唤醒词识别、语音转文本、状态管理以及语音气泡和加载动画等动态 UI 元素等功能。

1. 组件和状态初始化

本节设置 React 组件并初始化各种状态,例如用于跟踪应用当前状态(空闲、聆听、说话)的 appState,以及用于存储用户语音转录文本的 transcript

import React, { useState, useRef, useEffect } from "react";
import "./App.css";

const App = () => {
  const [appState, setAppState] = useState("idle");
  const [transcript, setTranscript] = useState("");
  // Additional state and ref declarations...
};

2. 语音识别设置

在此 useEffect Hook 中,初始化了两个语音识别实例:一个用于检测唤醒词“C3”,另一个用于主语音识别。此设置确保应用在提到“C3”时开始监听指令。

您可以轻松地将“C3”替换为您选择的任何其他唤醒词。

  useEffect(() => {
    // Wake word listener setup
    const WakeWordSpeechRecognition =
      window.SpeechRecognition || window.webkitSpeechRecognition;
    if (WakeWordSpeechRecognition && !wakeWordRecognitionRef.current) {
      wakeWordRecognitionRef.current = new WakeWordSpeechRecognition();
      wakeWordRecognitionRef.current.continuous = true;
      wakeWordRecognitionRef.current.interimResults = false;

      wakeWordRecognitionRef.current.onresult = (event) => {
        const transcript = event.results[event.results.length - 1][0].transcript
          .trim()
          .toLowerCase();
        if (transcript.includes("c3")) {
          toggleRecording(); // Start the main speech recognition process
        }
      };

      wakeWordRecognitionRef.current.start();
    }

    // Main speech recognition setup
    const SpeechRecognition =
      window.SpeechRecognition || window.webkitSpeechRecognition;
    if (SpeechRecognition && !recognitionRef.current) {
      recognitionRef.current = new SpeechRecognition();
      recognitionRef.current.continuous = false;
      recognitionRef.current.interimResults = false;

      recognitionRef.current.onresult = (event) => {
        const lastResultIndex = event.results.length - 1;
        const transcriptResult = event.results[lastResultIndex][0].transcript;
        setTranscript(transcriptResult);
        setAppState("playing");
        setShowSpeechBubble(true);
        setTimeout(() => setShowSpeechBubble(false), speechBubbleTimeout);
        fetchResponseFromLLM(transcriptResult);
      };

      recognitionRef.current.onend = () => {
        setShowSpinner(true);
      };
    }
  }, []);

3. 处理用户语音和响应

toggleRecording 控制语音识别过程,而 fetchResponseFromLLM 将用户的语音发送到 LLM 后端并处理响应。此响应随后通过语音合成说出,并用于更新 UI 上显示的聊天记录。

 const toggleRecording = () => {
    try {
      if (appState === "idle") {
        recognitionRef.current.start();
        setAppState("listening");
      } else if (appState === "listening") {
        recognitionRef.current.stop();
      }
    } catch (error) {
    }
  };
  const fetchResponseFromLLM = async (text) => {
    try {
      const response = await fetch(
        `https://c3-python-nostream.onrender.com/api/chat`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            messages: [
              {
                role: "user",
                content:
                  "You are an AI voice assistant called C3. You can provide any general information as well as answer basic questions about the Nvidia 10k report for year ended Jan 2023" +
                  text,
              },
            ],
          }),
        }
      );
      const data = await response.json();

      setChatHistory((prevHistory) => [
        ...prevHistory,
        { query: text, response: data.result.content },
      ]);
      speak(data.result.content);
    } catch (error) {
      console.error("Error communicating with LLM:", error);
    }
  };

4. 语音合成

speak 函数接收来自 LLM 的文本响应,并使用 SpeechSynthesis API 将其朗读出来,为用户提供互动体验。

  const speak = (text) => {
    if (synthRef.current && text) {
      const utterance = new SpeechSynthesisUtterance(text);

      const voices = window.speechSynthesis.getVoices();
      if (voices.length > 0) {
        utterance.voice = voices[3]; // You can change this to select different voices
      }

      utterance.onstart = () => {
        console.log("TTS starts speaking");
        setShowSpinner(false);
      };

      utterance.onend = () => {
        setAppState("idle");
        if (wakeWordRecognitionRef.current) {
          wakeWordRecognitionRef.current.start(); // Restart wake word listener after speaking
        }
      };
      synthRef.current.speak(utterance);
    }

5. UI 渲染

App 函数的 return 语句包含用于渲染应用 UI 的 JSX 代码。这包括用于开始/停止语音交互的按钮、转录文本显示区域以及显示交互历史记录的聊天侧边栏。

通过结合语音识别、LLM 集成和语音合成,此前端组件提供了一个全面且易于访问的界面,用于与 C3 语音助手互动。

后端服务器设置

  1. 初始化 Create-Llama:在您的终端中运行 npx create-llama@latest
  2. 按照提示设置一个 Python FastAPI 后端,该后端可以与我们的前端集成。
  3. 使用 poetry installpoetry shell 准备环境。
  4. 创建一个 .env 文件,内容为 OPENAI_API_KEY=<openai_api_key>
  5. 生成嵌入(可选):如果存在 ./data 目录,则运行 python app/engine/generate.py
  6. 执行 python main.py 启动服务器。
  7. 测试 API:使用 curl --location 'localhost:8000/api/chat' --header 'Content-Type: application/json' --data '{ "messages": [{ "role": "user", "content": "Hello" }] }' 进行测试。
  8. 修改 app/api/routers/chat.py 中的 API 行为。服务器支持所有来源的 CORS,可通过 ENVIRONMENT=prod 设置更改。

集成

后端服务器设置完成后,将其与前端集成非常简单。只需更新前端 App.js 中的 fetchResponseFromLLM 函数以调用后端服务器 URL。此更改确保前端发出请求时与您新配置的后端通信,从而有效地集成了这两个组件。

总结思考

总而言之,C3 语音助手不仅仅是一个技术展示;它是迈向 AI 民主化的一步。它旨在让 LLM 和 RAG 等强大的 AI 工具更易于访问和用户友好。这个项目不仅仅是代码行——它是打破技术壁垒、赋能每个人的推动力。

您的想法和反馈非常宝贵——让我们一起让 AI 更易于访问!

Github 仓库链接:前端后端

在 LinkedIn 上与我联系

LinkedIn 帖子