首页 > web前端 > js教程 > 如何使用 OpenAI、Vercel AI SDK 和 Ollama 以及 Next.js 构建 AI 助手

如何使用 OpenAI、Vercel AI SDK 和 Ollama 以及 Next.js 构建 AI 助手

DDD
发布: 2024-11-07 03:30:03
原创
690 人浏览过

在今天的博文中,我们将使用三种不同的 AI 模型构建 AI 助手:OpenAI 的 Whisper 和 TTS 以及 Meta 的 Llama 3.1。

在探索人工智能时,我想尝试不同的事情并创建一个通过语音工作的人工智能助手。这种好奇心促使我将 OpenAI 的 Whisper 和 TTS 模型与 Meta 的 Llama 3.1 结合起来,构建了一个语音激活助手。

以下是这些模型如何协同工作:

  • 首先,我们将音频发送到 Whisper 模型,该模型会将其从语音转换为文本。
  • 接下来,我们将该文本传递给 Llama 3.1 模型。 Llama 将理解文本并生成响应。
  • 最后,我们将 Llama 的回复发送到 TTS 模型,将文本重新转换为语音。然后我们会将音频流式传输回客户端。

让我们深入研究并开始构建这个优秀的人工智能助手!

开始使用

我们将使用不同的工具来构建我们的助手。为了构建我们的客户端,我们将使用 Next.js。不过,您可以选择您喜欢的框架。

要使用我们的 OpenAI 模型,我们将使用他们的 TypeScript / JavaScript SDK。要使用此 API,我们需要以下环境变量:OPENAI_API_KEY—

要获取此密钥,我们需要登录 OpenAI 仪表板并找到 API 密钥部分。在这里,我们可以生成一个新的密钥。

Open AI dashboard inside the API keys section

太棒了。现在,要使用我们的 Llama 3.1 模型,我们将使用 Ollama 和 Vercel AI SDK,并利用名为 ollama-ai-provider 的提供程序。

Ollama 将允许我们下载我们喜欢的模型(我们甚至可以使用不同的模型,例如 Phi)并在本地运行它。 Vercel SDK 将促进其在我们的 Next.js 项目中的使用。

要使用Ollama,我们只需下载它并选择我们喜欢的型号即可。对于本博文,我们将选择 Llama 3.1。安装 Ollama 后,我们可以通过打开终端并编写以下命令来验证它是否正常工作:

Terminal, with the command ‘ollama run llama3.1’

请注意,我写了“llama3.1”,因为这是我选择的模型,但您应该使用您下载的模型。

开始事情

是时候开始设置我们的 Next.js 应用程序了。让我们从这个命令开始:

npx create-next-app@latest
登录后复制
登录后复制
登录后复制
登录后复制

运行命令后,您将看到一些设置应用程序详细信息的提示。让我们一步一步来:

  • 为您的应用命名
  • 启用应用路由器

其他步骤是可选的,完全取决于您。就我而言,我还选择使用 TypeScript 和 Tailwind CSS。

现在已经完成了,让我们进入我们的项目并安装运行模型所需的依赖项:

npx create-next-app@latest
登录后复制
登录后复制
登录后复制
登录后复制

构建我们的客户端逻辑

现在,我们的目标是录制我们的声音,将其发送到后端,然后接收来自后端的语音响应。

为了录制音频,我们需要使用客户端功能,这意味着我们需要使用客户端组件。在我们的例子中,我们不想将整个页面转换为使用客户端功能并将整个树放在客户端包中;相反,我们更愿意使用服务器组件并导入客户端组件来逐步增强我们的应用程序。

所以,让我们创建一个单独的组件来处理客户端逻辑。

在我们的应用程序文件夹中,让我们创建一个组件文件夹,在这里,我们将创建我们的组件:

npm i ai ollama-ai-provider openai
登录后复制
登录后复制
登录后复制

让我们继续初始化我们的组件。我继续添加了一个带有一些样式的按钮:

app
 ↳components
  ↳audio-recorder.tsx
登录后复制
登录后复制
登录后复制

然后将其导入到我们的Page Server组件中:

// app/components/audio-recorder.tsx
'use client'
export default function AudioRecorder() {
    function handleClick(){
      console.log('click')
    }

    return (
        <section>
        <button onClick={handleClick}
                    className={`bg-blue-500 text-white px-4 py-2 rounded shadow-md hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-white transition duration-300 ease-in-out absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`}>
                Record voice
            </button>
        </section>
    )
}
登录后复制
登录后复制
登录后复制

现在,如果我们运行我们的应用程序,我们应该看到以下内容:

First look of the app, showing a centered blue button

太棒了!现在,我们的按钮不执行任何操作,但我们的目标是录制音频并将其发送到某个地方;为此,让我们创建一个包含我们逻辑的钩子:

// app/page.tsx
import AudioRecorder from '@/app/components/audio-recorder';

export default function Home() {
  return (
      <AudioRecorder />
  );
}
登录后复制
登录后复制
登录后复制

我们将使用两个 API 来录制我们的声音:navigator 和 MediaRecorder。导航器 API 将为我们提供有关用户媒体设备的信息,例如用户媒体音频,而 MediaRecorder 将帮助我们录制其中的音频。这就是他们一起玩的方式:

app
 ↳hooks
  ↳useRecordVoice.ts

import { useEffect, useRef, useState } from 'react';

export function useRecordVoice() {
  return {}
}
登录后复制
登录后复制
登录后复制

让我们逐步解释一下这段代码。首先,我们创建两个新状态。第一个用于跟踪我们何时录制,第二个用于存储 MediaRecorder 的实例。

// apps/hooks/useRecordVoice.ts
import { useEffect, useRef, useState } from 'react';

export function useRecordVoice() {
    const [isRecording, setIsRecording] = useState(false);
    const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);

     const startRecording = async () => {
        if(!navigator?.mediaDevices){
            console.error('Media devices not supported');
            return;
        }

        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        const mediaRecorder = new MediaRecorder(stream);
        setIsRecording(true)
        setMediaRecorder(mediaRecorder);
        mediaRecorder.start(0)
    }

    const stopRecording = () =>{
        if(mediaRecorder) {
            setIsRecording(false)
            mediaRecorder.stop();
        }
    }

  return {
    isRecording,
    startRecording,
    stopRecording,
  }
}
登录后复制
登录后复制
登录后复制

然后,我们将创建第一个方法,startRecording。在这里,我们将拥有开始录制音频的逻辑。
我们首先检查用户是否有可用的媒体设备,这要归功于导航器 API,它为我们提供了有关用户浏览器环境的信息:

如果我们没有媒体设备来录制音频,我们就返回。如果他们这样做,那么让我们使用他们的音频媒体设备创建一个流。

 const [isRecording, setIsRecording] = useState(false);
    const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);
登录后复制
登录后复制

最后,我们继续创建 MediaRecorder 的实例来录制此音频:

npx create-next-app@latest
登录后复制
登录后复制
登录后复制
登录后复制

然后我们需要一个方法来停止我们的录音,这就是我们的 stopRecording。在这里,如果存在媒体录制器,我们将停止录制。

npm i ai ollama-ai-provider openai
登录后复制
登录后复制
登录后复制

我们正在录制音频,但我们不会将其存储在任何地方。让我们添加一个新的 useEffect 和 ref 来完成此任务。
我们需要一个新的引用,这就是我们的音频数据块的存储位置。

app
 ↳components
  ↳audio-recorder.tsx
登录后复制
登录后复制
登录后复制

在 useEffect 中,我们将做两件主要的事情:将这些块存储在我们的 ref 中,当它停止时,我们将创建一个音频/mp3 类型的新 Blob:

// app/components/audio-recorder.tsx
'use client'
export default function AudioRecorder() {
    function handleClick(){
      console.log('click')
    }

    return (
        <section>
        <button onClick={handleClick}
                    className={`bg-blue-500 text-white px-4 py-2 rounded shadow-md hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-white transition duration-300 ease-in-out absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`}>
                Record voice
            </button>
        </section>
    )
}
登录后复制
登录后复制
登录后复制

是时候将此钩子与我们的 AudioRecorder 组件连接起来了:

// app/page.tsx
import AudioRecorder from '@/app/components/audio-recorder';

export default function Home() {
  return (
      <AudioRecorder />
  );
}
登录后复制
登录后复制
登录后复制

让我们看看硬币的另一面,后端!

设置我们的服务器端

我们希望在服务器上使用我们的模型来保证安全并运行得更快。让我们创建一个新路由并使用 Next.js 中的路由处理程序为其添加一个处理程序。在我们的 App 文件夹中,创建一个“Api”文件夹,其中包含以下路由:

我们希望在服务器上使用我们的模型来保证安全并运行得更快。让我们创建一个新路由并使用 Next.js 中的路由处理程序为其添加一个处理程序。在我们的 App 文件夹中,创建一个“Api”文件夹,其中包含以下路由:

app
 ↳hooks
  ↳useRecordVoice.ts

import { useEffect, useRef, useState } from 'react';

export function useRecordVoice() {
  return {}
}
登录后复制
登录后复制
登录后复制

我们的路线称为“聊天”。在route.ts文件中,我们将设置我们的处理程序。让我们从设置 OpenAI SDK 开始。

// apps/hooks/useRecordVoice.ts
import { useEffect, useRef, useState } from 'react';

export function useRecordVoice() {
    const [isRecording, setIsRecording] = useState(false);
    const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);

     const startRecording = async () => {
        if(!navigator?.mediaDevices){
            console.error('Media devices not supported');
            return;
        }

        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        const mediaRecorder = new MediaRecorder(stream);
        setIsRecording(true)
        setMediaRecorder(mediaRecorder);
        mediaRecorder.start(0)
    }

    const stopRecording = () =>{
        if(mediaRecorder) {
            setIsRecording(false)
            mediaRecorder.stop();
        }
    }

  return {
    isRecording,
    startRecording,
    stopRecording,
  }
}
登录后复制
登录后复制
登录后复制

在此路线中,我们将从前端作为 Base64 字符串发送音频。然后,我们将接收它并将其转换为 Buffer 对象。

 const [isRecording, setIsRecording] = useState(false);
    const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);
登录后复制
登录后复制

是时候使用我们的第一个模型了。我们希望将此音频转换为文本,并使用 OpenAI 的 Whisper Speech-To-Text 模型。 Whisper 需要音频文件来创建文本。由于我们有一个 Buffer 而不是文件,因此我们将使用他们的“toFile”方法将音频 Buffer 转换为音频文件,如下所示:

// check if they have media devices
if(!navigator?.mediaDevices){
 console.error('Media devices not supported');
 return;
}
// create stream using the audio media device
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
登录后复制

请注意,我们指定了“mp3”。这是 Whisper 模型可以使用的众多扩展之一。您可以在此处查看支持的扩展的完整列表:https://platform.openai.com/docs/api-reference/audio/createTranscription#audio-createtranscription-file

现在我们的文件已准备就绪,让我们将其传递给 Whisper!使用我们的 OpenAI 实例,我们将这样调用我们的模型:

// create an instance passing in the stream as parameter
const mediaRecorder = new MediaRecorder(stream);
// Set this state to true to 
setIsRecording(true)
// Store the instance in the state
setMediaRecorder(mediaRecorder);
// Start recording inmediately
mediaRecorder.start(0)
登录后复制

就是这样!现在,我们可以继续下一步:使用 Llama 3.1 来解释这段文本并给我们一个答案。我们将使用两种方法。首先,我们将使用“ollama-ai-provider”包中的“ollama”,它允许我们将此模型与本地运行的 Ollama 一起使用。然后,我们将使用 Vercel AI SDK 中的“generateText”来生成文本。
旁注:为了让我们的 Ollama 在本地运行,我们需要在终端中编写以下命令:

npx create-next-app@latest
登录后复制
登录后复制
登录后复制
登录后复制
npm i ai ollama-ai-provider openai
登录后复制
登录后复制
登录后复制

最后,我们有了最后一个模型:来自 OpenAI 的 TTS。我们想用音频回复用户,所以这个模型会非常有帮助。它将把我们的文字变成语音:

app
 ↳components
  ↳audio-recorder.tsx
登录后复制
登录后复制
登录后复制

TTS 模型会将我们的响应转换为音频文件。我们希望将此音频流式传输回用户,如下所示:

// app/components/audio-recorder.tsx
'use client'
export default function AudioRecorder() {
    function handleClick(){
      console.log('click')
    }

    return (
        <section>
        <button onClick={handleClick}
                    className={`bg-blue-500 text-white px-4 py-2 rounded shadow-md hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-white transition duration-300 ease-in-out absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2`}>
                Record voice
            </button>
        </section>
    )
}
登录后复制
登录后复制
登录后复制

这就是整个后端代码!现在,回到前端来完成所有连接。

将它们放在一起

在我们的 useRecordVoice.tsx 挂钩中,我们创建一个新方法来调用我们的 API 端点。此方法还将取回响应并向用户播放我们从后端流式传输的音频。

// app/page.tsx
import AudioRecorder from '@/app/components/audio-recorder';

export default function Home() {
  return (
      <AudioRecorder />
  );
}
登录后复制
登录后复制
登录后复制

太棒了!现在我们已经收到了流式响应,我们需要处理它并向用户播放音频。为此,我们将使用 AudioContext API。该 API 允许我们存储音频、对其进行解码并在准备好后向用户播放:

app
 ↳hooks
  ↳useRecordVoice.ts

import { useEffect, useRef, useState } from 'react';

export function useRecordVoice() {
  return {}
}
登录后复制
登录后复制
登录后复制

就是这样!现在,用户应该在其设备上听到音频响应。最后,让我们通过添加一个小的加载指示器来让我们的应用程序变得更好一点:

// apps/hooks/useRecordVoice.ts
import { useEffect, useRef, useState } from 'react';

export function useRecordVoice() {
    const [isRecording, setIsRecording] = useState(false);
    const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(null);

     const startRecording = async () => {
        if(!navigator?.mediaDevices){
            console.error('Media devices not supported');
            return;
        }

        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        const mediaRecorder = new MediaRecorder(stream);
        setIsRecording(true)
        setMediaRecorder(mediaRecorder);
        mediaRecorder.start(0)
    }

    const stopRecording = () =>{
        if(mediaRecorder) {
            setIsRecording(false)
            mediaRecorder.stop();
        }
    }

  return {
    isRecording,
    startRecording,
    stopRecording,
  }
}
登录后复制
登录后复制
登录后复制

结论

在这篇博文中,我们了解了组合多个人工智能模型如何帮助我们实现目标。我们学会了在本地运行 Llama 3.1 等 AI 模型,并在我们的 Next.js 应用程序中使用它们。我们还发现了如何将音频发送到这些模型并流回响应,将音频播放回用户。

这只是使用人工智能的众多方式之一——可能性是无限的。人工智能模型是令人惊叹的工具,它让我们能够创造出曾经难以以如此高的质量实现的东西。感谢您的阅读;现在,轮到你用 AI 构建一些令人惊叹的东西了!

您可以在 GitHub 上找到完整的演示:AI Assistant with Whisper TTS and Ollama using Next.js

以上是如何使用 OpenAI、Vercel AI SDK 和 Ollama 以及 Next.js 构建 AI 助手的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板