Alura > Cursos de Inteligência Artificial > Cursos de IA para Front End > Conteúdos de IA para Front End > Primeiras aulas do curso Vercel IA SDK: construindo um Chatbot com Next.js

Vercel IA SDK: construindo um Chatbot com Next.js

Construindo o Chatbot - Apresentação

Boas-vindas a mais um curso sobre Next.js. Desta vez, vamos construir um chatbot.

Meu nome é Vinicios Neves, instrutor da Alura.

Audiodescrição: Vinicios se identifica como um homem branco. É careca e possui barba e bigode. No rosto, usa óculos de armação quadrada, preta e grossa. No corpo, veste uma camisa azul-escuro. Está sentado em uma cadeira gamer, cinza e preta. Ao fundo, uma parede lisa, iluminada em tons de azul e rosa.

O que aprenderemos?

Neste curso, vamos implementar o Vidy, um assistente virtual que fala sobre filmes. Ao concluir o projeto, poderemos pedir a ele recomendações de filmes com robôs gigantes, por exemplo, e ele responderá com essas indicações.

Vamos desenvolvê-lo do zero, utilizando o SDK da Vercel. Focaremos em um misto entre a interface de pessoa usuária e o lado do servidor.

Pré-requisitos

Prepare-se para começar. Esperamos você na primeira aula.

Construindo o Chatbot - Implementando o hook useChat

Começaremos a construir chatbot para conversar especificamente sobre filmes. O objetivo deste vídeo é utilizar o Vercel e a SDK, sem focar muito no CSS e na componentização dos elementos em React.

Para isso, disponibilizamos um projeto base pronto. O URL estará disponível com mais detalhes na atividade de preparação do ambiente.

Ao carregar o GitHub, podemos clicar no botão verde denominado "Code" para expandi-lo e clicar em "Download ZIP" para baixar a pasta zip e descompactá-la. No caso, faremos o clone, copiando o endereço HTTPS sugerido pelo GitHub acima da opção de download.

Clonando o projeto base

Abrindo o terminal da nossa máquina, vamos configurá-lo na área de trabalho. Ao listar as pastas, verificaremos que não há nada lá dentro.

Nesse local, executaremos o comando abaixo para clonar o projeto base.

git clone

Instalando os pacotes

Após alguns segundos, entraremos na pasta com cd mais o nome da pasta e, em seu interior, executaremos o comando abaixo, que é um atalho para npm install. Isso instalará todos os pacotes necessários para o projeto rodar.

npm i

Após a instalação, limparemos o terminal e abriremos o VSCode na pasta. O VSCode já está configurado para funcionar e interagir com o terminal.

Executaremos code . para abrir o VS Code diretamente na pasta onde executamos o comando npm install.

code .

Acessando a aba do explorador, na lateral esquerda, visualizaremos projeto, um Next.js normal. Acessanod o arquivo package.json dentro da pasta "src", verificaremos as versões dos frameworks:

Quando rodamos npm install, esses pacotes foram instalados no node_modules.

Executando o projeto

Após essas etapas, podemos voltar ao terminal e executar o comando abaixo para levantar o projeto.

npm run dev

Após um momento, o servidor estará disponível na porta 3000. Copiaremos o endereço disponibilizado e colaremos no navegador.

http://localhost:3000

Após carregar essa página no navegador, veremos a estrutura do chatbot.

Interface de um chatbot denominado Vidy com um fundo escuro e texto em azul e branco. A parte superior mostra o logotipo da VidFlow e um campo de pesquisa com um ícone de lupa. À direita superior, há ícones de sino, câmera e um círculo com a foto da pessoa usuária. No centro, um ícone de robô e uma área de diálogo com a mensagem 'Boas-vindas! Eu sou o Vidy, seu assistente virtual!' e caixas de interação com a pessoa usuária na cor branca, alinhadas à esquerda. Há uma resposta exemplificada numa caixa azul, alinhada à direita, com o número 25. Na parte inferior, um campo para digitar mensagens com um botão de envio ao lado direito.

A estrutura base está implementada, mas é apenas visual, sem comportamento. Ao enviar uma mensagem no campo de texto inferior, nada acontece, O Next.js apenas recarrega a página.

Instalando a biblioteca AI

Com o projeto base rodando, começamos a implementar o chatbot. Voltando ao terminal, abriremos uma nova aba para não interromper o npm run dev em execução. Na nova aba, rodaremos o comando abaixo para instalar a biblioteca AI, o Vercel SDK, que configuraremos para controlar o fluxo do front-end ao back-end, processando com a LLM desejada.

npm i ai

Com a biblioteca instalada, o terminal confirmará a adição dos pacotes sem vulnerabilidades.

added 60 packages, and audited 461 packages in 6s

202 packages are looking for funding

run npm fund for details

found 0 vulnerabilities

Conectando o chat ao SDK

Voltando ao explorador do VSCode, acessando o caminho de pastas "src > app", abriremos o arquivo page. Há apenas um componente na página, o ChatContainer.

Clicando no nome dele com "Command + Clique esquerdo" (ou "Ctrl + clique esquerdo" no Windows), abriremos o arquivo index.jsx no caminho de pastas "src > components > ChatContainer", que exibirá o bloco ChatContainer.

Dentro deste bloco, há mensagens hardcoded, ou seja, uma lista fixa de mensagens, além do ChatHeader, das mensagens em si e do formulário.

Clicando no ChatForm com "Command + clique esquerdo", acessaremos o arquivo index.jsx do formulário HTML (form), dentro do caminho de pastas "src > components > ChatForm". Nesse arquivo, notaremos que o formulário contém o input e o botão para enviar a mensagem.

Precisamos conectar essa estrutura ao SDK.

Deixaremos esse arquivo do "ChatForm" aberto e voltaremos ao index.jsx do ChatContainer. Ao instalar a biblioteca AI, recebemos um hook chamado useChat(). Podemos adicioná-lo entre as chaves do ChatContainer, abaixo do bloco messages.

index.jsx da pasta "ChatContainer":

export const ChatContainer = () => {

    const messages = [
    ]
    
    useChat()
    
    // Código omitido
};

Se o VSCode não sugerir opções de autocomplete, basta importar manualmente com import { useChat } from 'ai/react' no final da relação de imports.

import Button from '../Button';
import ChatBubble from '../ChatBubble';
import { ChatForm } from '../ChatForm';
import { ChatHeader } from '../ChatHeader';
import { IconStop } from '../Icons';
import { Loader } from '../Loader';
import { RetryButton } from '../RetryButton';
import styles from './container.module.css';

import { useChat } from 'ai/react'

O hook useChat() retornará um objeto com tudo que precisamos. Podemos fazer destructuring para separar as partes necessárias com const {} = useChat().

O useChat() nos fornecerá entre as chaves uma lista de messagens. As mensagens agora vêm do hook, portanto, podemos remover a lista fixa messages acima dele.

export const ChatContainer = () => {
    
    const { messages } = useChat()
    
    // Código omitido
};

Salvaremos e verificaremos no navegador se há erros. Acessando a guia "Console" na aba de inspeção, não há erros, o que é um bom sinal. No terminal que executa o npm run dev, tudo também funciona corretamente.

Controlando valores de input

Precisamos controlar os valores do input no lado da clientela. Com um componente React controlado, passamos um value e um onChange. O useChat() fornece input e handleInputChange, portanto, os adicionaremos dentro do objeto, após messages.

O input tem um valor e um evento onChange disparado com o HTML quando a pessoa usuária digita.

Além do input e handleInputChange, trataremos a submissão do formulário com handleSubmit do useChat().

export const ChatContainer = () => {
    
    const { messages, input, handleInputChange, handleSubmit } = useChat()
    
    // Código omitido
};

No ChatForm, anotamos use client, pois com onChange no input da linha 11, o Next.js 14+ exige essa anotação para componentes do lado do cliente.

Descendo o código até a tag ChatForm, passaremos antes da barra e do sinal de maior um input={input} para o JSX, informando o mesmo nome para manter a coerência. Faremos o mesmo para handleInputChange e handleSubmit.

export const ChatContainer = () => {
    
    const { messages, input, handleInputChange, handleSubmit } = useChat()
    
    return (
        <section className={styles.container}>
            <ChatHeader />
            <div className={styles.chat}>
            
                {messages.map((msg) => (
                    <ChatBubble
                        key={msg.id}
                        message={msg.message}
                        isUser={msg.isUser} 
                        onRemove={() => console.log('remove message', msg.id)}
                    />
                ))}
                
            </div>
            <ChatForm
                input={input}
                handleInputChange={handleInputChange}
                handleSubmit={handleSubmit}
            />
        </section>
    );
};

Com isso, temos o componente de ordem maior que gerirá o estado e "cascateará" (enviará em fluxo, de cima para baixo) o que for necessário.

Acessando o chat

Após enviar ao ChatForm o que precisamos, vamos acessá-lo. Voltando ao arquivo index.jsx da pasta "ChatForm", anotaremos o 'use client' na linha 1, pois adicionaremos um onChange e um value dentro da tag input, abaixo do required.

index.jsx da pasta "ChatForm":

'use client'

import styles from './chat.module.css'
import { IconSend } from "../Icons"

export const ChatForm = () => {
    return (<form className={styles.form}>
        <input 
            className={styles.input} 
            placeholder="Digite sua mensagem..."
            required
            onChange={}
            value={}
        />
        <button  className={styles.btn}>
            <IconSend />
        </button>
    </form>)
}

Quando temos um onChange, precisamos anotá-lo como use client ao utilizar o ecossistema do Next na versão 14 e posteriores para evitar erros. Caso contrário, o Next informará que não podemos fazer isso no componente do lado do servidor.

Por fim, acessando a linha que retorna o formulário, em adicionaremos após o className um onSubmit. Podemos notar que onChange, value e onSubmit são propriedades nativas do HTML.

'use client'

import styles from './chat.module.css'
import { IconSend } from "../Icons"

export const ChatForm = () => {
    return (<form className={styles.form} onSubmit={}>
        <input 
            className={styles.input} 
            placeholder="Digite sua mensagem..."
            required
            onChange={}
            value={}
        />
        <button  className={styles.btn}>
            <IconSend />
        </button>
    </form>)
}

Em seguida, podemos recolher o que recebemos via props (propriedades). Na linha que inicia a const ChatForm, entre os parênteses iniciais, adicionaremos um par de chaves com Input, handleInputChange e handleSubmit.

Por fim, "cascatearemos" os três itens, adicionando-os entre as chaves dos três elementos HTML correspondentes, dentro do JSX. O submit será chamado quando o formulário for submetido em onSubmit={}, o handleInputChange será no onChange={} do input e o valor é o input em si, no value={}.

'use client'

import styles from './chat.module.css'
import { IconSend } from "../Icons"

export const ChatForm = ({ input, handleInputChange, handleSubmit }) => {
    return (<form className={styles.form} onSubmit={handleSubmit}>
        <input 
            className={styles.input} 
            placeholder="Digite sua mensagem..."
            required
            onChange={handleInputChange}
            value={input}
        />
        <button  className={styles.btn}>
            <IconSend />
        </button>
    </form>)
}

Com isso, conectamos todos os valores entregues pelo React Hook useChat do chatContainer à nossa marcação.

Recarregaremos no navegador a página da aplicação e notaremos que ainda não há erros no console, nem no terminal da nossa máquina.

Conclusão

Com a infraestrutura pronta, a biblioteca AI instalada, implementamos useChat(), obtendo tudo necessário do lado do cliente: input, onChange, onSubmit e as mensagens.

Próximos passos

A seguir, precisamos lidar com o lado do servidor ao submeter o formulário, capturando-o e processando-o de alguma forma.

Construindo o Chatbot - Streaming da resposta do LLM

Agora que já temos o lado do cliente implementado, vamos seguir para o lado do servidor. Quando alguém acessar e carregar nosso formulário e enviar uma mensagem, por exemplo, "Olá", essa mensagem deve chegar a algum lugar. O próprio navegador já indicou que falta implementar algo, que é o POST para nossa rota /api/chat. Precisamos implementar essa lógica.

Vamos usar um recurso do Next.js para API Routes e implementar a rota /api/chat. No VS Code, dentro da pasta "app", que é nosso app router, criaremos um novo arquivo. Dentro da pasta "app", criaremos uma pasta chamada "api", e dentro dela, outra pasta chamada "chat". Esse caminho, "api/chat", indicará ao Next.js qual rota a função que vamos implementar responderá. Dentro dessa pasta, criaremos um arquivo "route.js".

Precisamos implementar para indicar ao Next.js a qual verbo HTTP vamos responder. Faremos um export de uma função assíncrona, async function POST. Quando fazemos um export de uma função assíncrona POST, o Next.js já sabe que, se alguém fizer um post para essa rota, ele responderá com o resultado dessa função.

Também precisaremos do que vier na requisição, pois teremos que pegar o input dado pela pessoa usuária. Chamaremos isso de request, que é a requisição recebida como parâmetro.

export async function POST(request) {

}

Precisamos agora dar dois passos: primeiro, receber o input, ou seja, a mensagem enviada, e depois processá-la com algum modelo e enviar a resposta. Para esse processamento e envio da resposta, utilizaremos a OpenAI.

Incorporando a inteligência artificial ao projeto

Para usar a OpenAI, precisamos instalar uma biblioteca específica. A biblioteca que instalamos no último vídeo não se relaciona de forma específica aos modelos existentes hoje no mercado. Cada modelo tem uma sub-biblioteca específica conforme o modelo.

No terminal, que deixamos aberto no último vídeo, um deles ainda está rodando o npm run dev, e temos outro que não está rodando nada. Vamos pedir para esse segundo terminal instalar @ai-sdk/openai, que é a biblioteca que nos permitirá implementar o modelo do ChatGPT ou alguma variante dele, como o 3.5 ou 4.

Após a instalação, podemos começar a importar elementos. Primeiro, pegaremos o que veio do nosso front-end. Faremos um const preparado, pegando isso do nosso request para obter um JSON da requisição, ou seja, o corpo da mensagem. Faremos um await request.json(), pois é assíncrono. Essa é a requisição que o Next.js entrega, e quando pedimos .json(), ele nos envia tudo que está no corpo da mensagem.

Para utilizar o combo, abriremos o componente "ChatContainer". Dentro dele, temos um index.jsx, onde implementamos o useChat(). Quando submetemos, ou seja, no handleSubmit, ele nos entrega todas as mensagens disponíveis daquele chat. Vamos pegar essas mensagens, messages, do corpo da requisição e agora podemos pedir para nosso modelo interpretá-las.

Vamos importar da biblioteca que acabamos de instalar, @ai-sdk/openai, o openAI. Esse é o objeto de configuração que utilizaremos para definir qual modelo usaremos no processamento.

import { openai } from '@ai-sdk/openai'
export async function POST(request) {
    const { messages } = await request.json()    
}

Sabendo que esse é o modelo, começaremos a combinar a biblioteca AI com essa específica, ou seja, a genérica com o modelo específico. O processo de gerar essa resposta demora um pouco, então queremos fazer o que chamamos de stream, ou seja, enquanto o servidor gera a resposta, ele mantém a conexão aberta entre o cliente e o servidor, enviando e avisando quando termina.

A stream é uma API que qualquer requisição normal pode usar, não apenas as relacionadas à inteligência artificial.

Qualquer requisição que envolva um processamento longo, envio ou download de um arquivo grande, pode usar esse stream. Deixaremos uma atividade para quem quiser entender mais sobre como uma stream funciona nos bastidores.

Queremos implementar um streamText. O VS Code nos ajudará com isso, pois o streamText da biblioteca AI, nosso SDK, cuidará de tudo, permitindo que foquemos na regra de negócio: qual modelo usar, o que responder, etc.

O streamText espera alguns parâmetros de configuração, um deles é o modelo. Passaremos o openai, importado na linha 1, como função, indicando qual modelo disponível na OpenAI queremos usar. O VS Code sugere desde o GPT 3.5 turbo até o 4o. Usaremos o 4o.

Após passar o modelo, precisamos dizer ao streamText quais mensagens serão usadas para gerar a resposta. Ele espera um array de messages. Não podemos passar as messages diretamente como um objeto JSON. A própria biblioteca AI fornece uma função que converte esse objeto JSON para o formato que o streamText precisa. Chamaremos essa função convertToCoreMessages, passando as mensagens que pegamos na linha 5.

O streamText recebe como configuração o modelo, estamos usando o GPT-4o da OpenAI, mas poderíamos usar qualquer outro disponível no mercado, fechado ou aberto. Ele também recebe uma lista de mensagens, que é o que estamos focando em implementar nesse chatbot.

import { openai } from '@ai-sdk/openai'
import { convertToCoreMessages, streamText } from 'ai'

export async function POST(request) {
    const { messages } = await request.json()    

    const result = await streamText({
        model: openai('gpt-4o'),
        messages: convertToCoreMessages(messages)
     })
}

Guardaremos o resultado dessa stream em uma constante result. Como tudo está em inglês, manteremos assim. Sendo assíncrono, faremos um await. Quando começarmos a ter um resultado, podemos retornar para o front-end. Faremos um return do result, deixando que seja streamado, mantendo a conexão aberta enquanto não finaliza.

O método que usaremos para fazer essa ponte do stream é o toDataStreamResponse. Invocando esse método, implementamos nosso endpoint, nossa API, que receberá o resultado de um formulário. Pegaremos as mensagens, usaremos o streamText para passar o modelo, todas as mensagens convertidas, e retornaremos isso para o front-end.

import { openai } from '@ai-sdk/openai'
import { convertToCoreMessages, streamText } from 'ai'

export async function POST(request) {
    const { messages } = await request.json()    

    const result = await streamText({
        model: openai('gpt-4o'),
        messages: convertToCoreMessages(messages)
    })

    return result.toDataStreamResponse()
}

Escrevemos bastante código, vamos testar no Chrome. Voltando ao Chrome, recarregaremos a página, o navegador está aberto na localhost:3000. Pediremos, por exemplo, "Me diz quais os filmes mais premiados do Oscar" e veremos se ele responde.

Na aba "Network" de "Inspecionar", podemos ver o que está acontecendo. O chat retornou o que precisávamos, embora a resposta esteja grande. Algo está funcionando, mas a conexão com a parte visual do nosso código não está encaixando.

Precisamos resolver isso. A resposta parece estar chegando, mas ainda não conseguimos exibi-la no chat. Vamos resolver esse bug no próximo vídeo.

Sobre o curso Vercel IA SDK: construindo um Chatbot com Next.js

O curso Vercel IA SDK: construindo um Chatbot com Next.js possui 101 minutos de vídeos, em um total de 42 atividades. Gostou? Conheça nossos outros cursos de IA para Front End em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda IA para Front End acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas