Нам не нужно было начинать с большой стратегии. Мы просто хотели ускориться.
У нас были API, Swagger-спецификации и много повторяющейся работы; валидация, mock-данные, тестовые нагрузки. И подумали, «Почему бы не позволить LLM handles это?» и начали POC на этом. Это звучало правильно. Иonestly, в начале это работало. Мы дали ему спецификацию, попросили LLM сгенерировать нагрузки, валидировать входные данные, даже симулировать потоки. Выход выглядел чистым. Демонстрации шли гладко. Everyone был впечатлен.
Затем мы попробовали использовать его в реальном потоке. Это где-то начали получаться проблемы. Система не разбилась. Это было бы проще. Вместо этого вещи проваливались в маленьких способах. Одно запрос будет пройти, другое будет провалиться. Тот же самый структурный, тот же API, но слегка разные значения. Журналы не показывали ничего очевидного. Нет четкой ошибочной схемы. Просто неоднородность.
Одно пример остался у меня. Мы генерировали mock-данные для API спора. LLM читал OpenAPI-спецификацию и производил запросы нагрузки. Сначала взгляд, все выглядело нормально. Но когда мы действительно отправили эти запросы: Некоторые поля не соответствовали точным форматам. Enum значения были «близки», но не правильные. Требуемое поле случайно пропадало. IDs, которые должны были совпадать по вызовам, не выстраивались. Nothing было совсем не правильно. Но Nothing было надежным.
Мы попробовали исправить его очевидным способом. Лучшие запросы. Больше инструкций. Больше примеров. Стригче правила форматирования. Это улучшило вещи немного. Но не достаточно. Потому что проблема не была в запросе. Проблема была в том, что мы ожидали, что вероятностная система будет вести себя как детерминированная. И это не работает.
Это когда мы изменили подход. Мы перестали просить LLM сгенерировать конечные выходы. Вместо этого мы позволили коду взять контроль. Мы перешли к простой идее. Давайте пусть код делает то, что хорошо у него получается, а LLM делает только то, что хорошо у него получается.

Для того же проблемы mock-данных поток стал совсем другим. Сначала мы парсировали OpenAPI-спецификацию с помощью кода. Мы извлекали необходимые поля, типы, enum, все. Затем мы маппировали поля с помощью простой логики.
If it’s a name → use faker. \n If it’s an email → generate a proper email. \n If it’s an enum → pick from exact values, no guessing. \n If it’s an amount → keep it within a defined range.
Никаких сюрпризов. Только когда что-то не соответствовало, как weird custom поле мы просили LLM помочь. Мы держим LLM от того, чтобы он решал все, просто чтобы заполнить пробелы. Затем мы добавили строгую валидацию перед тем, как что-то продвигалось дальше. Если что-то не соответствовало схеме, оно было отклонено сразу. Без молчаливых провалов.
Разница была очевидной. Mock-данные стали надежными. Провалы стали предсказуемыми. Отладка снова стала простой. Мы больше не гадали, что система будет делать.
Большая перемена не была технической, но подхода. Мы перестали считать LLM системой. Это не так. Мы использовали его как только что компонент. Полезный, но не что-то, на которое вы полностью доверяете.
LLM хороши для понимания грязных входных данных. Они не хороши для соблюдения правил.
Системам нужны правила. Это где-то код приходит. Итак, теперь мы думаем об этом просто. Если что-то нужно быть правильным каждую раз, оно попадает в код. Если что-то нужно интерпретировать или гибкость, мы позволяем LLM handle это. Это все.
LLM-first выглядел хорошо в демонстрациях. Code-first работал в производстве. Это разница, которая имела значение для нас.