OpenWebUI сейчас — это медиакомбайн, который объединяет доступ к OpenAI, Ollama, Automaitc1111, ComfyUI, Wisper API, создание кастомных моделей, RAG на основе Langchain и ChromaDB, гибридный поиск на основе BM25, веб-поиск и многое другое.
Да, все это уже давно доступно, расписано и при знании языков программирования реализуется на том же Python. Но в данном случае мы можем создавать интересные и полезные чат-боты, даже будучи далекими от написания кода.
В этой статье мы хотим поделиться нашим опытом создания чат-бота технической поддержки, который призван помогать отделу фронтлайна отвечать на вопросы пользователей (а в будущем — стать частью нашей команды).
Исходные данные
У нас есть пользовательская документация, которая развернута на движке Material for MkDocs. А значит, мы имеем дерево директорий, внутри которых находятся .md-файлы с Markdown-разметкой. Также у нас имеется развернутая связка OpenWebUI и Ollama с загруженной моделью llama3-8b-instruct.
Нам нужно решить следующие задачи:
- Создать кастомного чат-бота, который будет взаимодействовать с пользователями на русском языке.
- Преобразовать всю пользовательскую документацию в понятный для LLM формат и сделать так, чтобы с ней было возможно работать в режиме RAG (Retrieval Augmented Generation).
- Иметь возможность дополнять и обновлять данные в векторной базе документации.
- Сделать так, чтобы чат-бот работал в режиме ответов на вопросы и не реагировал по возможности на попытки увести его за рамки IT-разговора.
- По возможности научить чат-бот предоставлять ссылки на исходную документацию.
- Создать настройки ограничений на вопросы для чат-бота, например не отвечать на запросы по географическому признаку.
Реализация «в лоб»
Сначала мы попробовали загрузить нашу текущую документацию в исходном формате и использовать llama3 без преднастроек. Получилось, мягко говоря, «не очень»:
- В исходных .md-файлах документации присутствуют куски Markdown-разметки для изображений, сносок, участков кода, выделение жирным и курсивом, внутренние и внешние ссылки. Также мы используем иконки и конструкцию ~~** **~~ для обозначения кнопок. Все это дает лишний «мусор» при разбиении документации на чанки.
- Модель sentence-transformers/all-MiniLM-L6-v2, которую OpenWebUI предлагает по умолчанию и которая отвечает за отображение предложений и абзацев в 384-мерном плотном векторном пространстве, и нужна для таких задач RAG, как кластеризация или семантический поиск, заточена под английский язык. Да, такие модели могут работать и с другими языками, но делают это гораздо хуже.
- llama3-8b опять же не очень хорошо умеет обращаться с русским языком, что сказывается и на качестве поиска, и на ответах — в них заметен сильный «акцент». Вдобавок модель хоть и instruct, но ее можно разговорить и увести в размышления, далекие от непосредственно ответов на запросы пользователей. Решением могло бы стать использование 70b модели, но для этого необходима видеокарта с 40 Гб видеопамяти, тогда как для llama3 8b можно использовать любую с 8 Гб видеопамяти.
И если третью проблему можно решить созданием кастомной модели (агента в терминологии OpenAI), то первые две требуют определенных телодвижений. Вот что у нас получилось в итоге.
Шаг за шагом настраиваем чат-бота техподдержки в OpenWebUI
Для начала надо преобразовать документацию в удобный для загрузки формат. Для этого был написан скрипт magic bash ai_text_generator, который проходит по всем директориям документации и через регулярные выражения в sed, awk и perl убирает и заменяет не нужную для RAG Markdown-разметку, а в конце документа формирует ссылку на реальную документацию на https://hostkey.ru/documentation.
В текущей реализации скрипт:
- генерирует полный URL документации;
- удаляет разметку для изображений;
- удаляет все аннотации;
- заменяет ~~** и **~~ на [ ] (форматирует кнопки);
- удаляет строки, начинающиеся с >. Через эту разметку у нас сделано оглавление в начале страницы;
- удаляет иконки;
- убирает разметку для жирного текста;
- удаляет внутренние ссылки, при этом оставляя внешние;
- переформатирует ссылки на почту;
- удаляет лишние пробелы в начале строки до первого символа;
- заменяет CRLF- на UNIX-формат (у нас документацию пишут и из-под Windows, и из-под Linux);
- удаляет пустые строки больше одной подряд;
- добавляет URL в конец всех файлов.
После отработки данного скрипта в директории ai_data мы получаем набор файлов, которые можно загрузить для формирования RAG в OpenWebUI.
Далее нужно добавить в OpenWebUI новую модель для работы с векторной базой документов и в Ollama LLM, которая была бы с русским языком на «ты».
- Заходим в Admin Panel — Settings — Documents и ставим в Embedding Model значение sentence-transformers/all-MiniLM-L12-v2. Мы перепробовали все рекомендованные модели из этого списка (https://www.sbert.net/docs/sentence_transformer/pretrained_models.html) и остановились на данной.
- Нажимаем на значок скачивания справа от поля и устанавливаем эту модель.
-
Сразу задаем параметры для нашего RAG:
- Top K = 10. Это означает, что при создании ответа система будет учитывать 10 наиболее релевантных документов.
- Chunk Size = 1024. Документы будут разбиты на фрагменты по 1024 токена для обработки.
- Chunk Overlap = 100. Между последовательными фрагментами будет перекрытие в 100 токeнов.
После этого можно перейти в раздел Workspace — Documents и подгрузить нашу документацию. Рекомендуем задать ей определенный тег коллекции (в нашем случае hostkey_ru), чтобы ее проще было подключать к модели или при API-запросах.
Добавляем в Ollama модель, которая работает с русским языком. Мы взяли модификацию llama3-8b под названием SAIGA от Ильи Гусева отсюда: https://huggingface.co/IlyaGusev/saiga_llama3_8b_gguf/blob/main/model-q4_K.gguf. Скачиваем ее себе.
Чтобы добавить gguf-модель через OpenWebUI идем в Admin Panel — Models, показываем скрытый раздел Experimental и загружаем модель, нажав на пустое поле Upload a GGUF model.
Если теперь подгрузить модель через кнопку справа, то она не будет работать, так как Modelfile Content по умолчанию к ней не подходит.
Поэтому вставляем в это поле следующий текст:
TEMPLATE "{{ if .System }}<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
{{ .Response }}<|eot_id|>"
PARAMETER num_keep 24
PARAMETER stop <|start_header_id|>
PARAMETER stop <|end_header_id|>
PARAMETER stop <|eot_id|>
И только потом подгружаем модель.
Теперь нам нужно создать кастомную модель для нашего чат-бота. Для этого снова идем в Workspace — Models и нажимаем на значок плюса.
Задаем имя чат-бота и ставим базовую модель (на скрине Russian llama3 SAIGA 8b — это переименованная в этом же разделе модель model-q4_K.gguf).
Потом прописываем System Prompt, который и определяет, кем будет себя воспринимать чат-бот. В нем мы задаем его поведение, ограничения, указываем, что хотим от него получить в итоге.
Для нашего чат-бота техподдержки он такой:
You are HOSTKEY an IT Support Assistant bot focused on providing users with IT support based on the content from knowledge base. Stay in character and maintain your focus on IT support, avoiding unrelated activities such as creative writing or engaging in non-IT discussions. You are speaking in Russian and only in Russian.
If you cannot find relevant information in the knowledge base or if the user asks non-related questions that are not part of the knowledge base, do not attempt to answer and inform the user that you are unable to assist and print text "Для получения дополнительной информации посмотрите нашу полную документацию: https://hostkey.ru/documentation" at the end.
Provide short step-by-step instructions and external links
Provide a link to relevant doc page about user question if it find in RAG and started with "Ищите больше информации по ссылке:"
Add text ""Для получения дополнительной информации посмотрите нашу полную документацию: https://hostkey.ru/documentation"" at the end.
Example of answer:
"
User: Как мне отменить аренду сервера?
Bot:
Вы можете отказаться от использования сервера в любое время. Для этого вам необходимо зайти в панель управления Invapi и выполнить следующие действия:
- Перейти во вкладку Billing в меню управления конкретным сервером.
- Нажать кнопку [Cancel service].
- Описать причину отмены и выбрать ее тип.
- Нажать кнопку [Confirm termination].
При немедленной отмене мы не возвращаем денежные средства за часы фактического использование сервера, включая время развертывания самого сервера, заказанного ПО и обработки заявки на отмену (до 12 часов). Неизрасходованный остаток средств возвращается на кредитный баланс клиента. Вывод средств с кредитного баланса выполняется согласно Правилам возврата денежных средств.
Отменить заявку возможно во вкладке Billing с помощью кнопки [Revoke]:.
Кроме того, если вам необходимо отменить услугу, включающую лицензию на ОС/программное обеспечение или имеющую индивидуальную/нестандартную конфигурацию, свяжитесь с нами через систему тикетов для оформления возврата вручную.
Подробнее смотрите здесь: https://hostkey.ru/documentation/billing/services_cancellation/.
"
Затем подключаем нужную коллекцию документов, нажав на кнопку Select Documents в разделе Knowledge и выбрав нужную по тегу.
Также нужно настроить дополнительные параметры, скрытые во вкладке Advanced Params. Нажимаем на Show и выставляем Temperature в 0.3, а Context Length — в 4089.
Нажав на кнопку Save & Update создаем кастомную модель нашего чат-бота техподдержки.
Все, наш чат-бот готов к труду и обороне от запросов пользователей. Он не ругается, не выходит из себя и готов работать 24 часа в сутки.
Советы по работе с RAG в OpenWebUI
- Если у вас будет много документов в RAG, то OpenWebUI желательно устанавливать с поддержкой GPU (ветка open-webui:cuda).
- Любые манипуляции с Embedding Model (смена, загрузка и т. п.) потребуют удаления и загрузки документов в векторную базу заново. Изменение параметров RAG этого не требует.
- При добавлении/удалении документов обязательно зайдите в кастомную модель, удалите и добавьте коллекцию этих документов заново, иначе поиск не будет работать или будет, но очень плохо. Также если чат-бот отвечает как-то не так, но документацию с нужной информаций в списке внизу отображает, то первым делом нужно удалить и заново добавить коллекцию документов.
- Хотя OpenWebUI распознает для создания RAG форматы pdf, csv, rst, xml, md, epub, doc, docx, xls, xlsx, ppt, ppt, txt, лучше все-таки загружать в него документы в plain text.
- Использование гибридного поиска хоть и улучшает вывод, но потребляет слишком много ресурсов, и ожидание ответа может затянуться на 20–40 секунд даже на мощном GPU. Эта проблема известна разработчикам, но решения пока нет.
Что дальше? Теперь чат-бота нужно интегрировать в существующую систему чатов компании. OpenWebUI имеет API и может работать как прокси к Ollama с добавлением своих фишек, но увы, он еще не документирован как следует. Изучение кода и коммитов позволяет понять, что нужно добавить в запросы, но похоже, это пока толком не работает (так, мы смогли вызвать только кастомную модель, но без RAG).
Ждем анонсированные разработчиками в последних версиях функции вызова OpenWebUI с RAG, web-поиском и прочим с примерами и описаниями.
Чат-бот при тестировании также помог выявить некоторые расхождения в понятийной части нашей документации, расхождения и дублирования статей, что позволило улучшить как бота, так и саму документацию.
P. S. В процессе работы над данной статьей вышла новая модель от Google Gemma 2, которая даже в версии 9b достаточно прилично работает с русским языком. Instruct-версию этой модели можно установить в Ollama (версия gemma2:9b-instruct-q5_K_M весит 6,6 Гб), но мы пока остановились на текущей конфигурации, а gemma 2 поставили в планы на новое тестирование.