24.03.2026, 13:55

Telegram-бот для анализа бизнеса: Python + Gemini + ZvenoAI за один вечер

Допустим, вы хотите быстро изучить незнакомую компанию. Скидываете ссылку в ChatGPT — и получаете что-то вроде «не имею актуальных данных об этом сайте». Просите поискать — получаете общие фразы про цветочный рынок России.
ChatGPT без плагинов не видит конкретные сайты и не знает мелких российских компаний. Выдаёт текст, который звучит правдоподобно, но ни к чему не привязан.
В этой статье мы соберём Telegram-бота, который реально работает: ходит на сайт, ищет отзывы, находит конкурентов. Всё это через единый API ZvenoAI — никаких дополнительных сервисов. Весь код — меньше 200 строк.

Что получится

Бот принимает ссылку или название компании и возвращает структурированный анализ:

🏢 Что за компания и какого масштаба

📦 Продукты и цены

📊 Бизнес в цифрах (трафик, сроки, объём)

🎯 Целевая аудитория и позиционирование

⚔️ Конкуренты с кратким сравнением

⭐ Реальные отзывы — что хвалят, что ругают

💪 Сильные стороны и риски

В конце статьи — полный реальный ответ бота по цветочному магазину rus-buket.ru.

Стек

  • Python 3 + aiogram 3 — асинхронный Telegram-бот

  • ZvenoAI API (api.zveno.ai/v1) — OpenAI-совместимый шлюз ко всем моделям

  • Gemini 2.5 Flash (google/gemini-3-flash-preview) — основная модель, умеет tool use, 1M токенов контекста

  • Perplexity Sonar Pro (perplexity/sonar-pro) — поиск в интернете с актуальными данными

  • agent-browser — просмотр веб-страниц из кода

  • Один API-ключ ZvenoAI — и Gemini, и Perplexity идут через него. Не нужно регистрироваться у каждого вендора отдельно.

Как устроен агент

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

Запрос пользователя ↓ Gemini Flash ←──── системный промпт + история ↓ finish_reason? ┌────┴────┐ tool_calls stop ↓ ↓ Выполнить Финальный инструмент ответ ↓ Добавить результат в messages → снова Gemini

Gemini сам решает, какой инструмент вызвать и в каком порядке. Для анализа бизнеса он обычно делает три вызова за один запрос: открывает сайт, ищет отзывы, ищет конкурентов. После трёх вызовов у него достаточно данных для финального отчёта.

Шаг 0. Создаём бота и получаем ключи

BotFather. Открываем Telegram, ищем @BotFather, отправляем /newbot. Указываем имя и username (должен заканчиваться на bot). Получаем токен вида 8710689253:AAH2….

ZvenoAI. Идём на zveno.ai, логинимся, в разделе API Keys создаём новый ключ. Названия типа ai_business_bot помогут не запутаться, когда ключей станет несколько.

Шаг 1. Структура проекта

ai_business_bot/ bot.py ← точка входа, aiogram handlers agent.py ← LLM-агент: tool use loop с Gemini tools.py ← реализация инструментов: browse + search config.py ← настройки из env .env ← токены (не в git) requirements.txt

Создаём venv и ставим зависимости:

python3 -m venv .venv .venv/bin/pip install "aiogram>=3,<4" "openai>=1.0" python-dotenv

Три пакета, никаких лишних зависимостей. OpenAI SDK используем не для OpenAI — он совместим с любым endpoint, в том числе с ZvenoAI.

Шаг 2. config.py

Все токены из .env, никаких хардкодов:

import os from dotenv import load_dotenvload_dotenv() TELEGRAM_TOKEN = os.environ["TELEGRAM_TOKEN"] ZVENO_API_KEY = os.environ["ZVENO_API_KEY"] ZVENO_BASE_URL = "https://api.zveno.ai/v1" MAIN_MODEL = "google/gemini-3-flash-preview" SEARCH_MODEL = "perplexity/sonar-pro"

.env:

TELEGRAM_TOKEN=ваш_токен_от_botfather ZVENO_API_KEY=ваш_ключ_от_zveno_ai

Обратите внимание: две разные модели через один API. MAIN_MODEL — Gemini, который управляет агентом и пишет итоговый отчёт. SEARCH_MODEL — Perplexity, который умеет искать в интернете. Переключаемся между ними меняя одну строку.

Шаг 3. tools.py — два инструмента агента

генту доступны два инструмента: просмотр страницы и поиск в интернете.

browse_url — открываем сайт

Используем agent-browser — CLI-инструмент для управления браузером из скриптов. Устанавливать не нужно, npx скачает сам при первом вызове.

Есть нюанс: agent-browser — stateful-демон с одним глобальным состоянием браузера. Если вызвать close, весь браузер закрывается. Поэтому открываем новую вкладку, читаем, закрываем только её через tab close:

import subprocess import asyncio import logging from openai import AsyncOpenAI from config import ZVENO_API_KEY, ZVENO_BASE_URL, SEARCH_MODEL logger = logging.getLogger(__name__) _client = AsyncOpenAI(api_key=ZVENO_API_KEY, base_url=ZVENO_BASE_URL) def _run_agent_browser(url: str) -> str: def run(*args): return subprocess.run( ["npx", "--yes", "agent-browser", *args], capture_output=True, text=True, timeout=30, ) try: run("tab", "new", check=True) run("open", url, check=True) run("wait", "--load", "networkidle", check=True) result = run("get", "text", "body", check=True) text = result.stdout.strip() except subprocess.CalledProcessError as e: text = f"Ошибка загрузки: {e.stderr[:300]}" except subprocess.TimeoutExpired: text = "Страница не загрузилась за 30 секунд." finally: subprocess.run( ["npx", "--yes", "agent-browser", "tab", "close"], capture_output=True, timeout=10, ) # Обрезаем, чтобы не переполнить контекст модели if len(text) > 8000: text = text[:8000] + "\n... [текст обрезан]" return text async def browse_url(url: str) -> str: logger.info("browse_url: %s", url) return await asyncio.to_thread(_run_agent_browser, url)

Весь бот асинхронный, но subprocess.run блокирует поток. asyncio.to_thread переносит этот вызов в пул потоков — бот не зависает.

search_web — поиск через Perplexity

Perplexity Sonar Pro не поддерживает tool use — используем его как обычный completion:

async def search_web(query: str) -> str: logger.info("search_web: %s", query) response = await _client.chat.completions.create( model=SEARCH_MODEL, messages=[ { "role": "system", "content": ( "Ты помощник для поиска информации о компаниях. " "Отвечай по-русски. Приводи источники в конце ответа." ), }, {"role": "user", "content": query}, ], ) return response.choices[0].message.content or "Нет результатов."

Perplexity возвращает живые данные из интернета с источниками. Gemini потом использует эти данные при написании отчёта.

Шаг 4. agent.py — tool use loop

Gemini получает запрос, решает какие инструменты вызвать, мы выполняем вызовы и отдаём результаты обратно. Цикл продолжается до финального ответа.Сначала определяем инструменты в формате OpenAI function calling:

TOOLS = [ { "type": "function", "function": { "name": "browse_url", "description": "Открывает веб-страницу и возвращает текст.", "parameters": { "type": "object", "properties": { "url": {"type": "string", "description": "URL страницы"} }, "required": ["url"], }, }, }, { "type": "function", "function": { "name": "search_web", "description": "Ищет информацию о компании в интернете.", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "Поисковый запрос"} }, "required": ["query"], }, }, }, ]

Системный промпт задаёт алгоритм работы: сначала смотрим сайт, потом ищем отзывы и конкурентов, потом делаем анализ:

SYSTEM_PROMPT = """\ Ты — профессиональный AI-аналитик бизнеса. Твоя задача — провести глубокий анализ. Алгоритм работы: 1. Если есть URL — browse_url сайта компании. 2. search_web: "отзывы [название] отзовик irecommend" 3. search_web: "конкуренты [название] рынок [сфера] Россия" 4. Собери анализ в структурированный отчёт. Формат ответа (строго на русском): 🏢 Компания — название, сфера, год, масштаб, география 📦 Продукты и цены — ассортимент, ценовой диапазон, уникальные позиции 📊 Бизнес в цифрах — трафик, количество заказов, сроки, рейтинги 🎯 Аудитория и позиционирование — кто покупает, УТП ⚔️ Конкуренты — 3-5 конкурентов с кратким сравнением ⭐ Отзывы — плюсы (2-3 из реальных), минусы (2-3 из реальных), рейтинг 💪 Сильные стороны — 3-4 реальных преимущества ⚠️ Риски — 3-4 конкретные проблемы Только конкретные данные, никаких общих фраз. Если данных нет — так и скажи. """

Сам loop — до 5 итераций на случай сложных запросов:

async def run(user_message: str) -> str: messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_message}, ] for _ in range(5): response = await _client.chat.completions.create( model=MAIN_MODEL, messages=messages, tools=TOOLS, tool_choice="auto", ) choice = response.choices[0] message = choice.message if choice.finish_reason == "tool_calls": messages.append(message) for tc in message.tool_calls: fn_name = tc.function.name args = json.loads(tc.function.arguments) logger.info("Tool call: %s(%s)", fn_name, args) result = await _TOOL_FN[fn_name](**args) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": result, }) continue # отдаём результаты обратно модели return message.content or "Агент не вернул ответ." return "Достигнут лимит итераций."

Когда finish_reason == «tool_calls» — Gemini хочет вызвать инструмент. Выполняем, добавляем результат в messages, идём на следующую итерацию. Когда finish_reason == «stop» — это финальный текстовый ответ.

Шаг 5. bot.py — aiogram handlers

import asyncio import logging from aiogram import Bot, Dispatcher, F from aiogram.filters import CommandStart, Command from aiogram.types import Message from aiogram.enums import ChatAction import agent from config import TELEGRAM_TOKEN bot = Bot(token=TELEGRAM_TOKEN) dp = Dispatcher() @dp.message(CommandStart()) async def cmd_start(message: Message) -> None: await message.answer( "👋 Я AI-аналитик бизнеса. Отправь ссылку на сайт или название компании.\n\n" "Примеры:\n" "• `/analyze https://rus-buket.ru`\n" "• `расскажи о компании Яндекс`", parse_mode="Markdown", ) @dp.message(Command("analyze")) async def cmd_analyze(message: Message) -> None: parts = (message.text or "").split(maxsplit=1) if len(parts) < 2: await message.answer("Укажи URL: `/analyze https://example.com`", parse_mode="Markdown") return await _process(message, parts[1].strip()) @dp.message(F.text) async def handle_text(message: Message) -> None: await _process(message, message.text or "") async def _process(message: Message, query: str) -> None: # Показываем typing пока агент думает stop = asyncio.Event() async def keep_typing(): while not stop.is_set(): await bot.send_chat_action(message.chat.id, ChatAction.TYPING) await asyncio.sleep(4) typing = asyncio.create_task(keep_typing()) try: answer = await agent.run(query) except Exception as e: answer = f"Ошибка: {e}" finally: stop.set() typing.cancel() if len(answer) > 4096: answer = answer[:4090] + "\n…" await message.answer(answer, parse_mode="Markdown") async def main(): await dp.start_polling(bot) if __name__ == "__main__": logging.basicConfig(level=logging.INFO) asyncio.run(main())

Typing action в Telegram гаснет через 5 секунд — поэтому обновляем его каждые 4 секунды в фоновой coroutine. Пока агент работает, пользователь видит «печатает…».

Шаг 6. Запуск

.venv/bin/python bot.py

В логах увидите что-то такое:

INFO Starting bot... INFO aiogram.dispatcher: Run polling for bot @ai_business_analysis_bot INFO agent: Tool call: browse_url({'url': 'https://rus-buket.ru'}) INFO agent: Tool call: search_web({'query': 'Русский букет отзывы отзовик irecommend'}) INFO agent: Tool call: search_web({'query': 'конкуренты rus-buket.ru доставка цветов Россия 2024'})

Три инструмента за одну итерацию — агент сам решил что нужно сделать и в каком порядке.Приветствие на /start:

Что получилось

Отправляем боту /analyze https://rus-buket.ru и через 32 секунды получаем:

Вот полный ответ бота:

🏢 Компания«Русский Букет» — международная сеть службы доставки цветов и подарков.

Сфера: E-commerce, флористика, логистика подарковГод основания: 2012 (активное развитие с этого периода)

География: Работает в более чем 100 городах России, доставка в 100+ стран мира

Масштаб: Одна из крупнейших федеральных сетей. Головной офис — Новосибирск. Сеть из 2000+ партнёрских салонов

📦 Продукты и цены

Ассортимент: монобукеты (розы, пионы, тюльпаны), авторские композиции, цветы в коробках, комнатные растения. Дополнительно: игрушки, сладости, воздушные шары.Бюджет: 1 500–2 500 ₽ (небольшие букеты, кенийские розы)

Средний чек: 4 500–7 000 ₽ (сборные авторские букеты)Премиум: от 15 000 ₽ (101 роза, гигантские корзины)Уникальные позиции: фотоотчёт в момент вручения (бесплатно), анонимная доставка

📊 Бизнес в цифрах

Трафик сайта: ~350–450 тыс. визитов в месяцСроки доставки: от 2 часов в крупных городах

В пиковые даты (8 марта, 14 февраля): сотни заказов в час

Рейтинг приложения: ТОП-3 в App Store / Google Play по версии РоскачестваПрограмма лояльности: кэшбэк до 10%

🎯 Аудитория и позиционирование

Основная аудитория — мужчины 25–45 лет (70% заказов), экспаты, люди, живущие далеко от родственников. Позиционирование: «Доставка радости через расстояния». Акцент на надёжности, фотоотчёте и SMS-уведомлениях.

УТП: гарантия свежести (замена букета в течение 24 часов) и международный охват через единое окно.

⚔️ Конкуренты

Flowwow — маркетплейс местных флористов, больше уникальности, но меньше стандартизации

Флорист.ру — федеральная сеть, схожая модель, ориентирована на массовый сегмент

Семицветик / Lush Flowers — региональные игроки, сильны в конкретных городах

Wildberries / Ozon — растут в нише, агрессивные цены, но нет экспресс-доставки цветов

Местные флористы — ниже цены и выше уникальность букетов, но нет масштаба и гарантий

Отзывы покупателей

Плюсы:

Скорость: «Заказал из США для мамы в Самаре, привезли через 2 часа»Сервис: бесплатная открытка и качественное фото получателя с букетомСоответствие: цветы в реальности часто совпадают с фото на сайте

Минусы:Праздничный коллапс: в 8 марта бывают задержки до 5–10 часов, замены цветов без согласования

Стойкость: жалобы на «уставшие» цветы в регионах с низким контролем партнёров

Средний рейтинг: 4.7–4.9 из 5 на крупных агрегаторах

💪 Сильные стороны

IT-платформа: удобное приложение с историей заказов и напоминаниями о датах

Глобальность: оплата картой РФ — доставка в Европу или СШАСтандартизация: жёсткие чек-листы для партнёров-флористов

SEO: топ выдачи по ключевым городам России

⚠️ Риски

Зависимость от партнёров: в малых городах качество зависит от конкретного местного магазина

Логистика: рост цен на топливо и курьеров давит на маржуИмпортозависимость: высокая доля эквадорских и кенийских цветов — цены волатильны из-за курса валют

ChatGPT без плагинов про rus-buket.ru практически ничего бы не сказал — компания слишком маленькая для его обучающей выборки. Здесь агент реально пошёл и посмотрел.

Сколько стоит один запрос

Один запрос к боту включает:

1 вызов browse_url — только CPU и трафик (agent-browser запускается локально, это бесплатно)

2 вызова search_web — 2 запроса к Perplexity Sonar Pro

2 вызова Gemini Flash — первый решает что делать и вызывает инструменты, второй пишет итоговый отчёт

Вот реальные данные из ZvenoAI для одного анализа rus-buket.ru:

Разбивка:

  • Gemini 3 Flash (первый вызов, решение о инструментах): 739 токенов → 0.07 ₽

  • Sonar Pro (поиск отзывов): ~1 000 токенов → 1.93 ₽

  • Sonar Pro (поиск конкурентов): ~1 100 токенов → 1.95 ₽

  • Gemini 3 Flash (финальный синтез отчёта): 2 800 + 1 300 токенов → 0.63 ₽

Итого: ~4.6 ₽ за полный анализ. Почти всё съедает Perplexity — два поисковых запроса по ~2 ₽ каждый. Gemini Flash работает практически бесплатно.На 500 ₽ баланса ZvenoAI — около 100 полных анализов.

Подводные камни

1. agent-browser — один глобальный демон. Нельзя вызвать close — закроется весь браузер вместе с другими вкладками. Используйте tab new + tab close.

2. Gemini возвращает `finish_reason: «tool_calls»`, а не `»stop»`. Нужен цикл, а не одиночный вызов API. Один запрос = один tool call, потом нужно снова звать модель с результатом.

3. Perplexity Sonar Pro не поддерживает tool use. Передавать ему tools — ошибка. Используйте как обычный completion.

4. Длинные страницы переполняют контекст. Обрезайте до ~8 000 символов. Для большинства сайтов достаточно — главная страница содержит нужное описание продуктов и сервиса.

5. Telegram typing action истекает через 5 секунд. Нужно обновлять каждые 4 секунды пока агент работает. Иначе пользователь видит тишину и не понимает, идёт ли что-то.6. SimilarWeb и похожие сервисы блокируют headless-браузеры через CloudFront. Данные о трафике лучше получать через search_web — Perplexity находит эти цифры из открытых источников.

Итог

Весь код — меньше 200 строк на четырёх файлах. Один API-ключ ZvenoAI закрывает и Gemini (tool use + анализ), и Perplexity (поиск). Никаких дополнительных сервисов.Агент не заменяет полноценное исследование — цифры по трафику стоит перепроверять, данные о партнёрах бот не знает. Но за 30 секунд и 5 ₽ он даёт куда больше, чем ChatGPT без плагинов по той же компании.

Зарегистрируйтесь на офлайн-участие или смотрите трансляцию
24 марта в 15:00 / Центр событий РБК
Регистрация на конференцию DATA
Реклама.
АО «Диджитал альянс»
ИНН 7731276913
Erid: 2W5zFJD2xtq