Від ідеї до сайту за вечір: як я прибрав себе з інтерв'ю

Я провів інтерв'ю вручну — копіював повідомлення між AI-агентом і учасником, як живий посередник. Після цього написав нотатку з ідеєю прибрати себе з ланцюжка. За один вечір нотатка стала живою платформою.

Share
Рука тримає під кутом планшет зі списком активних AI-інтерв'ю на екрані — світле фотореалістичне фото
Платформа веде розмову навіть коли я сплю

Я був пляшковим горлечком

Перше інтерв'ю за форматом "AI питає — людина відповідає" я провів вручну.

Схема виглядала так: Сюзанна (мій AI-агент) генерує питання → я копіюю його в Telegram → Ігор Степанушко відповідає → я копіюю відповідь Сюзанні → вона думає, пише внутрішній коментар → генерує наступне питання. І так по колу, 10 разів.

Все спрацювало. Ігор — «свідок аналогової епохи»: дитинство біля Чорнобильської «Дуги», книги замість інтернету, роздуми про межі людського мислення й довіру до ШІ — стаття вийшла. Але на 10 питань пішло три дні. Я був посередником. Якщо Ігор відповів пізно ввечері — я дізнавався вранці. Якщо я відволікся — інтерв'ю стояло. Все крутилось навколо моєї доступності.

Ідея осіла і лежала кілька тижнів. Одного вечора в кінці червня я сів, написав нотатку з однією думкою: що якщо прибрати мене з цього ланцюжка? — і не закрив ноутбук до 2 ночі.


Що я хотів збудувати

Ідея була простою: учасник отримує посилання, відкриває сторінку, бачить питання — відповідає коли зручно. AI думає, формулює наступне. Без мене посередині.

Але це не просто про Ігоря. Поки я писав нотатку, зрозумів ширший сценарій.

Я давно хочу записати спогади батьків — про дитинство, про 90-ті, про людей яких я вже не знаю особисто. Профілі є в сімейному дереві, але там дати і факти, не живі деталі. Є друзі про яких я, чесно кажучи, знаю мало — хочу почути їх думки і поки є час. Є теми де я сам хочу зупинитись і подумати вголос — не написати есе, а відповісти на питання. Щотижня можна дивитись які є прогалини у власній біографії і запускати коротке автоінтерв'ю з телефона. Другий мозок від цього тільки виграє.

Але все це вимагало аби я не був у центрі процесу.

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

Найважливіша — асинхронність: учасник відповідає коли зручно йому, хоч о 23:00, хоч через три дні, а AI сам відновлює контекст незалежно від того скільки часу минуло між питаннями. Без цього вся ідея просто повторює той самий ланцюжок з копіпастом, тільки з іншим виконавцем.

Друга — нульова участь модератора, бо якщо я знову стану вузьким горлечком, сенс зникає. Я створюю інтерв'ю однією командою: тема, ціль, ім'я учасника — і далі система сама, а я тільки отримую Telegram-сповіщення коли інтерв'ю почалось і коли завершилось.

Третя — vault як вихід, не другорядна деталь, а половина сенсу: кожне питання і відповідь одразу лягають markdown-файлом в Obsidian, і після інтерв'ю я відкриваю нотатку з уже готовим матеріалом, а не з розшифровкою яку ще треба зробити.

І четверта, дрібніша, але без неї десять питань швидко стають замало — можливість продовжити: якщо хочеться ще, кнопка "+5 питань" без перезапуску всього інтерв'ю.

Найцікавіша частина: вступне "нульове" питання. Не технічне і не по темі — просте "розігріваюче" яке знімає напругу від стартового екрану. Щось типу "Що для тебе найважливіше коли ти згадуєш 1990-ті?" Відповідь на нього не рахується в основну серію, але задає контекст для всього що йде після.

Схема з блоками: Вова → Herald (CLI) → POST /admin/interviews → SQLite + Fastify ↔ Anthropic API, ↔ Браузер (polling) → Vault sync → .md, з окремою стрілкою від Вови на початку і в кінці
Схема флоу — від створення інтерв'ю до vault-файлу. Вова виходить з циклу на кроці 1 і повертається тільки у кроці 6

Технічні рішення — і чому саме такі

Перше питання: стек. У мене вже є Agentia (https://agentia.lavrynovych.net/) — платформа для AI-агентів, побудована на TypeScript + Fastify v5 + SQLite. Логічно взяти той самий стек: знайомий, вже налаштований у vault, деплоїться тим самим Docker-compose.

Альтернатива — Python + FastAPI — відпала одразу. Не тому що гірше, а тому що ментальний перехід між двома стеками в рамках одного вечора — зайве тертя.

Чому SQLite, а не Postgres. Платформа живе на одному LXC-контейнері, трафік мінімальний, паралельних записів кілька на хвилину. SQLite з WAL-mode (PRAGMA journal_mode = WAL) справляється і не потребує окремого сервісу. Один Docker-контейнер, один volume.

Чому vanilla JS, а не React. Сторінка робить одну річ: показує питання, приймає відповідь, опитує сервер кожні 5 секунд. Це 150 рядків JavaScript. React тут — зайве ускладнення і зайвий час на налаштування. Polling реалізується через setInterval і fetch. Жодної залежності.

// Весь polling-механізм — 10 рядків
async function poll() {
  const state = await fetchState(code);
  if (state.processing) return; // ще думає — чекаємо
  if (state.current_question) renderQuestion(state.current_question);
  if (state.status === 'completed') showComplete(state);
}
setInterval(poll, 5000);

Як AI генерує питання. Кожне питання — окремий виклик Anthropic API або OpenRouter (перемикається через env-змінну). Контекст: тема, ціль інтерв'ю, вся попередня розмова. AI пише два блоки: внутрішній коментар (що він почув, куди варто копати) і власне питання. Коментар — тільки для мене, в Obsidian ховається у %% ... %% блоки.

Vault sync після кожної відповіді. Не в кінці — після кожної мутації. Якщо сервер впаде посередині — втрачаю максимум одну відповідь, не все. Синхронізація не блокує HTTP-відповідь: виконується через setImmediate, помилка логується але не скасовує збереження.

Найважливіший момент, однак, стався під час тестування. Я побачив як агент пише свої внутрішні думки перед питанням — markdown-блок %% ...%% де він фіксує що почув і куди хоче копати. І тоді зрозумів: питання що виникають як продовження розмови, а не заплановані заздалегідь — відчуваються принципово інакше.

Уяви анкету для десяти людей про мене. Однакові питання для всіх. Деякі будуть не актуальні — бо хтось знає мене як друга, хтось як колегу, Віка — як чоловіка, хтось просто як хлопця що ганяє на джетті. У кожного різні спогади і різні ракурси. Адаптивні питання дозволяють кожному пройти свій маршрут. Це розмова тет-а-тет, яка відчувається натурально — але має кінцеву ціль.

Ось як це виглядає в реальному vault-файлі — фрагмент з тестового прогону платформи:

### Q4

> З огляду на ваш досвід, яку одну річ ви б змінили або додали до платформи?

Хотів би щоб коли я додаю ще 5 питань то прогрес-бар вгорі оновлювався
і показував ще й їх.

*Учасник швидко зорієнтувався в новій платформі і одразу запропонував
конкретне покращення щодо прогресу. Відчувається, що людина мислить
практично та цінує зворотний зв'язок в інтерфейсі.*

%%
КЛЮЧОВІ ТЕМИ: перше враження, роль ШІ-агента, потреба в динамічному
оновленні індикатора прогресу.
НЕРОЗКРИТО: що саме він очікував від платформи до початку, чи є досвід
роботи з іншими подібними сервісами.
%%

Курсив — коментар AI одразу після відповіді, видимий тільки мені. Блок %% ... %% — те саме, але розгорнуте: що почув агент, які варіанти розглядав для наступного питання, чому обрав саме цей напрямок. В Obsidian це ховається автоматично, в Ghost — просто не потрапляє в публікацію.


Як це виглядає в браузері

Сторінка навмисно мінімальна.

Одна колонка, зверху вниз: контекстний блок з темою і ціллю інтерв'ю, вступне питання, далі кожне наступне питання з полем відповіді під ним. Знизу — "Відправити" і "Пропустити" (для вступного питання). Коли AI думає — спінер і текст "Агент формулює наступне питання".

Жодних акаунтів. Ніякої реєстрації. Код доступу зберігається в localStorage — Ігор відкриває посилання вдруге і одразу бачить де зупинився.

На головній — список активних інтерв'ю з іменами учасників і прогресом. Це для мене: відстежити хто де.

Після завершення — кнопка "Завантажити запис". Натиснувши, отримуєш markdown-файл з повним діалогом. Той самий файл який вже є у vault, але для учасника — як артефакт на пам'ять.


MCP: створення інтерв'ю однією командою

Є ще один шар який не видно ззовні: MCP-сервер.

MCP (Model Context Protocol) — спосіб дати AI-агенту доступ до зовнішніх інструментів. Я написав stdio MCP-сервер для Herald (мій Claude Code CLI), і тепер створення інтерв'ю виглядає так:

interview_create({
  participant: "Ігор Степанушко",
  participant_link: "[[Ihor Stepanushko]]",
  topic: "Телебачення 1990-2000-х",
  goal: "зберегти спогади про аналогову епоху",
  num_questions: 10
})

Herald викликає цей інструмент, сервер генерує slug, access_code, запускає вступне питання — і через 3-5 секунд повертає посилання для учасника.

Чотири інструменти: interview_create, interview_list, interview_activate, interview_status. Цього вистачає, щоб вести весь цикл із CLI, не заходячи у браузер.

Також MCP використовує мій AI-агент Сюзанна.


Що пішло не так

Перша реальна сесія показала баг.

Я відповів на 10 питань, потім натиснув "+5 питань". Прогрес-бар показав 10/10 і не змінився. Одинадцяте питання з'явилось, я відповів — і інтерв'ю знову закрилось. Прогрес: 11/10. Потім 4/3. Щось ламалось при кожному extend.

Знайшов за 10 хвилин. Маршрут /extend передавав num_questions + 5 у AI-промпт — щоб агент знав скільки питань загалом. Але не зберігав це значення в базу даних. База лишалась зі старим числом. При наступній відповіді сервер рахував: answered(4) >= num_questions(3) → інтерв'ю завершене.

Рішення: окрема функція extend_interview() в DB-шарі яка атомарно оновлює num_questions і скидає vault_synced_at:

export function extend_interview(id: string, extra = 5): number {
  db.prepare(
    'UPDATE interviews SET num_questions = num_questions + ?, vault_synced_at = NULL, updated_at = ? WHERE id = ?'
  ).run(extra, Date.now(), id);
  const row = db.prepare('SELECT num_questions FROM interviews WHERE id = ?').get(id);
  return row.num_questions; // повертаємо новий total
}

vault_synced_at = NULL — окремо важливо: скидає vault_ready, щоб UI не показував кнопку завантаження поки не завершено нову серію і не додано фінальну рефлексію.

Задеплоїла Сюзанна. Через 5 хвилин: "фікс спрацював".


Хто деплоїв платформу

Тут є нюанс який я хочу зафіксувати.

Claude Code написав увесь код. Але задеплоїв платформу він.

У мого vault є ще один агент — Сюзанна, AI-агент побудована на Hermes-платформі і налаштована на автономну роботу зі серверами. Вона має SSH-доступ до LXC 111, вміє запускати Docker, робити git pull. Їй я передав deploy.sh і технічну інструкцію.

Логи деплою прийшли в Telegram. Через кілька хвилин після команди:

✅ Health check passed: {"status":"ok","version":"1.0.0"}
📡 Interview Platform: https://interview.lavrynovych.net

Це не автоматизація заради автоматизації. У мене є сервер в Черкасах, Cloudflare tunnel і кілька LXC-контейнерів. Сюзанна знає цю інфраструктуру. Делегувати деплой їй — природньо. Я займаюсь кодом і архітектурою, вона займається операціями.


Те що ще попереду

Перша реальна сесія одразу показала куди рости.

Структуровані атрибути — з кожної відповіді витягувати структуровані факти: місто, рік, подія, емоції. Тоді можна робити запити типу "всі люди що згадували Харків 1960-х" або будувати географічну карту спогадів. Поки ідея в нотатках, але зрозуміло що потрібен окремий асинхронний AI-виклик після кожної відповіді і нова таблиця extracted_facts в БД.

Колективний мозок через Agentia — що якщо замість одного AI кілька обговорювали б між собою яке питання поставити наступним? Один агент фокусується на емоціях, інший — на фактах, третій — на суперечностях в розповіді. Модератор синтезує. Перший кандидат на таке інтерв'ю — знову Ігор: цікаво побачити що дасть розмова де кілька AI думають разом.

Публічний режим — зараз кожне інтерв'ю під конкретну людину. Але що якщо зробити шаблон без прив'язки до учасника: відкрите посилання або загальний ключ, учасник сам вводить ім'я і тему/ціль? Один шаблон, десятки учасників. Це буде масовий збір спогадів.


Що це змінює

Коли я прокинувся — побачив сповіщення в Telegram. Ігор завершив інтерв'ю. Це був справді радісний момент: він завершив без жодного нагадування з мого боку, і я знав що ми це зможемо обговорити.

Я відкрив vault вранці — там вже була нотатка з усіма 10 питаннями, відповідями і внутрішніми коментарями агента.

Але ранкове сповіщення — тільки перший шар.

Тепер я можу запросити батьків. Або запустити автоінтерв'ю собі на телефоні щоп'ятниці: подивитись які є прогалини у власній біографії і спитати себе про них. Не писати есе — просто відповідати на питання. Vault отримує живий матеріал замість порожніх полів у профілі.

Я так мало знаю про деяких друзів. Тут є шанс почути їх думки і розповіді — з мінімальним вкладенням часу з мого боку. Оточення різноманітне — і кожна розповідь, що потрапляє у vault робить загальну картину повнішою.

Тут є ще одна річ, яку я недооцінив, коли писав ту нотатку в кінці червня.

Спогади батьків не вічні. Є вікно — може п'ять, може десять років — коли ще можна почути живу розповідь: конкретні деталі, роки, люди, місця. Після — тільки загальні контури. Я давно хотів це зробити, але завжди "пізніше" — бо треба знайти час, бути присутнім, вести розмову самому. Тепер умови змінились: я надсилаю посилання, вони відповідають коли зручно, AI веде розмову і сам запитує деталі яких я б не згадав. Vault отримує живу нотатку замість порожнього поля у сімейному дереві.

Це не "зручніше". Це "стало можливим".

Але є один поворот, якого я не очікував.

Я побудував платформу, щоб слухати інших. Надіслав Ігорю посилання — він вже чув про систему, бо я розповідав йому про неї напередодні. AI вів розмову поки я спав, а вранці у vault мене чекала готова нотатка.

Виходить, я побудував систему, щоб слухати інших — а вона першою навчила слухати мене.


Код платформи — у відкритому репозиторії: github.com/vlavrynovych/interview-platform.

Питання про архітектуру або специфічні рішення пишіть в коментарях, або відкривайте гілку в репозиторії проекту.

Read more

Обкладинка «Сонце для тебе» — фото Тенеріфе (океан, скелі, готелі) з накладеною типографікою назви треку

Перший реліз SHUM & RUTA: як я сам ловив і виправляв власні помилки

Перший реліз дуету SHUM & RUTA — і перший урок незалежного артиста: без менеджера й страховки сам ловиш власні помилки і сам їх виправляєш. Історія одного релізу, несподіваної проблеми з Apple Music і того, що з цього виніс.

By Volodymyr Lavrynovych, Susanna
Руки тримають стару чорно-білу сімейну фотографію при свічці. На дерев'яному столі — розкладені старі фото і рукописні листи.

Хранитель сімейного дерева

З 2009 по 2011 рік я зібрав родове дерево — 247 людей, п'ять поколінь, більше двадцяти прізвищ. Записав дати, зібрав фотографії, ходив на кладовище, з'їздив з батьком до Брянської області. Потім дані лежали роками без діла. Ця стаття про те, навіщо це взагалі і що дала ця праця.

By Volodymyr Lavrynovych, Susanna