Агентный пайплайн аналитика: от интервью до C4-модели одной цепочкой
Системный анализ — это не про рисование схем и не про заполнение шаблонов ТЗ. Это про цепочку преобразований: смутные ожидания заказчика → структурированные требования → доменная модель → архитектурная схема. Каждый этап съедает часы, и на каждом этапе аналитик переключает контекст — из режима «слушаю и задаю вопросы» в режим «формализую» и потом в режим «проектирую».
Агентный пайплайн собирает эти этапы в сквозную цепочку. Не набор изолированных AI-помощников, а конвейер: один агент передаёт результат следующему, а между ними — гейты валидации, которые отбрасывают брак и отправляют на итерацию. На выходе — готовая C4-модель с уровнями Context, Container и Component плюс структурированные ADR-заметки. Всё, что обычно занимает две недели встреч и правок, ужимается в несколько прогонов за вечер.
Я собрал такой пайплайн на одном из проектов — страховая автоматизация, три стейкхолдера с противоречивыми ожиданиями. Разбираю по шагам.
Пайплайн целиком: три агента, три гейта
Сначала схема, потом детали по каждому агенту.
Синие — исходные артефакты (интервью). Фиолетовый — предобработка данных. Зелёные — агенты, каждый решает свою задачу. Жёлтые ромбы — гейты валидации: без них пайплайн исправно генерирует чушь и не морщится. Серый — финальный артефакт, документация, которую можно нести архитектору.
Петли обратной связи («Итерация») возвращают результат агенту на доработку. Это не дефект проектирования, а ровно то, что происходит в голове у живого аналитика: написал → перечитал → нашёл дыру → дописал. Агенты делают то же самое, только быстрее и без кофе.
Гейты — это главное
Без валидационных гейтов агентный пайплайн деградирует до генератора текста без тормозов. LLM охотно выдаёт правдоподобный ответ на что угодно — и если не проверять промежуточные результаты, ошибка с первого этапа размножается на всех последующих. Вы получаете красивую C4-диаграмму, которая описывает несуществующую систему.
Гейт — это либо ещё один вызов LLM с ролью «критика» (промпт: «Найди противоречия в этих требованиях»), либо жёсткое правило (проверка структуры JSON, наличие обязательных полей). Комбинация того и другого работает надёжнее чистого LLM-ревью: формальные проверки отсекают синтаксис, смысловые — семантику.
У меня на проекте без гейтов агенты в 3 случаях из 10 придумывали сущности, которых нет в требованиях. Просто потому, что «в страховой системе всегда есть модуль расчёта премии». С гейтами — примерно 1 случай, и тот ловится на этапе ревью модели.
Агент 1: от аудиозаписи к структурированным требованиям
Первый этап — превратить часовой разговор со стейкхолдерами в список требований, где каждое имеет источник (кто сказал), приоритет и связь с другими. Вручную это часа три-четыре: расшифровка, группировка, чистка повторов, перевод с «нам бы хотелось чтобы как-то» на «система должна».
Агент делает это за пару прогонов. Схема простая:
- На входе: аудиозапись или текстовая расшифровка интервью (Whisper или аналог справляются с транскрибацией).
- Промпт: «Извлеки из интервью функциональные и нефункциональные требования. Для каждого укажи: источник (стейкхолдер), тип (ФТ/НФТ), приоритет. Сгруппируй по доменным областям. Отметь противоречия — если стейкхолдер А говорит X, а стейкхолдер Б говорит Y по одному вопросу.»
- Формат выхода: структурированный JSON. Это критично — следующий агент парсит JSON, а не свободный текст. Если агент 1 выдал «размышлизмы» вместо полей — вся цепочка ломается. Поэтому в системном промпте жёстко: «Ответ — только JSON. Без комментариев, без markdown-обёртки.»
Я долго подбирал структуру JSON. Остановился на такой:
\{ "requirements": [ \{ "id": "FR-001", "type": "functional", "source": "Главный андеррайтер", "text": "Система автоматически рассчитывает базовый тариф", "priority": "high", "domain": "андеррайтинг", "related_to": [] \} ], "conflicts": [ \{ "req_a": "FR-001", "req_b": "FR-007", "description": "Андеррайтер хочет автоматический расчёт, compliance — ручное подтверждение" \} ], "missing_context": [ "Не указан SLA на расчёт тарифа" ]\}Если вы ещё не пробовали гонять интервью через LLM, есть смысл начать с отдельного поста про превращение интервью в структурированные требования — там про промпты и нюансы извлечения. Я здесь фокусируюсь именно на цепочке.
Первый гейт: проверяем JSON на валидность, считаем количество требований (если меньше пяти при часовом интервью — агент схалтурил), сравниваем список стейкхолдеров в источнике со списком участников интервью (все ли услышаны?). Если гейт не прошёл — дорабатываем промпт и запускаем агента заново.
Агент 2: доменная модель из требований
Когда список требований готов, второй агент строит доменную модель. Это уже не линейный текст, а граф: сущности предметной области, их атрибуты и связи.
Промпт агенту выглядит так:
Построй доменную модель на основе требований.Выдели:- Сущности (Entities) и их атрибуты- Value Objects (неизменяемые объекты-значения)- Агрегаты (группы сущностей с общим корнем)- Связи между сущностями (один-ко-многим, многие-ко-многим)- Бизнес-инварианты (правила, которые не могут быть нарушены)
Для каждой сущности укажи, из какого требования она следует (ID из FR-NNN).Формат ответа: JSON.Почему важно требовать привязку к ID требований: если агент придумал сущность, которой нет в исходном списке FR — это видно сразу. Гейт 2 сравнивает идентификаторы и отбрасывает «галлюцинированные» сущности.
Пример выхода агента (кусок JSON):
\{ "entities": [ \{ "name": "Договор страхования", "type": "aggregate_root", "attributes": ["номер", "дата_начала", "дата_окончания", "статус"], "from_requirements": ["FR-001", "FR-003", "FR-012"], "invariants": ["Дата окончания > дата начала"] \} ], "value_objects": [ \{ "name": "Страховая сумма", "attributes": ["валюта", "значение"], "from_requirements": ["FR-004"] \} ], "relationships": [ \{ "from": "Договор страхования", "to": "Клиент", "type": "many-to-one", "description": "Договор привязан к одному клиенту" \} ]\}Агрегаты агент выделяет сам — по косвенным признакам в требованиях (транзакционная целостность, ссылочная целостность). Это не всегда точно, поэтому после второго гейта я обычно прогоняю модель глазами. Но 70% времени агент попадает правильно — для стартового наброска более чем достаточно.
Агент 3: от доменной модели к C4-диаграммам
Третий агент берёт доменную модель и строит C4: уровни Context, Container и Component. Это переход от «что система делает» к «как она устроена».
Промпт — самый объёмный из трёх:
Построй C4-модель на основе доменной модели.
Уровень 1 — Context:- Система как «чёрный ящик» в окружении пользователей и внешних систем.- Для каждого внешнего актора — тип (пользователь/система) и характер взаимодействия.
Уровень 2 — Container:- Разбей систему на контейнеры (сервисы, БД, очереди, файловые хранилища).- Для каждого контейнера — технологический стек и зона ответственности.
Уровень 3 — Component:- Для каждого контейнера — ключевые компоненты и их интерфейсы.
Формат: JSON с полным описанием каждого уровня + Mermaid-код для каждой диаграммы.Mermaid-код, который генерирует агент, я валидирую отдельным шагом — прогоняю через Mermaid-рендерер. Если синтаксис ломается — фидбек агенту с текстом ошибки. Обычно хватает одной итерации.
Что агент делает хорошо: связывает сущности из доменной модели с компонентами на уровне Component. Правило «одна Bounded Context → один контейнер» он держит стабильно. Что делает плохо: путает синхронные и асинхронные связи между контейнерами. Если в требованиях нет явного упоминания «событие» или «асинхронно» — агент по умолчанию рисует REST-вызовы. Я добавляю в промпт отдельную инструкцию: «Связи между контейнерами разных bounded context — по умолчанию асинхронные, через брокер сообщений». Помогает в 80% случаев.
Третий гейт проверяет полноту: все ли сущности доменной модели покрыты на уровне Component? Не появилось ли контейнеров, которых нет в модели? Соответствует ли количество внешних систем списку интеграций из требований?
Оркестрация: как агенты передают друг другу данные
Протокол передачи между агентами — JSON фиксированной схемы. Никакого свободного текста, никаких «резюме» на естественном языке. Почему:
- Агент 1 передаёт агенту 2 требования → второй парсит их как структуру, а не интерпретирует текст. Интерпретация текста на каждом шаге накапливает ошибки, как испорченный телефон.
- Гейты между агентами проверяют не «похоже ли это на правду», а конкретные поля: количество требований, покрытие ID, наличие обязательных атрибутов.
- Если агент 3 сломался, я могу перезапустить только его — на вход подать доменную модель из кэша, а не гонять всю цепочку заново.
Это банальная вещь — разделение контрактов данных, — которую в распределённых системах знают все. Но в LLM-пайплайнах почему-то часто игнорируют и пытаются гонять промпты с «вот тебе весь предыдущий вывод, продолжай». Не надо так. JSON-контракты между агентами — ровно то, что превращает набор чат-ботов в работающий конвейер.
По этой же причине я не использую монолитный промпт «сделай всё от интервью до C4». LLM начинает путаться на третьем шаге — требования вроде извлекла, доменную модель вроде построила, но C4 уже плывёт. Три отдельных агента с узкой ответственностью дают более стабильный результат.
Про оркестрацию нескольких агентов в общем случае есть подробный разбор — мультиагентные системы: оркестрация нескольких AI-агентов. Здесь я показываю конкретный прикладной пайплайн.
Что идёт не так: грабли, на которые я наступил
Агент 1 любит придумывать требования. Особенно если в интервью есть фраза вроде «ну, стандартный набор функций». LLM додумывает этот «стандартный набор» исходя из обучающей выборки — вставляет авторизацию, ролевую модель, аудит. Гейт этого не ловит, потому что JSON валидный и количество требований нормальное. Лечится дополнительным промптом: «Включай только требования, явно упомянутые в интервью». Не помогает на 100%, но снижает частоту.
Доменная модель расползается. Агент 2 иногда плодит сущности-дубликаты с чуть разными названиями: «Клиент» и «Страхователь» как две разные сущности, хотя по контексту это одно лицо. Гейт сравнивает названия по нечёткому совпадению (fuzzy string matching) и подсвечивает подозрительные пары. Работает костыльно, но лучше, чем ничего.
C4-уровни путаются. Агент 3 может утащить базу данных на уровень Context или нарисовать внешний API на уровне Component. Это не ошибка синтаксиса Mermaid, это ошибка семантики уровней. Лечится перекрёстной проверкой: после генерации C4 я прошу того же агента (с другим промптом — роль «архитектор-рецензент») проверить, что каждый элемент находится на своём уровне. Цитирую критерии: «Context — только система и внешние акторы. Container — только исполняемые/хранимые единицы. Без компонентов и классов на этих уровнях».
Контекст интервью теряется к третьему агенту. Даже при JSON-контрактах информация сжимается. Живой аналитик помнит интонацию стейкхолдера: «вот это критично, а это — “хорошо бы”». Агент эту интонацию теряет на этапе формализации. Решение: в JSON от агента 1 добавляю поле stakeholder_emphasis («настойчивость заявления» — субъективная оценка по шкале 1–3). Агент 3 учитывает это при приоритизации связей на C4.
Когда это окупается, а когда — нет
Пайплайн имеет смысл, когда:
- Проект проходит фазу discovery: несколько стейкхолдеров, с десяток интервью, объёмная предметная область. Ручная обработка займёт недели, агенты справляются за день-два.
- Нужна быстрая итерация архитектурных гипотез: «а что если выделить расчёт тарифов в отдельный сервис?» — перегнать изменённую доменную модель через агента 3 и получить обновлённую C4 за 10 минут.
- Вы работаете в домене с устоявшейся терминологией (банки, страхование, e-commerce). LLM понимает контекст и реже галлюцинирует.
Не окупается:
- Когда требований — пять штук, система — один сервис с CRUD. Пайплайн из трёх агентов тут избыточен и только создаёт иллюзию работы.
- Когда предметная область узкоспециальная и терминология не встречается в обучающей выборке LLM. Агенты будут путаться, а вы потратите больше времени на правки, чем на ручное проектирование.
- Когда вам нужен продакшен-уровень без ручного ревью. Такого пока нет ни у кого — LLM ошибается, и архитектурные решения без проверки живым архитектором выкатывать нельзя.
Что в итоге
Агентный пайплайн не заменяет аналитика. Он заменяет рутину: расшифровку, форматирование, первичную структуризацию, набросок диаграмм. Живой человек делает то, что у LLM получается плохо: задаёт уточняющие вопросы стейкхолдерам, принимает архитектурные решения на основе контекста, которого нет в тексте, и несёт за них ответственность.
На моём проекте пайплайн сократил фазу discovery с трёх недель до четырёх дней. Три дня — автоматические прогоны и итерации. Один день — ручное ревью и финализация. Я не жму «сгенерировать и забыть» — я жму «сгенерировать, проверить, поправить и подписать».
Попробуйте собрать такой конвейер на следующем проекте. Начните с двух агентов (требования → доменная модель) и одного гейта. Когда заработает — добавляйте третий. За один вечер не соберёте, но за два — вполне.