Как показывать что AI думает: паттерны анимации прогресса агента
Основной чат
Чат для вайбкодеров: новости, гайды, поиск исполнителей, маркетплейс и разбор реальных кейсов.
Спиннер придуман для операций длиной 0.5–2 секунды. LLM-запрос занимает 4–15 секунд. Многошаговая задача агента — до нескольких минут. За это время спиннер перестаёт работать как индикатор прогресса и становится источником тревоги: «что-то происходит?», «система зависла?», «стоит ли ждать?»
Проектирование AI-интерфейсов требует нового motion-словаря.
Почему старые паттерны не работают
Традиционный загрузчик предполагает бинарное состояние: либо готово, либо нет. Кнопка — спиннер — результат.
AI-процессинг другой. Это серия шагов с разной длительностью, с промежуточными результатами, с возможностью частичного ответа который уже несёт ценность. И — главное — пользователь не знает как долго ждать. Нет progress bar который доходит до 100%. Это принципиально другой тип неопределённости.
Три проблемы которые решает правильная анимация AI-прогресса:
Тревога. «Система работает или зависла?» — спиннер не отвечает. Streaming output или progress log отвечает.
Потеря контроля. Пользователь не видит что происходит. Expandable process details дают контроль не прерывая работу.
Непредсказуемость. Неизвестно когда закончится. Конкретные шаги в progress log делают ожидание осмысленным.
Пять паттернов
Streaming output — самый эффективный
Текст появляется токен за токеном по мере генерации. Пользователь видит прогресс в реальном времени и начинает читать до того как ответ закончился.
Почему работает: нет ощущения ожидания. Процесс видим. Можно оценить направление ответа раньше чем он завершён — и при необходимости остановить.
Анимация: тихая. Только появление новых символов. Cursor-blink не нужен, typing indicator поверх не нужен.
// React: накопление токенов в state
const [text, setText] = useState('')
// При получении каждого токена:
setText(prev => prev + token)
// Рендеринг с автоскроллом к низу
useEffect(() => {
containerRef.current?.scrollTo(0, containerRef.current.scrollHeight)
}, [text])
Progress log — нарратив вместо спиннера
Последовательность коротких сообщений о том что агент делает прямо сейчас. Не статус — нарратив.
Анализирую запрос...
Ищу релевантные данные в 3 источниках...
Нашёл 12 совпадений, фильтрую по дате...
Формирую ответ...
Почему работает: пользователь понимает что система активна и что именно делает. Ожидание становится осмысленным — у него есть структура.
Анимация: каждая строка появляется fade-in (opacity 0→1, translateY 4→0, 200мс). Текущий шаг — с пульсирующей точкой. Завершённые — с галочкой.
@keyframes logEntry {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
.log-item { animation: logEntry 0.2s ease-out forwards; }
.log-item.active::after {
content: '●';
animation: pulse 1s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
Skeleton с постепенным заполнением
Структура ответа показывается заранее как skeleton — заполняется по мере готовности. Для структурированного контента: карточки, таблицы, списки.
Почему работает: пользователь видит что придёт. Нет пустоты. Нет тревоги о неизвестности — форма результата уже известна.
.skeleton {
background: linear-gradient(
90deg,
var(--color-surface) 25%,
var(--color-surface-hover) 50%,
var(--color-surface) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
from { background-position: 200% 0; }
to { background-position: -200% 0; }
}
Animated thinking indicator
Три точки с typing-анимацией — стандарт мессенджеров. Работает для conversational AI с коротким временем ответа (до 3–4 секунд).
После 5 секунд начинает выглядеть как зависание. Использовать только как первый шаг — потом переключать на progress log или streaming.
Expandable process details
Агент работает — пользователь видит краткий статус. По клику — детальный лог каждого шага. Progressive disclosure: детали доступны, но не навязаны.
Паттерн: collapsed by default с иконкой «показать подробности». Разворачивается через height: 0 → auto (interpolate-size: allow-keywords в CSS или через JavaScript).
Что не работает
Determinate progress bar без реального прогресса. Если bar движется независимо от реального состояния — пользователь чувствует это. Доверие падает быстро.
Percentage без контекста. «47% выполнено» — что именно? Без понимания что считается — число бессмысленно и создаёт ложное ощущение предсказуемости.
Спиннер дольше 4 секунд как единственный индикатор. После 4 секунд пользователь не знает ждать ли ещё, закрыть ли страницу. Нужен дополнительный сигнал.
Анимация прогресса без кнопки «Стоп». Многошаговая задача агента может пойти не туда. Пользователь должен иметь возможность остановить в любой момент.
Промпт для Codex / Claude Code
Реализуй паттерн отображения прогресса AI-ответа для [компонент].
Паттерны в зависимости от типа ответа:
1. Текстовый ответ < 5 сек: streaming output (накопление токенов в state)
2. Текстовый ответ > 5 сек: typing indicator (3 сек) → streaming
3. Многошаговая задача: progress log с конкретными шагами
4. Структурированный контент: skeleton с shimmer → постепенное заполнение
Общие требования:
- Кнопка «Стоп» видна всё время пока агент работает
- После 4 сек без изменений: показать текстовый статус "всё ещё работаю..."
- При streaming: автоскролл к последнему токену
- При ошибке: явное сообщение с кнопкой "Попробовать снова"
Короткий ответ (< 5 сек):
☐ Streaming output или typing indicator
Длинный ответ (> 5 сек):
☐ Progress log с конкретными шагами
☐ Кнопка «Стоп» всегда видна
Структурированный контент:
☐ Skeleton с shimmer → заполнение
Любой AI-процессинг:
☐ Нет спиннера как единственного индикатора > 3 сек
☐ После 4 сек: текстовый статус "всё ещё работаю"
☐ Есть cancel/stop механизм
☐ При ошибке: конкретное сообщение + retry
Почему это важнее чем кажется
В 2023 году большинство AI-интерфейсов показывали спиннер пока модель думает. Пользователи привыкли. Ждали. Иногда обновляли страницу — не понимая зависло ли.
В 2026 году ожидания изменились. Пользователи знают что LLM может думать секунды и минуты. Они знают что можно видеть процесс через streaming. Спиннер без прогресса воспринимается не как «нормально» а как «этот продукт не позаботился».
Motion для AI-прогресса — это уже не «приятное дополнение». Это базовое ожидание которое влияет на доверие к продукту.
Разные паттерны для разных типов AI-задач
Не все AI-задачи одинаковы с точки зрения UX прогресса.
Синхронные запросы (< 3 сек). Пользователь нажал → ждёт → получил результат. Typing indicator или skeleton достаточно. Streaming не нужен — ответ приходит быстро.
Асинхронные запросы (3–15 сек). Пользователь нажал → видит процесс → получает результат по мере готовности. Streaming для текста, skeleton для структуры. Кнопка «Стоп» обязательна.
Агентные задачи (> 15 сек, возможно минуты). Пользователь дал задание → агент работает несколько шагов → результат. Progress log с конкретными шагами. Expandable details. Уведомление когда готово (если пользователь ушёл делать другое).
Один паттерн на все три типа — это ошибка.
Когда показывать кнопку «Стоп»
Всегда. Без исключений.
Любой AI-процесс может пойти не туда: неправильная интерпретация запроса, бесконечный loop, слишком долгое время. Пользователь должен иметь возможность остановить в любой момент.
Кнопка «Стоп» должна быть:
- Видима всё время пока агент работает (не скрыта за кликом)
- Достаточно большой для уверенного нажатия (минимум 44×44px)
- Деструктивной по виду — но не пугающей (не красная кнопка с иконкой опасности)
После нажатия «Стоп» — показать что успело сделать, не просто очистить экран.
Что происходит когда ответ приходит частями
Многие AI-системы возвращают результат не одним куском а частями по мере готовности. Первая карточка готова через 2 секунды, вторая через 4, третья через 7.
Правильный паттерн: показывай каждую часть сразу как она готова, с плавным появлением. Skeleton для тех что ещё не готовы. Не жди пока всё будет готово — это потеря времени восприятия.
Пример: дашборд с четырьмя метриками. Каждая загружается независимо. Не показывать skeleton 7 секунд а потом все четыре сразу — показывать каждую по мере готовности. Пользователь уже читает первую пока загружаются остальные.
Accessibility: анимации прогресса и assistive technology
Screen readers не видят анимированный прогресс. Если progress log обновляется визуально но без ARIA-анонсов — пользователь со screen reader не знает что происходит.
Минимальное решение: aria-live="polite" на контейнере progress log. Каждое новое сообщение будет озвучено автоматически.
<div aria-live="polite" aria-label="Статус выполнения задачи">
<p>Анализирую запрос...</p>
</div>
Для streaming output: aria-live="off" на потоковом контейнере (каждый токен отдельно слишком шумно) + aria-live="polite" на финальном результате.
Кнопка «Стоп» должна иметь понятный aria-label: «Остановить выполнение задачи», не просто «Стоп».