
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 语音助手互动。
后端服务器设置
- 初始化 Create-Llama:在您的终端中运行
npx create-llama@latest
。 - 按照提示设置一个 Python FastAPI 后端,该后端可以与我们的前端集成。
- 使用
poetry install
和poetry shell
准备环境。 - 创建一个
.env
文件,内容为OPENAI_API_KEY=<openai_api_key>
。 - 生成嵌入(可选):如果存在
./data
目录,则运行python app/engine/generate.py
。 - 执行
python main.py
启动服务器。 - 测试 API:使用
curl --location 'localhost:8000/api/chat' --header 'Content-Type: application/json' --data '{ "messages": [{ "role": "user", "content": "Hello" }] }'
进行测试。 - 修改
app/api/routers/chat.py
中的 API 行为。服务器支持所有来源的 CORS,可通过ENVIRONMENT=prod
设置更改。
集成
后端服务器设置完成后,将其与前端集成非常简单。只需更新前端 App.js
中的 fetchResponseFromLLM
函数以调用后端服务器 URL。此更改确保前端发出请求时与您新配置的后端通信,从而有效地集成了这两个组件。
总结思考
总而言之,C3 语音助手不仅仅是一个技术展示;它是迈向 AI 民主化的一步。它旨在让 LLM 和 RAG 等强大的 AI 工具更易于访问和用户友好。这个项目不仅仅是代码行——它是打破技术壁垒、赋能每个人的推动力。
您的想法和反馈非常宝贵——让我们一起让 AI 更易于访问!