Дисклеймер. Я строю сервис AI-обратной связи для художников, поэтому это не обзор рынка и не сравнение «кто лучше». Это разбор продуктово-технического решения изнутри: какие компромиссы у multi-agent review, где он ломается, сколько стоит и как мы проверяли качество. Технические параметры и пороги — из нашего кода и рабочих метрик, а не из маркетинга.
О чём речь
Когда любитель учится рисовать, ему нужна обратная связь — но живой тьютор дорог и занят, а друзья говорят «ну, прикольно». Последние два года появилась третья опция: загрузить рисунок AI-критику и получить разбор. Категория уже сложилась — к маю 2026 я насчитал не меньше пяти бесплатных сервисов, делающих ровно это (таблица ниже).
Мы пошли другим путём: вместо одного отзыва от одной модели — совет из трёх LLM-персон на разных моделях плюс четвёртая роль, судья, который синтезирует общий вердикт. Ниже — почему мы так решили, как это устроено технически, и где эта схема проигрывает обычному single-critic. Спойлер: проигрывает в нескольких местах, и это важная часть разговора.
Часть 1. Контекст: single critic как готовая категория
Что уже есть на рынке
Это не «у нас уникальная идея». Single-AI-critic — сложившаяся категория. Вот сервисы, которые я проверил 21 мая 2026 (на момент проверки у всех был публичный бесплатный сценарий, у большинства — без регистрации):
|
Сервис |
Что делает |
|---|---|
|
Один отзыв, выбор стиля (Detailed/Constructive/Sarcastic), без логина |
|
|
Composition / style / technique feedback, персональные suggestions |
|
|
Feldman Method + critique/rating/marketability |
|
|
Критика без регистрации |
|
|
Один отзыв, без логина |
Снаружи продуктовая механика выглядит похоже: загрузка изображения → один итоговый отзыв. Внутри это может быть один vision-вызов или более сложный пайплайн — мне их устройство неизвестно, — но пользователь в итоге получает один голос. На две недели — полезно; на дистанции в месяц проявляется ограничение, о котором ниже.
Какую проблему мы хотели решить иначе
У одного отзыва — один тон. Если модель настроена как «строгий преподаватель», она давит на ошибки; как «поддерживающий друг» — хвалит даже слабые работы. Большинство сервисов калибруют это в среднем: аккуратный безопасный отзыв.
Аналогия — Rotten Tomatoes. Там не показывают «усреднённый отзыв всех критиков»; там показывают процент критиков, которым понравилось, — то есть распределение, а не среднее. Нам показалось, что обратная связь на рисунок устроена так же: три точки зрения дают читателю не «больше текста», а разные оси сигнала — где они сходятся, там, по нашим наблюдениям, объективная проблема (или сила); где расходятся — область для собственного суждения.
Важная оговорка про границы: мы говорим про process-first инструмент (на выходе — разбор и план практики), а не про image-generator (Midjourney, DALL-E — на выходе готовая картинка). Это разные задачи и разные рынки; council для генерации не нужен, а generator не закрывает эту конкретную петлю обучения: разбор собственной работы → следующий шаг практики. Дальше — только про process-first.
Часть 2. Архитектура: fan-out / fan-in + судья
Общая схема
Логически один review — это четыре шага: три персоны параллельно (fan-out), затем судья поверх их выводов (fan-in). В базовом режиме это и есть четыре LLM-вызова; в two-stage режиме (см. ниже) их физически больше — об этом честно в конце части.
рисунок (jpg/png)
|
v
[нормализация + download, timeout 30s]
|
+-----------------+-----------------+
| | | <- fan-out (errgroup, 3 goroutines)
v v v
[Persona 1] [Persona 2] [Persona 3]
technician storyteller coach
vision-модель A vision-модель B vision-модель C
| | |
+--------+--------+-----------------+
| fan-in (WaitGroup)
v
[Судья / synthesis]
text-модель D: видит 3 отзыва в JSON, НЕ рисунок
|
v
[quality gate: score >= 0.6 ? ]
|
v
UI (вердикт сверху, персоны раскрываются по клику)
Под капотом нет «оркестратора», который сводит всё в один большой промпт. Это буквально четыре отдельных вызова: три персоны запускаются в errgroup параллельно, судья ждёт fan-in и получает уже готовые три отзыва в JSON.
Роли
Три персоны + судья. Внутренние имена не важны — важны роли, входы и типичные ошибки:
|
Роль |
Вход |
Класс модели |
Что возвращает |
Где чаще ошибается |
|---|---|---|---|---|
|
Technician |
рисунок + история практики |
vision-capable |
пропорции, перспектива, контраст, edges (строгий тон) |
геометрия: «видит» наклон таза не там, где он есть |
|
Storyteller |
рисунок |
vision-capable |
нарратив, настроение, читается ли intention |
приписывает intention, которого автор не вкладывал |
|
Coach |
рисунок + прошлая сессия |
vision-capable |
сравнение с прошлой практикой, следующий шаг |
излишний оптимизм в оценке прогресса |
|
Судья (synthesis) |
3 отзыва в JSON (без рисунка) |
text-only |
общий вердикт, согласие/расхождение, следующее действие |
при слабой модели даёт размытый или шаблонный синтез |
Важно про судью: он не четвёртый визуальный критик. Судья работает на text-only модели и рисунок не видит — на вход ему идут только три уже готовых разбора в JSON. То есть он синтезирует и проверяет согласованность отзывов, а не пересматривает изображение. Это экономит ещё один (дорогой) vision-вызов, но означает: качество судьи упирается в качество отзывов на входе — если персоны ошиблись синхронно, судья этого не поймает по картинке.
Ключевое решение: разные классы моделей на разных персонах. Мы пробовали три промпта на одной модели — получаются три варианта одного тона. У каждой LLM свой «тон по умолчанию», который не убирается полностью промптом; чтобы получить слабее коррелированные сигналы, персоны живут на разных моделях. Diversity тогда возникает не только из промпта, но и из базовой «фактуры» голоса.
Two-stage pipeline: как мы экономим vision-вызовы
Vision-вызов дороже text-вызова, а при retry судьи или персоны не хочется заново гонять картинку. Поэтому у нас есть двухэтапный режим (feature-gated, раскатывается по проценту трафика):
-
Stage 1 (vision): модель читает изображение и возвращает структурированные факты (описательные поля, не оценки).
max_tokens ~2000, temp 0.2. -
Stage 2 (text): text-only модель получает факты Stage 1 + системный промпт персоны и возвращает полный анализ с оценками.
max_tokens ~4000, temp 0.4.
Факты Stage 1 кладутся в in-memory кэш с TTL 5 минут по ключу (персона, модель, hash(URL)). Если Stage 2 надо переретраить — vision-вызов не повторяется, берётся из кэша. Это гарантирует ровно один vision-вызов на персону, даже когда text-стадия падает и переселектит модель.
Честно про арифметику вызовов: базовая (single-stage) схема — это 4 LLM-вызова (3 персоны
-
судья). В two-stage режиме на каждую персону приходится два вызова (vision-extraction
-
text-analysis), так что полный review — это до
3 × 2 + 1 = 7LLM-вызовов. Кэш Stage 1 не уменьшает базовое число вызовов — он лишь не даёт ему расти при retry text-стадии.
Часть 3. Инженерные компромиссы
Здесь — самое интересное, и здесь же видно, где council проигрывает.
Таймауты и retries
Параллельность не означает «быстро при сбое». Что зафиксировано в коде:
-
загрузка изображения — context timeout 30s (поверх HTTP-клиента с потолком 60s, выделенного именно под download картинки);
-
сами LLM-вызовы идут через провайдер-адаптер с собственным retry и экспоненциальным backoff (1s → 2s, cap 2s) — единого «таймаута на вызов» в секундах мы не хардкодим, ограничение приходит от контекста запроса и пула провайдеров;
-
персона: до 3 попыток, пауза 2s между ними, на каждой попытке выбирается другая модель (упавшая попадает в exclusion-список персоны);
-
судья: до 4 попыток, пауза 5s, тоже с переселектом модели.
Здесь важно не соврать про latency. Персоны идут параллельно, поэтому wall-clock — не сумма трёх персон, а примерно max(время самой медленной персоны с её retry) + время судьи с его retry + quality-gate. Типичный review укладывается в 30–60 секунд; под retries деградирует, но именно по этой формуле «max + судья», а не «сумма персон». Для сравнения: single critic — это один вызов и почти всегда секунды. Первый компромисс: council дороже по latency, особенно в хвосте распределения.
Как судья обрабатывает противоречия
Судья не просто «суммаризирует». На выходе персон — оценки по нескольким dimensions, и мы считаем расхождение:
-
по каждому измерению считаем
std_devоценок трёх персон; -
если
std_dev > 1.5— измерение помечается как disagreement, и судья получает это как явный сигнал (кто что поставил); -
общий уровень согласия —
agreement = 1 − (max_std_dev / 5), зажат в [0, 1] (5 — максимальный возможный разброс на шкале 0–10).
Плюс анти-галлюцинационный фильтр: пункт (сильная сторона / слабость / совет) попадает в сводный вердикт судьи, только если его упомянули минимум две персоны. Это защита от случая, когда одна модель «придумала» деталь, которой на рисунке нет. Оговорка: это не доказывает истинность пункта — лишь снижает вероятность одиночной галлюцинации. Важная деталь, чтобы не потерять ценность ролей: фильтр применяется только к финальному summary. Ролевые замечания остаются в карточках персон как есть — даже если технический пункт назвал один только technician, он виден в его карточке. Мы не выкидываем сигнал, мы лишь не повышаем одиночное наблюдение до уровня «консенсус совета».
Quality gate: где «больше моделей» оказалось хуже
Самый честный урок. На бесплатном пуле моделей судья иногда выдавал синтез хуже, чем дал бы один хороший single-critic: слабая text-модель в роли судьи писала шаблонный или размытый вердикт. «Больше моделей» само по себе не улучшает результат — узким местом становится судья.
Что мы сделали:
-
ввели порог качества судьи: модель допускается в пул судьи только при
quality >= 0.75иconfidence >= 0.20(отрезает fail-class модели); -
добавили online-оценку готового вердикта: если score < 0.6 — retry с другой моделью судьи; если < 0.4 после retries — вердикт помечается low-quality и запускается внешний retry всего council (до 2 раз);
-
на полное исчерпание retries — математический fallback: вердикт собирается чисто арифметически (mean/min/max/std_dev по измерениям + consensus по пересечению), без LLM.
Был и отдельный казус: судья периодически дословно копировал пример вердикта из своего же промпта — плейсхолдер утекал в ответ. Лечится не «усилением инструкции», а структурным sentinel-guard’ом на границе парсинга, который ловит эхо и уводит в retry/fallback.
Стоимость
По стоимости council — примерно в 3–4 раза дороже single-critic: минимум четыре вызова вместо одного, причём три из них vision (дороже text). Основная денежная нагрузка — именно в трёх vision-стадиях персон, а не в судье: судья text-only и сам по себе дешёвый. Но судья — самый критичный элемент: если модель судьи слабая, деградирует вся схема, сколько бы хороших отзывов ни пришло на вход. То есть «дёшево» и «можно сэкономить» — не одно и то же: экономить на судье опасно не по деньгам, а по качеству. Это второй компромисс и прямая причина, почему бесплатный план ограничен по числу разборов: безлимит выжег бы unit-экономику на vision-вызовах.
Что мы измеряем
Чтобы это не было «вкусовщиной», в council зашиты Prometheus-метрики (RED-стиль): счётчики retry по персонам с причиной (rate_limit / server_error / timeout / parse / capability), failure судьи, срабатывания low-quality-гейта, сколько пунктов отфильтровал consensus-фильтр, latency по стадиям, попадания в кэш Stage 1. Это то, по чему мы видим регрессии — например, что free-pool судьи деградировал, мы поймали именно по метрике failure-rate, а не по жалобам.
Когда single critic объективно лучше
Чтобы было честно, council — не универсальный выбор:
-
Быстрая итерация над одной деталью («правильно ли наклонена голова») — тут нужен один быстрый ответ, и три персоны + судья только добавляют latency и шум.
-
Профессионал, который точно знает, что ищет — ему быстрее открыть reference и сравнить, чем читать синтез трёх голосов.
-
Нет регулярной практики — если рисуешь раз в месяц, ни одна система обратной связи не поможет: навык растёт от объёма практики, а не от качества разбора разового рисунка.
Council оправдан в узком окне: regular-practice self-learners уровня intermediate beginner, которым нужен разнотипный направленный сигнал. Вне этого окна обычный single-critic выигрывает по простоте, latency и стоимости.
FAQ
Чем это отличается от живого тьютора? Тьютор лучше, если он есть: он видит контекст (как ты сидишь, держишь карандаш, в каком настроении). AI видит только финальное изображение. Council — это «лучше, чем ничего» в окне, когда живого тьютора рядом нет: 24/7, не выгорает, стабильно отдаёт разнотипный сигнал.
Почему именно три персоны, а не пять или семь? По нашим наблюдениям, при добавлении персон сверх четырёх сигналы начинают дублироваться, а судье сложнее синтезировать (теряет часть входов либо выдаёт размытый текст). Плюс latency и стоимость растут линейно по числу персон, а прирост качества выходит на плато. Три — эмпирический оптимум для рисования (техника / нарратив / тренинг). Для другой задачи оптимум, скорее всего, другой.
Совет ошибается? Да, регулярно — типичные ошибки в таблице ролей выше. Преимущество не в том, что персоны не ошибаются, а в том, что они ошибаются не одинаково: технический промах по геометрии не коррелирует с тем, что оценивают нарратив и коуч. Структура «три сигнала + судья + consensus-фильтр» снижает цену индивидуальной ошибки, но не убирает её.
Автор: VitaliySemenov


