OpenAI заложила фундамент для революции в сфере искусственного интеллекта с появлением ChatGPT, открывая новую эру в области AI, которую активно используют как отдельные люди, так и бизнес-сообщества. OpenAI даже предоставила API для разработки персонализированных AI-решений, включая чат-ботов, виртуальных помощников и многого другого. Доступ к этому API можно получить через SDK, которые OpenAI выпустила для разных языков программирования, а также через оболочки, разработанные для удобства создания интерфейсов.

Компания Vercel внесла свой вклад, разработав AI SDK для создания интерактивных пользовательских интерфейсов с использованием TypeScript и JavaScript. Примечательно, что этот SDK является проектом с открытым исходным кодом и поддерживает Vercel Edge runtime.
В рамках данной статьи мы создадим SQL Expert ChatBot, применив API OpenAI и SDK от Vercel. Мы рассмотрим вопросы стриминговых ответов, настройки запросов и многое другое.
Получение API ключа
Первым шагом будет создание аккаунта на OpenAI и получение ключа для API. Сперва мы проходим регистрацию и после того, как мы выполнили вход в свой аккаунт, нам нужно перейти в раздел управления API-ключами
. Здесь мы можем сгенерировать новый ключ и дать ему имя, в моем случае - vercel-ai-sdk
, но вы можете выбрать другое наименование на свое усмотрение.

Предлагаю не медлить и перейти к обзору Vercel AI SDK и процессу настройки на устройстве.
Конфигурация Vercel AI SDK
Vercel AI SDK предназначен для взаимодействия с API OpenAI и обладает рядом инструментов для эффективного использования функционала OpenAI API.
Для старта мы создаем приложение на базе Next.js и подключаем необходимые зависимости: пакет ai
для Vercel AI SDK и пакет openai
для клиента OpenAI API:
pnpm dlx create-next-app ai-sql-expert
cd ai-sql-expert
pnpm install ai openai
Проверьте, чтобы конфигурация вашего проекта была следующей:

Создаем файл .env
в корневой директории проекта и добавим в него сгенерированный ранее OpenAI API ключ:
OPENAI_API_KEY=xxxxxxxxx
На этом настройка SDK OpenAI и Vercel AI подходит к завершению, а значит мы можем перейти к настройке API-маршрутов.
Настройка API-маршрутов
Мы организуем API-маршрут для обработки сообщений от пользователей таким образом, чтобы после отправки сообщения пользователем OpenAI API обрабатывал запрос и возвращал ответ в Next.js.
В начале создаем файл route.ts
в папке api/chat
из директории src/app
и пропишем в нем следующий код:
# app/api/chat/route.ts
import OpenAI from 'openai';
import { OpenAIStream, StreamingTextResponse } from 'ai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export const runtime = 'edge';
export async function POST(req: Request) {
const { messages } = await req.json();
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages,
});
const stream = OpenAIStream(response);
return new StreamingTextResponse(stream);
}
Немного пояснений:
Сперва мы предоставляем ключ API для настройки экземпляра OpenAI:
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, });
С помощью этой строки мы указываем Next.js использовать среду выполнения на краю (edge runtime) для обработки API-запросов:
export const runtime = 'edge';
Для извлечения данных из поля messages, , которые приходят в теле JSON-запроса мы используем
POST
-функцию.Далее мы применяем метод
openai.chat.completions.create
для отправки этих сообщений в модельgpt-3.5-turbo
для дальнейшей обработки. Этот метод генерирует ответы, основываясь на введенных сообщениях.Параметр
stream
мы устанавливаем в значениеtrue
, поскольку это позволяет передавать ответы в формате реального времени.ППолучив ответ от OpenAI, код трансформирует его в формат удобного текстового потока с использованием функции
OpenAIStream
.И в завершение, функция формирует ответ, включающий в себя текстовые потоки, созданные OpenAI в качестве реакции на запросы пользователя, через класс
StreamingTextResponse
и отправляет его обратно:export async function POST(req: Request) { const { messages } = await req.json(); const response = await openai.chat.completions.create({ model: 'gpt-3.5-turbo', stream: true, messages, }); const stream = OpenAIStream(response); return new StreamingTextResponse(stream); }
Теперь у нас есть API-маршрут для приема и обработки пользовательских запросов с помощью API OpenAI. Перейдем к созданию UI для нашего бота.
Разработка пользовательского интерфейса
Перед тем как приступить к работе с UI, определим константы для начальный сообщений чата, которые мы будем использовать в качестве настраиваемого промта для направления поведения разрабатываемого бота.
Создаем файл chat.constants.ts
в папке constant
, расположенной в директории src/app
и напишем следующий код:
import { Message } from 'ai/react';
export const INITIAL_MESSAGES: Message[] = [
{
id: '',
role: 'system',
content: 'Вы эксперт по SQL и можете создать генерируемые SQL-запросы для любой проблемы.
Убедитесь, что возвращаемый вами запрос с правильным форматированием и отступом.
Убедитесь, что возвращаемый запрос снабжен корректным объяснением и комментариями.
Если вы не в силах решить проблему, вы вправе запросить дополнительные сведения.
Если пользователь не может понять запрос, вы можете объяснить запрос простыми словами.
Если пользователь предоставляет неправильную подсказку, вы можете запросить правильную подсказку.
`,
},
];
Мы задаем системе руководящие принципы поведения в различных ситуациях, формулируем соответствующие модели ответов и определяем механизм их доставки.
Далее вставим следующий код в src/app/pages.tsx
:
'use client';
import { useChat } from 'ai/react';
import Markdown from 'react-markdown';
import { INITIAL_MESSAGES } from './constant/chat.constants';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
initialMessages: INITIAL_MESSAGES,
});
return (
<div>
<div className="text-center py-8">
<h2 className="text-center text-2xl font-bold mb-2">Эксперт SQL</h2>
<p>
Добро пожаловать в Эксперта SQL. Вы можете задать любые вопросы, связанные с SQL, и эксперт вам поможет.
</p>
</div>
<div className="flex flex-col w-full max-w-2xl pb-24 mx-auto stretch gap-4">
{messages
.filter((m) => m.role !== 'system')
.map((m) => (
<div
key={m.id}
className="bg-gray-100 p-4 rounded flex gap-2 flex-col"
>
<span className="font-medium">
{m.role === 'user' ? 'Вы' : 'Эксперт'}
</span>
<Markdown>{m.content}</Markdown>
</div>
))}
<form
className="flex gap-4 fixed bottom-0 w-full mb-8"
onSubmit={handleSubmit}
>
<input
autoFocus
className="p-2 border border-gray-300 rounded shadow-xl outline-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500 flex-grow max-w-xl"
value={input}
placeholder="Задайте свой вопрос по SQL.."
onChange={handleInputChange}
/>
<button
className="border p-2 px-4 rounded shadow-xl border-gray-300 bg-purple-500 text-white"
type="submit"
>
Отправить
</button>
</form>
</div>
</div>
);
}
Добавим немного пояснений:
Мы применяем компонент для визуализации сообщений и форму для отправки сообщений пользователем:
'use client';
Так как API возвращает ответы в формате markdown, мы используем библиотеку
react-markdown
для их интерпретации.import Markdown from 'react-markdown';
Хук
useChat
предназначен для создания интерфейса чата в нашем приложении с интеграцией чат-бота. Он облегчает процесс отображения сообщений от нашего AI-провайдера, управления вводом в чате и обновления UI при поступлении новых сообщений. Хук также поддерживает различные опции, и в нашем случае мы используем параметрinitialMessages
для настройки начального поведения системы.import { useChat } from 'ai/react'; const { messages, input, handleInputChange, handleSubmit } = useChat({ initialMessages: INITIAL_MESSAGES, });
Также мы настраиваем отображение сообщений, пришедших от API. Учитывая, что мы задали системе настраиваемый промт, мы отсееваем его и оставляем только сообщения пользователя и эксперта:
<div className="flex flex-col w-full max-w-2xl pb-24 mx-auto stretch gap-4"> {messages .filter((m) => m.role !== 'system') .map((m) => ( <div key={m.id} className="bg-gray-100 p-4 rounded flex gap-2 flex-col" > <span className="font-medium"> {m.role === 'user' ? 'Вы' : 'Эксперт'} </span> <Markdown>{m.content}</Markdown> </div> ))} ... </div>
Мы реализуем форму для отправки сообщений пользователями. Для обработки события отправки формы применяем функцию
handleSubmit
, которую предоставляет хукuseChat
.В качестве механизма управления вводом данных в текстовое поле используем функцию
handleInputChange
в сочетании с состояниемinput
.Кроме того, мы оснащаем форму кнопкой, предназначенной для ее отправки:
<div className="flex flex-col w-full max-w-2xl pb-24 mx-auto stretch gap-4"> ... <form className="flex gap-4 fixed bottom-0 w-full mb-8" onSubmit={handleSubmit} > <input autoFocus className="p-2 border border-gray-300 rounded shadow-xl outline-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500 flex-grow max-w-xl" value={input} placeholder="Задайте свой вопрос по SQL.." " onChange={handleInputChange} /> <button className="border p-2 px-4 rounded shadow-xl border-gray-300 bg-purple-500 text-white" type="submit" > Отправить </button> </form> </div>
Благодаря Vercel AI SDK у нас есть такие инструменты, как хук useChat, облегчающий работу, начиная от отображения сообщений до управления пользовательским вводом.
Мы успешно разработали UI для нашего SQL Expert bot, а это значит, что пора запустить сервер разработки и провести тестирование.
Тест бота
Запустим сервер следующей командой в терминале:
pnpm run dev
ИЛИ
yarn run dev
ИЛИ
npm run dev
Перейдем по адресу http://localhost:3000 в нашем браузере.

Вы можете опробовать бота в работе со следующими примерами промтов:
«Сформулируй SQL‑запрос для выявления суммарного числа заказов, сделанных каждым клиентом за прошедший год, с сортировкой по убыванию количества заказов.»
«Напиши SQL‑запрос для идентификации клиентов, чьи расходы превысили $1000 за последние три месяца.»
«Разработай SQL‑запрос для определения среднего времени выполнения заказа, исходя из временных отметок его размещения и завершения.»
«Спроектируй SQL‑запрос для выявления продуктов с самой высокой прибыльностью, учитывая их себестоимость и цену продажи.»
«Сконструируй SQL‑запрос для анализа тренда ежемесячного дохода за последний год, с разбивкой по месяцам.»
«Определи через SQL‑запрос клиентов, которые не совершали покупок за последние 6 месяцев.»
«Составь SQL‑запрос для выявления пяти категорий товаров с наибольшим объемом продаж за последний квартал.»
«Разработай SQL‑запрос для определения заказов, включающих товары, отсутствующие на складе в момент покупки.»
«Вычисли через SQL‑запрос общий доход от постоянных клиентов по сравнению с новыми за последний месяц.»
«Сформулируй SQL‑запрос для выявления аномалий или отклонений в количестве заказов по сравнению со средними показателями каждого товара за определенные исторические периоды.»
Для наглядности, взгляните на демонстрацию работы следующего промта:
"Представим, твоей задачей стоит оптимизация производительности базы данных крупной компании в сфере электронной коммерции. Составь SQL-запрос для выявления десяти наиболее продаваемых товаров по доходу за последний месяц, учитывая количество продаж и цену за единицу."
Заключение
В этом гайде мы с вами написали функционирующий SQL Expert Bot. Попробуйте настроить его, согласно своим предпочтениям, экспериментируйте и делитесь своими результатами!
Полезные ссылки:
Благодарю за внимание!