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);
}

Немного пояснений:

  1. Сперва мы предоставляем ключ API для настройки экземпляра OpenAI:

    const openai = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });
  2. С помощью этой строки мы указываем Next.js использовать среду выполнения на краю (edge runtime) для обработки API-запросов:

    export const runtime = 'edge';
  3. Для извлечения данных из поля messages, , которые приходят в теле JSON-запроса мы используем POST -функцию.

  4. Далее мы применяем метод openai.chat.completions.create для отправки этих сообщений в модель gpt-3.5-turbo для дальнейшей обработки. Этот метод генерирует ответы, основываясь на введенных сообщениях.

  5. Параметр stream мы устанавливаем в значение true, поскольку это позволяет передавать ответы в формате реального времени.

  6. ППолучив ответ от OpenAI, код трансформирует его в формат удобного текстового потока с использованием функции OpenAIStream.

  7. И в завершение, функция формирует ответ, включающий в себя текстовые потоки, созданные 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>
  );
}

Добавим немного пояснений:

  1. Мы применяем компонент для визуализации сообщений и форму для отправки сообщений пользователем:

    'use client';
  2. Так как API возвращает ответы в формате markdown, мы используем библиотеку react-markdown для их интерпретации.

    import Markdown from 'react-markdown';
  3. Хук useChat предназначен для создания интерфейса чата в нашем приложении с интеграцией чат-бота. Он облегчает процесс отображения сообщений от нашего AI-провайдера, управления вводом в чате и обновления UI при поступлении новых сообщений. Хук также поддерживает различные опции, и в нашем случае мы используем параметр initialMessages для настройки начального поведения системы.

    import { useChat } from 'ai/react';
    
    const { messages, input, handleInputChange, handleSubmit } = useChat({
        initialMessages: INITIAL_MESSAGES,
      });
  4. Также мы настраиваем отображение сообщений, пришедших от 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>
  5. Мы реализуем форму для отправки сообщений пользователями. Для обработки события отправки формы применяем функцию handleSubmit, которую предоставляет хук useChat

  6. В качестве механизма управления вводом данных в текстовое поле используем функцию handleInputChange в сочетании с состоянием input

  7. Кроме того, мы оснащаем форму кнопкой, предназначенной для ее отправки:

    <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 в нашем браузере.

Вы можете опробовать бота в работе со следующими примерами промтов:

  1. «Сформулируй SQL‑запрос для выявления суммарного числа заказов, сделанных каждым клиентом за прошедший год, с сортировкой по убыванию количества заказов.»

  2. «Напиши SQL‑запрос для идентификации клиентов, чьи расходы превысили $1000 за последние три месяца.»

  3. «Разработай SQL‑запрос для определения среднего времени выполнения заказа, исходя из временных отметок его размещения и завершения.»

  4. «Спроектируй SQL‑запрос для выявления продуктов с самой высокой прибыльностью, учитывая их себестоимость и цену продажи.»

  5. «Сконструируй SQL‑запрос для анализа тренда ежемесячного дохода за последний год, с разбивкой по месяцам.»

  6. «Определи через SQL‑запрос клиентов, которые не совершали покупок за последние 6 месяцев.»

  7. «Составь SQL‑запрос для выявления пяти категорий товаров с наибольшим объемом продаж за последний квартал.»

  8. «Разработай SQL‑запрос для определения заказов, включающих товары, отсутствующие на складе в момент покупки.»

  9. «Вычисли через SQL‑запрос общий доход от постоянных клиентов по сравнению с новыми за последний месяц.»

  10. «Сформулируй SQL‑запрос для выявления аномалий или отклонений в количестве заказов по сравнению со средними показателями каждого товара за определенные исторические периоды.»

Для наглядности, взгляните на демонстрацию работы следующего промта:

"Представим, твоей задачей стоит оптимизация производительности базы данных крупной компании в сфере электронной коммерции. Составь SQL-запрос для выявления десяти наиболее продаваемых товаров по доходу за последний месяц, учитывая количество продаж и цену за единицу."

Заключение

В этом гайде мы с вами написали функционирующий SQL Expert Bot. Попробуйте настроить его, согласно своим предпочтениям, экспериментируйте и делитесь своими результатами!

Полезные ссылки:

Благодарю за внимание!

Комментарии (1)


  1. Sid1111
    16.03.2024 09:34
    +1

    Вот все говорят ИИ сейчас сам все кодить будет, только подключи его. Но у каждой компании есть объективные причины не подключать его. Никто не хочет сливать весь свой код неизвестно куда. Мне кажется нужно делать коробочную версию ИИ которая будет работать на уже имеющихся данных. И например говорить есть коробка на 2010год знаний. Потом выходит коробка на 2020год. Компания просто сносит свой старый и устанавливает новый. Вот тогда он действительно уничтожит множество профессий.