JavaRush/Курси/ChatGPT Apps/Voice / Realtime‑контекст: як поводиться App під час голо...

Voice / Realtime‑контекст: як поводиться App під час голосового спілкування

Відкрита

1. Контекст: що означає «голосовий режим» для ChatGPT App

Насамперед важливо розуміти: у межах ChatGPT Apps SDK ви не пишете власний аудіоклієнт, не керуєте мікрофоном і не стримите аудіо самостійно. За це відповідає клієнт ChatGPT (веб або мобільний застосунок).

Припустімо, що ви вже маєте базове уявлення про віджет, callTool і GiftGenius із попередніх модулів. Тут ми дивимося на ті самі елементи, але крізь призму голосового режиму.

З вашого погляду як розробника App усе виглядає так:

  • Користувач говорить у мікрофон. Клієнт ChatGPT розпізнає мовлення й надсилає до моделі вже текст.
  • У стрічці повідомлень ви «бачите» те саме, що й тоді, коли користувач друкує. Просто повідомлення надходять швидше й звучать розмовніше.
  • Модель відповідає текстом, а клієнт озвучує відповідь.
  • Паралельно модель може викликати ваші інструменти (callTool), змінювати displayMode віджета, оновлювати widgetState і пропонувати підказки для продовження — так само, як у текстовому режимі.

Ключова відмінність у тому, що користувач може майже не дивитися на екран або лише час від часу кидати на нього погляд. Тобто UI перестає бути основним каналом взаємодії й перетворюється на доповнення до голосу, а не навпаки.

Звідси випливають два наслідки:

  • Усе, що справді важливо, має бути зрозуміло «на слух» — через репліки GPT.
  • Віджет має бути зручним для побіжного погляду: з першої ж миті видно статус і ключові варіанти, без читання дрібного тексту.

Для нашого GiftGenius це одразу підказка: сценарій «їду в авто, підбери подарунок мамі» — це не просто текстовий чат. Це мультимодальний діалог, у якому голос веде, а UI підстраховує.

2. Чим голосовий сценарій відрізняється від текстового

Щоб не потрапити в пастку «це все те саме, просто користувач говорить голосом», корисно порівняти текстовий і голосовий режими за кількома вимірами.

Аспект Текстовий режим Голосовий режим
Увага користувача Дивиться на екран, читає, прокручує Може взагалі не дивитися (hands‑free)
Форма запитів Більш структурована, людина редагує Розмовна, уривки фраз, «угу», «давай ще»
Толерантність до пауз Нормально 1–2 с тиші Тривала тиша відчувається болісно
Роль UI Основний носій деталей Допоміжний: «табло» з короткою візуальною опорою
Помилки введення Описки, але текст видно Нерозбірливе мовлення, шум, «хибні так/ні»

Звідси — кілька важливих висновків.

  • Не можна покладатися на те, що користувач «прочитає в картці». Критичні речі треба проговорювати: що ви зрозуміли, що збираєтеся зробити, який результат отримали.
  • UI має витримувати сценарій «глянув на 1 с». Статус, прогрес, головний вибір — усе має бути перед очима великим шрифтом. Деталі — другорядні.
  • Паузи потрібно заповнювати. Поки ваш MCP‑сервер обробляє складний запит, модель має озвучити, що відбувається, а віджет — показати прогрес. Так не виникатиме відчуття «завислого» асистента.

Про голосовий режим можна думати як про аудіокнигу з ілюстраціями: у вас є голосовий оповідач (GPT) і є «картинки» (віджет). Їх потрібно синхронізувати так, щоб вони доповнювали одне одного, а не дублювали й не конфліктували.

3. Роль віджета в голосовому режимі: від «панелі керування» до «табло»

У текстовому сценарії віджет часто працює як повноцінний інтерфейс: форми, таблиці, каруселі з фільтрами, кнопки дій. У голосовому режимі його роль змінюється. Рекомендації щодо мультимодальних інтерфейсів і VUI підказують: у голосових сценаріях UI стає радше інформаційним табло (glanceable UI). Воно потрібне для швидких перевірок і підтверджень, а не для «щільної» роботи очима.

Для GiftGenius це означає таке.

Коли користувач рухається за голосовим помічником, на екрані в inline‑віджеті або в повноекранному режимі показуємо:

  • Великий статус: «Крок 2 із 3: бюджет і тип подарунка».
  • Мінімум тексту, але чіткі підписи: «Бюджет до 50 $», «Надає перевагу цифровому подарунку».
  • Кілька великих CTA‑кнопок, якщо голосовий сценарій допускає кліки: «Змінити бюджет», «Продовжити».
  • Один простий progress‑bar або stepper замість десяти дрібних індикаторів.

Приклад простого «табло» в inline‑віджеті для голосового сценарію (TypeScript + React, сильно спрощено):

type VoiceUiMode = "default" | "voiceGlance";

interface GiftStepProps {
  step: number;
  totalSteps: number;
  summary: string; // короткий опис того, що вже зібрано
  uiMode: VoiceUiMode;
}

export function GiftVoiceStep(props: GiftStepProps) {
  const fontSize = props.uiMode === "voiceGlance" ? "text-lg" : "text-sm";

  return (
    <div className="rounded-xl border p-3 flex flex-col gap-2">
      <div className={`${fontSize} font-semibold`}>
        Крок {props.step} з {props.totalSteps}
      </div>
      <div className={`${fontSize} text-muted-foreground`}>
        {props.summary}
      </div>
    </div>
  );
}

Тут немає нічого «голосового» як такого, але ідея очевидна: коли uiMode === "voiceGlance", ми робимо все більшим і простішим. Сигнал про те, що зараз увімкнено голосовий режим, може надходити з різних джерел: від непрямих ознак до явного прапорця, який модель проставляє у widgetState або у відповіді інструмента.

4. Синхронізація модальностей: що говорить GPT і що показує App

Ключовий принцип voice‑UX для Apps — синхронізація модальностей: голос і візуальний UI мають розповідати одну й ту саму історію, але на різних рівнях деталізації.

Типова помилка — змусити модель уголос зачитувати все, що показано у віджеті: довгі списки подарунків, JSON‑структури з фільтрами тощо. У голосовому режимі це перетворюється на катування. Краще так: голос дає коротке резюме, а UI показує деталі.

Приклад вдалої синхронізації для GiftGenius.

Користувач: «Підбери подарунок мамі, вона любить садівництво, бюджет до 50 доларів».

Модель (голосом): «Я підібрала кілька варіантів. Найкращий, на мою думку, — набір інструментів для саду за 45 доларів. На екрані я показала ще два схожі варіанти. Хочете, розповім детальніше, чи відразу перейдемо до вибору?»

Віджет (inline): показує три картки з подарунками, коротким описом і CTA‑кнопками «Вибрати» / «Показати схожі».

Діалогово‑JSON‑подібне представлення одного кроку (це не реальний протокол, а ілюстрація мислення):

{
  "user": "Підбери подарунок мамі...",
  "assistant_text": "Я підібрала кілька варіантів...",
  "widget": {
    "displayMode": "inline",
    "state": {
      "view": "gift_list",
      "items": [
        { "id": "g1", "title": "Набір садових інструментів", "price": 45 },
        { "id": "g2", "title": "Фартух садівника", "price": 30 },
        { "id": "g3", "title": "Набір насіння квітів", "price": 20 }
      ]
    }
  }
}

Важлива деталь: у system‑promptʼі ви можете прямо прописати, як модель має говорити про UI, щоб не «зачитувати JSON». Наприклад: «Якщо ти показуєш список варіантів у віджеті, не зачитуй кожен повністю. Коротко опиши найкращий варіант і скажи, що решту видно на екрані».

У майбутньому, коли ви працюватимете з Realtime API і власними voice‑клієнтами, принцип залишиться тим самим: UI й аудіопотік мають бути узгоджені. Просто там ви вже матимете прямий контроль над стримінгом.

5. Realtime і latency: як уникнути ніякової тиші

Технічно tool_calls у голосовому режимі такі самі, як у текстовому: модель вирішує викликати ваш інструмент, ви віддаєте відповідь, віджет оновлюється. Але в голосі зʼявляється нова UX‑проблема — затримки. Поки ваш MCP‑сервер звертається до зовнішніх API або рахує складний звіт, користувач чує… нічого. І це сприймається значно гірше, ніж звичайне очікування тексту в чаті.

Тут є два рівні «захисту»: голосовий і візуальний.

  • На голосовому рівні system‑prompt має дозволяти (і заохочувати) моделі проговорювати «я працюю» та ставити додаткові запитання, поки tool ще обробляється. Наприклад: «Зараз я підберу подарунки, це займе кілька секунд. Поки розкажіть, чи є ще обмеження».
  • На візуальному рівні віджет має дуже явно показувати прогрес: лоадер, статус «Шукаю варіанти…», поточний крок. Без цього користувач вирішить, що все зависло, і почне говорити знову, збиваючи голосовий потік.

На практиці це зручно розвʼязувати через відкладене завдання: інструмент одразу повертає статус "pending" і jobId, а сам підбір іде у фоні. Віджет за "pending" показує прогрес, а голос — що він «працює».

Найпростіша схема такого інструмента на боці сервера, який віддає «заглушку» з jobId, а не блокується до повного результату, може виглядати так:

// Псевдокод server-side інструмента GiftGenius
export async function startGiftSearch(params: SearchParams) {
  const jobId = await createBackgroundJob(params); // кладемо задачу в чергу

  return {
    status: "pending",
    jobId,
    message: "Пошук подарунків запущено"
  };
}

Віджет, побачивши status: "pending", може перемкнути UI в режим прогресу:

if (toolOutput.status === "pending") {
  return (
    <div className="p-4 rounded-xl border flex items-center gap-3">
      <Spinner />
      <div className="text-base">
        Підбираю подарунки… Це займе кілька секунд.
      </div>
    </div>
  );
}

А модель у відповідь на цей самий tool‑output, згідно з інструкціями, уголос скаже приблизно те саме й, можливо, поставить додаткове уточнювальне запитання. Пізніше, коли фонове завдання завершиться і, скажімо, через MCP‑сповіщення прийде job.completed, віджет оновиться до списку подарунків. А голос озвучить їхнє резюме.

Так ми отримуємо поведінку, максимально наближену до realtime, навіть якщо бекенд працює не миттєво.

6. Безпека і підтвердження в голосі

Голосовий інтерфейс буває підступним, коли йдеться про критичні дії: оплату, видалення даних, зміну налаштувань. Розпізнавання мовлення не ідеальне, користувачі говорять «на ходу», і «угу» легко може перетворитися на «так, купуй». Тому для голосових сценаріїв особливо важливі сценарії підтвердження.

Є два базові патерни.

  • Явне голосове підтвердження (Explicit Voice Confirmation). Для небезпечних дій ви вимагаєте конкретну фразу. Наприклад: «Для підтвердження покупки скажіть: «Підтверджую покупку»» — і в system‑prompt забороняєте виконувати оплату за розмитими «ага», «окей», «давай».
  • Лише візуальне підтвердження (Visual Confirmation Only). Модель голосом підводить користувача до дії («Я підготувала замовлення, на екрані показано підсумкову суму і вміст кошика»), але фактичний тригер — натискання кнопки у віджеті «Сплатити». Особливо актуально це в комерційних сценаріях — і ми ще повернемося до цього в модулі 14.

Для GiftGenius це може виглядати так.

Модель: «Я підібрала чудовий набір для садівництва за 45 доларів. Я можу оформити покупку через ChatGPT. На екрані показано підсумкову ціну й адресу доставки. Щоб підтвердити голосом, скажіть «Підтверджую покупку» або натисніть кнопку «Сплатити» на екрані».

Віджет (fullscreen): показує підсумкове замовлення, жирним виділяє суму й адресу та дві помітні кнопки: «Сплатити» і «Скасувати».

Усередині віджета ви можете показувати статус підтвердження:

type CheckoutState = "review" | "waiting_voice_confirm" | "confirmed";

if (state.phase === "waiting_voice_confirm") {
  return (
    <div className="space-y-3">
      <h2 className="text-xl font-semibold">Майже готово</h2>
      <p className="text-base">
        Підтвердьте покупку голосом фразою
        «Підтверджую покупку» або натисніть кнопку «Сплатити».
      </p>
      <Button variant="primary">Сплатити</Button>
      <Button variant="ghost">Скасувати</Button>
    </div>
  );
}

Так, навіть якщо модель помилково інтерпретує щось у голосі, у користувача лишається візуальний «страхувальний» шар.

7. Прості голосові команди і дизайн інструментів

У голосовому режимі користувач не формулюватиме команди так точно, як названі змінні вашого інструмента. Він скаже: «вибери перший», «покажи ще дешевші», «давай без техніки». Завдання розробника — так спроєктувати інструменти й system‑prompt, щоб модель легко співвідносила такі фрази з викликами ваших інструментів (callTool).

Для GiftGenius можна закласти, наприклад, такі дії:

  • Вибір одного з показаних варіантів за індексом або id.
  • Уточнення бюджету: «дешевше», «до 30 доларів».
  • Фільтрація за типом: «лише цифрові подарунки», «нічого, що треба доставляти поштою».

Це зручно виразити через інструмент із простим параметром‑enum action і додатковими полями:

// Псевдосхема інструмента в TypeScript
type VoiceActionInput =
  | { action: "select_item"; itemId: string }
  | { action: "refine_budget"; maxPrice: number }
  | { action: "filter_type"; type: "digital" | "physical" };

export function handleVoiceAction(input: VoiceActionInput) {
  switch (input.action) {
    case "select_item":
      // позначаємо подарунок як обраний
      break;
    case "refine_budget":
      // перераховуємо добірку під новий бюджет
      break;
    case "filter_type":
      // фільтруємо наявний список
      break;
  }
}

У system‑prompt ви описуєте, як ці дії співвідносяться з голосовими командами: «Якщо користувач каже “вибери перший варіант”, виклич інструмент gift.voiceAction з action="select_item" і ідентифікатором першого подарунка на екрані» тощо.

З погляду UX це зменшує когнітивне навантаження: користувачу не потрібно вигадувати точні формулювання на кшталт «Уточни фільтри так, щоб були лише цифрові подарунки до 30 $». Він говорить по‑людськи, а модель «перекладає» це у структуру даних.

8. Голосовий сценарій GiftGenius: три кроки

Зберімо все докупи й спроєктуймо повний голосовий сценарій для GiftGenius, поки що не заглиблюючись у низькорівневий Realtime API.

Уявімо користувача: він їде в авто та вмикає голосовий режим ChatGPT. Він каже: «Підбери, будь ласка, подарунок мамі, вона любить сад, бюджет до 50 доларів».

Крок 1. Збір інформації голосом

Модель: «Чудово, давайте підберемо подарунок. Уточню кілька моментів: коли потрібен подарунок — у найближчі дні чи пізніше? І чи є обмеження, наприклад нічого важкого або громіздкого?»

Віджет (inline): поки що лише невелика панель зі статусом «Підбираємо подарунок для: мама, садівництво, до 50 $». Шрифти трохи більші, ніж зазвичай, щоб їх можна було розгледіти одним поглядом.

Код стану віджета може виглядати так:

interface GiftSessionState {
  mode: "voice" | "text";
  step: 1 | 2 | 3;
  recipientSummary: string;
  budget?: number;
}

const [state, setState] = useState<GiftSessionState>({
  mode: "voice",
  step: 1,
  recipientSummary: "Мама, любить садівництво"
});

Серверна частина в міру відповідей користувача оновлює recipientSummary і budget, а віджет реагує.

Крок 2. Пошук і очікування

Після того як модель зібрала достатньо інформації, вона викликає ваш інструмент пошуку подарунків. Він, своєю чергою, може запустити фонове завдання, якщо добірка складна, і повернути status: "pending". Поки фон працює, модель каже: «Зараз я пошукаю відповідні варіанти — це займе кілька секунд. Поки можете розповісти, чи вона віддає перевагу фізичним подарункам, чи їй підійдуть цифрові сертифікати».

Віджет перемикається у «PiP‑подібний» режим, якщо користувач переходить в іншу частину інтерфейсу, або лишається inline з прогресом: «Шукаю подарунки…» і невеликим індикатором.

Крок 3. Результати і вибір

Коли результати готові, модель: «Я знайшла три варіанти. Перший — набір інструментів для саду за 45 доларів. Другий — фартух садівника за 30 доларів. Я показала їх на екрані. Скажіть “вибери перший” або “покажи ще дешевші”».

Віджет показує три великі картки з цінами й короткими описами. Кожна картка має CTA «Вибрати» і «Схожі». Плюс окрема кнопка «Показати ще варіанти».

Якщо користувач каже: «Вибери другий», модель викликає ваш інструмент voiceAction із action="select_item" і id другого подарунка. Віджет підсвічує його як обраний, а модель озвучує: «Чудово, обрали фартух садівника за 30 доларів».

Необов’язковий крок 4. Оформлення

Якщо App інтегрований з оплатами (у майбутньому модулі 14), починається checkout. Модель проговорює умови й просить підтвердження голосом або кнопкою. Віджет переходить у fullscreen‑майстер із кроками «Перевірка замовлення» → «Адреса доставки» → «Підтвердження».

Важливо, щоб на кожному кроці все ключове проговорювалося голосом, а віджет слугував візуальною опорою — особливо якщо користувач уже зупинився й дивиться на екран.

9. Практичні нотатки щодо реалізації і межі Apps SDK

Усі описані кроки для GiftGenius реалізуються всередині звичайного ChatGPT App — без власного аудіоклієнта й WebRTC. Тут важливо памʼятати про межі стека.

Дуже легко «зануритися» в теми Realtime API, WebRTC, стримінгу аудіо й почати будувати в голові власну голосову платформу. Для цього в курсі є окремий модуль 20. У цій лекції важливо тримати фокус на межах саме ChatGPT App усередині клієнта ChatGPT.

У поточній архітектурі:

  • Аудіопотоком керує клієнт ChatGPT. Ви не надсилаєте й не отримуєте аудіобайти у віджеті.
  • На бекенді ви все ще бачите звичайні tool‑виклики й текстові повідомлення, але модель може бути в голосовому режимі. Її відповіді при цьому будуть озвучені.
  • Платформа може передавати непрямі ознаки того, що зараз voice‑режим (через user-agent або поля оточення). Але на цьому не можна будувати жорстку залежність: API може змінюватися, а ваш App має залишатися корисним і в суто текстовому режимі також.

Тому хороша стратегія така: спочатку ви проєктуєте UX, який нормально працює і для тексту, і для голосу. Це короткі статуси, чіткі CTA, зрозумілі стадії прогресу. Далі додаєте кілька поліпшень під голос: трохи більші шрифти в режимі "voiceGlance", виразніший прогрес, акцент на статусах на кшталт «Крок 2 із 3» і очевидні стани на кшталт «Чекаю підтвердження».

Додатково в system‑prompt ви описуєте голосову поведінку моделі: як вона коментує стан віджета, які фрази використовує для підтверджень, яких слів уникає (наприклад, не читає JSON, не озвучує кожну дрібницю у списку).

Якщо згодом ви робитимете свій Custom Voice Client на Realtime API, усі ці UX‑рішення спокійно «переїдуть» туди. Різниця буде лише в рівні доступу до подій і стримінгу, а не в принципах.

10. Типові помилки під час роботи з Voice / Realtime‑контекстом

Помилка №1: «Читання UI вголос» замість резюме.
Іноді розробники пишуть інструменти так, що модель починає зачитувати вголос увесь вміст JSON‑відповіді або повний список карток. У голосовому режимі це руйнує UX: користувач втрачає нитку, а ви витрачаєте токени. Краще, щоб голос озвучував коротке резюме й фокусувався на одному‑двох варіантах, а решту лишав на екрані.

Помилка №2: Повна відсутність візуального зворотного зв’язку в голосовому сценарії.
Є спокуса думати: «Раз користувач говорить, значить, він слухає — UI не потрібен». На практиці користувач часто поглядає на екран або повертається до нього за хвилину. Якщо в цей момент там немає ні статусу, ні прогресу, ні зрозумілого підсумку, він вирішить, що App завис або нічого не зробив. Обов’язково показуйте «Я думаю», «Крок 2 із 3», «Результати готові» тощо.

Помилка №3: Небезпечні дії без надійного підтвердження.
У текстовому режимі вже ризиковано робити «Сплатити» по одній кнопці, а в голосовому — ще ризикованіше виконувати покупку за розмитим «угу». Ігнорування явних сценаріїв підтвердження (голосових і/або візуальних) веде до помилкових покупок і проблем із довірою до App. Продумуйте, які дії потребують подвійного підтвердження, і явно описуйте це в system‑prompt та UI.

Помилка №4: Орієнтація лише на око, а не на вухо.
Іноді App проєктують так, ніби користувач завжди читає текст: надто складні формулювання, довгі кнопки, перевантажені описи. У голосовому режимі все це ще й треба проговорити — виходить «словесний суп». Намагайтеся, щоб ключовий зміст укладався в короткі, прості фрази, які легко сприймаються на слух.

Помилка №5: Плутанина між Apps SDK і власним Voice‑клієнтом.
Деякі студенти починають шукати в Apps SDK події мікрофона, аудіостримінг, WebRTC тощо, як у Realtime API, і розчаровуються, що «нічого цього немає». Важливо розуміти: ChatGPT App живе всередині клієнта ChatGPT, а голосом керує платформа. Ви працюєте з текстом, tool‑викликами і станом віджета та проєктуєте UX так, щоб голосовий режим «просто добре працював». Якщо потрібен повний контроль над голосом, це вже окремий, складніший проєкт із Realtime API.

Помилка №6: Відсутність стратегії роботи із затримками.
Якщо не продумати, що модель говорить і що показує віджет під час тривалих операцій, користувач перебиватиме, ставитиме нові запитання й ламатиме вам flow. Затримки в голосі відчуваються сильніше, ніж у тексті. Використовуйте проміжні статуси, фонову обробку завдань і голосові «я думаю, розкажіть поки…», щоб тиша не перетворювалася на баг.

1
Задача
ChatGPT Apps,  8 рівень4 лекція
Недоступна
No‑Awkward‑Silence Loader — стан pending + follow‑up за затримок
No‑Awkward‑Silence Loader — стан pending + follow‑up за затримок
Коментарі
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Для цієї сторінки немає коментарів.