AI Engineering - обзор книги
Evgenii NikitinСколько копий сломано вокруг определения термина AI... В этой книге Чип Хуен делает ход конём - оказывается, AI engineering - это только про большие генеративные модели. Да и вообще это книга про LLM, хотя в паре месте и упоминаются мультимодальные и картиночные модели.
Впрочем, бог с ним, с названием, как сама книжка? В целом - скорее, понравилась. Фактически это очередная попытка написать большой справочник по миру LLM-систем в проде, ближе всего к этой книге по духу LLMs in Production, про которую я уже кратко писал. Если вы уже работаете с LLM-системами, целиком читать особого смысла нет. Можно выбрать интересные главы или даже части глав и читать только их.
Сначала хотел подробно написать про содержание книги, но там всё понятно из, собственно, содержания. Поэтому вместо этого расскажу про какие-то запомнившиеся мне особенности, мысли или фрагменты книги. От себя добавлю кое-какие ссылки на инструменты и примеры.

Сильной стороной книжки является довольно широкое описание разных подходов к одной и той же проблеме. Допустим, вы хотите, чтоб модель отдавала вам ответ в каком-то заданном структурированном форматие - JSON, XML, HTML и так далее. К этой проблеме есть много потенциальных ключиков, каждый со своими плюсами и минусами, про которые тоже обычно написано:
- Затюнить промпт и навалить в него примеров формата
- Сделать постпроцессинг - например, через либу json-repair
- Сделать валидацию ответа - руками или через либу типа Instructor и добавить ретраи
- Сделать ограниченный сэмплинг - занулять вероятности токенов, которые противоречат ожидаемому формату ответа
- Зафайнтюнить модель - может хватить даже 100-200 примеров
Всё - эксперимент
Одна из ключевых мыслей книги - всё должно расцениваться как эксперимент. Любое изменение в идеале должно тригерить пайплайн оценки. Например, пересчитываем метрики, если:
- Поменяли промпт (даже на один символ)
- Поменяли настройки модели - температуру, top-p, top-k, repetition penalty
- Поменяли логику пайплайна - например, заменили реранкер-модель на реранкинг через LLM
- Поменяли саму LLM
Такой подход называется evaluation-based development. В идеале оценка должна производиться автоматически. Самый популярный подход сейчас - это LLM-as-a-Judge. Стандартная практика - описываем критерии оценки в промпте и просим модель оценить ответ бинарно или по шкале, но есть и более хитрые подходы, которые позволяют оценивать, насколько корректны факты в ответе модели:
- Само-верификация. Просим модель сгенерить ответ на один и тот же вопрос несколько раз, и проверяем консистентность ответов.
- Разбиваем ответ на несколько независимых компонентов, и используем API поиска для получения фактической информации
Вообще помимо "правильности" ответа в разных задачах могут быть важны самые разные критерии оценки модели:
- Локальная консистентность - противоречит ли ответ модели предоставленному контексту
- Глобальная консистентность - противоречит ли ответ модели общеизвестным фактам
- Безопасность ответа - мат, насилие, секс, расизм, сексизм и так далее
- Следование инструкциям - отвечает ли модель в нужном формате
- Ролеплей - следует ли модели заданной роли
- Цена и скорость
Поскольку изменение промпта может вести к самым неожиданным последствиям, их обязательно надо версионировать, а ещё лучше - вместе с мета-данными (когда изменили, почему, кто).

Лучше всего, конечно же, со временем собрать свой датасет (можно начать и с 20-30 примеров), но можно и поискать релевантные задачи в проектах типа LM Evaluation Harness.
Фейлы агентов
Глава про RAG и агентов довольно объёмная, но, наверное, самая интересная часть про потенциальные фейлы агентов. Если коротко, то они могут зафейлиться тысячей разных способов (это правда, испытано на практике):
- Выбран неправильный инструмент под задачу - например, поиск по БД вместо поиска по документации
- Выбран правильный инструмент, но он вызван с неверным списком параметров - например, поиск по БД вызван без названия базы данных
- Выбран правильный инструмент с правильным списком параметров, но неверными значениями - например, поиск по документации вызван с фильтром по источникам, но выбраны несуществующие источники
- Всё выбрано правильно, но цель не достигнута, и агент это признал
- Всё выбрано правильно, цели не достигнута, но агент утверждает, что всё окей
- Всё выбрано правильно, цель достигнута, но это всё заняло час, и пользователь уже закрыл нафиг вашего агента
- Ошибка в работе самого инструмента - например, поиск по БД отвалился по таймауту или сгенерили валидный, но неправильный SQL-запрос
В анализе фейлов не обойтись без какого-нибудь средства мониторинга. Например, мы используем Langfuse.

Суперион - трансформеров из трансформеров
Узнал я и что-то совсем для себя новенькое. Несколько лет назад я читал статью Model soups про усреднение весов разных моделей. Оказывается, такая штука довольно активно применяется в LLM-мире. К примеру, мы можем обучить несколько моделей или адаптеров под разные задачи, а потом смёрджить их в единую модель. Основные подходы:

- Суммация - соединяем веса линейной комбинацией или методом SLERP
- Стакинг слоёв - собираем из маленьких моделей большого франкенштейна
- Конкатенация - используется реже, так как не предоставляет особых преимуществ по сравнению с раздельным использованием моделей в плане памяти или скорости
Синтетика или натуральный продукт
Три кита любых наборов данных - качество, разнообразие и количество. Оказывается, синтетические данные могут помочь с каждым из этих критериев:
- Количество - почти всегда автоматическая генерация данных дешевле, чем человеческая
- Разнообразие - можно создать пайплайн генерации данных, который будет покрывать самые разные темы и сценарии (хороший пример) или добавит именно тех данных, которых у нас мало
- Качество - в некоторых случаях ИИ-данные лучше, чем человеческие, по крайней мере, если не задействовать дорогих экспертов
Генерировать данные можно не только с помощью LLM:
- Генерация по правилам - берём шаблон и подставляем разные значения
- Аугментация - замена с помощью синонимов
- Симуляция - например, игровые данные
Оптимизируем инференс с умом
Оптимизировать инференс можно на трёх уровнях.
На уровне моделей самыми популярными методами являются квантизация и дистилляция. Но есть ещё группа интересных методов, связанных с главным ограничением LLM - авторегрессивный декодинг, а иначе говоря, а ничё тот факт, что мы генерируем по токену за раз.
- Спекулятивный декодинг - достаточно популярный метод, даже имплементирован в vLLM. Используем более быструю модель, чтоб предсказывать следующие n токенов, а потом в параллель проверяем основной моделью все подпоследовательности. Берём самую большую верно сгенерированную подпоследовательность.
- Инференс с референсом. Часто модели требуется процитировать документ из контекста или заменить в исходном промпте небольшой кусочек. Мы можем сильно сократить время, если будем брать эти кусочки напрямую, а не генерировать их "из памяти". Вот статья для более подробного изучения. Иногда мы хотим процитировать ответ какого-то инструмента - например, вместе с ответом вывести SQL-запрос или код, который сгенерировал предыдущий шаг. Тогда тоже лучше добавить его в стрим руками, а не просить модель скопировать его из инпута.
- Параллельный декодинг. Самая эзотерическая техника - сразу обучаем модель предсказывать несколько токенов, например, с помощью отдельных голов. Довольно сильно усложняет логику работы модели.
Многие фишки, связанные с ускорением реализованы из коробки в инференс-фреймворках - том же vLLM или Dynamo AI от NVIDIA.
Архитектура LLM-системы и фидбек от юзеров
Финальная глава посвящена постепенному усложнению LLM-системы. Итоговый вариант выглядит как-то так:

Из интересных паттернов можно отметить:
- Роутинг - выбираем, в какую модель, а, может быть, и вообще не модель отправить пользователя. Для этого обычно используется intent classifier, который определяет чего хочет пользователь.
- Гейтвей - специальный компонент, который помогает использовать разные модели, как локальные, так и облачные через единый интерфейс - к примеру, litellm.
- Кэширование - точное (если запрос пользователя полностью совпадает с более ранним, то можем сразу отдать ответ) и семантическое (значительно сложнее реализовать хорошо)
- Input/output guardrails - проверки запросов пользователей и ответов LLM на соответствие нашим критериям качества и безопасности. Много готовых валидаторов есть в проекте Guardrails AI.
Одним из самых важных компонентов LLM-системы является сбор пользовательского фидбека. Собирать лучше всё, что можно, заранее не предсказать, что будет полезно, а что нет:
- Как часто пользователи стопают генерацию
- Длина чата - как по токенам, так и по количеству раундов реплик
- Все инпуты и аутпуты модели вместе с параметрами запросов
- Динамика распределения длин инпутов и аутпутов, а также распределений токенов - меняется ли поведение пользователей или модели?
- Метрики скорости - time-to-first-token, time-per-output-token, total latency