LLM - мощный инструмент, но его эффективность в продакшене зависит не от одного «хитрого промпта», а от всей архитектуры: что мы даём модели, как управляем её рассуждением и как проверяем/обрабатываем результат. В этой статье - компактная карта паттернов, разбитая по этапам конвейера: Input -> Reasoning -> Output.

Введение

Статей про LLM - вагон, и у всех свои "трюки". Мне не хватало схемы, которая раскладывала бы эти "трюки" по полочкам.

Это моя попытка такую схему составить. Я сгруппировал основные известные мне паттерны по их месту в конвейере обработки запроса. Это не истина в последней инстанции, а просто карта местности, какой ее вижу я.

С чем мы работаем? Наши инструменты.

В центре всего стоит LLM. Ее задача - предсказывать следующее слово на основе предыдущих. Мы даем ей текст, а она, токен за токеном, его продолжает.

Но есть важный нюанс: LLM на выходе дает не сам токен, а распределение вероятностей по всему своему словарю. А уже отдельный алгоритм сэмплирования (т.е. выбора следующего токена) решает, какой токен выбрать из этого распределения. В простейшем случае мы всегда берем самый вероятный (greedy-подход), но чаще всего мы управляем процессом с помощью параметров:

  • temperature: Наша "ручка креативности". Низкая температура (близкая к 0) делает ответы более предсказуемыми и фактическими, высокая - более разнообразными и случайными.

  • top_p / top_k: Ограничивают выборку только самыми вероятными токенами, отсекая "хвост" распределения.

Но самый мощный рычаг управления генерацией это Structured Outputs. Вместо того чтобы просить LLM вернуть JSON и потом молиться, чтобы он был валидным, мы вмешиваемся в сам процесс сэмплирования.
На каждом шаге, перед выбором следующего токена, мы применяем "маску", которая на лету запрещает все токены, нарушающие нашу заранее определенную структуру. Модель хотела бы сгенерировать что-то не то, но мы ей просто не позволяем.

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

Мы увидим дальше, как этот низкоуровневый механизм используется в сильных паттернах.

Еще один инструмент в нашем ящике это модели-эмбеддеры. В отличие от генераторов, они не пишут текст, а "понимают" его. Они превращают фрагмент текста в числовой вектор (эмбеддинг) так, чтобы похожие по смыслу тексты имели похожие векторы. Это позволяет нам сравнивать текст по смыслу.

Входные данные

Все начинается с контекста. Что мы "покажем" модели перед тем, как она начнет генерировать ответ? От этого зависит 90% успеха. На этом этапе мы формируем входные данные, которые получит LLM.

Промптинг

Это базовый уровень. Тут мы ставим задачу для LLM. В коде промпт обычно выглядит как большая строка, часто с плейсхолдерами для переменных. Перед отправкой в модель мы форматируем этот шаблон, подставляем в него запрос пользователя, найденные через RAG документы или примеры хороших ответов (few-shot prompting).
Именно здесь мы формулируем задачу для LLM, указываем стиль ответа, формат вывода.

Хороший материал на эту тему: https://www.promptingguide.ai/

RAG

Retrieval Augmented Generation - генерация дополненная поиском. Выполняем поиск информации, добавляем ее в контекст, генерируем ответ на основе найденной информации.
Тема очень популярная, различных подходов и хитростей тут много, можно набрать на еще одну статью.

Memory

Цель - дать LLM память, хотя бы в рамках одного диалога.
Возможные подходы:

  • Держать в контексте полную историю диалога - вычислительно дорого, длина ограничена.

  • Использовать скользящее окно диалога с несколькими последними сообщениями.

  • Сжатие диалога - иногда просим LLM сжать историю сообщений.

  • Дополнительные системы - например, даем LLM возможность сохранять факты о пользователе, как в фиче "Память" у веб версии ChatGPT. Пример "дополнительной системы" для памяти - https://arxiv.org/abs/2501.13956

Управление мышлением модели

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

Декомпозиция задачи

Частая проблема при решении сложных задач - модель пытается ответить сходу и ошибается. Мы можем разбить задачу на небольшие шаги. Самый простой вариант - Chain of Thought prompting (CoT, цепочка мыслей) . Мы просто добавляем инструкцию вида "думай по шагам".
Другой вариант - мы вручную прописываем для модели жесткий план, которому она должна следовать при рассуждении.

Агенты и инструменты

Модель следует не заданному плану решения, а строит его сама, в реальном времени. Агент - это LLM, у которой есть доступ к набору инструментов (Tools), например:

  • Поиск в интернете

  • API вашей CRM

  • Интерпретатор кода

  • Вызов другой LLM системы Сам механизм, который позволяет модели "вызывать" различные инструменты называется Function Calling. Например: ReAct промптинг - просим модель сначала порассуждать, а затем вызвать подходящую функцию. (Вызов функции может выглядеть как сигнатура python функции в ответе модели, или как особый токен при генерации) Несколько агентов можно объединять в одну мультиагентную систему для совместного решения задач.

Routing

Простой, но полезный паттерн. Просим LLM быть диспетчером, чтобы решить по какой ветке логики отправить запрос дальше.
Например: у нас в RAG сценарии две разных базы знаний и нам нужно выбрать релевантную.

Schema Guided Reasoning

Это не столько отдельный паттерн, сколько дополнительный инструмент который прокачивает все остальные паттерны.
Суть Schema Guided Reasoning (SGR) - в использовании Structured Outputs не просто для форматирования финального ответа, а для управления самим процессом мышления модели. Мы описываем логику рассуждений в виде строгой схемы, например Pydantic-класса, и модель вынуждена заполнять схему шаг за шагом. Это гарантирует нам, что все этапы рассуждений будут пройдены, ничего не будет пропущено.
Используя Enum, вложенные классы и даже динамическое создание классов мы можем упаковывать сложные цепочки рассуждений в один запрос к LLM.
Подробнее: https://abdullin.com/schema-guided-reasoning/patterns

Выход: работаем с результатом генерации

Форматирование и валидация

Просим LLM указывать результат работы в определенном формате, например XML или JSON. (Для JSON можно использовать Structured Outputs чтобы гарантировать корректность)

В случае когда не получилось распарсить ответ, можно использовать исправляющий промпт вида "твой ответ дал ошибку: {ошибка}, исправь форматирование своего ответа..."

Guardrails

Ответ может быть идеальным по форме, но недопустимым по содержанию. Guardrails это фильтр безопасности, который может работать:

  1. На входе - опасные запросы от пользователя

  2. На выходе - недопустимые ответы нашей системы Может быть реализовано разными способами:

  • Обычные regex фильтры

  • Использование специализированных моделей-классификаторов (например Llama Guard)

  • Использование второй LLM для оценки ответа

Caching

Если мы получаем запрос на который уже отвечали ранее, то ответ можно просто достать из кеша. Это экономит время и вычисления. Кеширование может быть как на основе полного совпадения строки, так и на основе семантического сходства, для этого используются эмбеддинги запросов.
Пример - https://github.com/zilliztech/gptcache

Жизненный цикл

Тут не про обработку одного запроса, а про принципы разработки системы вцелом.

Observability (наблюдаемость)

Нельзя починить то, что невидно. Observability - возможность заглянуть под капот приложения. Не просто запрос и финальный ответ, а весь путь:

  • В какую подсистему Router отправил запрос?

  • Какие документы извлек RAG?

  • Какие "мысли" думал агент когда выбирал инструмент?

  • Сколько времени и токенов понадобилось на каждом шаге? Это основа для отладки и мониторинга, а также главный источник данных для Data Flywheel. Инструмент: https://phoenix.arize.com/

Evaluation (Оценка)

"Как понять что мои изменения не сломали то, что уже работало?" - Вместо ручной проверки "на глазок", мы создаем наборы данных и прогоняем их через систему, замеряя метрики. Самый мощный подход здесь это LLM-as-a-Judge. Для оценки качества ответа нашей системы мы используем более мощную LLM, которая сравнивает результат с эталоном или оценивает по заданным критериям.

Fine-tuning (Дообучение)

Иногда промптов и RAG недостаточно, чтобы добиться нужного поведения системы. Fine-tuning это процесс дополнительного обучения LLM модели на вашем собственном датасете.

Data Flywheel (Маховик данных)

Система должна самосовершенствоваться. Цикл выглядит так:

  • Пользовали взаимодействуют с системой

  • Мы собираем запросы, ответы, обратную связь (лайки, дизлайки, копирование ответа, правки)

  • Эти данные становятся "топливом" - мы используем их для создания тестовых и обучающих датасетов

  • Обновленная, более умная система выкатывается в продакшен И так по кругу.

Заключение

Я попробовал описать паттерны работы с LLM так, как они выглядят с моей позиции. Я уверен, что далеко не все расписал идеально, это скорее приглашение к диалогу, чем готовая классификация.

Какими паттернами пользуетесь вы? Чего не хватает/лишнее в моей схеме? Жду ваших мнений в комментариях.

Комментарии (1)


  1. zarfaz
    05.10.2025 10:54

    Круто. Очень круто. Я бы с удовольствием почитал бы примеры/гайды по реализации подключения апи которые позволяют на ленту во время мыслительного процесса дозапрашивать информацию или корректировать структуру ответа