Введение
Приветствую! Я — автор портала текстовых игр в жанре "квест" questio.ru. Основная цель проекта — полностью автоматизировать роль автора игры, исключив человеческое вмешательство. Это стало возможным с появлением доступных LLM достаточной "сообразительности".
Но, как это часто бывает, разработка быстро сместилась с решения бизнес-задач на преодоление технических трудностей. В этой статье я поделюсь опытом, который, надеюсь, поможет начинающим разработчикам избежать типичных ошибок при работе с LLM (Large Language Models).
Общие проблемы
Первое, с чего я хочу начать - озвучить очевидное: промышленный продукт, стабильно выдающий корректный результат, и тестовый стенд с "надёжностью 90%" — это, как говорится, две большие разницы. Основные препятствия на пути к стабильности:
Галлюцинации — модель придумывает лишнее или нерелевантное.
Бесконечный поток — самоповтор, ведущий к исчерпанию лимита ответа.
Проблемы с интерпретацией — непонимание сложных запросов.
Время обработки — задержки из-за сложных промтов или форматов (например, JSON).
Для достижения требуемого уровня надёжности код часто приходится усложнять, перестраивать и дополнять проверками. Рассмотрим ключевые аспекты подробнее.
Локальная модель или API: есть ли разница?
Изначально проект задумывался для локальных моделей, но проблемы с точностью промптов заставили опробовать разные API-варианты (GigaChat, ChatGPT-4o). И результаты получились не однозначные. Проблем с большими моделями меньше, но они все равно есть. И также, как и локальные модели, каждая требует индивидуальной настройки (один и тот же результат достигается в разных моделях разными промтами или, что тоже самое, один и тот же промт на разных моделях дает разный результат).
Результаты сравнений:
Галлюцинации и "сваливания в бред" остались, хотя их частота снизилась.
Локальные модели обязательно требуют дополнительных запросов для уточнения и корректировки (имеется ввиду размер 7B, модельки побольше почти не требовали корректуры).
Крупные модели лучше работают с JSON
Крупные модели лучше понимают задачу, но разница оказалась гораздо скромнее, чем ожидалось
Вывод:
Крупные модели (кроме "nano"-версий) в общем случае работают лучше, но все равно не идеально. Даже под топовые модели нужна индивидуальная подстройка и большое внимание к потенциальным ошибкам.
Для текстовых квестов принципиальной разницы между локальными и API-моделями нет. Локальные решения дешевле, но требуют больше доработок. Если есть ограничения на время разработки, данный фактор становится существенным.
Примечание: Рассуждающие модели дают качественно лучшие результаты, но их стоимость делает их непрактичными для массового применения.
Галлюцинации и как с ними бороться
Под "галлюцинациями" понимаются:
Отступление от требований промта (неточное соблюдение оформления, слишком длинный ответ при требовании краткости, упущение важных деталей или придумывание того, что не было запрошено и т.д.).
Зацикливание или артефакты в ответах (бесконечное генерирование "хвоста" одними и теми же фразами, подмешивание вставок на разных языках, специальных символов).
Методы борьбы:
Ограничение размера ответа
Если ответ совпадает с лимитом, вероятно, он неполный или невалидный.
Решение:
Повторить запрос, изменив параметрtemperature
и/или увеличив лимит. И так до тех пор, пока "упор в лимит" не исчезнет. Также иногда помогает варьирование других параметров (например, штраф за повтор).Упрощение промтов
Комплексные промты работают нестабильно: модель может забыть часть условий. Часто в этом случае помогает замена на последовательность более простых запросов - для каждого атрибута по очереди.
Пример:Плохо: "Придумай персонажа с именем, внешностью, характером и списком предметов в инвентаре."
Лучше: "Придумай имя персонажа" → "Опиши его внешность" → "Добавь 3 предмета в инвентарь."
Поддержка русского языка
Большинство открытых моделей небольшого размера обучены на одном или нескольких европейских языках. Русскоязычные тексты если и участвовали в обучении, то явно в недостаточном количестве. Поэтому русский язык в запросе - нормально, а вот в ответе - почти всегда имеет проблемы. От явных (включение иностранных слов или их частей) до малозаметных (плохо согласованные словосочетания, повторы).
Для русского языка лучше подходят отечественные модели (GigaChat, YandexGPT) или тюнингованные (Saiga), но они хуже понимают, что от них хотят. Для таких моделей требуется тщательная разработка последовательности запросов с декомпозицией изначально комплексной задачи.
Универсальные модели (например, Llama, Gemma) могут смешивать языки или выдавать некорректные формулировки, но у них лучше с понимаем требований.
Проблемы с логикой и креативностью
Сложные текстовые конструкции и неоднозначные термины часто приводят к неожиданным результатам - LLM не до конца понимает, что с чем связано, что главное, а что второстепенное и так далее. Также к проблемам "по логике" можно отнести однообразность ответов при просьбе проявить креативность или явном запрете "не используй следующие слова/названия:...".
Решения:
Сведение к бинарным ответам ("ответь только да или нет").
Использование генераторов случайных вариантов на основе заранее подготовленных списков (кодом делаем небольшую выборку, а потом просим LLM выбрать наиболее подходящий из выбранных вариантов). Пример:
Вместо "Придумай название города" → "Выбери номер из списка: 1. Ривенделл, 2. Даркхольм, 3. Берег Слёз."
Такой подход позволяет заставлять модель креативить: случайным образом выбираем какой-нибудь нетипичный вариант и заставляем LLM выкрутиться. В противном случае (если "придумай сама") часто получаем одно и тоже.
JSON or not JSON?
Как известно, все современные модели поддерживают генерацию JSON в качестве ответа. Такой способ получения данных отлично подходит для различных формализаций.
Ведь мало получить ответ от LLM, надо из него еще данные нужные выцепить. Например, при формировании игрового персонажа было бы неплохо сразу получить и его тип, и имя, и описание внешнего вида, и характер.
Однако при простой текстовой формулировке "где что" в ответе LLM понять бывает очень сложно. Формат JSON решает эту проблему, раскладывая данные по полям.
Плюсы JSON:
Структурированный вывод. Легко извлекать данные (например, для игрового персонажа).
Минусы:
JSON требует времени: гарантированный вывод в JSON не является способностью LLM, это - заслуга движка, под управлением которого LLM работает. Движок перегенерирует ответ или его часть до получения корректного синтаксиса. Это приводит к тому, что запросы с JSON могут выполняться от 1 до 15 секунд против 1-2 секунд для простого текста.
Рекомендация: Для задач, где время критично, используйте простое форматирование с проверкой полноты ответа.
Итоговые рекомендации
Минимизируйте лимиты ответа (в токенах).
Проверяйте размер ответа — если совпадает с лимитом, запрос стоит повторить.
Делайте контрольные запросы для корректировки текста.
Запрещайте лишнее: добавляйте "Не пиши комментариев и пояснений".
Дробите сложные промты на несколько простых.
Давайте выбор вместо "Придумай сам" — это повышает вариативность.
Заключение
Работа с LLM требует баланса между качеством, скоростью и стоимостью. Лучшие результаты достигаются через итерации, упрощение и строгий контроль. Надеюсь, этот опыт поможет вам в ваших проектах!