Библиотека · Четыре проекта от начала до конца

Build-Along — Telegram-бот для бизнеса за 7 дней

СтроительОбновлено: октябрь 2026
102 из 105 в библиотеке

Время: ~30 часов over 7 дней (build + sell + ship)


Суть урока

Лекция — это архитектор показывает чертёж дома. Lab — это ты с бригадой кладёшь кирпичи по дням, и к воскресенью в доме горит свет и в нём живёт первый жилец.

Этот урок — не теория. Это пошаговая стройка реального AI-продукта по дням недели. Не "как теоретически делают Telegram-ботов". А "вот ты в понедельник, вот что делаешь час за часом — и в воскресенье у тебя задеплоенный бот в production, первый клиент подключён, Stripe готов выставлять инвойсы каждый месяц".

После основных уроков курса ты знаешь "как теоретически". Сейчас доказываешь себе, что можешь поставить продукт за неделю end-to-end.

Важно: суммы в этом уроке (например, $500 в месяц) — примеры для расчёта, а не прогноз дохода и не рекомендация цены. Найдёшь ли ты клиента и сколько он будет платить, зависит от ниши, рынка и твоей работы: курс результат не гарантирует.

🎨 Образ: Lecture vs Lab — это как разница между уроком вождения по учебнику и реальной поездкой по городу с инструктором. Учебник можно перечитать. Поездка — это где ты потеешь, тормозишь не там, инструктор кричит, но к концу 7 дней ты водишь.


🎯 End state Day 7

В воскресенье вечером у тебя на руках:

  • ✅ Telegram-бот задеплоен на Cloudflare Workers, отвечает 24/7
  • ✅ Первый клиент подключён, биллинг через Stripe настроен (сумму вы согласуете сами; $500/мес в уроке — пример)
  • ✅ Подключённый инструмент (Stripe API или email — зависит от ниши)
  • ✅ State management через Cloudflare KV (бот помнит контекст)
  • ✅ Rate limiting + cost guardrails (не разорит тебя на API)
  • ✅ Audit log всех взаимодействий
  • ✅ Мониторинг вызовов (в уроке — Helicone, см. примечание в Дне 7)
  • ✅ Шаблон onboarding для клиентов №2-10

Экономика: пример расчёта — в разделе «Real cost breakdown» в конце урока. Цифры там условные: считай по своим тарифам и своей цене.


📋 Pre-requisites

Чтобы Lab имел смысл, у тебя должно быть:

  • ✓ Платный тариф Claude с Claude Code (Pro или Max; актуальные цены: Актуальное сейчас)
  • ✓ API-ключ из Claude Console: API оплачивается отдельно от подписки
  • ✓ Claude Code установлен и работает (см. урок Установка и настройка)
  • ✓ Cloudflare account (бесплатный tier хватит)
  • ✓ Stripe account (для билинга)
  • ✓ Telegram account
  • ✓ Прошёл основные уроки курса (понимаешь CLAUDE.md, hooks, deploy basics)
  • ✓ ~30 часов времени в течение 7 дней (4-5 часов/день)
  • ✓ Готовность звонить незнакомым людям (Day 1 — discovery calls)

🎨 Образ: в лабораторию химик не приходит без халата и очков. В этот Lab — без аккаунтов и времени. Если нет — сначала подготовь, потом возвращайся.


DAY 1 (5 часов): Ниша + первое customer interview

Цель дня: вечером ты знаешь конкретную нишу + конкретную боль + 2-3 реальных потенциальных клиента согласились созвониться.

Час 1: Pick niche — методология 3 вопросов

Не выбирай "малый бизнес". Это не ниша, это вселенная. Ниша — это пересечение:

  1. Какую группу людей ты знаешь лучше других? (риелторы Эквадора, дантисты Москвы, фитнес-тренеры Майами)
  2. У кого из них болит так, что они уже платят деньги за слабые решения? (например за Calendly + Zoom + Notion + ассистента-фрилансера)
  3. Какую часть боли может закрыть Telegram-бот с LLM внутри? (lead qualification, appointment booking, FAQ автоответ, follow-up sequences)

Записывай ответы. Пересечение трёх ответов = твоя ниша.

🎨 Образ: ниша — это пересечение трёх кругов. Один круг — твой опыт. Второй — реальная боль рынка. Третий — что бот может сделать. Где все три пересеклись — там стой.

Час 2: 20 потенциальных клиентов

Открываешь Google Sheets. Колонки: имя, компания, контакт, источник, статус.

20 строк. Источники: LinkedIn (поиск по нише), Instagram, локальные бизнес-чаты в Telegram, рекомендации знакомых.

НЕ масс-парсинг. 20 живых имён которых ты можешь обосновать "почему именно этот человек подходит".

Час 3: Cold outreach 5 контактам

Шаблон сообщения (адаптируй под нишу):

Напиши в чат
Привет, [Имя]!

Я строю AI-помощника для [ниша] — отвечает в Telegram на типовые
вопросы клиентов, квалифицирует лиды, бронирует встречи. Без шаблонов.

Не продаю — изучаю проблемы. 15 минут на созвон расскажешь как у тебя
сейчас устроена работа с клиентами? Что бесит?

В благодарность за время — пришлю отчёт-выжимку по индустрии (3 страницы,
3 конкретных идеи как сэкономить часы в неделю).

Когда удобно — завтра или послезавтра?

Отправь 5 DM. В разные платформы. НЕ копипаст — переписывай первые 2 строки под человека.

Час 4-5: 2-3 discovery calls

Часть из 5 ответит; если не ответил никто — меняй текст или нишу. Созвон 15 минут. Скрипт:

Напиши в чат
1. (2 мин) Расскажи о себе и бизнесе.
2. (5 мин) Как сейчас устроена работа с клиентами после первого контакта?
   Кто отвечает на сообщения? Сколько уходит часов в день?
3. (3 мин) Какие 3 вопроса клиенты задают ЧАЩЕ ВСЕГО?
4. (2 мин) Если бы у тебя был помощник который отвечает на эти 3
   вопроса в Telegram 24/7 — сколько это стоило бы тебе сэкономленного
   времени в часах в неделю?
5. (3 мин) Если бы такой инструмент стоил [твоя цена] в месяц — это дорого,
   нормально или дёшево? Почему?

Записывай дословно. Не "оптимизируй" в голове.

Output Day 1

В конце дня в Google Doc:

  • Ниша (одна строка)
  • 3 боли клиентов (дословные цитаты)
  • 3 типовых вопроса от клиентов которые бот будет закрывать
  • Реакция на твою цену (готовы / дорого / нормально)
  • Имя первого человека готового стать тест-клиентом

Если нет первого тест-клиента — Day 2 не начинаешь. Возвращайся в Час 3.

🎨 Образ: строить дом без покупателя — это строить дачу мечты в чистом поле, надеясь что кто-то её купит. Сначала покупатель, потом фундамент.


DAY 2 (5 часов): MVP design + CLAUDE.md + scaffold

Цель дня: есть дизайн 3 happy paths + написан CLAUDE.md + Worker scaffold создан + бот в BotFather зарегистрирован.

Час 1: Conversation flows (3 happy paths)

На бумаге или в Miro нарисуй 3 диалога которые бот должен пройти end-to-end.

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

Flow 1: Качественный лид

Напиши в чат
Клиент: Здравствуйте, ищу квартиру в Куэнке до $80K
Бот: Привет! Помогу подобрать. 3 вопроса:
     1. Сколько спален нужно?
     2. Район важен или гибко?
     3. Срок переезда?
Клиент: 2 спальни, центр, через 3 месяца
Бот: Записал. Это качественный лид — передаю риелтору.
     Он позвонит сегодня до 18:00. Подтверди номер для звонка.

Flow 2: FAQ автоответ

Напиши в чат
Клиент: А нужна ли виза для покупки квартиры?
Бот: Иностранцы могут покупать недвижимость в Эквадоре без визы.
     Нужен только паспорт + RUC (получаешь за 1 день).
     Хочешь подробнее обсудить с риелтором — напиши "созвон".

Ответ бота в этом примере условный: юридические факты бот берёт только из FAQ, который проверил клиент.

Flow 3: Не наш клиент

Напиши в чат
Клиент: А ипотеку для меня сделаете?
Бот: Мы работаем с прямыми покупками, не с ипотекой.
     По ипотеке могу порекомендовать [партнёр] — напишу контакт?

Зачем это сейчас: Day 3 ты не будешь придумывать "что бот должен говорить". Только реализовывать что нарисовано.

Час 2: CLAUDE.md system prompt

Создай папку проекта:

bash
mkdir telegram-bot-prod && cd telegram-bot-prod
git init

CLAUDE.md (это будет system prompt для LLM):

Напиши в чат
# Помощник риелтора в Куэнке, Эквадор

## Роль
Ты — первый контакт клиентов риелторской компании в Куэнке.
Отвечаешь в Telegram. Цель — квалифицировать лида и передать
человеку, либо ответить на FAQ.

## Стиль
- Кратко, дружелюбно, на ВЫ
- Без воды и эмодзи в каждой строке
- Если не знаешь ответ — НЕ выдумывай, скажи "уточню у риелтора"

## 3 happy paths
[Скопируй сюда flows из Часа 1]

## Что НИКОГДА не делаешь
- Не обещаешь цены без согласования с риелтором
- Не утверждаешь юридические факты ("100% легально")
- Не отправляешь персональные данные клиента наружу
- Не работаешь с темой ипотеки (направляешь к партнёру)

## Когда передавать человеку
- Клиент готов смотреть квартиру в течение месяца
- Клиент задаёт вопрос которого нет в FAQ
- Клиент пишет "хочу созвон" / "позвоните"

## Tools
- save_lead(name, phone, budget, bedrooms) — сохраняет в KV
- notify_realtor(lead_id) — отправляет уведомление риелтору

Это продуктовая конституция бота. Меняешь её — меняется поведение.

Час 3-4: Cloudflare Worker scaffold

Setup:

bash
npm create cloudflare@latest -- telegram-bot
# мастер спросит шаблон (Hello World) и язык (TypeScript); флаги команды меняются,
# актуальный вариант смотри в документации Cloudflare
cd telegram-bot
npm install @anthropic-ai/sdk

src/index.ts — minimal scaffold:

typescript
import Anthropic from "@anthropic-ai/sdk";

export interface Env {
  ANTHROPIC_API_KEY: string;
  TELEGRAM_BOT_TOKEN: string;
  TELEGRAM_WEBHOOK_SECRET: string;
  BOT_KV: KVNamespace;
  SYSTEM_PROMPT: string;
}

interface TelegramUpdate {
  update_id: number;
  message?: {
    message_id: number;
    from: { id: number; first_name: string; username?: string };
    chat: { id: number };
    text?: string;
  };
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Webhook secret verification
    const secret = request.headers.get("X-Telegram-Bot-Api-Secret-Token");
    if (secret !== env.TELEGRAM_WEBHOOK_SECRET) {
      return new Response("Forbidden", { status: 403 });
    }

    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    const update: TelegramUpdate = await request.json();
    if (!update.message?.text) {
      return new Response("OK"); // ignore non-text
    }

    const chatId = update.message.chat.id;
    const userText = update.message.text;

    // TODO Day 3: real LLM call here
    const reply = `Echo: ${userText}`;

    await sendTelegramMessage(env, chatId, reply);
    return new Response("OK");
  },
};

async function sendTelegramMessage(env: Env, chatId: number, text: string) {
  const url = `https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage`;
  await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ chat_id: chatId, text }),
  });
}

wrangler.toml:

toml
name = "telegram-bot"
main = "src/index.ts"
compatibility_date = "2026-10-01"

[[kv_namespaces]]
binding = "BOT_KV"
id = "your-kv-namespace-id"  # создадим позже

Новые проекты Cloudflare создаёт с файлом wrangler.jsonc; формат wrangler.toml тоже поддерживается. Если мастер создал wrangler.jsonc, перенеси туда те же поля.

Установи secrets (НИКОГДА не коммить):

bash
wrangler secret put ANTHROPIC_API_KEY
wrangler secret put TELEGRAM_BOT_TOKEN
wrangler secret put TELEGRAM_WEBHOOK_SECRET  # генерируй openssl rand -hex 32

Час 5: Telegram bot через BotFather

В Telegram → @BotFather → /newbot → имя + username → получаешь токен.

Подключаешь webhook (после первого wrangler deploy):

bash
WEBHOOK_URL="https://telegram-bot.your-subdomain.workers.dev"
SECRET="<тот же что в wrangler secret>"
curl -X POST "https://api.telegram.org/bot${BOT_TOKEN}/setWebhook" \
  -H "Content-Type: application/json" \
  -d "{\"url\":\"${WEBHOOK_URL}\",\"secret_token\":\"${SECRET}\"}"

Напиши боту "привет" в Telegram. Должен ответить "Echo: привет". Работает — переходи к Day 3.

🎨 Образ: Day 2 — это разметка фундамента. Ещё не дом, но колышки забиты. Дальше — стены.


DAY 3 (5 часов): Core conversation handling

Цель дня: бот отвечает осмысленно через LLM, errors не валят его, каждое взаимодействие в audit log.

Час 1-3: Webhook + LLM + response

Заменяешь // TODO Day 3 на реальный вызов:

typescript
import Anthropic from "@anthropic-ai/sdk";

async function generateReply(
  env: Env,
  userText: string,
  conversationHistory: Array<{ role: "user" | "assistant"; content: string }>,
): Promise<string> {
  const client = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });

  const response = await client.messages.create({
    model: "claude-sonnet-5-5", // вынеси в переменную окружения; актуальные модели: «Актуальное сейчас»
    max_tokens: 500,
    system: env.SYSTEM_PROMPT, // загружено из KV или wrangler vars
    messages: [
      ...conversationHistory,
      { role: "user", content: userText },
    ],
  });

  // Извлекаем текст из response
  const textBlock = response.content.find((b) => b.type === "text");
  return textBlock && textBlock.type === "text"
    ? textBlock.text
    : "Извините, не понял. Можете переформулировать?";
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const secret = request.headers.get("X-Telegram-Bot-Api-Secret-Token");
    if (secret !== env.TELEGRAM_WEBHOOK_SECRET) {
      return new Response("Forbidden", { status: 403 });
    }

    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    const update: TelegramUpdate = await request.json();
    if (!update.message?.text) return new Response("OK");

    const chatId = update.message.chat.id;
    const userText = update.message.text;

    try {
      // Загружаем историю беседы из KV (Day 4 расширим)
      const historyKey = `chat:${chatId}:history`;
      const historyRaw = await env.BOT_KV.get(historyKey);
      const history: Array<{ role: "user" | "assistant"; content: string }> =
        historyRaw ? JSON.parse(historyRaw) : [];

      // Генерим ответ
      const reply = await generateReply(env, userText, history);

      // Обновляем историю (last 10 messages)
      const updatedHistory = [
        ...history,
        { role: "user" as const, content: userText },
        { role: "assistant" as const, content: reply },
      ].slice(-20);

      await env.BOT_KV.put(historyKey, JSON.stringify(updatedHistory), {
        expirationTtl: 60 * 60 * 24 * 7, // 7 days
      });

      // Audit log
      await logEvent(env, {
        ts: new Date().toISOString(),
        chat_id: chatId,
        user_text: userText,
        bot_reply: reply,
        tokens: 0, // TODO заполним из response
      });

      await sendTelegramMessage(env, chatId, reply);
    } catch (e) {
      console.error("Bot error:", e);
      await sendTelegramMessage(
        env,
        chatId,
        "Извините, временная проблема. Попробуйте через минуту.",
      );
    }

    return new Response("OK");
  },
};

Час 4-5: Error handling + audit log

logEvent — пишем в KV или внешний сервис:

typescript
interface AuditEntry {
  ts: string;
  chat_id: number;
  user_text: string;
  bot_reply: string;
  tokens: number;
}

async function logEvent(env: Env, entry: AuditEntry): Promise<void> {
  const key = `audit:${entry.ts}:${entry.chat_id}`;
  await env.BOT_KV.put(key, JSON.stringify(entry), {
    expirationTtl: 60 * 60 * 24 * 90, // 90 days retention
  });
}

Защитные паттерны:

typescript
// Лимит длины сообщения от пользователя
if (userText.length > 1000) {
  await sendTelegramMessage(
    env,
    chatId,
    "Сообщение слишком длинное. Сократите до 1000 символов.",
  );
  return new Response("OK");
}

// Timeout для LLM call
const replyPromise = generateReply(env, userText, history);
const timeoutPromise = new Promise<string>((_, reject) =>
  setTimeout(() => reject(new Error("LLM timeout")), 15000),
);
const reply = await Promise.race([replyPromise, timeoutPromise]);

Deploy:

bash
wrangler deploy

Напиши боту 5 разных сообщений. Все должны получить осмысленные ответы. История диалога сохраняется (попробуй "а ты помнишь что я тебе говорил выше?").

🎨 Образ: Day 3 — стены подняты. Бот говорит. Но дом ещё без водопровода — Day 4.


DAY 4 (5 часов): Connect tools (MCP integration + state)

Цель дня: бот делает не только разговор — реально работает с инструментами (Stripe, email или твой Use Case API).

Час 1-2: Tool integration

Для риелтора Эквадора нужно: сохранить лида + уведомить риелтора в Telegram.

Расширяем CLAUDE.md (system prompt) с tools:

typescript
const tools: Anthropic.Tool[] = [
  {
    name: "save_lead",
    description: "Сохраняет квалифицированного лида в базу",
    input_schema: {
      type: "object",
      properties: {
        name: { type: "string", description: "Имя клиента" },
        phone: { type: "string", description: "Телефон с кодом страны" },
        budget_usd: { type: "number", description: "Бюджет в USD" },
        bedrooms: { type: "number", description: "Кол-во спален" },
        timeline_months: { type: "number", description: "Срок переезда" },
      },
      required: ["name", "phone", "budget_usd", "bedrooms"],
    },
  },
  {
    name: "notify_realtor",
    description: "Отправляет уведомление риелтору о новом лиде",
    input_schema: {
      type: "object",
      properties: {
        lead_id: { type: "string" },
        urgency: { type: "string", enum: ["high", "medium", "low"] },
      },
      required: ["lead_id", "urgency"],
    },
  },
];

Tool execution loop:

typescript
async function generateReplyWithTools(
  env: Env,
  userText: string,
  history: Array<Anthropic.MessageParam>,
): Promise<string> {
  const client = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });
  const messages: Array<Anthropic.MessageParam> = [
    ...history,
    { role: "user", content: userText },
  ];

  // Loop max 5 раз чтобы избежать infinite tool calls
  for (let i = 0; i < 5; i++) {
    const response = await client.messages.create({
      model: "claude-sonnet-5-5", // вынеси в переменную окружения; актуальные модели: «Актуальное сейчас»
      max_tokens: 1000,
      system: env.SYSTEM_PROMPT,
      tools,
      messages,
    });

    if (response.stop_reason === "end_turn") {
      const textBlock = response.content.find((b) => b.type === "text");
      return textBlock && textBlock.type === "text" ? textBlock.text : "";
    }

    if (response.stop_reason === "tool_use") {
      messages.push({ role: "assistant", content: response.content });

      const toolResults: Array<Anthropic.ToolResultBlockParam> = [];
      for (const block of response.content) {
        if (block.type === "tool_use") {
          const result = await executeTool(env, block.name, block.input);
          toolResults.push({
            type: "tool_result",
            tool_use_id: block.id,
            content: JSON.stringify(result),
          });
        }
      }
      messages.push({ role: "user", content: toolResults });
      continue;
    }

    break;
  }

  return "Извините, не смог обработать запрос. Попробуйте ещё раз.";
}

async function executeTool(
  env: Env,
  name: string,
  input: unknown,
): Promise<unknown> {
  if (name === "save_lead") {
    const lead = input as {
      name: string;
      phone: string;
      budget_usd: number;
      bedrooms: number;
    };
    const leadId = `lead_${Date.now()}`;
    await env.BOT_KV.put(`leads:${leadId}`, JSON.stringify(lead));
    return { lead_id: leadId, status: "saved" };
  }
  if (name === "notify_realtor") {
    const { lead_id, urgency } = input as { lead_id: string; urgency: string };
    // Реальный notify через Telegram на чат риелтора
    const realtorChatId = Number(env.REALTOR_CHAT_ID);
    await sendTelegramMessage(
      env,
      realtorChatId,
      `Новый лид (${urgency}): ${lead_id}`,
    );
    return { status: "notified" };
  }
  return { error: `Unknown tool: ${name}` };
}

Час 3-4: State management (KV pattern)

Уже частично сделали в Day 3. Расширяем — храним не только историю, но и профиль клиента:

typescript
interface ClientProfile {
  chat_id: number;
  first_seen: string;
  name?: string;
  phone?: string;
  budget_usd?: number;
  bedrooms?: number;
  status: "new" | "qualifying" | "qualified" | "passed_to_realtor";
  last_interaction: string;
}

async function loadProfile(env: Env, chatId: number): Promise<ClientProfile> {
  const raw = await env.BOT_KV.get(`profile:${chatId}`);
  if (raw) return JSON.parse(raw);
  return {
    chat_id: chatId,
    first_seen: new Date().toISOString(),
    status: "new",
    last_interaction: new Date().toISOString(),
  };
}

async function saveProfile(env: Env, profile: ClientProfile): Promise<void> {
  profile.last_interaction = new Date().toISOString();
  await env.BOT_KV.put(`profile:${profile.chat_id}`, JSON.stringify(profile), {
    expirationTtl: 60 * 60 * 24 * 365, // год
  });
}

Профиль передаёшь в system prompt чтобы бот помнил контекст между сессиями:

typescript
const profile = await loadProfile(env, chatId);
const systemWithContext = `${env.SYSTEM_PROMPT}

## Профиль текущего клиента
${JSON.stringify(profile, null, 2)}

Используй эту информацию чтобы не задавать вопросы на которые уже знаешь
ответ. Обновляй профиль через tools.`;

Час 5: Testing harness

Создаёшь tests/conversation.test.ts:

typescript
import { describe, test, expect } from "vitest";

describe("Bot conversation flows", () => {
  test("Flow 1: качественный лид сохраняется", async () => {
    const responses = await simulateConversation([
      "Здравствуйте, ищу квартиру в Куэнке до $80K",
      "2 спальни, центр, через 3 месяца",
      "Меня зовут [клиент], связь через бот",
    ]);
    expect(responses).toContain("записал");
    // Проверяем что lead сохранён в KV
    const leads = await env.BOT_KV.list({ prefix: "leads:" });
    expect(leads.keys.length).toBeGreaterThan(0);
  });

  test("Flow 2: FAQ закрывается без передачи риелтору", async () => {
    const reply = await simulateConversation([
      "А нужна ли виза для покупки квартиры?",
    ]);
    expect(reply[0]).toMatch(/виза.*не нужна|без визы/i);
  });

  test("Flow 3: ипотека → направление к партнёру", async () => {
    const reply = await simulateConversation(["А ипотеку для меня сделаете?"]);
    expect(reply[0]).toMatch(/ипотек|партнёр/i);
  });
});

Запусти: npx vitest. Все 3 теста должны быть зелёными.

🎨 Образ: Day 4 — водопровод и электричество подведены. Дом функционирует.


DAY 5 (4 часа): Polish + safety + production deploy

Цель дня: бот не разорит тебя, не упадёт под нагрузкой, и задеплоен в production.

Час 1: Rate limiting per user

Не дай одному клиенту жечь твой API key:

typescript
async function checkRateLimit(env: Env, chatId: number): Promise<boolean> {
  const key = `ratelimit:${chatId}`;
  const countRaw = await env.BOT_KV.get(key);
  const count = countRaw ? parseInt(countRaw) : 0;

  if (count >= 30) return false; // max 30 messages в минуту

  await env.BOT_KV.put(key, String(count + 1), { expirationTtl: 60 });
  return true;
}

// В fetch handler:
if (!(await checkRateLimit(env, chatId))) {
  await sendTelegramMessage(
    env,
    chatId,
    "Слишком много сообщений. Подождите минуту.",
  );
  return new Response("OK");
}

Час 2: Cost guardrails

Daily cost cap чтобы случайный bug не сжёг лишние деньги за ночь:

typescript
async function checkDailyCost(env: Env): Promise<boolean> {
  const today = new Date().toISOString().split("T")[0];
  const key = `cost:${today}`;
  const costRaw = await env.BOT_KV.get(key);
  const costCents = costRaw ? parseInt(costRaw) : 0;
  return costCents < 1000; // $10/день hard cap
}

async function recordCost(env: Env, tokens: number): Promise<void> {
  const today = new Date().toISOString().split("T")[0];
  const key = `cost:${today}`;
  const costRaw = await env.BOT_KV.get(key);
  const currentCents = costRaw ? parseInt(costRaw) : 0;
  // Грубая завышенная оценка: 1 цент за каждые 100 токенов. Нужна как предохранитель;
  // актуальные цены токенов — на странице «Актуальное сейчас»
  const addCents = Math.ceil(tokens / 100);
  await env.BOT_KV.put(key, String(currentCents + addCents), {
    expirationTtl: 60 * 60 * 48,
  });
}

// В fetch:
if (!(await checkDailyCost(env))) {
  await sendTelegramMessage(
    env,
    chatId,
    "Сервис временно перегружен. Завтра возобновится.",
  );
  return new Response("OK");
}

Час 3: Helpful error messages

Шаблоны частых ошибок (вместо stack trace):

typescript
const ERROR_MESSAGES = {
  timeout:
    "Думаю слишком долго. Попробуйте задать вопрос проще или повторить через минуту.",
  rate_limit:
    "Слишком много сообщений подряд. Подождите минуту — отвечу.",
  api_down:
    "У нас технические проблемы. Риелтор получил уведомление и перезвонит лично.",
  unknown_topic:
    "Не совсем понял ваш вопрос. Передаю риелтору — он перезвонит в течение часа.",
};

Час 4: Production deploy

bash
# Проверяем секреты в production
wrangler secret list

# Deploy
wrangler deploy

# Verify webhook
curl "https://api.telegram.org/bot${BOT_TOKEN}/getWebhookInfo"

В Cloudflare dashboard → Workers → твой бот → Logs (real-time). Должны видеть incoming requests.

Smoke tests на production:

  1. Полный Flow 1 (квалифицированный лид)
  2. Flow 2 (FAQ)
  3. Flow 3 (не наш клиент)
  4. Edge case: длинное сообщение (>1000 символов)
  5. Edge case: 35 сообщений за минуту (rate limit срабатывает)

Все прошли — Day 6.

🎨 Образ: Day 5 — установили предохранители, заземление, пожарную сигнализацию. Дом безопасен.


DAY 6 (5 часов): First customer onboarding

Цель дня: реальный клиент начинает использовать бота для своего бизнеса. К концу дня — первые лиды через бот.

Час 1-2: Customer onboarding flow

Создай чеклист onboarding (можно в Notion / Markdown):

Напиши в чат
# Onboarding чеклист для клиента

## Перед звонком (мы делаем)
- [ ] Создан выделенный Telegram бот для клиента (свой токен)
- [ ] CLAUDE.md адаптирован под бизнес клиента
  - [ ] Имя компании, тон голоса
  - [ ] 3 happy paths из их реальных диалогов
  - [ ] FAQ из их базы (5-10 типовых вопросов)
- [ ] Tools настроены (если нужен лид-сейв — куда писать)
- [ ] Notify-чат риелтора получен и проверен

## На звонке (30 минут)
- [ ] Покажи бота в действии (3 happy paths live)
- [ ] Объясни как редактировать FAQ (доступ к CLAUDE.md)
- [ ] Расскажи как смотреть лиды (KV или Google Sheet sync)
- [ ] Покажи как добавить тебя в чат если бот зависнет
- [ ] Обсуди billing — сумма и способ оплаты через Stripe, инвойс на email
- [ ] Договорись о trial period (7 дней) либо сразу первый платёж

## После звонка
- [ ] Отправь Stripe invoice
- [ ] Подключи продакшен webhook к их Telegram-каналу
- [ ] Установи мониторинг на их трафик (Helicone tag по client_id)
- [ ] Напиши follow-up через 24 часа: "Как первый день?"

Час 3-4: 30-min onboarding call

Структура:

Напиши в чат
0-5 мин: small talk + контекст
5-15 мин: live demo (3 happy paths)
15-25 мин: настройка под их бизнес (правки в CLAUDE.md live)
25-30 мин: billing + next steps

Главное правило: клиент должен сам написать первое сообщение боту на звонке и получить ответ. Это момент "вау" который продаёт.

Час 5: Handle first day live issues

Что будет чаще всего:

  • Бот ответил не то на специфичный вопрос → правишь CLAUDE.md (FAQ секция)
  • Tool сработал не так → правишь tool description / parameters
  • Клиент хочет ещё один happy path → добавляешь

Каждое исправление коммитишь:

bash
git commit -m "fix(bot): добавить FAQ про комиссию риелтора"
wrangler deploy

Цикл фидбека — минуты, не дни. Это и есть live ops.

Output Day 6

  • Бот в production у клиента
  • 2-5 лидов прошли через бот (хотя бы в тестовом режиме)
  • Клиент видел реальный value, готов платить

🎨 Образ: Day 6 — заехал первый жилец. Свет горит, кран работает, ты рядом с гаечным ключом на случай если что.


DAY 7 (3 часа): Pricing + sustainability

Цель дня: деньги получены, мониторинг настроен, план на неделю 2.

Час 1: Stripe invoice

bash
# Через Stripe CLI или dashboard
stripe customers create \
  --name="Имя клиента" \
  --email="<client_email>"

# Invoice
stripe invoices create \
  --customer=cus_XXX \
  --collection_method=send_invoice \
  --days_until_due=7

# Сумма ниже — пример (50000 центов = $500); подставь свою цену
stripe invoice_items create \
  --customer=cus_XXX \
  --invoice=in_XXX \
  --description="Telegram AI бот — 1 месяц" \
  --amount=50000 \
  --currency=usd

stripe invoices send --id=in_XXX

Или проще: в Stripe Dashboard → Invoices → Create. 5 минут руками.

Для recurring (Month 2+): создай Subscription с ценой, о которой вы договорились, autopay через Stripe Customer Portal.

Час 2: Мониторинг вызовов (Helicone)

В уроке для мониторинга используется Helicone: прокси между твоим кодом и API, который показывает каждый вызов и его стоимость. Важно: с марта 2026 Helicone входит в Mintlify и работает в режиме поддержки (исправления и поддержка новых моделей, без новых функций). Условия бесплатного уровня смотри на сайте сервиса. Альтернативы: Langfuse, Portkey или раздел usage в Claude Console.

typescript
// Замени Anthropic SDK init:
const client = new Anthropic({
  apiKey: env.ANTHROPIC_API_KEY,
  baseURL: "https://anthropic.helicone.ai", // proxy через Helicone
  defaultHeaders: {
    "Helicone-Auth": `Bearer ${env.HELICONE_API_KEY}`,
    "Helicone-Property-Client": "client_001", // тег по клиенту
  },
});

Регистрируешь на helicone.ai → копируешь key → wrangler secret put HELICONE_API_KEY. Видишь все вызовы: cost, latency, error rate. Без своего dashboard.

Час 3: Retrospective + week 2 plan

Открой блокнот, ответь честно:

  1. Что сработало? (например: cold DM работают если первая строка персональная)
  2. Что НЕ сработало? (например: 2 из 5 потенциальных клиентов отказались на этапе цены — слишком дорого для них)
  3. Что я узнал о нише? (например: они уже платят за Calendly и ассистента-фрилансера, поэтому цену надо обосновать дополнительными возможностями)
  4. Один следующий клиент — кто и как?

Заведи Week 2 spreadsheet:

Напиши в чат
Day 8: 10 outreach DM (используем что сработало в Week 1)
Day 9: 3 discovery calls
Day 10: 1 onboarding call
Day 11: support текущего клиента + правки на основе фидбека
Day 12: 2 onboarding calls (если 2 лида созрели)
Day 13: invoice 2 новых клиента
Day 14: retro Week 2

Цель Week 2: расширить воронку: больше разговоров и подключений. Конкретные цифры задай сам по результатам первой недели.

🎨 Образ: Day 7 — праздник новоселья. Жилец платит первый месяц аренды, ты вешаешь объявление "сдаются ещё квартиры в этом доме".


💰 Real cost breakdown

Цифры ниже — оценки автора, чтобы показать структуру расходов. Это не прайс-лист и не прогноз дохода. Актуальные цены токенов и тарифов: Актуальное сейчас.

Период Расходы твои Источник
Day 1-2 (research + design) $0 только время
Day 3 (LLM testing) небольшие Anthropic API smoke tests
Day 4 (tool dev) небольшие tool execution tests
Day 5 (load testing) небольшие rate limit + cost cap проверки
Day 6 (live с клиентом) небольшие реальные диалоги
Day 7 (Stripe + monitoring) $0 бесплатные уровни сервисов
Итого setup десятки долларов зависит от тарифов и числа тестов
Month 1 ops (1 клиент) LLM + Cloudflare Workers Free: 100 000 запросов в день; платный план от $5 в месяц (на октябрь 2026)
Выручка зависит от договорённости цену и условия определяешь сам

Что съест экономику при росте:

  • Запросов больше, чем даёт бесплатный план Workers → платный план Cloudflare
  • Растёт расход токенов → prompt caching (повторяющийся контекст стоит заметно дешевле) и Batch API для несрочных задач
  • Поддержка отнимает больше пары часов в неделю → виртуальный ассистент или автоматизация

С ростом числа клиентов растёт и поддержка. Заранее считай экономику на одного клиента: цена минус токены, инфраструктура и твоё время.


⚠️ Common pitfalls

Day 1: ниша слишком широкая

❌ "AI-бот для малого бизнеса" ✅ "AI-бот для риелторов в Куэнке которые продают иностранцам"

Широкая ниша = generic outreach = почти нет ответов.

Day 3: skip error handling

Если в Day 3 ты "пока что без try/catch, потом добавлю" — в Day 6 на live звонке бот упадёт перед клиентом. Перед клиентом. Это не та история которую хочешь рассказывать.

Day 6: забыл Stripe ToS / refund policy

Без письменного refund policy первый недовольный клиент устроит chargeback и Stripe может ограничить твой аккаунт. Простейший вариант: "Pro-rated refund в первые 14 дней, дальше no refund, можно отменить subscription любым числом." Условия возвратов зависят от страны; это не юридическая консультация.

Day 7: не настроил monitoring

Через 2 недели одно tool execution начинает падать в edge case. Ты узнаёшь от клиента "у нас не работает". С мониторингом (например, Helicone) — узнаёшь сам, а не от клиента.

Любой день: vendor lock-in

Если хардкодишь имя модели в 15 местах — переключиться на Haiku или Gemini для cost optimization будет день работы. Один константа MODEL_NAME в env vars решает проблему за 30 секунд.


✅ What you have at end Day 7

Конкретно. Не "потенциал". Не "опыт". Реальные артефакты:

  1. Working Telegram bot в Cloudflare Workers production — отвечает 24/7, deployed, monitored
  2. Первый клиент подключён — условия оплаты согласованы
  3. Stripe billing — invoice отправлен, recurring настроен (если клиент согласился платить)
  4. Audit log — все взаимодействия сохранены 90 дней (основа для аудита)
  5. Cost monitoring — дашборд мониторинга, видишь токены и цену по клиенту
  6. Rate limiting + cost cap — система не разорится от bug или abuse
  7. Customer onboarding template — для клиентов №2-10 (уже не с нуля)
  8. 3 verified happy paths — реально работают на реальном бизнесе
  9. Cold outreach playbook — что писать, в какой канал, какой response rate ты получил
  10. Retrospective — что работает, что нет, plan Week 2

📈 Scaling plan (Week 2-4, кратко)

Week 2 (расширяем воронку):

  • 30 cold DM используя working Week 1 шаблоны
  • 6-8 discovery calls
  • 2-3 onboarding
  • Затраты: 25 часов твоего времени

Week 3-4 (шаблонизируем):

  • Шаблонизируй onboarding (auto-setup нового bot за 30 минут)
  • Виртуальный ассистент на подготовку списков и первичную переписку
  • Customer success: weekly check-in письма (auto)
  • Затраты: 20 часов / неделю

Month 2:

  • Случайный referral начнёт работать
  • Можно пересмотреть цену для новых клиентов, если ниша позволяет
  • Подумай про "white label" — бот под брендом клиента (premium tier)

Где НЕ масштабироваться:

  • Не делай "AI бот для всего на свете" — теряешь нишу
  • Не нанимай sales людей на раннем этапе — слишком рано
  • Не пиши свой dashboard — готового мониторинга хватит надолго


📚 Источники


Ключевые выводы

Lab — это не урок. Это 7 дней реальной работы где результат — рабочий бот в production и первый клиент, с которым согласованы условия. Если в воскресенье вечером у тебя только "потенциал" — Lab провален, начни Week 2 как Week 1.

Customer first, code second. Day 1 идёт на discovery calls, а не на код. Без проверенной боли — самый красивый бот не нужен никому. Без оплаченного контракта — продукт ещё не существует.

Экономика бота — это не магия AI. Это правильная ниша + правильная цена + правильные guardrails. Цена оправдана, когда экономит клиенту время, которое стоит дороже твоего сервиса; считай это на цифрах конкретного клиента.


Следующий урок

→ Build-Along — SaaS MVP за 7 дней

Отметка хранится только в этом браузере и никуда не отправляется. Мой прогресс