Семантические решения: модели для смысла, код для инвариантов

Симптом

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

Контекст

После нескольких итераций доработок и исправлений разрослись кодовая база, логика и набор gates. Агент начал упираться в пользовательские запросы, которые не были предусмотрены workflow. Модель не могла гибко обработать некоторые из них, застревала и повторяла попытки.

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

Корневая причина

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

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

Ошибочное предположение

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

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

Обнаружение

Хороший человек посоветовал подумать в сторону замещения части детерминированной логики работой модели.

Ранним сигналом стала стоимость изменений: каждое новое исключение требовало новой ветки, gate или состояния. Дополнительным сигналом были повторы агентского цикла на запросах, которые не были известны при проектировании workflow.

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

Исправление

Я заменил значительную часть заранее зашитой orchestration-логики на тулзы для работы с сущностями. Модель стала отвечать за интерпретацию запроса и выбор последовательности действий, а не за выполнение гарантирующих операций напрямую.

В коде остались HITL, явные переходы state machine, авторизация, валидация, транзакции, идемпотентность, лимиты и другие критические проверки. Тулзы могут предоставлять модели CRUD-операции, но сами операции по-прежнему выполняются детерминированным кодом с проверкой прав и инвариантов.

Проверка

Регрессионная проверка должна включать запросы, которые не были известны при проектировании workflow. Для каждого сценария нужно проверить:

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

Общий вывод

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

Цель не в том, чтобы заменить код моделью, а в том, чтобы не зашивать в код семантику, которую модель может гибко обработать и чьё выполнение можно проверить через инструменты и постусловия.