П/ВИН

Я собрал онлайн генератор коммерческих предложений: 12 слайдов, GPT-4o и админка без деплоя

·8 мин чтения

Ручная подготовка КП для каждого клиента - это узкое место, которое убивает одновременно скорость и качество. Я прошёл через это и понял: нужен онлайн генератор коммерческих предложений, который работает как код - персонализированный, воспроизводимый, с настройками через UI.

В итоге получился kp-generator — модуль внутри платформы germanyun.online, который превращает результаты диагностического звонка в полноценную, персонализированную презентацию из 12 слайдов. Под капотом: Next.js, Supabase, GPT-4o, двухфазная генерация промтов и полная админ-панель для тонкой настройки каждого слайда.

Почему КП — это продуктовая задача, а не дизайнерская

Началось всё с простого вопроса: почему конверсия после диагностического звонка не такая, как хотелось бы? Ответ оказался неожиданным — не потому что плохо ведём встречи, а потому что КП после них выглядит как типовой документ, а не как точный ответ на боль конкретного клиента.

Я провёл небольшое исследование: изучил структуры B2B-презентаций по материалам Gong (67К записей звонков), Prospeo 2026, Brixon Group, подходы McKinsey и Forrester. Ключевой вывод — персонализация даёт 3.2x рост конверсии. Не просто «вставить имя клиента», а реально отвечать на конкретные боли, которые он озвучил на звонке.

При этом каждый слайд должен работать по своей логике: титул с обещанием, точка А (где клиент сейчас), цена бездействия, before/after по релевантным модулям, социальные доказательства, ROI-расчёт, оффер. Это не «красивый PDF», это нарративная структура с конкретной целью.

Архитектурно задача звучала так: взять транскрипт звонка → обработать существующим промтом → получить структурированную диагностику → объединить с данными программы → сгенерировать 12 персонализированных Stitch-промтов → получить слайды.

Архитектура: от транскрипта до слайда

Первое решение, которое пришло в голову — сделать «набор промтов в документе». Быстро, без кода. Но это тупик: нет воспроизводимости, нет версионирования, нет возможности итерировать на основе данных.

Поэтому выбрал путь полноценного модуля. Вот итоговая схема:

Транскрипт звонка
    ↓ (существующий GPT-промт)
Структурированная диагностика (pointA[], keyFindings[])
    +
program-content.ts (10 модулей программы)
    +
Реальные кейсы клиентов
    +
Данные клиента (имя, бизнес, тариф)
    ↓
МЕТА-ПРОМТ (двухфазная генерация)
    ↓
12 персонализированных Stitch-промтов
    ↓
Готовые слайды презентации

Двухфазная генерация — ключевое архитектурное решение. Фаза 1: мета-промт анализирует диагностику и определяет какие боли наиболее критичны для этого клиента, какие кейсы релевантны, какой тариф рекомендовать. Фаза 2: на основе этого контекста генерируются конкретные промты для каждого слайда с подставленными данными.

Почему не один промт на всё? Потому что контекстное окно ограничено, и качество сильно падает когда просишь GPT сделать 12 разных вещей за раз. Разбивка на фазы даёт лучший контроль над каждым элементом.

База данных: схема kp в Supabase

Для хранения всего, что связано с KP-модулем, создал отдельную схему kp в Supabase. Пять таблиц:

-- Конфигурация слайдов
CREATE TABLE kp.slides (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  order_index integer NOT NULL,
  title text NOT NULL,
  is_active boolean DEFAULT true,
  prompt_template text NOT NULL,
  created_at timestamptz DEFAULT now()
);
 
-- Дизайн-система
CREATE TABLE kp.design_settings (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  key text UNIQUE NOT NULL,
  value text NOT NULL
);
 
-- Кейсы клиентов
CREATE TABLE kp.cases (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  client_name text NOT NULL,
  business_type text,
  result_text text NOT NULL,
  is_active boolean DEFAULT true
);
 
-- Настройки промтов
CREATE TABLE kp.prompt_settings (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  key text UNIQUE NOT NULL,
  value text NOT NULL
);
 
-- Сгенерированные КП
CREATE TABLE kp.generated_kps (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  client_name text NOT NULL,
  generated_prompts jsonb,
  metadata jsonb,
  created_at timestamptz DEFAULT now()
);

Отдельная схема вместо таблиц в public — это сознательный выбор. Изолирует KP-логику, проще управлять правами через PostgREST, и не засоряет основную схему.

Seed-данные включают 12 дефолтных слайдов с детальными Stitch-промтами и настройками дизайн-системы (цвета, шрифты, отступы).

Backend: 6 API роутов

Весь backend — это 6 Next.js API Routes под /api/generator/kp/:

  • GET/POST /slides — список слайдов и создание нового
  • PUT/DELETE /slides/[id] — обновление и удаление слайда
  • POST /slides/reorder — изменение порядка (drag-and-drop)
  • GET/PUT /design — дизайн-настройки
  • GET/PUT /cases — кейсы клиентов
  • GET/PUT /settings — настройки промтов
  • POST /generate — основной эндпоинт генерации КП

Ключевой роут — /generate. Принимает данные клиента + результаты диагностики, запускает двухфазную генерацию, возвращает массив промтов для каждого слайда:

// app/api/generator/kp/generate/route.ts
export async function POST(req: Request) {
  const { clientData, diagnosticResults } = await req.json();
 
  // Фаза 1: анализ контекста
  const contextAnalysis = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      { role: 'system', content: META_PROMPT },
      { role: 'user', content: JSON.stringify({ clientData, diagnosticResults }) }
    ]
  });
 
  const context = JSON.parse(contextAnalysis.choices[0].message.content);
 
  // Фаза 2: генерация промтов для каждого слайда
  const slides = await getActiveSlides(); // из Supabase
  const generatedPrompts = await Promise.all(
    slides.map(slide => generateSlidePrompt(slide, context, clientData))
  );
 
  // Сохраняем результат
  await saveGeneratedKP(clientData.name, generatedPrompts, context);
 
  return Response.json({ prompts: generatedPrompts });
}

Функция generateSlidePrompt берёт шаблон из БД и интерполирует плейсхолдеры — {{client_name}}, {{business_type}}, {{top_pain}}, {{relevant_case}} и т.д. Здесь была одна из ранних ошибок: шаблон для слайда с ROI-расчётом содержал {{monthly_loss}}, который мог быть undefined если диагностика не выявила чётких финансовых потерь. Добавил fallback-логику и проверку обязательных полей перед генерацией.

Frontend: трёхтабный layout

Пользовательский интерфейс разбит на три таба, добавленных поверх существующего визарда генератора:

  1. Документы — существующий PDF-генератор (диагностика, КП-документ, дорожная карта)
  2. Презентация — новый KP-модуль с формой клиента и просмотром промтов
  3. Админ — полная настройка системы

Стейт шарится между табами через React Context. Это позволяет, например, заполнить данные клиента в «Презентации» и не терять их при переключении в «Админ» для проверки настроек.

// context/KPContext.tsx
interface KPContextType {
  clientData: ClientData;
  setClientData: (data: ClientData) => void;
  generatedPrompts: SlidePrompt[] | null;
  isGenerating: boolean;
  generate: () => Promise<void>;
}
 
export const KPContext = createContext<KPContextType | null>(null);

Админ-панель: тонкая настройка через UI

Админ-панель — это то, ради чего всё и затевалось. Четыре секции:

Слайды — drag-and-drop менеджер с inline-редактором. Можно менять порядок слайдов, включать/выключать отдельные, редактировать шаблон промта прямо в браузере без деплоя.

┌─────────────────────────────────────────────────────┐
│  Слайды презентации                    [+ Добавить] │
│─────────────────────────────────────────────────────│
│  ≡ 1. Титул + Обещание               🟢 [✏️] [🗑️] │
│  ≡ 2. Контекст рынка                 🟢 [✏️] [🗑️] │
│  ≡ 3. Точка А клиента                🟢 [✏️] [🗑️] │
│  ≡ 4. Цена бездействия               🟡 [✏️] [🗑️] │
│  ≡ 5. Before / After                 🟢 [✏️] [🗑️] │
└─────────────────────────────────────────────────────┘

Дизайн-система — настройка визуального стиля. Цвет акцента (выбрали насыщенный благородный красный), шрифты, отступы — всё через UI, сохраняется в kp.design_settings.

Кейсы — управление реальными историями успеха клиентов. Их подставляет AI в релевантные слайды.

Настройки промтов — параметры мета-промта: тон, длина описаний, акценты в нарративе.

Главная ценность: я могу итерировать на промтах и структуре слайдов без единой строки кода и без деплоя. Это критично для экспериментов с конверсией.

Деплой: Dokploy и ручной триггер

Деплой проходил через Dokploy на VPS. Пуш в main должен был автоматически триггерить деплой через вебхук, но после нескольких коммитов обнаружилось, что контейнер не обновился — вебхук не сработал.

Диагностика показала: Dokploy не знал о новой ветке. Пришлось:

  1. Убедиться что вебхук настроен на нужную ветку
  2. Запустить деплой вручную через API Dokploy
  3. Отслеживать логи билда через SSH

Билд занял около 4 минут (клонирование + npm ci + next build). После завершения все 6 KP API роутов появились в /api/generator/kp/*, страница /generator с тремя табами отдавала 200.

Полезный урок: всегда проверяй что вебхук деплоя привязан к нужной ветке, особенно после реорганизации репозитория.

Что получилось: 21 файл, 3261 строка

За несколько сессий разработки (от идеи до деплоя) получили:

  • 5 таблиц в Supabase схеме kp
  • 6 API роутов с полным CRUD и генерацией
  • 12 дефолтных слайдов с детальными Stitch-промтами
  • Трёхтабный UI с общим стейтом
  • Полная админ-панель — слайды, дизайн, кейсы, настройки
  • 21 файл, 3261 строк нового кода
  • 2 коммита в main, автодеплой через Dokploy

Визуальный стиль: светлый минимал, акцентный цвет — насыщенный благородный красный. Структура слайдов — гибрид из Data-Driven Spine (Gong) и нарративного подхода: обещание → контекст рынка → точка А → цена бездействия → before/after → кейсы → ROI → оффер.

Выводы и уроки

Главный урок этого проекта — итерируемость важнее идеальности. Когда промты живут в БД и редактируются через UI, я могу за 10 минут протестировать другой нарратив на следующем клиенте. Если бы они были захардкожены в коде, каждый эксперимент требовал бы PR и деплоя.

Второй урок — двухфазная генерация бьёт одно большое задание. Это контринтуитивно: кажется, что один промт «сделай КП» проще. Но качество ответа на 12 разных задач в одном контексте значительно ниже, чем последовательное: сначала «пойми клиента», потом «напиши этот конкретный слайд с этим конкретным контекстом». GPT-4o работает лучше когда задача сфокусирована.

Третий урок — отдельная схема в БД для фичи стоит своих затрат. Изоляция kp.* от основного public.* сделала работу чище: отдельные права, отдельный PostgREST-эндпоинт, нет риска случайно задеть другие таблицы. Когда фича разрастается — это окупается.

Четвёртый урок — деплой нужно проверять сразу после пуша. Мы обнаружили что вебхук не сработал только когда пошли проверять результат. Следующий шаг — добавить мониторинг деплоя: после каждого пуша в main автоматически дёргать /api/health и сравнивать версию сборки. Это сэкономит время диагностики.

По сути, kp-generator — это хороший пример того, как AI-фича должна быть устроена в продакшене: не просто «вызвать GPT», а полноценная система с версионированием промтов, настройками через UI, логированием результатов и возможностью итерировать без кода. Именно такой подход позволит со временем оптимизировать конверсию на основе реальных данных, а не интуиции.

Паша Вин
Паша Вин

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.

Связанный проект

Slides Generator