Добавим приставку нейро: взгляд на интеграцию LLM в продукт со стороны фронтенда

Добавим приставку нейро: взгляд на интеграцию LLM в продукт со стороны фронтенда

Привет, это Андрей Мелихов, ведущий разработчик интерфейсов в Yandex Cloud. Я работаю в команде DataLens — BI-системы для визуализации больших наборов данных на дашбордах и графиках.

В прошлом году в DataLens появился чат-интерфейс: пользователь общается с ИИ-ассистентом, который строит графики, пишет формулы и решает аналитические задачи. В рамках работы над этим продуктом, который получил имя Нейроаналитик, мы пошли не совсем очевидным путём и перераспределили ответственность между командами фронтенда и бэкенда. В статье — наш опыт. В конце — демо-проект, чтобы увидеть реализацию изнутри.

ИИ, бэкендеры и фронтендеры

Подключение LLM к продукту — задача, с которой сегодня сталкиваются многие команды. Обычно за неё берётся бэкенд. Мы выбрали другой подход: первый этап интеграции с моделью взяла на себя фронтенд-команда. Считаем, что у такого подхода есть конкретные преимущества.

Важная ремарка. Под фронтендерами мы (как минимум наша команда DataLens) понимаем скорее фуллстек-инженеров с уклоном в UX и JavaScript/TypeScript. В нашей зоне ответственности — и браузерный, и серверный код на Node.js, собственные CI/CD-пайплайны и DevOps, обслуживающий задачи фронтенда. Но интерфейсы нам всё же ближе, чем данные.

Типичный сценарий: бэкенд интегрирует LLM, поднимает эндпоинты, а фронтенд подключается к ним и рисует интерфейс чата. Это рабочая схема, но возникает вопрос о распределении ответственности. Действительно ли вся интеграция с моделью должна лежать на бэкенде?

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

  • Данные уже под рукой. В современных интерфейсах сходятся данные из множества бэкендов. Если нужно построить ИИ-инструмент поверх — контекст уже на клиенте, и дозапросить нужное несложно.
  • Быстрые итерации. Когда команда ищет подходящую нишу для ИИ в продукте, скорость экспериментов критична. Фронтенд позволяет быстро проверять гипотезы без изменений в основном бэкенде.
  • Меньше координации на старте. BFF на Node.js — привычная инфраструктура. Подключился к совместимому API — и можно двигаться, не блокируя другие команды.

Это не значит, что бэкенду здесь нечего делать. С развитием продукта неизбежно появляются задачи, которые должны решаться на сервере. Но начать можно силами фронтенда — и результат будет вполне продуктовым.

Архитектура: как это устроено

В нашей архитектуре между клиентом и LLM добавляется слой BFF — например, на Node.js или Bun. Конкретная технология не принципиальна: аналогичное решение можно реализовать и на Python, если фронтенд-команда готова поддерживать его. Важно, что BFF — привычный инструмент для фронтенд-команд. Он изначально отвечает за подготовку view-ориентированных данных. Теперь он берёт на себя и взаимодействие с моделью.

Существующий бэкенд остаётся без изменений. Интеграция на первом этапе переносится в зону ответственности фронтенд-команды. Схема остаётся привычной: просто появляется дополнительный бэкенд.

В периметре BFF находятся ключи доступа, rate limiting, CORS, мониторинг и логирование. Логика взаимодействия с API LLM переносится на сервер — на тот бэкенд, который «бэкенд для фронтенда».

Чтобы показать реализацию, я собрал демо-проект на GitHub — монорепозиторий с сервером на Express и клиентом на React. Интерфейс реализует упрощённую BI-систему: слева дашборд, справа — ИИ-ассистент. Сценарий: пользователь запрашивает, например, топ-5 продуктов. Ассистент вызывает функцию getChartData, получает данные и формирует ответ.

Прототип я собрал с помощью ИИ. Сначала составил план в Superpowers, затем код-ассистент реализовал основу, я проверил и подключил Gravity UI для интерфейса. В итоге — рабочее приложение с простой кодовой базой, доступной в репозитории.

Цель примера — показать, что для фронтенд-разработчика, знакомого с Express, базовая интеграция с LLM не представляет особой сложности. Дальше можно наращивать: тулинг, контекст, продвинутые сценарии.

Четыре компонента интеграции

Для добавления ИИ в продукт фронтенд-разработчику понадобятся четыре вещи:

  1. UI-кит — компоненты для чат-интерфейса.
  2. API SDK — библиотека для общения с LLM.
  3. Тулинг — механизм, позволяющий модели вызывать функции и получать данные.
  4. Контекст — данные и состояние приложения, передаваемые модели.

UI-кит: интерфейс для чата

Антон Непша сделал хороший обзор UI-китов для создания ИИ-агентов: AI Elements от Vercel, Assistant UI, prompt-kit, shadcn-chatbot-kit.

Но не все одинаково удобны. Решение от Vercel, например, оказалось перегруженным: много слоёв абстракции, проблемы совместимости с опенсорс-моделями, недостаточная гибкость. При выборе важна не только функциональность, но и сложность интеграции.

Мы используем AIKit от команды Gravity UI. У него проработанный визуал, корректная отрисовка ответов, встроенная история вызовов. Не нужно вручную подключать обработку markdown или расширения вроде GFM для таблиц. Кстати, мой коллега Илья Ломтев уже писал о нём на Хабре.

Сейчас AIKit внедряется во все продукты Yandex Cloud, включая консоль и SourceCraft. Он будет активно развиваться и получать функциональность, нужную разным командам.

В моём демо доступна версия с AIKit — она заметно симпатичнее. Эта версия лежит в отдельной ветке.

API SDK: общение с моделью

Когда OpenAI предложили модель через API, это стал поворотный момент. Локальное развёртывание LLM оказалось слишком дорогим — и появился единый сетевой интерфейс, на основе которого разработчики строят продукты.

Сегодня провайдеров много, но большинство ориентируются на совместимость с OpenAI API. Поддержка есть и в Yandex AI Studio. Смена baseURL позволяет использовать Mistral, Groq, Fireworks, Ollama, Together, xAI и другие. Для разработчика это значит: один клиент — много моделей.

Рынок снова присматривается к Anthropic API — во многом благодаря популярности Claude Code и желанию провайдеров привлечь его пользователей. Но в целом OpenAI API доминирует.

Во многих туториалах сразу предлагают LangChain. Это мощный инструмент с реализациями на Python, JavaScript/TypeScript, Java, Go, .NET. Но начинать с него, на мой взгляд, не стоит. Это слишком высокоуровневая абстракция, которая скрывает детали. Дебажить сложно — часто требуются отдельные инструменты вроде LangSmith.

Начинать с LangChain — как изучать веб-разработку с React. Сначала стоит понять, как всё работает на уровне API. LangChain и аналоги — для более зрелых проектов.

Возьмём OpenAI SDK для Node.js. Минимальный пример:

Создаём клиент, передаём массив сообщений. system задаёт поведение модели, user — содержит запрос. Вся коммуникация строится как цепочка сообщений — на этом и основан Chat Completions API.

Ответ API включает usage с количеством токенов (полезно для логирования и контроля затрат) и choices с результатом. Забираем текст, отображаем в интерфейсе — чат готов.

Но без тулинга такая система — просто красивая обёртка над генерацией текста. Модель ничего не знает о данных в вашем продукте.

Тулинг: модель как агент

Тулинг — это механизм, позволяющий модели запрашивать информацию из приложения. Без него чат остаётся «голым» генератором текста. С тулингом система превращается в агента, способного взаимодействовать с данными.

В 2023 году OpenAI добавили в API возможность передавать модели список доступных функций. Теперь модель может явно сообщить: «Хочу вызвать эту функцию с такими параметрами». Аналогичный подход используется в MCP: разработчик описывает инструменты, а модель учится их вызывать.

В параметре tools передаём описание функций:

Описание — это JSON: название, назначение и параметры функции. Например: «У тебя есть функция getChartData, которая принимает массив ID чартов и возвращает их данные».

В ответ модель возвращает tool_calls — список функций, которые хочет вызвать.

Дальше — дело разработчика. Вызываем реальную функцию, получаем данные и возвращаем результат модели в сообщении с ролью tool.

Полный массив сообщений, который уходит модели на следующем шаге:

Получив данные, модель формирует осмысленный ответ. Объём зависит от модели — она может ограничиться парой предложений или развернуть полноценный анализ. Но не стоит питать иллюзий: модель — не калькулятор. Ей нельзя отдавать вычисления на откуп. Для полноценной аналитики нужна серьёзная обвязка из тулинга.

Responses API: что дальше

OpenAI представили новый Responses API и предлагают переходить к нему от Chat Completions. По их словам, старый API был спроектирован в сжатые сроки, но быстро стал основой для большинства интеграций с LLM. Буквально: в пятницу задизайнили, во вторник — уже в продакшене.

Эволюция OpenAI API отражает рост потребностей разработчиков:

  • Первое поколение — простая схема «запрос → ответ».
  • Второе поколение — Chat Completions, адаптированный под диалог.
  • Третье поколение — Responses API, заточенный под агентные сценарии.

Responses API встраивает RAG, function calling, поиск — всё, что раньше приходилось собирать вручную. Для фронтенд-разработчика это упрощает жизнь: нужен BFF на Express, обращение к API, а остальная магия — внутри.

Есть нюанс: передача данных стороннему провайдеру означает, что информация хранится вне вашего периметра. Для продуктовых решений это не всегда приемлемо. Но концепция «агентность по умолчанию» набирает обороты и снижает порог входа.

Контекст: память для модели

У LLM нет памяти. Она опирается только на то, что передано в запросе. Если отправить два сообщения по отдельности — сначала представиться, потом спросить «как меня зовут?» — модель не ответит.

Поэтому в каждом запросе нужно передавать историю переписки. Но на практике одной истории мало. Для нормальной работы ИИ-функциональности модели нужен более широкий контекст.

Проблема в том, что контекстное окно ограничено: 128–256 тысяч токенов, у некоторых — до миллиона. Но даже при большом окне модели не всегда эффективно используют весь объём — они фокусируются на части данных. Возникает задача: какие фрагменты передавать, а какие отбросить?

В DataLens Editor значимым контекстом становится не только чат. Модели нужно знать о коде пользователя, результатах выполнения, ошибках в консоли. Всё это живёт на клиенте — и это территория фронтенда.

Вот что делает управление контекстом сложной задачей:

  • Данные могут быть огромными. Микротаблицу легко уложить в контекст. В реальности данные разбиты на страницы, и передать их целиком зачастую невозможно.
  • Модели плохо считают. Если дать LLM большой массив чисел и попросить посчитать среднее — нет гарантий, что учтёт все данные. Точные вычисления нужно делать на стороне приложения.
  • Контекст быстро растёт. С каждым сообщением и вызовом инструмента объём передаваемых данных увеличивается.

Степень участия бэкенда зависит от архитектуры. Если логика на сервере — часть задач можно делегировать ему. Но клиентский контекст — код, состояние интерфейса, результаты выполнения — доступен только фронтенду. Поэтому даже при классической архитектуре фронтенд-разработчикам приходится самостоятельно управлять этим объёмом. В крупных приложениях обработка контекста становится отдельной инженерной задачей.

С развитием продукта неизбежно появляются задачи для бэкенда: фоновые операции, взаимодействие с клиентом через API. Но реализовать их будет проще, если перед глазами уже есть работающая фича на клиенте.

Работы хватит всем

Базовая интеграция складывается из четырёх компонентов — интерфейс, SDK, тулинг, контекст — и этого достаточно для работающего приложения. Область работы с LLM остаётся новой и быстро развивающейся.

Здесь много серьёзных задач: управление контекстом, проектирование ИИ-интерфейсов, стриминг, function calling. Форматы взаимодействия тоже меняются: ещё недавно все сидели в IDE, а сейчас набирают популярность консольные агенты. Пространство для работы только расширяется.

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

Мы всё ещё в начале пути. Не знаем, куда всё повернёт завтра. Интерфейсы меняются, модели становятся мощнее, а то, что вчера казалось экспериментом, сегодня уже в продакшене. На этом этапе неважно, где — главное делать: пробовать, ломать, проверять гипотезы. Инструменты уже есть, порог входа ниже, чем кажется, и работы действительно хватит всем.

Читать оригинал