Всем привет! В последние годы большие языковые модели (LLM) стали набирать огромную популярность в решении множества задач, начиная от классического поиска по документам и заканчивая анализом финансовых новостей для принятия решений. В этой статье мы расскажем, как применили эти технологии для создания интеллектуального помощника, готового ответить на ваши вопросы по Jmix и помочь в написании кода.
Что такое Jmix AI Assistant?
Jmix AI Assistant — это агент на основе LLM, который может значительно ускорить вашу разработку на Jmix благодаря использованию четырех инструментов: поиска по актуальной версии документации, UI Samples, материалов тренингов, а также поиска по части форума, где ассистент может найти ответы на специфические вопросы.
Протестировать работу ассистента вы можете здесь.
Первые эксперименты
Когда мы начали разработку ассистента, применение агентов казалось необязательным в этой задаче; нам казалось, что стандартного RAG-подхода должно хватить с головой. В качестве LLM мы взяли GPT-4, которая уже обладала некоторыми знаниями о Jmix и Java-разработке.
Мы загрузили в векторную БД Chroma несколько страниц из документации, и на простые вопросы модель отвечала хорошо. Однако мы быстро поняли, что для ответа на более масштабные вопросы модели не хватает контекста, из-за чего она часто галлюцинировала и додумывала ответ. Для решения этой проблемы мы обратились к фреймворку LangChain: чтобы не обрывать предложение на полуслове, мы воспользовались RecursiveCharacterTextSplitter, а в качестве типа ретривера выбрали ParentDocumentRetriever, который позволил нам осуществлять поиск по маленьким чанкам, а возвращать весь текст со страницы документации (или большую его часть), что сильно повысило качество ответов.
Тогда мы решили проверить, насколько хорошо агент может писать код, но тут же столкнулись с тем, что часто одного запроса к ретриверу недостаточно. Кроме того, помимо основной документации нам захотелось также предоставить модели дополнительные данные, которые содержали бы актуальные сниппеты кода, а добавлять всю информацию в один ретривер не хотелось. Так мы пришли к выводу, что нужно пробовать применять в этой задаче агентный подход.
Что такое LLM-агент?
По сути агент — это некая абстракция, которая означает, что большая языковая модель способна неким образом взаимодействовать со средой и получать от неё дополнительную информацию для решения поставленной задачи. На сегодняшний день существует большое множество типов LLM-агентов с различным функционалом, начиная с Chain-of-Thought, и заканчивая OpenAI Tools Agent.
Мы начали с проверки, сможет ли агент использовать один из 3 предложенных ему инструментов (или же источников данных), чтобы дать готовый ответ. Тогда мы воспользовались OpenAI Functions, встроенным в LangChain, и это действительно работало, но только для простых вопросов. У данного подхода был существенный минус — невозможность построить логическую цепочку, которая позволила бы модели интерпретировать полученную из инструментов информацию, чтобы затем воспользоваться следующим из них.
Мы рассматривали разные варианты агентных систем, такие как Chain-of-Thought и его логическое продолжение Tree-of-Thought, позволяющие полноценно анализировать запрос пользователя и разбивать его на логические кусочки. Возможность строить связные мысленные рассуждения — это именно то, чего не хватало при использовании OpenAI Functions, однако при этом отсутствовала важная функциональность вызова инструментов. И тогда мы открыли для себя новый тип агентов — ReAct, о котором расскажем в следующем абзаце.
ReAct LLM-agent
Одной из главных особенностей ReAct агента является его способность не просто рассуждать, как это делается в Chain of Thoughts, но и активно использовать доступные инструменты. После формирования первоначального плана на основе внутренних рассуждений, ReAct переходит к выполнению конкретных действий для получения информации и / или совершения действий. Это может быть запрос к базе данных, вызов API или любое другое действие, способное привести к требуемому результату.
После каждого выполненного действия ReAct-агент анализирует результаты и получает обратную связь от окружающей среды. Эта информация помогает агенту корректировать свои следующие шаги и решать, достаточно ли имеющейся у него на данный момент информации для ответа. Такой подход позволяет ReAct более гибко отвечать на запросы пользователей и самостоятельно решать, каким инструментом он воспользуется дальше.
Превращаем абстрактного агента в Jmix AI Assistant
После того как мы определились с типом агента, необходимо было понять, какие данные мы можем использовать для того, чтобы помочь агенту с ответом. Было принято решение расширить информацию в ретривере на все страницы документации, а также создать два дополнительных ретривера, которые бы выполняли функции поиска по UI Samples, а также части форума, в которой содержались ответы на реальные вопросы от пользователей, на которые не нашлось ответа в документации.
Кроме этого, была мысль сделать хайповую мультиагентную систему, где главный ReAct-агент будет запрашивать информацию от LLM, у которых будет доступ к ретриверам, содержащим нужную информацию. Этот подход показал себя неплохо — ассистент начал давать правдоподобные ответы на сложные запросы вроде такого: «How to make a Master Detail screen in which there will be a list of employees in the table on the left, and only a list of onboarding steps on the right with the ability to mark progress?». Однако, если копнуть чуть глубже, то оказывалось, что моделям, которые находились внутри инструментов, недоставало контекста изначальной задачи, вследствие чего их ответ главному агенту мог быть нерелевантен, хотя необходимая информация содержалась в ретривере. Кроме этого, была ещё одна большая проблема: цена таких запросов. Хоть и кажется, что один запрос к GPT-4 стоит недорого, но когда в системе работают четыре агента, и каждый требует платы как за input, так и за output токены, становится немного больно.
В конечном итоге было принято решение в инструментах оставить только ретриверы, а при отсутствии искомой информации в них — выдавать сообщение-подсказку для агента с просьбой переформулировать вопрос или воспользоваться другим инструментом.
По итогам внутреннего тестирования многие разработчики отметили, что за счет системы «мыслей» ассистент хорошо подсказывает верное направление идеи для решения задачи и этим самым помогает, даже если не может правильно сформировать окончательный ответ.
В качестве оболочки для взаимодействия с агентом мы решили использовать Streamlit, поскольку он позволил в короткие сроки продемонстрировать все способности агента, включая демонстрацию «мыслей» и результатов работы инструментов в раскрывающихся блоках, благодаря StreamlitCallbackHandler, доступному в LangChain.
Подготовка Jmix AI Assistant к релизу
В рамках тестирования нашего ассистента мы собрали большое количество фидбека и провели ряд улучшений нашего ассистента:
-
Перешли на более продвинутую модель GPT-4o;
-
Обновили базу знаний Jmix AI Assistant, добавили материалы из тренингов, а также больше топиков с форума;
-
Добавили переформулирование запроса пользователя несколькими способами для повышения качества векторного поиска;
-
Проанализировали отклики пользователей и на их основе улучшили системные инструкции агента;
-
Увеличили стабильность ответов агента за счёт перехода на более современный фреймворк для работы с агентами LangGraph;
-
Уменьшили вероятность ответа агента на других языках, не соответствующих языку запроса пользователя;
-
Добавили память контекста разговора;
-
Перешли от Streamlit к собственному фронту на Jmix:
Всё это позволило нам вывести Jmix AI Assistant на новый уровень и сделать его по-настоящему эффективным помощником в решении повседневных задачах, связанных с разработкой на Jmix, однако мы не планируем останавливаться на достигнутом.
Дальнейшие планы развития сервиса
Уже сейчас в Jmix AI Assistant мы видим большой потенциал для дальнейшего использования, и, конечно, мы планируем заниматься его развитием. В процессе разработки мы столкнулись со многими проблемами, часть из которых затронули в этой статье, и сейчас хотели бы рассказать, над чем мы будем работать в ближайшие месяцы.
В первую очередь, мы бы хотели снизить процент тех случаев, когда искомая информация содержится в ретриверах, но по тем или иным причинам не доходит до агента. В настоящий момент ограниченная длина контекста в LLM не позволяет нам подавать полные версии всех найденных материалов, поэтому мы хотим научиться тщательно фильтровать ту информацию, которую получаем с ретриверов. В этом может помочь использование модели-реранкера, которая сможет более точно переранжировать выходные документы из ретривера перед подачей в LLM. Тогда можно будет подавать меньшее количество документов, поскольку мы будем более уверены в их релевантности.
Ещё одна идея заключается в вынесении возможности просмотреть полный текст страницы документации в отдельный инструмент, что позволит возвращать большее количество чанков документов со ссылками на источник, по которым агент сам сможет получить полную информацию.
Конечно, нельзя не затронуть важность использования хороших эмбеддинговых моделей при использовании ретриверов. На текущий момент мы используем модели семейства text-embedding-3 от OpenAI для построения эмбеддингов, однако мы активно экспериментируем с использованием других моделей, которые могут хорошо поднять уровень релевантных ответов.
Ещё одно потенциальное улучшение – параллельное применение нескольких ретриверов разной природы для повышения разнообразия в выдаче ретриверов. Например, параллельно со стандартным ретривером, можно воспользоваться BM25 – вероятностным алгоритмом ранжирования, используемым для определения релевантности документов поисковому запросу, который является логическим продолжением TF-IDF. Это позволит системе лучше производить поиск по тем словам и словосочетаниям, которые мало встречались в обучающей выборке эмбеддинговых моделей.
Это лишь часть наших идей по дальнейшему улучшению сервиса, но, надеюсь, вы тоже почерпнули для себя пару перспективных идей для своих проектов.
Заключение
Разработка Jmix AI Assistant является примером того, как современные технологии могут радикально трансформировать процесс разработки программного обеспечения. Применение LLM-агентов позволяет не только ускорить написание кода и поиск по документации, но и значительно повысить скорость изучения новых для разработчиков инструментов, таких как Jmix.
Тем не менее, путь развития Jmix AI Assistant ещё далек от завершения. Впереди нас ждет много интересных задач, но уже сейчас ясно, что применение искусственного интеллекта в программной инженерии — это возможность сильно ускорить разработку, посвящая больше времени проектированию, а не чтению документации и написанию кода.
Автор: RomanOdobesku