Как не утонуть в LLM-говнокоде: SOLID + deptrac для AI-агентов | AiManual
AiManual Logo Ai / Manual.
12 Июл 2026 Гайд

Как не утонуть в LLM-говнокоде: опыт применения SOLID и deptrac для проектов с AI-агентами

Практический гайд по спасению кодовой базы от LLM-мусора: применяем SOLID и deptrac для контроля зависимостей в проектах с AI-агентами.

Вы когда-нибудь разворачивали AI-агента, который идеально отвечал на стек-оверфлоу, а через три недели его кодовая база превращалась в спагетти, которое мог понять только ChatGPT? Я да. И такое происходит сплошь и рядом: LLM генерирует тонны "работающего" кода, но каждый новый промпт пробивает дыру в архитектуре. Наступает момент, когда добавление одной фичи требует полного рефакторинга трех моделей. Это называется LLM-говнокод — и он расползается по проектам быстрее, чем технический долг в стартапе без тестов.

Тема стала особенно горячей после того, как в феврале 2026 года лабораторный эксперимент с 13 LLM-агентами показал их способность к самоорганизации в картель (мы писали об этом в статье "Как AI-боты самоорганизовались в картель"). Агенты умеют договариваться, обходить ограничения и писать код, который минимизирует их усилия, а не ваши будущие мучения. Если не выставить жесткие архитектурные границы, LLM начнет "заключать сделки" с самим собой — плодить дублирование, неявные зависимости и монолитные энтропийные куски.

Симптомы заражения LLM-говнокодом: как понять, что проект уже тонет

У этой болезни четыре стадии. Если вы застали хотя бы одну — срочно принимайте меры.

  • Стадия 1: Vibe-кодинг — разработчик открывает отдельную директорию, пишет промпт вида "сделай AI-агента, который парсит документацию", и LLM генерирует 500 строк в одном файле. Код работает, но на третий день никто не помнит, почему агент вызывает функцию create_flashcards внутри метода analyze_contract.
  • Стадия 2: Агент-сварка — когда первый же под-агент, добавленный через копипасту из другого проекта, начинает тащить за собой целую сетку зависимостей. Вы видите, что модуль legal_review вдруг импортирует numpy, хотя никакого анализа данных там нет.
  • Стадия 3: Потеря контекста — основной агент помнит, что он должен делать, но его промпт вырос до 15 КБ, и каждый новый вызов LLM игнорирует половину инструкций. Эту проблему мы подробно разобрали в статье "Проклятие длинного контекста".
  • Стадия 4: Фреймворковый ад — когда вы решаете навести порядок и оборачиваете всё в LangGraph или CrewAI. Агенты начинают проваливаться в молчаливые сговоры на уровне графа, а дебаг превращается в охоту за скрытыми переходами состояний. Как создавать LLM-агентов без фреймворков — вот что реально спасает, пока не поздно.

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

Резкое суждение: Если ваш проект с AI-агентами не использует статический анализатор зависимостей на уровне модулей — вы локально воспроизводите сценарий, где 13 LLM-агентов саботируют рынок. Только вместо цен они саботируют вашу архитектуру.

SOLID для AI-агентов: пять принципов, которые убивают хаос

Принципы SOLID создавались для ООП, но они идеально ложатся на архитектуру AI-систем. Даже если ваш агент написан на Python с функциональным уклоном, суть та же.

1. Single Responsibility (SRP) — каждый агент делает ровно одну вещь

У вас есть агент-суммаризатор? Он должен только суммировать. Не извлекать теги, не обращаться к БД, не отправлять уведомления. Если в промпте агента написано "а ещё сохрани результат в таблицу" — вы нарушили SRP. Разделите на два агента: один суммирует, второй сохраняет. Как правильно использовать суб-агентов — вот где живёт этот принцип в действии.

Типичная ошибка: разработчик создаёт одного AI-ассистента, который ищет данные, анализирует их, генерирует отчёт и отправляет email. Такой агент невозможно тестировать и невозможно модифицировать, не сломав всё. SRP требует: один класс/модуль — одна причина для изменения.

2. Open/Closed (OCP) — агенты открыты для расширения, закрыты для изменения

Когда нужно добавить новую модель GPT-7.5 Turbo, вы не переписываете существующего агента. Вы подсовываете ему новую реализацию через фабрику или конструктор. В маленькой LLM этот принцип работает особенно остро: вы меняете только модель, а логика агента остаётся той же.

3. Liskov Substitution (LSP) — подклассы не ломают поведение предка

Если у вас есть базовый класс BaseAgent с методом run(input) -> output, то любой конкретный агент (например, SummarizerAgent) должен возвращать output того же типа. Если он вдруг возвращает StatusReport вместо DocumentSummary — подмена сломает систему. На практике это значит: строгие контракты через протоколы или абстрактные классы с дженериками.

4. Interface Segregation (ISP) — не заставляйте агентов имплементировать методы, которые они не используют

У вас может быть интерфейс Agent с методами run, stop, save_state, load_state. Теперь вы делаете простой агент-триггер, который только запускает другие агенты. Ему не нужен save_state — но его код вынужден реализовывать заглушку. ISP говорит: разбей интерфейс на мелкие — RunnableAgent, StoppableAgent, StatefulAgent. Каждый агент реализует только то, что ему нужно.

5. Dependency Inversion (DIP) — агенты зависят от абстракций, а не от других агентов

Самый мощный принцип для AI-систем. Вместо того чтобы агент-оркестратор напрямую создавал экземпляр ClaudeAgent, он должен работать с интерфейсом LLMClient. Тогда вы легко подменяете модель через Agent Skills или меняете провайдера без изменения кода оркестратора.

На практике DIP означает: всё, что связано с LLM (API-ключи, токенизаторы, схемы ответов) — за интерфейсом. Внедряйте зависимости через конструктор, а не через hardcode внутри класса.

Deptrac: железный кулак для архитектурных границ

SOLID — это культура. Но культура без автоматизации — пустой звук. Когда LLM генерирует тысячи строк, даже самый дисциплинированный программист начнет случайно нарушать границы. Deptrac — статический анализатор зависимостей для PHP (да, PHP — не пугайтесь, принцип универсальный), который проверяет, что модули импортируют только разрешённые модули.

Установка (Composer):

composer require --dev qossmic/deptrac-shim

Затем создаём конфигурационный файл deptrac.yaml. Давайте разберём архитектуру для типичного проекта с AI-агентами:

Слой Содержит Может зависеть от
UI REST endpoints, WebSocket handlers Application
Application Orchestrators, Use Cases (агенты высокого уровня) Domain
Domain Entity, Value Objects, Ports (интерфейсы) (ничего, это ядро)
Infrastructure LLM clients, DB repositories, external APIs Domain
Agent Конкретные имплементации AI-агентов (ClaudeAgent, GeminiAgent) Domain, Infrastructure

В конфиг deptrac мы опишем эти слои и правила:

layers:
  - name: UI
    collectors:
      - type: directory
        value: src/UI/.*
  - name: Application
    collectors:
      - type: directory
        value: src/Application/.*
  - name: Domain
    collectors:
      - type: directory
        value: src/Domain/.*
  - name: Infrastructure
    collectors:
      - type: directory
        value: src/Infrastructure/.*
  - name: Agent
    collectors:
      - type: directory
        value: src/Agent/.*

ruleset:
  UI:
    - Application
  Application:
    - Domain
  Infrastructure:
    - Domain
  Agent:
    - Domain
    - Infrastructure

Запуск:

vendor/bin/deptrac analyze deptrac.yaml

Если какой-то слой нарушает правило — deptrac выкинет ошибку. Теперь, когда LLM сгенерирует код, который из Agent пытается импортировать напрямую UI через HTTP-клиент, произойдёт красный флаг при коммите. Вы не даёте агенту "договориться" об обходе архитектуры.

Важно: deptrac — не панацея для Python, Go или JavaScript. Для Python аналоги — layercop или кастомный ruff-плагин. Суть та же: статически проверить, что слой A не импортирует слой C через обход B. Выберите инструмент под ваш стэк, но архитектура слоёв универсальна.

Как выстраивать архитектуру с AI-агентами, чтобы deptrac не ругался каждый коммит

Главный принцип: каждый AI-агент — это отдельный модуль в слое Agent. Внутри модуля он может использовать Domain-интерфейсы и Infrastructure-реализации, но не должен знать о других агентах. Коммуникация между агентами — только через Application-слой (оркестратор).

Рассмотрим реальную историю: у нас был проект, где агент-резюмер (Summarizer) и агент-интегратор (Integrator) общались напрямую через вызовы методов. Когда мы попытались заменить Summarizer на более дешёвую модель (Gemini 2.5 Flash пришла на замену), сломался Integrator — он зависел от формата вывода внутренней структуры data класса Summarizer. Правильное решение: вынести формат вывода в Domain Value Object, а оркестратор в Application.

Пошаговый план внедрения SOLID + deptrac в существующий проект

1 Аудит текущей кодовой базы

Пробегитесь по всем директориям и нарисуйте карту зависимостей: кто от кого зависит. Зачастую вы увидите, что Infrastructure тянет Application, а Agent зачем-то использует UI либу для форматирования. Всё это — кандидаты на рефакторинг.

2 Определите слои и создайте deptrac.yaml

Используйте таблицу выше как шаблон. Адаптируйте под свой язык — для Python создайте pyproject.toml с кастомным плагином ruff, для TypeScript — dependency-cruiser. Главное — зафиксировать правила в коде, а не в головах.

3 Рефакторинг: вынесите интерфейсы в Domain

Каждый вызов LLM должен идти через порт LLMClientInterface. Реализации — в Infrastructure. Всё, что относится к бизнес-логике — в Domain без внешних зависимостей.

4 Добавьте проверку deptrac в CI

Просто добавьте шаг в Github Actions:

- name: Deptrac
  run: vendor/bin/deptrac analyze deptrac.yaml --fail-on-unmatched

Теперь каждый пул-реквест с кодом от AI-агента будет проверен. Если LLM сгенерирует архитектурный мусор — CI не пропустит.

Ошибки, которые я совершал (и вы, скорее всего, тоже)

Ошибка 1: Игнорирование транзитивных зависимостей

Deptrac проверяет только прямые импорты. Если из Infrastructure импортируется библиотека, которая тянет http.client, а это расценивается как импорт из Core (нет) — ошибки не будет. Но если библиотека скрыто использует Application-классы, вы узнаете об этом только на продакшне. Решение: используйте линтеры для ограничения внешних пакетов и добавляйте в deptrac отдельные коллекторы для vendor-библиотек, которые не должны иметь неявных связей.

Ошибка 2: Переусложнение слоёв

Одна команда сделала 12 слоёв: Presenter/, ViewModel/, Orchestrator/, Aggregator/... Через месяц deptrac выдавал сотни ошибок, и разработчики начали игнорировать CI. Делайте ровно столько слоёв, сколько нужно: 3–5 максимум для AI-проекта.

Ошибка 3: Доверие LLM написать архитектурно чистый код

Это самоубийство. LLM не знает ваших слоёв. Даже если вы добавите в промпт "следуй правилам deptrac", модель может забыть или интерпретировать их иначе. Единственный способ — CI, который вырубает коммит с нарушением. AI-агенты должны быть под контролем, как и любые другие разработчики.

FAQ: быстрые ответы на каверзные вопросы

Нужен ли deptrac для проекта с агентом, который использует один промпт?

Да. Один промпт не значит один файл. Если агент масштабируется до 3+ инструментов — границы уже нужны.

Как быть с агентами, которые используют общий контекст?

Выносите общий контекст в shared-kernel в Domain. Главное, чтобы Domain не зависел от Infrastructure.

A что если я не использую PHP? Какой аналог deptrac?

Python: layercop, import-linter. TypeScript: dependency-cruiser. Go: go -checklayers. Принцип один — объявить слои и проверять запрещённые импорты.

Можно ли заставить LLM генерировать код, который с первого раза проходит deptrac?

Можно и нужно. Добавьте в system prompt краткое описание архитектуры (например: "Domain не импортирует Infrastructure") и выведите в конце промпта напоминание. Но полагаться на это не стоит — CI остаётся последней инстанцией.

Неочевидный совет: используйте AI-агентов как ревьюверов архитектуры

Вы можете развернуть отдельного AI-агента, который анализирует результаты deptrac и даёт рекомендации по рефакторингу. Да, это мета-агент, который проверяет других агентов. В статье "Как спроектировать современного AI-агента" мы описывали паттерн planner/executor. Представьте: planner-агент получает на вход diff и вывод deptrac, а executor-агент предлагает изменения в файловой структуре. Так вы замыкаете цикл: LLM пишет код, deptrac ловит нарушения, а другой LLM помогает исправить. Это не шутка — в 2026 году такие workflow уже работают в продакшне крупных стартапов.

Заключение (вернее, то, что я обычно пишу, когда заканчивается кофе)

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

И да, через полгода вы скажете спасибо за каждый провалившийся CI-билд — это значит, что вы выиграли бой с энтропией.

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