1. Що таке follow‑up-и в ChatGPT App і навіщо вони потрібні
Почнімо з визначення — людською, а не маркетинговою мовою. Follow‑up у контексті ChatGPT App — це програмно ініційований наступний крок у діалозі. Зазвичай це коротка підказка у вигляді кнопки: після натискання вона перетворюється на нове текстове повідомлення від користувача в чаті й запускає продовження сценарію спілкування людини з ШІ.
З погляду моделі follow‑up — це просто ще одне повідомлення користувача. Жодної магії: коли користувач натискає вашу кнопку «Показати варіанти дешевше», в історію чату потрапляє щось на кшталт «Покажи подарунки дешевше». Модель сприймає це як звичайну фразу користувача, застосовує system‑prompt і описи інструментів (tools), вирішує, який інструмент викликати, і, можливо, знову показує ваш віджет — уже з іншими даними.
Чому це важливо:
- Модель мислить у термінах тексту. Якщо після натискання ви просто викликаєте інструмент напряму (без повідомлення), то обходите основний «мозок» системи й втрачаєте частину контексту. Follow‑up зберігає прогрес розмови в історії та допомагає моделі розуміти, що саме відбувається.
- Користувач залишається у звичній парадигмі чату: він або пише повідомлення, або натискає підказки. Follow‑up-и — це ті самі «швидкі відповіді» (quick replies), як у сучасних месенджерах.
- Це головний міст між вашим UI і текстовою частиною ChatGPT. Без follow‑up-ів віджет перетворюється на німий острів: користувач поклацав — і не зовсім розуміє, що робити далі.
На follow‑up-и можна дивитися як на кнопку «Наступне запитання до ШІ», тільки сформульовану вами. Далі ми розглядатимемо follow‑up-и не як «ще одну UI-функцію», а як основний спосіб вибудовувати діалог навколо віджета.
2. «Діалог навколо віджета»: conversational sandwich
Щоб легше побачити, як follow‑up-и допомагають будувати «діалог навколо віджета», корисно уявити розмову як бутерброд із трьох шарів:
- зверху — текст ChatGPT перед віджетом (pre‑text),
- посередині — ваш віджет (UI),
- внизу — follow‑up-и та наступні повідомлення (post‑interaction).
Схематично:
sequenceDiagram
participant U as Користувач
participant G as ChatGPT
participant W as Віджет
U->>G: "Підбери подарунок сестрі до 100$"
G->>G: Вирішує викликати App
G->>U: Pre-text: "Зараз відкрию GiftGenius і підберу ідеї"
G->>W: Передає toolOutput для рендеру
W-->>U: Картки подарунків + кнопки follow-up
U->>W: Клік по "Показати варіанти дешевше"
W->>G: sendFollowUpMessage("Покажи подарунки дешевше, до 50$")
G->>G: Новий прогін моделі, виклик інструментів
G->>W: Новий toolOutput, оновлений віджет
Pre‑text зазвичай повністю генерує модель на основі system‑prompt і контексту. У цьому шарі вона «пояснює», що зараз відбудеться («Зараз відкрию застосунок, який допоможе обрати подарунок»). Керувати цим шаром ми будемо пізніше — у модулі про інструкції.
Віджет — це ваш звичний React‑компонент: він відображає toolOutput, використовує widgetState, дає кнопки та варіанти вибору.
Шар post‑interaction — це те, чим ми займаємося сьогодні. За допомогою follow‑up-ів і надсилання повідомлень ви задаєте зрозумілий маршрут далі: «Показати дорожче», «Змінити бюджет», «Почати підбір заново», «Перейти до оформлення».
Якщо спростити, follow‑up — це керівна репліка, що замикає цикл: UI → текст → новий UI.
3. Технічна модель: як клік по follow‑up перетворюється на новий tool‑call
Розгляньмо уважніше ланцюжок подій «за лаштунками». Зручно думати про це як про гібридний цикл взаємодії: натискання → API → текст в історії → нове рішення моделі → новий виклик інструмента → оновлений UI.
Послідовність приблизно така:
- Користувач натискає кнопку у вашому віджеті.
- Віджет викликає API window.openai.sendFollowUpMessage або, на рівні React, зручний хук-обгортку useSendMessage (у прикладах нижче ми використовуватимемо саме цей хук). Аргумент — звичайний рядок: текст, який ніби написав користувач.
- ChatGPT додає це повідомлення в історію як новий user_message.
- Модель виконує новий прохід: враховує всю історію, включно з попереднім toolOutput, і вирішує, чи викликати інструмент, який саме та з якими аргументами.
- Ваш бекенд або MCP виконує tool‑call і повертає toolOutput.
- ChatGPT показує нову відповідь: можливо, знову віджет (із новими даними), можливо, текст, а можливо, комбінацію.
Важливо: майже ніколи не варто безпосередньо з віджета викликати той самий інструмент, який викликала модель, — в обхід цього циклу. Інакше модель «втрачає» крок: в історії немає запиту, який ініціював повторний виклик. У довгих сценаріях згодом це обертається заплутаним контекстом.
Можна звести це до короткої формули:
User Click → useSendMessage("...") → ChatGPT (LLM) → Tool Call → New toolOutput → Widget rerender
4. Види follow‑up-ів і хто їх вигадує
Ми розібралися, що відбувається «за лаштунками» після натискання follow‑up. Тепер подивімося, які follow‑up-и взагалі має сенс показувати користувачеві та які ролі вони можуть відігравати в сценарії.
У реальному застосунку є кілька «сімейств» follow‑up-ів. Я поділяю їх за різними вимірами: статичні й динамічні, а також за роллю (drill‑down, pivot, commit тощо).
Для практики зручніше подивитися на таблицю.
| Тип | Що робить | Приклад тексту/кнопки |
|---|---|---|
| Підказковий (Suggestive) | Допомагає, якщо користувач не знає, що запитати далі | «Показати ще ідеї», «Звузити за інтересами» |
| Уточнювальний (Drill-down / Parametric) | Звужує попередній запит за параметрами | «Дешевше», «Лише цифрові подарунки», «Лише Nike» |
| Поворот (Pivot) | Змінює гілку сценарію | «Почати підбір заново», «Показати подарунки для дитини» |
| Навігаційний (Navigation) | Переносить на інший крок процесу | «Перейти до оформлення», «Повернутися до вибору» |
| Завершальний (Commit) | Підтверджує дію | «Замовити цей подарунок», «Зберегти підбір» |
За походженням ідей є дві великі групи.
По‑перше, follow‑up-и, які пропонує сам застосунок. Це наші кнопки у віджеті, привʼязані до логіки UI та даних: наприклад, «Показати схожі на [Назва подарунка]» або «Фільтрувати лише за інтересом travel». Такі підказки можна жорстко прописати (static) або формувати динамічно на основі toolOutput (dynamic).
По‑друге, «рідні» підказки ChatGPT — маленькі підказки-чипи під повідомленням, які модель вигадує сама. Ви їх безпосередньо не контролюєте, тож сприймайте як приємний бонус, а не як надійний механізм. Ваш застосунок у будь-якому разі має працювати навіть без них.
У цій лекції нас більше цікавить перша група: кнопки й підказки, які ви малюєте у своєму React‑компоненті й після натискання надсилаєте через sendFollowUpMessage.
5. Реалізація follow‑up-ів у React: GiftGenius як приклад
Продовжимо наш умовний застосунок GiftGenius, що підбирає подарунки. Після виклику інструмента get_gift_ideas віджет отримує toolOutput зі списком подарунків. У попередніх темах ми вже робили сітку карток. Тепер додамо секцію follow‑up-ів.
Припустімо, у SDK у нас є хуки useWidgetProps і useSendMessage. Назви тут умовні, але концепція збігається з тим, як це влаштовано в довідковій реалізації:
import { useWidgetProps, useSendMessage } from '@/openai-apps';
export const GiftSuggestions: React.FC = () => {
const { toolOutput } = useWidgetProps();
const sendMessage = useSendMessage();
const gifts = toolOutput?.data?.gifts ?? [];
if (gifts.length === 0) {
return <div>Подарунків не знайдено. Спробуйте змінити запит.</div>;
}
const handleCheaper = () => {
sendMessage('Покажи подарунки дешевше, до 50$');
};
const handleDigital = () => {
sendMessage('Покажи лише цифрові подарунки: сертифікати, підписки тощо.');
};
return (
<div className="flex flex-col gap-4">
<div className="grid grid-cols-2 gap-2">
{gifts.map((gift: any) => (
<GiftCard key={gift.id} item={gift} />
))}
</div>
<div className="border-t pt-3 text-sm">
<div className="text-xs text-gray-500 mb-2">Що зробити далі?</div>
<div className="flex flex-wrap gap-2">
<button
onClick={handleCheaper}
className="px-3 py-1 rounded-full bg-gray-100 hover:bg-gray-200"
>
Дешевше
</button>
<button
onClick={handleDigital}
className="px-3 py-1 rounded-full bg-gray-100 hover:bg-gray-200"
>
Лише цифрові
</button>
</div>
</div>
</div>
);
};
Тут важливі кілька моментів.
По‑перше, sendMessage отримує рядок — цей текст зʼявиться в чаті так, ніби його ввів користувач. Ваш застосунок не має імітувати «внутрішній» системний протокол: просто формулюйте фрази так, щоб модель їх правильно зрозуміла.
По‑друге, секція follow‑up-ів візуально відокремлена (розділювальна лінія зверху, дрібний текст «Що зробити далі?»), щоб користувачеві було ясно: це радше продовження діалогу, а не елементи самих карток.
По‑третє, кнопок небагато — дві. UX‑рекомендації радять триматися діапазону приблизно від двох до чотирьох варіантів: цього достатньо, щоб допомогти, але не перевантажити.
Динамічний follow‑up на основі даних
Припустімо, ви хочете дозволити користувачеві заглибитися в конкретний подарунок: «Показати схожі на ось цей». Тоді текст follow‑up має згадувати потрібний об’єкт, і ви можете згенерувати його на ходу.
const handleShowSimilar = (giftTitle: string) => {
sendMessage(
`Покажи схожі подарунки на "${giftTitle}", можна в тому самому бюджеті або трохи дорожче`
);
};
І в картці:
<button
onClick={() => handleShowSimilar(gift.title)}
className="mt-2 text-xs text-blue-600 underline"
>
Показати схожі
</button>
Так ви реалізуєте динамічний follow‑up, привʼязаний до конкретних даних із toolOutput. Саме такі кнопки роблять діалог «розумним», а не просто набором універсальних «Далі / Назад».
6. Чому ми надсилаємо текст, а не напряму викликаємо tool
Типова думка фронтенд-розробника: «Раз у мене є useCallTool, навіщо мені надсилати текст? Я можу відразу викликати get_gift_ideas з іншими параметрами». Іноді це справді потрібно (про це більше — в модулі про виклик інструментів (tools) з віджета), але за замовчуванням краще йти через текстовий follow‑up. Причини доволі практичні.
По‑перше, історія чату залишається цілісною. Ззовні все виглядає так, ніби користувач сам написав: «Покажи подарунки дешевше». За тиждень він відкриє історію й легко зрозуміє, що відбувалося. Якщо ж ви робили все через прямі виклики інструментів, між повідомленнями користувача раптово зʼявлятимуться різні віджети — без видимих причин.
По‑друге, модель може ухвалювати додаткові рішення. Наприклад, зрозуміти, що замість повторного виклику того ж інструмента краще спершу уточнити бюджет: «Ви впевнені, що хочете знизити бюджет до 5 $? Можливо, залишимо хоча б 20 $?» Такі гнучкі сценарії стають неможливими, якщо ви жорстко кодуєте «натискання → той самий tool з іншими аргументами».
По‑третє, модель може викликати зовсім інший інструмент. Припустімо, користувач натиснув «Звʼязатися з підтримкою», а ваш system‑prompt «вчить» модель, що в такому разі слід викликати create_support_ticket, а не get_gift_ideas. Follow‑up як текст дає моделі свободу перемкнутися на інший інструмент.
Тому практичне правило модуля 3: після дії в UI у більшості випадків надсилаємо текстовий follow‑up, а не викликаємо інструмент напряму. Прямі виклики інструментів із віджета залишаємо для специфічних ситуацій, коли нам точно не потрібен новий користувацький крок в історії.
7. Звʼязка follow‑up-ів і станів: не розсинхронізувати UI і текст
Є цікава проблема: UI «живе» своїм життям, а текст у чаті — своїм. Припустімо, після натискання ви змінюєте фільтр у віджеті й одночасно надсилаєте follow‑up. Якщо ви оновили лише UI, але не зафіксували новий стан через widgetState, під час наступного рендера ChatGPT відновить старий widgetState. У результаті віджет знову покаже старі фільтри, хоча в історії чату вже є крок «про дешевше». Досвід виходить доволі дивний.
Тому хороший підхід такий: після натискання follow‑up одночасно:
- оновлювати widgetState,
- надсилати follow‑up-повідомлення.
Приклад:
import { useWidgetState, useSendMessage } from '@/openai-apps';
type GiftWidgetState = {
priceFilter?: 'any' | 'cheap' | 'premium';
};
export const GiftFollowups: React.FC = () => {
const [widgetState, setWidgetState] = useWidgetState<GiftWidgetState>();
const sendMessage = useSendMessage();
const handleCheaper = () => {
setWidgetState({ ...widgetState, priceFilter: 'cheap' });
sendMessage('Покажи подарунки дешевше, приблизно до 50$');
};
// ...
};
Тепер і ChatGPT, і ваш UI «знають», що фільтр змінився. Якщо модель перебудує віджет пізніше, вона побачить оновлений widgetState і зможе, наприклад, згенерувати новий pre‑text на кшталт «Ось ідеї в більш бюджетному сегменті».
8. Проєктування хороших follow‑up-ів
Хороший follow‑up — це половина UX‑успіху. Він не просто «гарний» — він економить користувачеві розумову енергію.
Є кілька практичних принципів, які варто було б повісити собі над монітором.
По‑перше, лаконічність. Follow‑up — не місце для поем. Одна коротка фраза, зрозуміла без контексту UI, зазвичай ідеальна: «Дешевше», «Лише преміум», «Змінити отримувача». Якщо потрібно щось довше, подумайте, чи не має це бути звичайний текст GPT, а не кнопка.
По‑друге, орієнтованість на дію. Формулюйте так, щоб було ясно, що саме відбудеться. «Ще ідеї» — нормально. «Докладніше про пункт 2» краще замінити на «Розкажи детальніше про другий варіант» (так модель зрозуміє, про що йдеться, навіть без UI).
По‑третє, продовження сценарію, а не його повторення. Замість «Ще раз підібрати подарунок» краще «Змінити бюджет» або «Змінити хобі отримувача». Follow‑up має зрушувати користувача вперед або вбік, а не повертати в початкову точку без потреби.
По‑четверте, обмежена кількість. Дві–чотири кнопки внизу віджета — майже завжди достатньо. Плашка з десяти варіантів перетворюється на іспит із вибору долі: користувач губиться й не натискає нічого.
І насамкінець — зважайте на тональність. Якщо весь застосунок спілкується дружньо, дивно вставити кнопку «ПІДТВЕРДИТИ ЗАМОВЛЕННЯ» великими літерами. Follow‑up — частина того ж діалогу, що й текст моделі; стиль має збігатися.
9. «Діалоги навколо віджета» загалом: хто за що відповідає
Важливо не сприймати віджет як «головного героя», а ChatGPT — як «рамку навколо нього». Усе навпаки: модель і далі веде діалог, а віджет — лише один зі способів показати дані та скоригувати їх.
Типовий сценарій для GiftGenius виглядає так:
- Користувач: «Потрібен подарунок сестрі‑айтішниці до 100 $».
- Модель: вступне повідомлення (pre‑text) — пояснює, що зараз відкриє GiftGenius і що він робить.
- Віджет: показує добірку ідей, дає follow‑up-и.
- Користувач: або сам пише повідомлення, або тисне кнопку follow‑up (наприклад, «Показати лише цифрові подарунки»).
- Модель: інтерпретує це як текст, викликає потрібний tool і, за потреби, коментує результат (post‑text) або знову показує віджет.
- І так по колу, доки завдання не буде розвʼязано — аж до підтвердження вибору, замовлення тощо.
Follow‑up-и тут — клей, що поєднує кожен виток цього циклу. Без них користувач після віджета опиняється у підвішеному стані: усе красиво, але «що далі» — незрозуміло.
У складніших сценаріях (workflows, агенти) follow‑up-и допомагають моделювати багатокрокові воронки: «Спочатку обери отримувача», «Тепер уточни бюджет», «Тепер підтвердимо вибір». Але в цьому модулі нам важливо просто побачити: навіть найпростіший однокроковий застосунок дуже виграє від пари продуманих підказок.
10. Практика: що варто зробити просто зараз
Хороша вправа для закріплення — допрацювати ваш поточний навчальний віджет.
Якщо у вас уже є список результатів (наприклад, тих самих подарунків, готелів або документів), додайте під ним невеликий блок «Що далі?» із двома–трьома кнопками. Спробуйте зробити так, щоб ці кнопки відповідали типам із таблиці вище: одна має бути уточнювальною (drill‑down, наприклад, «Дешевше»), інша — поворотом (pivot, «Змінити отримувача»), третя — за потреби навігаційною (navigation, «Перейти до оформлення»).
Усередині обробників натискань викличте useSendMessage зі змістовним текстом природною мовою, а також — за потреби — оновіть widgetState. Потім знову запустіть сценарій у ChatGPT і подивіться, як виглядає діалог: наскільки зрозуміліше стало, що робити після віджета.
Спробуйте також навмисно зробити погані follow‑up-и: довгі, розпливчасті, з десятком варіантів — і порівняйте відчуття. Це швидкий спосіб відчути різницю на собі.
11. Типові помилки під час роботи з follow‑up-ами
Помилка №1: «Мовчазний» віджет.
Розробник робить відмінний UI, але взагалі не дає follow‑up-ів. Користувач бачить картки, думає «ну прикольно», а далі має сам здогадатися, що можна, наприклад, попросити: «Покажи дешевше» або «Зміни отримувача». Більшість людей просто не здогадуються — і йдуть. Мінімум одна–дві підказки «що далі» під віджетом розвʼязують цю проблему.
Помилка №2: Забагато кнопок.
Протилежна крайність — перевантажити користувача десятком варіантів: «Змінити бюджет», «Змінити інтереси», «Змінити валюту», «Зберегти підбір», «Поділитися з другом», «Показати схожі», «Звернутися до підтримки» тощо. Виходить психологічний «шведський стіл», на якому обирати важко. Краще почати з двох–трьох найчастіших дій, а решту залишити моделі та звичайному тексту.
Помилка №3: Логіка діалогу лише на фронтенді.
Іноді намагаються «оптимізувати» і замість надсилання follow‑up через useSendMessage (або низькорівневий sendFollowUpMessage) безпосередньо викликають той самий tool із віджета, оновлюючи UI. В історії чату при цьому немає ані слова про те, що сталося. За кілька кроків модель починає плутатися — а ви разом із нею. Правильний шлях: тримати логіку діалогу на рівні тексту й інструментів, а віджет — як тонкий UI‑шар.
Помилка №4: Неочевидні або двозначні формулювання.
Кнопка «Ще» без контексту може означати що завгодно: ще подарунків, ще тексту, ще грошей? Так само формулювання на кшталт «Перерахувати» або «Перезібрати» незрозумілі і моделі, і користувачеві. Кращі follow‑up-и конкретні: «Показати більше варіантів у цьому бюджеті», «Показати лише цифрові подарунки».
Помилка №5: Несинхронізований UI і текст.
Класика: після натискання «Дешевше» ви оновили UI‑фільтр, але не надіслали follow‑up у чат або не оновили widgetState. У результаті в історії немає кроків про зміну бюджету, а під час наступного рендера віджета фільтр «повернувся» назад. Зʼявляється відчуття «зламаного» інтерфейсу. Використовуйте комбінацію setWidgetState + sendMessage, щоб текст і UI крокували в ногу.
Помилка №6: Спроба контролювати «вбудовані» підказки ChatGPT.
Іноді розробники розраховують, що ChatGPT сам згенерує потрібні follow‑up-чипи під повідомленням, і не додають свої. Але ці підказки не гарантовані й не керуються застосунком. Ставтеся до них як до приємного бонусу, але завжди додавайте власні, критично важливі follow‑up-кнопки у віджеті.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ