Подключаем умный поиск (GPT) к своей базе документов

Есть отечественный файрвол (NGFW). У этого файрвола есть документация для пользователей powered by GitBook, на русском языке. В этой документации работает простой поиск — только по словам и словосочетаниям. И это плохо, потому что нет ответов на вопросы:

  • Какие алгоритмы шифрования ipsec поддерживаются у вас?

  • Как заблокировать ютуб?

  • Как настроить DMZ?

Хочется, чтобы поиск был «умным» и чтобы пользователи могли обращаться с подобными вопросами именно к поиску, а не к инженерам тех. поддержки. AI или ML внутри — не важно, как это называть. Но на простые вопросы из списка выше поиск должен отвечать.

Я решил эту задачу (Retrieval Question Answering), используя OpenAI API. Казалось бы, уже опубликованы сотни похожих инструкций, как это сделать. Но под катом будет не инструкция, а рассказ про сложности, которые пришлось решить на пути от идеи до запуска поиска: как прикинуть бюджет, быстро собрать прототип с сохранением всех ответов и оценок обратной связи, организовать альфа- и бета-тестирование, позвать асессоров и разметить собранный датасет, подвести итоги проекта и наметить следующие шаги.

Постановка задачи

Итак, уточняем задачу.

  1. Есть документация по продукту, внутри — база документов в формате markdown.

  2. Нужно по этой документации сделать «умный» поиск (далее — GPT поиск или бот).

  3. В ответах мы хотим увидеть и ссылки на источники. Как окажется позже, наличие правильно подобранных ссылок на источники даже при ответе плохого качества — нравится пользователям поиска.

Те, кто знакомы с большими языковыми моделями (LLM), знают, что эта задача называется Retrieval QA.

При решении этой задачи главным ограничением выступает максимальная длина запроса к модели: нужно как-то передать в модель всю базу документов, по которой должен работать поиск, а длина запроса ограничена. И самый простой способ обойти это ограничение — сначала во всей базе документов оставить только те, которые могут иметь отношение к ответу на вопрос, а затем передать эти документы (вместе с исходным вопросом) в языковую модель, и перейти к шагу генерации ответа.

Предварительные изыскания

Перед тем, как браться за задачу, проверим, а можно ли её вообще не делать?

Что было сделано на первых шагах (пишу во множественном числе, потому что мне помогали коллеги):

  1. Для начала мы собрали статистику поиска по документации продукта и ещё раз убедились, что поиск нужно делать. Вопросы, которые задавали пользователи продукта, в большинстве случаев оставались без ответов (см. три вопроса во вступительной части).

  2. Разобрались с GitBook, нельзя ли подключить плагин поиска Algolia или подобный? Нет, в июне 2023 года это сделать было нельзя. Были лишь планы разрешить сторонним разработчикам создавать свои собственные плагины, но без указания сроков.

  3. На тот момент у GitBook был доступен «умный» AI поиск — Lens (сегодня он по умолчанию включен). Мы включили его для теста, но оказалось, что с русским языком он не работает. Выглядело это так себе: задаёшь вопрос на русском языке, а получаешь ответ на английском. Да ещё и качество в сравнении с привычными чат-ботами chat.openai.com и perplexity.ai неудовлетворительное.

  4. Сначала попробовали сделать «умный» поиск локальным — скачать к себе языковую модель, локально задавать ей вопросы и получать ответы. Наиболее подходящей моделью тогда оказалась Dolly. Запустили прототип, на русском языке сравнили качество ответов модели с привычными чат-ботами, не понравилось.

Jupyter блокнот для знакомства с LLM Dolly

Jupyter блокнот для знакомства с LLM Dolly

  1. Всё, что мы смогли до сих пор сделать, по качеству ответов сильно уступало чат-боту ChatGPT. Похоже, пришло время протестировать OpenAI API.

Разработка прототипа «умного» поиска

Шаг 1. Быстро решить задачу Retrieval QA.

В этом проекте я решал задачу Retrieval QA впервые, за основу для первых набросков взял эту инструкцию.

Пример построен с применением фреймворка для работы с языковыми моделями — LangChain, в качестве языковой модели используется модель GPT 3.5 Turbo от компании OpenAI. Код не буду повторять, расскажу лишь про неочевидные детали:

  1. Для работы с OpenAI API понадобится API-ключ, который можно получить после регистрации на https://openai.com/ (доступ из России блокируется, помогает VPN).

  2. Услуга платная, для новых пользователей дают несколько долларов, чтобы протестировать возможности.

  3. Начать можно с пробной версии, а дальше уже привязать карту (не российскую, помогает поисковая выдача по запросу: «Оплата иностранных сервисов, подписок и товаров»).

Итак, цепочка RetrievalQAWithSourcesChain работает, я получаю ответы на вопросы по базе документов. При выставленном параметре «температура» в ноль (так я прошу модель в качестве источников использовать только предъявленную базу документов) качество ответов вполне устраивает, ответы заметно лучше по сравнению с Dolly и GitBook Lens.

Теперь всё это нужно упаковать в сервис для пользователей.

Шаг 2. Прикинуть бюджет.

Но сначала прикидываю бюджет. Точнее, выбираю между GPT-3.5 Turbo и GPT-4. Тариф на входящие токены для модели GPT-3.5 Turbo — $0.001 за тысячу токенов, в то время как у GPT-4 — $0.03. Тариф на исходящие токены для GPT-3.5 Turbo — $0.002, а у GPT-4 — $0.06.

Выходит, что вопросы и ответы у модели GPT-4 дороже в ~30 раз. Пока выбираю модель GPT-3.5 Turbo.

Первые эксперименты показали, что с учётом всех комиссий один вопрос к модели стоит ~1 российский рубль. Сразу же для защиты от непорядочных бета-тестеров выставляю порог расходов в личном кабинете OpenAI. Если начнут ддосить — сервис просто перестанет давать ответы.

Пока это всего лишь прототип для проверки гипотезы, поэтому на следующих шагах быстро собираю финальное решение.

Шаг 3. Быстро сделать фронтенд.

Фронтенд будет обращаться к бекенду с вопросами пользователей. Для быстрого старта подойдёт Twitter Bootstrap 5.

Шаг 4. Быстро сделать бекенд.

Выбираю FastAPI. Не зря он fast, отлично подходит для решаемой задачи.

Шаг 5. Подключить к модели полную документацию продукта.

Шаг 6. Сохранить все вопросы и ответы.

Для разбора ответов и улучшения сервиса будем сохранять все вопросы пользователей, полученные моделью ответы, источники, ошибки. В самом продукте активно используется ClickHouse, поэтому эти данные складываю туда же.

Шаг 7. Арендовать сервер, где будет работать поиск.

Поскольку к OpenAI API нельзя обращаться напрямую из России, выбираю дата-центр за пределами.

Шаг 8. Выбрать доменное имя.

Шаг 9. Подключить сертификаты LE.

И проверить, что всё работает и нет предупреждений безопасности, которые могут отпугнуть будущих пользователей поиска.

Шаг 10. Запустить альфа-тестирование.

Устраиваем альфа-тестирование нового поиска среди сотрудников компании. Обрабатываем шквал скриншотов с замечаниями и предложениями, даже находим ошибки в исходной документации, исправляем. Стало понятно, что перед бета-тестированием нужно добавить обратную связь.

Вспоминаем Алексея Лысенкова — бессменного ведущего передачи «Сам себе режиссер». Он рассказывал, что когда команда редакторов просматривает присланные видеоролики, то первые комментарии — они самые живые и смешные, но в эфир попасть не могут, потому что не пройдут цензуру. Чувствуем себя примерно так же, особенно разбирая вопрос про коз.

Лог первых вопросов и ответов модели

Лог первых вопросов и ответов модели

Шаг 11. Доработать поиск по результатам альфа-тестирования.

Список доработок:

  1. Замечаем, что иногда модель отвечает на английском языке. Исправляю это уточнением промпта.

  2. Обнаруживаем недокументированные коды ошибок модели OpenAI, корректно обрабатываю их.

  3. Набираем примеры разметки источников. Иногда они дописываются в текст ответа, иногда — в структуру данных «источники». Иногда используется HTML разметка, иногда нет.

  4. Делаю источники кликабельными, удобно же.

  5. Добавляю возможность для каждого ответа выставить оценку: «Плохо» (-1), «Нормально» (0), «Хорошо» (+1). Учу бекенд принимать оценки и сохранять в ClickHouse.

Шаг 12. Запустить бета-тестирование.

Публикуем новость о запуске поиска в официальных каналах компании-разработчика файрвола, запускаем бета-тестирование.

Шаг 13. Оценить результаты тестирования.

Набираем за несколько дней более 300 запросов и уходим обрабатывать ответы. По каждому запросу просим наших дорогих асессоров (сотрудников компании-разработчика файрвола, которые в совершенстве владеют документацией) разметить выборку. Проверить каждый ответ и выставить свою оценку: -1, 0, 1. Также просим прикрепить комментарий, чтобы знать, что можно улучшить.

Разметка асессорами собранного датасета с вопросами и ответами

Разметка асессорами собранного датасета с вопросами и ответами

Подведение итогов

Для оценки качества поиска я выбрал простой подход — Human Evaluation. По размеченной пользователями и асессорами выборке составил таблицу:

Статистика

От пользователей

От асессоров

Всего запросов (с 22.09 по 02.10)

336

336

Количество отрицательных оценок

18

122

Количество нейтральных оценок

7

63

Количество положительных оценок

18

139

Без оценок

293

12

Асессорам мы доверяем больше, чем пользователям. Поэтому смотрим на самый правый столбец.

Положительных оценок больше отрицательных. Но это ещё не значит, что GPT поиск работает хорошо. Среди ответов с положительной оценкой асессоров много «плохих» вопросов, не относящихся к теме, вроде: «Как настроить велосипед». Поэтому вместе с асессорами обсуждаем детали.

Что работает хорошо:

  • Если вопрос подробно сформулирован и про это есть конкретно в документации, то GPT поиск генерирует хорошие ответы. Иногда даже добавляет то, чего может не хватать в документации для понимания.

  • Бот приемлемо хорошо умеет собирать ответ из нескольких блоков с разных страниц документации. Опять же, если вопрос подробно сформулирован и части ответов есть в документации.

  • В подавляющем большинстве случаев бот находит корректные источники в документации. Уже в этом есть польза, даже если сам ответ плохого качества.

  • Всё, что мы пробовали до GPT-3.5 Turbo (Dolly, GitBook Lens), по качеству на голову ниже.

  • Однозначно, такой поиск работает лучше поиска по умолчанию в GitBook.

Что работает плохо:

  • Бот плохо отвечает на вопросы, которые не освещены в документации. За исключением общих и/или энциклопедических вопросов («Что такое DNS?»).

  • Модель может фантазировать.

  • Редко, но модель может ошибаться в источниках.

  • Модель может давать неполные ответы.

  • Модель может давать по большей части верные ответы, но добавлять и неподходящие блоки.

  • Иногда ответ генерируется на данных из файлов changelog старых версий.

Финальный вывод: GPT поиск по документации файрвола работает лучше других поисков, почти всегда даёт правильные источники, генерирует ответ хорошего качества при условии конкретного и подробного вопроса, который описан в документации.

От идеи до запуска GPT поиска прошло около 3 месяцев, ещё месяц ушёл на подведение итогов. Опыт внедрения современных технологий искусственного интеллекта считаем успешным, пора задуматься о стратегии развития AI/ML в компании.

Что делать дальше:

  • Ответить на вопрос: «Если модель сгенерирует неправдоподобный/ложный ответ, будет ли это безопасно для пользователя?»

  • Научить бота не отвечать матом.

  • Научить бота распознавать версию файрвола в вопросе и генерировать ответ по документации нужной версии.

  • Разобраться, как максимально ускорить сервис. Сейчас отвечает достаточно медленно (и Сэм Альтман признаёт это).

  • Перенести поиск в общую инфраструктуру компании.

  • Добавить фильтрацию чувствительных данных, которые могут появиться в ответе.

  • Попробовать GPT-4, GigaChat API, Yi-34B и что там ещё.

Короткие итоги от меня лично. Теперь я согласен с Chip Huyen: «It«s easy to make something cool with LLMs, but very hard to make something production-ready with them.» (Building LLM applications for production)

GPT поиск доступен для всех желающих по ссылке: https://gpt-docs.ideco.ru/

Скриншот умного поиска

Скриншот умного поиска

Вместе со мной над проектом работали:

  • @socketpair — идейный вдохновитель

  • @fisher85 — научный руководитель

  • дорогие асессоры Анастасия и Владислав

© Habrahabr.ru