Перейти к содержанию
Новое AiManual теперь в MAX Подписаться
Публикация AiManual

Проектирование предсказуемых LLM-ассистентов: от принципов оркестрации до боевого продакшена

Разбираем архитектуру предсказуемых LLM-ассистентов для регулярных бизнес-задач: оркестратор против агента, модульная декомпозиция, замена моделей через абстрак

Коротко

Что будет в материале

  1. 01

    Ассистент или агент: почему для регулярных задач предсказуемость важнее автономности

  2. 02

    Архитектура оркестратора: декомпозиция на сменяемые подсистемы

  3. 03

    Продакшен-проблемы и как их решать без боли

  4. 04

    Кейс: сверка техзаданий с документацией - от идеи до production-решения

Инженерные команды, внедряющие языковые модели в регулярные бизнес-процессы, быстро упираются в дилемму: поручить задачу автономному агенту или построить контролируемого ассистента. Агент принимает решения сам - выбирает инструменты, определяет порядок действий, обрабатывает ошибки. Ассистент работает по жёстко заданному пайплайну, где каждый шаг предсказуем и проверяем. Для повторяющихся операций - сверки документов, извлечения структурированных данных, валидации контрактов - ассистент оказывается надёжнее, дешевле и прозрачнее. Эта статья разбирает архитектуру такого ассистента на реальном кейсе: автоматическая проверка соответствия технического задания проектной документации.

Ключевое отличие - детерминированность оркестрации. Ассистент не решает, что делать. Он выполняет ровно ту последовательность шагов, которую спроектировал инженер: препроцессинг, чанкинг, retrieval, генерация, постобработка, экспорт. Каждый этап изолирован, логируем и заменяем. Модель вызывается через абстрактный интерфейс - переход с Gemini на Qwen сводится к изменению одного параметра конфигурации. Сырые ответы кэшируются, что позволяет пересобрать финальный результат без повторного инференса. При сбое на одном чанке пайплайн не падает - проблемный фрагмент логируется и пропускается, а отчёт собирается из успешных частей.

Такой подход не про исследовательские задачи, где нужна креативность и нестандартные решения. Он про production-конвейер, который работает стабильно, аудируемо и дёшево. Разберём архитектурные решения по порядку.

Ассистент или агент: почему для регулярных задач предсказуемость важнее автономности

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

Агентные архитектуры дают модели свободу выбора инструментов и последовательности действий. Это мощно, когда задача плохо формализована. Но цена - непредсказуемость. Модель может пойти по неоптимальному пути, пропустить критичный шаг, сгенерировать невалидный вызов инструмента. В регулярных бизнес-процессах такая вариативность превращается в операционные риски. Подробнее о границах применимости агентов - в разборе когнитивных ловушек код-агентов, где скорость генерации кода оборачивается лавиной техдолга.

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

Критерии выбора: когда ассистент справляется лучше

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

Вторая - требование повторяемости. При сверке техзаданий с документацией бизнес ожидает, что один и тот же документ, прогнанный дважды, даст идентичный результат. Агент с элементами случайности в выборе стратегии этого не гарантирует. Оркестратор - да, потому что пайплайн детерминирован, а температура модели зафиксирована на нуле.

Третья - безопасность и аудируемость. Регуляторные сценарии требуют полной трассировки решений. В ассистенте каждый вызов модели логируется вместе с контекстом и ответом. При проверке можно восстановить, почему система пришла к конкретному выводу. В агенте цепочка рассуждений размазана по истории вызовов инструментов, и восстановить логику сложнее.

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

Архитектура оркестратора: декомпозиция на сменяемые подсистемы

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

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

Эта архитектура перекликается с подходом к управлению промптами из практических стратегий промпт-инжиниринга: системный промпт, каскадные вызовы и управление контекстом тоже выносятся в отдельные слои, а не вшиваются в код модели.

Препроцессор: подготовка данных и чанкинг

Качество retrieval напрямую зависит от того, как нарезаны документы. Неправильный чанкинг ломает поиск: релевантный фрагмент оказывается разрезан между двумя чанками, и модель не видит полного контекста. Либо наоборот - чанк слишком большой, и retrieval притягивает нерелевантные куски, раздувая промпт и сбивая генерацию.

Для сверки техзаданий с документацией применяется двухуровневая стратегия. Сначала документ разбивается по семантическим блокам: разделы, подразделы, пункты требований. Границы определяются по заголовкам и структуре документа, а не по фиксированному количеству токенов. Затем внутри каждого блока выполняется более мелкая нарезка с перекрытием в 10-15% - это страхует от потери контекста на стыках.

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

Модель: абстрактный интерфейс для легкой замены

Вендорская привязка к конкретной модели - риск, который материализуется в самый неподходящий момент. Провайдер поднимает цены, меняет API, закрывает доступ. Команда, которая зашила вызовы конкретной модели по всему коду, тратит недели на миграцию. Абстрактный интерфейс снимает эту проблему.

Паттерн прост: все вызовы к модели проходят через адаптер, который реализует единый интерфейс. Интерфейс определяет метод generate(prompt, context, options) -> RawResponse. Конкретная реализация для Gemini знает, как упаковать промпт в формат API Google и распарсить ответ. Реализация для Qwen делает то же самое для своего API. Остальной код не знает, какая модель под капотом - он работает с интерфейсом.

# Конфигурация модели - единственное место, которое меняется при миграции
model:
  provider: "qwen"  # было "gemini"
  endpoint: "https://api.qwen.example.com/v1"
  model_name: "qwen-max"
  temperature: 0.0
  max_tokens: 4096

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

Подход работает и для локальных моделей. Адаптер для vLLM или LMDeploy реализует тот же интерфейс, но ходит на внутренний эндпоинт. Выбор бэкенда для инференса - отдельная инженерная задача, детально разобранная в сравнении vLLM, LMDeploy и Triton с TensorRT-LLM.

Постпроцессор и экспорт: валидация и приведение к бизнес-формату

Сырой ответ модели - это текст, который может не соответствовать ожидаемой структуре. Модель способна вернуть JSON с ошибкой в синтаксисе, пропустить обязательное поле или обернуть результат в лишние пояснения. Постпроцессор решает три задачи: парсинг, валидацию и переспрос.

Парсинг извлекает структурированные данные из ответа. Если модель должна вернуть JSON, постпроцессор находит JSON-блок в тексте и разбирает его. Валидация проверяет схему: наличие обязательных полей, типы данных, допустимые значения. При невалидном ответе выполняется переспрос - модель получает сообщение об ошибке и требование исправить структуру. Количество переспросов ограничено: после трёх неудачных попыток чанк помечается как проблемный и передаётся на обработку по стратегии graceful degradation.

Экспорт - финальный этап, который адаптирует валидированный результат под потребителя. Для API это сериализация в JSON определённой структуры. Для базы данных - запись в таблицы с нужными связями. Для UI - подготовка markdown-отчёта с выделением расхождений. Экспорт не содержит бизнес-логики, только форматирование. При изменении требований к выходному формату меняется только этот модуль.

Продакшен-проблемы и как их решать без боли

Переход от прототипа к production-системе вскрывает проблемы, которые не видны на демо-стенде. Модель может ответить не то, сетевой вызов - упасть по таймауту, бизнес - запросить другой формат отчёта. Архитектура ассистента должна держать эти удары без деградации пользовательского опыта.

Кэширование сырых ответов: пересборка без повторных вызовов модели

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

При изменении постпроцессора или экспорта система берёт кэшированные ответы и прогоняет их через новые версии модулей. Вызовов модели - ноль. При изменении препроцессора или стратегии чанкинга хеш входных данных меняется, и кэш для этих чанков инвалидируется - модель вызывается повторно. Это даёт тонкий контроль: платим за инференс только когда реально изменились входные данные или промпт.

Проблема инвалидации кэша глубже, чем кажется. Нестабильные компоненты промпта - системные инструкции, порядок сообщений, динамически подставляемые данные - могут менять хеш даже при идентичных входных документах. Это ведёт к неоправданным повторным вызовам. Инструменты вроде Cache-Hunter, разобранные в анализе кэширования запросов к LLM, помогают отследить и устранить такие утечки.

Graceful degradation: пропуск чанка вместо падения всего пайплайна

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

Стратегия graceful degradation: каждый чанк обрабатывается в изолированном контексте. При возникновении необрабатываемой ошибки чанк логируется с полным контекстом - входные данные, промпт, сообщение об ошибке, трассировка. В итоговый отчёт проблемный фрагмент попадает с пометкой «не проверено» и ссылкой на лог для ручного разбора. Остальные чанки обрабатываются штатно.

Это критично для бизнес-сценариев. Лучше получить отчёт, покрывающий 95% документа с явно обозначенными пробелами, чем не получить ничего. Инженер или аналитик добирает оставшиеся 5% вручную, а не разгребает последствия полного отказа системы.

Синхронная архитектура для изолированных сессий

Асинхронные очереди - стандартный выбор для высоконагруженных систем. Но для сценариев вроде сверки одного документа по запросу пользователя синхронный подход даёт преимущества в простоте и отлаживаемости. Запрос пришёл, пайплайн отработал, результат вернулся. Нет промежуточных состояний, нет гонок, нет необходимости управлять очередями и ретраями на уровне инфраструктуры.

Изоляция сессий достигается естественно: каждый запрос обрабатывается в своём потоке или процессе, со своим контекстом и историей вызовов. Данные разных пользователей не пересекаются. Мониторинг сводится к трём метрикам на каждый запрос: время ответа, статус завершения, количество проблемных чанков. Отладка - к воспроизведению конкретного запроса с теми же параметрами.

Для пакетной обработки синхронный подход масштабируется горизонтально: запускается N воркеров, каждый тянет следующий документ из очереди и обрабатывает синхронно. Это проще, чем распределённая асинхронная система с ручным управлением стейтом, и достаточно для объёмов в десятки тысяч документов в сутки.

Кейс: сверка техзаданий с документацией - от идеи до production-решения

Бизнес-задача: заказчик передаёт техническое задание на разработку, подрядчик - проектную документацию. Нужно проверить, что все требования из ТЗ покрыты в документации, а реализации не противоречат заявленным спецификациям. Ручная сверка одного комплекта занимает у инженера 4-6 часов. При потоке в 50 комплектов в месяц это 200-300 человеко-часов - два полных FTE только на проверку.

Решение построено на описанной архитектуре оркестратора. Препроцессор принимает PDF техзадания и документации, извлекает текст, очищает от артефактов вёрстки. Чанкинг нарезает ТЗ на отдельные требования - каждый чанк содержит одно проверяемое утверждение. Документация разбивается по разделам, соответствующим архитектурным блокам системы.

RAG-поиск для каждого требования находит top-3 релевантных раздела документации. Модель получает промпт: «Дано требование ТЗ и фрагменты документации. Определи, покрыто ли требование, есть ли расхождения, и приведи цитаты из документации». Ответ - структурированный JSON с вердиктом и обоснованием. Постпроцессор валидирует JSON, агрегирует результаты по всем требованиям и формирует сводный отчёт с метриками покрытия.

Метрики на пилотной выборке из 30 комплектов: точность определения покрытых требований - 92%, пропуск критических расхождений - менее 1%. Время обработки одного комплекта - 3-4 минуты. Кэширование сырых ответов позволило трижды менять формат отчёта по запросу заказчика без единого дополнительного вызова модели. Пропуск проблемных чанков сработал на 2% требований, где модель стабильно возвращала невалидный JSON из-за сложной табличной структуры в документации - эти случаи ушли на ручную проверку, не заблокировав обработку остальных 98%.

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

Синхронная архитектура с пулом воркеров обрабатывает 50 комплектов в час на четырёх инстансах. Отказоустойчивость проверена: при падении API провайдера модели необработанные документы остаются в очереди, после восстановления сервиса обработка продолжается без потери данных. Полный лог каждого вызова модели доступен для аудита - заказчик может проверить обоснование любого вердикта вплоть до исходного промпта и ответа.

Подписаться на канал