SaaS на LLM: как собрать сервис на связке разных нейросетей и не разориться
Почему одна модель — это риск
Когда ты строишь SaaS на одном API, ты зависишь от:
- Доступности — упал API → упал твой сервис
- Цены — подняли тариф → твоя маржа сжалась
- Качества — поменяли модель → ответы стали хуже
- Санкций/блокировок — запретили доступ из РФ → всё
Решение: федеративная архитектура. Несколько LLM работают как единая система. Если одна недоступна — запрос уходит на другую. Если задача простая — идёт на дешёвую модель. Сложная — на мощную.
Архитектура bbb2.ru: 4 модели, 1 endpoint
┌─────────────────────┐
│ API Router │
│ (LLMRouter.php) │
└──────────┬──────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌──────────────┐ ┌──────────────────┐
│ DeepSeek v4 │ │ Alice LLM │ │ YandexGPT 5.1 │
│ (генерация │ │ (тексты, │ │ (извлечение │
│ сложного │ │ SEO, │ │ сущностей, │
│ контента) │ │ валидация) │ │ промпты) │
└────────────────┘ └──────────────┘ └──────────────────┘
│ │ │
└──────────────────┼──────────────────┘
│
▼
┌─────────────────────┐
│ Fallback Queue │
│ (если основная │
│ модель упала) │
└─────────────────────┘
Код: роутер LLM
<?php
// LLMRouter.php — федеративный роутер запросов к разным LLM
class LLMRouter {
private array $models = [
"deepseek" => [
"url" => "https://api.deepseek.com/v1/chat/completions",
"key" => "DEEPSEEK_API_KEY",
"cost_per_1k_tokens" => 0.014, // $
"max_tokens" => 32000,
"priority" => 1, // основной для сложных задач
],
"alice" => [
"url" => "https://ai.api.cloud.yandex.net/v1/responses",
"key" => "YANDEX_API_KEY",
"cost_per_1k_tokens" => 0.025, // ₽
"max_tokens" => 4000,
"priority" => 2, // для текстов и SEO
"folder_id" => "YANDEX_FOLDER_ID",
],
"yandexgpt" => [
"url" => "https://ai.api.cloud.yandex.net/v1/responses",
"key" => "YANDEX_API_KEY",
"cost_per_1k_tokens" => 0.015, // ₽
"max_tokens" => 2000,
"priority" => 3, // для технических задач
"folder_id" => "YANDEX_FOLDER_ID",
],
];
// Роутинг: выбираем модель по задаче
function route(string $task, string $prompt, array $options = []): string {
$model = $this->selectModel($task);
return $this->callWithFallback($model, $prompt, $options);
}
private function selectModel(string $task): string {
return match(true) {
str_contains($task, "generate_business_plan") => "deepseek",
str_contains($task, "seo_audit") => "alice",
str_contains($task, "extract_entities") => "yandexgpt",
str_contains($task, "validate") => "alice",
str_contains($task, "simple_text") => "yandexgpt", // дешёвая модель для простого
default => "deepseek",
};
}
private function callWithFallback(string $primaryModel, string $prompt, array $options): string {
$models = [$primaryModel];
// Добавляем fallback-модели в порядке приоритета
foreach ($this->models as $name => $cfg) {
if ($name !== $primaryModel) {
$models[] = $name;
}
}
$lastError = "";
foreach ($models as $modelName) {
try {
$result = $this->callModel($modelName, $prompt, $options);
if ($result !== false) return $result;
} catch (\Exception $e) {
$lastError = $e->getMessage();
error_log("LLM {$modelName} failed: {$lastError}, trying fallback...");
}
}
return "Все модели недоступны: {$lastError}";
}
private function callModel(string $modelName, string $prompt, array $options): string|false {
$cfg = $this->models[$modelName];
$apiKey = getenv($cfg["key"]);
if (!$apiKey) return false;
if ($modelName === "deepseek") {
return $this->callDeepSeek($cfg, $prompt, $options);
} else {
return $this->callYandex($cfg, $modelName, $prompt, $options);
}
}
private function callDeepSeek(array $cfg, string $prompt, array $options): string|false {
$body = json_encode([
"model" => "deepseek-chat",
"messages" => [["role" => "user", "content" => $prompt]],
"max_tokens" => $options["max_tokens"] ?? 4000,
"temperature" => $options["temperature"] ?? 0.7,
]);
$ch = curl_init($cfg["url"]);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Authorization: Bearer " . getenv($cfg["key"]),
],
CURLOPT_TIMEOUT => 60,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode !== 200) return false;
$data = json_decode($response, true);
return $data["choices"][0]["message"]["content"] ?? false;
}
private function callYandex(array $cfg, string $modelName, string $prompt, array $options): string|false {
$modelMap = ["alice" => "aliceai-llm/latest", "yandexgpt" => "yandexgpt-5.1/latest"];
$model = $modelMap[$modelName] ?? "yandexgpt-5.1/latest";
$body = json_encode([
"model" => $model,
"messages" => [["role" => "user", "text" => $prompt]],
"max_tokens" => $options["max_tokens"] ?? 2000,
"temperature" => $options["temperature"] ?? 0.3,
]);
$ch = curl_init($cfg["url"]);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Authorization: Api-Key " . getenv($cfg["key"]),
"x-folder-id: " . getenv($cfg["folder_id"] ?? "FOLDER_ID"),
],
CURLOPT_TIMEOUT => 60,
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
return $data["result"]["text"] ?? ($data["choices"][0]["message"]["content"] ?? false);
}
}
// Использование
$router = new LLMRouter();
// Простой текст → YandexGPT (копейки)
$simple = $router->route("simple_text", "Напиши описание продукта в 2 предложениях");
// Сложная генерация → DeepSeek
$complex = $router->route("generate_business_plan", "Напиши бизнес-план кофейни...");
// SEO → Alice
$seo = $router->route("seo_audit", "Проанализируй страницу...");
Мониторинг: что работает, что упало
Роутер пишет логи каждого вызова. Раз в час cron проверяет health:
<?php
// llm_health_check.php — мониторинг доступности моделей
function healthCheck(): array {
$router = new LLMRouter();
$results = [];
foreach (["deepseek", "alice", "yandexgpt"] as $model) {
$start = microtime(true);
$response = $router->route("simple_text", "Ответь одним словом: OK");
$latency = round((microtime(true) - $start) * 1000);
$results[$model] = [
"status" => $response !== false ? "UP" : "DOWN",
"latency_ms" => $latency,
];
}
// Если модель упала — алерт в Telegram
foreach ($results as $model => $status) {
if ($status["status"] === "DOWN") {
sendTelegramAlert("🔴 {$model} упала! Latency: {$status["latency_ms"]}ms");
}
}
return $results;
}
Экономика: сколько реально стоит SaaS на LLM
Типичный SaaS на LLM обрабатывает 10 000 запросов в месяц. Стоимость на разных моделях:
| Модель | Цена за 1K токенов | На 10K запросов* |
|---|---|---|
| ChatGPT 4o | $0.0025 (~0.25 ₽) | ~5 000 ₽ |
| DeepSeek v4 | $0.00014 (~0.013 ₽) | ~260 ₽ |
| Alice LLM | 0.025 ₽ | ~500 ₽ |
| YandexGPT 5.1 | 0.015 ₽ | ~300 ₽ |
| Гибрид (роутер) | ~0.015 ₽ (среднее) | ~300 ₽ |
*при 2000 токенов на запрос (средний)
300 ₽ в месяц на API при 10 000 запросах. Это себестоимость SaaS, за который можно брать 990–4 990 ₽/мес с пользователя.
Модели монетизации SaaS на LLM
| Продукт | Тариф | Себестоимость API | Маржа |
|---|---|---|---|
| Генератор бизнес-планов | 1 990–9 990 ₽ | ~5 ₽ на план | 99.7% |
| SEO-аудитор | 990 ₽/мес | ~10 ₽/мес | 99% |
| AI-консультант (бот) | 1 500 ₽/мес | ~15 ₽/мес | 99% |
Маржа 99% возможна потому, что API стоит копейки, а ценность продукта — в логике, архитектуре и результате.
Где взять клиентов на SaaS
Не надо таргета и бюджетов. Работает другое:
- Пишешь статью как эта — с кодом, схемами, цифрами. Публикуешь на vc.ru, Хабре, в Telegram-каналах
- В статье — CTA: «Если нужно готовое решение — вот ссылка на сервис»
- Первый клиент приходит не с рекламы, а со статьи, которую ты написал 3 месяца назад
Моя воронка на bbb2.ru: статья на vc.ru → 12 000 просмотров → 340 переходов на сайт → 8 заявок → 3 продажи.
Что это значит для вас: SaaS на LLM не требует команды, инвестиций и офиса. Нужен роутер (код выше), домен, VPS за 600 ₽ и понимание задачи клиента. 300 ₽ в месяц на API — и можно продавать продукт за 1 990–9 990 ₽. Маржа — 99%. Код открытый, архитектура рабочая. Берите и стройте.