# 200 отзывов → 5 JTBD за 2 часа: AI-синтез user research

> Пошаговый процесс извлечения Jobs-to-be-Done из пользовательских отзывов с помощью LLM. Промпты для анализа, кластеризация, примеры output и готовый workflow.
> Author: Roman Belov · Published: 2026-04-02 · Source: https://futurecraft.pro/ru/blog/jtbd-ai-research/

Большинство пользовательских отзывов никогда не превращаются в product decisions. Причина банальна: ручной анализ 200+ ревью занимает 2–3 недели, и к моменту завершения приоритеты уже сменились. Команда читает отзывы, выписывает цитаты на стикеры, группирует по интуиции. Результат субъективен, не воспроизводим и не масштабируем.

Статья о том, как за 2 часа пройти путь от сырых отзывов до пяти конкретных Jobs-to-be-Done, используя LLM для каждого этапа: извлечение сигналов, кластеризация, формулировка JTBD, валидация.

## Почему JTBD, а не просто "боли пользователей"

Jobs-to-be-Done фреймворк фокусируется не на том, что пользователь говорит, а на том, чего он пытается достичь. Отзыв "приложение тормозит" — это не боль. Боль в том, что человек пытался быстро найти маршрут перед выходом из отеля и не смог. Контекст, мотивация и ожидаемый результат важнее конкретной жалобы.

Формула JTBD:

```
Когда [ситуация], я хочу [мотивация], чтобы [ожидаемый результат]
```

Пример из реального анализа travel-приложения:

- **Отзыв:** "Почему нельзя скачать маршрут оффлайн? В горах нет интернета!"
- **JTBD:** Когда путешествую в зонах без связи, хочу иметь доступ к маршруту оффлайн, чтобы не терять ориентацию и не срывать планы.

Разница принципиальная. Отзыв указывает на фичу (оффлайн-режим). JTBD указывает на job (навигация без связи) и outcome (не срывать планы). Второе позволяет найти несколько решений: оффлайн-карты, предзагрузка, SMS-fallback, печатная версия маршрута.

## Откуда брать отзывы

Источники для анализа:

- **App Store и Google Play** — отзывы с оценками, дают корреляцию между sentiment и конкретными проблемами
- **Zendesk/Intercom** — тикеты поддержки, самые детальные описания проблем
- **Typeform/Google Forms** — ответы на опросы, структурированные данные
- **Reddit/Twitter** — органические упоминания, без bias опросника
- **Записи интервью** — транскрипты custdev, самый богатый контекст

Формат для загрузки в LLM — простой CSV или JSON:

```json
[
  {
    "id": "review_001",
    "source": "google_play",
    "rating": 2,
    "text": "Планировал поездку в Бали, приложение предложило...",
    "date": "2026-02-15"
  }
]
```

Важно сохранять `id` и `source` для трассировки. Каждый кластер и каждый JTBD должны ссылаться на конкретные отзывы, иначе результат нельзя проверить.

Оптимальный объём для одного цикла: 150–300 отзывов. Меньше — недостаточно для статистически значимых кластеров. Больше — нужно разбивать на батчи (об этом ниже).

## Шаг 1: Извлечение сигналов

Первый проход LLM извлекает из каждого отзыва структурированные сигналы. Не классификацию, не sentiment, а именно сигналы: что человек пытался сделать, что пошло не так, какой результат ожидал.

### Промпт для извлечения сигналов

```
Ты - исследователь пользовательского опыта. Для каждого отзыва извлеки:

1. **situation** - контекст и ситуация использования (что происходило)
2. **intent** - что пользователь пытался сделать
3. **outcome_expected** - какой результат ожидал
4. **outcome_actual** - что получил в реальности
5. **emotion** - эмоциональный фон (frustration/disappointment/anger/neutral/satisfaction)
6. **signal_strength** - насколько явно выражена потребность (1-5)

Если информации недостаточно для поля - ставь null.
Отвечай строго в JSON. Не додумывай того, чего нет в тексте.

Отзывы:
{reviews_batch}
```

### Пример output

```json
{
  "review_id": "review_042",
  "situation": "Планирование семейной поездки с детьми на выходные",
  "intent": "Найти маршрут с учётом детских интересов",
  "outcome_expected": "Маршрут с детскими площадками и кафе с детским меню",
  "outcome_actual": "Стандартный туристический маршрут без учёта детей",
  "emotion": "disappointment",
  "signal_strength": 4
}
```

### Батчинг

200 отзывов не влезут в один промпт. Оптимальный размер батча — 15–25 отзывов за вызов, в зависимости от длины текстов. При контексте в 128K токенов можно увеличить до 40–50, но качество извлечения падает: LLM начинает пропускать нюансы в отзывах из середины списка.

Разбивка на батчи автоматизируется скриптом на 20 строк. Общее время обработки 200 отзывов при 20 штук на батч: 10 вызовов, примерно 5–7 минут.

Подробнее о выборе модели и мониторинге LLM-вызовов — в [гайде по LLM observability с Langfuse](/ru/blog/llm-observability-langfuse/).

## Шаг 2: Кластеризация сигналов

После извлечения получается 200 структурированных записей. Следующий шаг — найти повторяющиеся паттерны.

Два подхода:

### Подход A: LLM-кластеризация (быстро, хорошо для < 200 записей)

Передать все извлечённые сигналы в LLM и попросить сгруппировать:

```
У тебя 200 извлечённых сигналов из пользовательских отзывов.
Каждый сигнал содержит: situation, intent, outcome_expected, outcome_actual.

Задача:
1. Сгруппируй сигналы по похожим intent + situation (не по словам, а по смыслу)
2. Каждый кластер назови коротко и описательно
3. Для каждого кластера укажи:
   - название кластера
   - количество сигналов в нём
   - список review_id
   - общий паттерн: типичная ситуация + типичное намерение
   - средний signal_strength

Не создавай кластеры меньше 3 сигналов - это шум.
Допускается, что один сигнал попадёт в два кластера.

Формат: JSON.
```

### Подход B: Embeddings + HDBSCAN (точно, масштабируется)

Для датасетов больше 200 записей LLM-кластеризация теряет точность. Альтернатива: преобразовать каждый сигнал в embedding, затем кластеризовать алгоритмом HDBSCAN.

```python
from sentence_transformers import SentenceTransformer
import hdbscan

model = SentenceTransformer('all-MiniLM-L6-v2')

# Конкатенация ключевых полей для embedding
texts = [
    f"{s['situation']} {s['intent']} {s['outcome_expected']}"
    for s in signals
]
embeddings = model.encode(texts)

clusterer = hdbscan.HDBSCAN(min_cluster_size=5, min_samples=3)
labels = clusterer.fit_predict(embeddings)
```

HDBSCAN не требует заранее указывать количество кластеров. Он сам определяет плотные зоны и выделяет шум (label = -1). Для 200 записей типично получить 8–15 кластеров и 10–20% шума.

### Пример результата кластеризации

| # | Кластер | Сигналов | Средний strength | Top review_ids |
|---|---------|----------|-----------------|----------------|
| 1 | Оффлайн-доступ к маршруту | 34 | 4.2 | 042, 089, 156... |
| 2 | Персонализация под состав группы | 28 | 3.8 | 017, 033, 091... |
| 3 | Интеграция с бронированием | 22 | 3.5 | 005, 067, 134... |
| 4 | Навигация внутри маршрута | 19 | 3.9 | 023, 078, 145... |
| 5 | Бюджетный контроль поездки | 17 | 3.3 | 011, 056, 112... |
| 6 | Социальное планирование (совместный доступ) | 15 | 3.6 | 028, 073, 167... |
| 7 | Локальные рекомендации vs туристические | 14 | 4.0 | 009, 048, 133... |
| 8 | Адаптация маршрута в реальном времени | 12 | 3.7 | 036, 082, 159... |

8 кластеров. Из них нужно вывести 5 JTBD.

## Шаг 3: Формулировка JTBD из кластеров

Каждый кластер содержит повторяющийся паттерн "ситуация + намерение + ожидание". Задача LLM на этом шаге — преобразовать паттерн в формулировку JTBD.

### Промпт для формулировки JTBD

```
Ты - product strategist. Перед тобой результаты кластеризации
пользовательских отзывов. Для каждого кластера сформулируй JTBD.

Формат JTBD:
"Когда [ситуация], я хочу [действие/возможность],
чтобы [ожидаемый результат]."

Правила:
1. Ситуация должна быть конкретной, не абстрактной
2. Действие формулируется от лица пользователя
3. Результат описывает конечную ценность, не фичу
4. Если два кластера описывают один job - объедини
5. Ранжируй по: (количество сигналов * средний signal_strength)

Кластеры:
{clusters_json}

Для каждого JTBD укажи:
- Формулировка JTBD
- Score (количество * strength)
- Источники: кластеры и review_ids
- Текущее состояние: как пользователь решает эту задачу сейчас
- Возможные решения (2-3 варианта, от простого к сложному)
```

### Пример output: 5 JTBD

**JTBD #1 (Score: 142.8)**
Когда нахожусь в поездке без стабильного интернета, хочу иметь полный доступ к запланированному маршруту, чтобы не зависеть от связи и следовать плану.

- Кластеры: 1, 4
- Текущее решение: скриншоты Google Maps, заметки в Notes
- Варианты: оффлайн-кэш маршрута → предзагрузка карт + POI → полный оффлайн-режим с синхронизацией

**JTBD #2 (Score: 106.4)**
Когда планирую поездку с семьёй или друзьями, хочу учесть интересы и ограничения каждого участника, чтобы маршрут подходил всем.

- Кластеры: 2, 6
- Текущее решение: Google Docs со списком пожеланий, голосование в чате
- Варианты: фильтры по составу группы → профили участников с предпочтениями → AI-оптимизация маршрута под всех

**JTBD #3 (Score: 77.0)**
Когда выбираю места для посещения, хочу получать рекомендации от локалов, а не стандартные туристические списки, чтобы увидеть настоящий город.

- Кластеры: 7
- Текущее решение: Reddit, блоги экспатов, вопросы в Telegram-чатах
- Варианты: метка "local pick" на POI → партнёрство с локальными гидами → AI-курирование на основе нетуристических источников

**JTBD #4 (Score: 77.0)**
Когда бронирую отели и билеты, хочу делать это прямо из маршрута, чтобы не переключаться между приложениями и не терять контекст.

- Кластеры: 3
- Текущее решение: копирование названий из приложения в Booking/Aviasales
- Варианты: deep links на booking-платформы → встроенное сравнение цен → одна кнопка бронирования

**JTBD #5 (Score: 56.1)**
Когда в поездке что-то идёт не по плану (закрыто, погода, задержка), хочу быстро адаптировать маршрут, чтобы не тратить время на ручное перепланирование.

- Кластеры: 8
- Текущее решение: поиск альтернатив в Google Maps на месте
- Варианты: кнопка "предложить замену" → автодетект закрытых мест → real-time rerouting с учётом контекста

## Шаг 4: Валидация результатов

JTBD, полученные через LLM, требуют проверки. Три метода валидации:

### Обратная трассировка

Каждый JTBD должен прослеживаться до конкретных отзывов. Если нельзя найти 5+ отзывов, которые явно описывают этот job, формулировка может быть галлюцинацией модели.

Промпт для проверки:

```
Вот JTBD: "{jtbd_statement}"
Вот 10 отзывов, которые якобы относятся к этому JTBD:
{reviews}

Для каждого отзыва ответь:
- Релевантен ли он этому JTBD? (да/нет/частично)
- Какая именно фраза в отзыве подтверждает связь?

Итого: сколько из 10 действительно релевантны?
```

Порог: минимум 70% релевантных. Ниже — JTBD нужно переформулировать или он основан на слабых сигналах.

### Тест на overlap

Два JTBD не должны описывать один и тот же job разными словами. Проверка:

```
Вот 5 JTBD. Проверь каждую пару на overlap:
- Описывают ли они одну и ту же ситуацию?
- Описывают ли одно и то же намерение?
- Можно ли их объединить без потери смысла?

Для каждой пары: overlap score от 0 (полностью разные) до 1 (дубликат).
```

Overlap выше 0.6 — кандидаты на объединение.

### Тест на actionability

JTBD бесполезен, если из него не следует ни одного решения. Для каждого JTBD нужно ответить на вопрос: "Можно ли за 2 недели сделать минимальное решение для этого job?" Если нет — JTBD слишком абстрактный и нужна декомпозиция.

## Полный workflow: 2 часа

| Этап | Время | Что делать |
|------|-------|------------|
| Сбор и подготовка данных | 30 мин | Экспорт отзывов, приведение к единому формату, дедупликация |
| Извлечение сигналов (Шаг 1) | 20 мин | 10 батчей по 20 отзывов, параллельные вызовы LLM |
| Кластеризация (Шаг 2) | 20 мин | LLM-кластеризация или embeddings + HDBSCAN |
| Формулировка JTBD (Шаг 3) | 15 мин | Один вызов LLM на базе кластеров |
| Валидация (Шаг 4) | 25 мин | Обратная трассировка, overlap, actionability |
| Оформление результата | 10 мин | Документ с JTBD, scores, ссылками на отзывы |

Общее время: ~2 часа. Ручной аналог: 2–3 недели.

Ключевое отличие от ручного процесса — воспроизводимость. Один и тот же датасет при повторном прогоне даёт совпадение JTBD на 85–90% (тестировалось на трёх прогонах с temperature 0.3). Ручной анализ двумя аналитиками даёт совпадение 40–60%.

## Выбор модели и стоимость

Для каждого шага подходит разная модель:

| Шаг | Рекомендуемая модель | Почему |
|-----|---------------------|--------|
| Извлечение сигналов | Claude Sonnet, GPT-5.4 | Нужно точное следование формату + извлечение нюансов |
| Кластеризация | Claude Opus 4.8, GPT-5.5 | Нужно мышление, понимание семантической близости |
| Формулировка JTBD | Claude Opus 4.8, GPT-5.5 | Стратегическое мышление, синтез |
| Валидация | Claude Sonnet, GPT-5.4 | Сравнение и верификация, не нужен глубокий анализ |

Стоимость обработки 200 отзывов (примерная):

- Извлечение: ~150K input tokens, ~80K output tokens → $1.5–3
- Кластеризация: ~100K input, ~20K output → $1–2
- Формулировка + валидация: ~50K input, ~15K output → $0.5–1

Итого: $3–6 за полный цикл. Для сравнения: один день работы product-аналитика обходится в $300–500.

Настройка [context engineering](/ru/blog/context-engineering-guide/) для промптов критически важна. Чем точнее системный промпт описывает ожидаемый формат и ограничения, тем меньше итераций на исправление.

## Типичные ошибки

**Анализ без контекста продукта.** LLM не знает, что приложение уже умеет делать. Без описания текущей функциональности модель генерирует JTBD для фич, которые уже существуют. Решение: добавить в системный промпт краткое описание продукта и его текущих возможностей.

**Слишком абстрактные JTBD.** "Когда планирую поездку, хочу удобный инструмент, чтобы всё было просто" — это не JTBD, а wish. Решение: в промпте указать минимальный уровень конкретности ситуации.

**Игнорирование позитивных отзывов.** Из пятизвёздочных отзывов тоже извлекаются JTBD — те, которые продукт уже закрывает. Это валидация текущей стратегии. Фильтровать только негативные отзывы — терять половину картины.

**Кластеризация по словам, а не по смыслу.** "Карта тормозит" и "маршрут не загружается оффлайн" — разные слова, один кластер (доступ к маршруту). LLM-кластеризация справляется лучше keyword-matching, но иногда группирует по поверхностному сходству. Ручная проверка топ-3 кластеров обязательна.

## С чего начать

1. Собрать 100–200 отзывов из основного канала (App Store, Zendesk, любой источник с наибольшим объёмом)
2. Привести к формату JSON с полями `id`, `source`, `text`, `rating`, `date`
3. Прогнать Шаг 1 (извлечение сигналов) на 20 отзывах вручную. Проверить качество extraction
4. Если качество устраивает — автоматизировать батчинг и прогнать весь датасет
5. Выполнить кластеризацию (для первого раза LLM-кластеризация проще)
6. Сформулировать JTBD и провалидировать обратной трассировкой

Минимальный стек: один LLM API (Claude или GPT-5.4), Python-скрипт для батчинга, Google Sheets для финального результата. Никаких специализированных инструментов не нужно.

Результат: документ с 5–8 ранжированными JTBD, каждый со score, source reviews и вариантами решений. Это основа для product roadmap, которую можно обновлять каждый месяц при поступлении новых отзывов.

---

*Нужна помощь с user research и продуктовой аналитикой на базе AI? Я помогаю стартапам внедрять AI-решения и строить продукты — [belov.works](https://belov.works).*
