Досвід створення системи AI-асистентів для знайомств з інтеграцією Laravel + Node.js + Queue
Вступ
Під час розробки AI Gateway для Telegram UserBots ми зіткнулися з низкою специфічних проблем, які не описані в документації. Ця стаття розповідає про реальний досвід вирішення проблем синхронізації, queue механізмів та специфіки UserBot API.
Архітектура проекту
Стек технологій:
- Frontend: React + TypeScript
- Backend: Laravel (PHP 8.3)
- AI Gateway: Node.js + TypeScript + Prisma
- Queue: Laravel Database Queue
- UserBots: MadelineProto + Docker
- База даних: PostgreSQL (AI Gateway) + MySQL (Laravel)
Основний workflow:
|
1 2 3 4 |
Frontend → Laravel API → Queue System → AI Gateway → OpenAI/Google → Response ↓ ↓ UserBot Polling ← Telegram ← AI Response ← |
Проблема 1: Ненадійна синхронізація між сервісами
Проблема
Коли користувач оновлював personality бота через фронтенд, дані зберігалися в Laravel, але синхронізація з AI Gateway часто не відбувалася. Фронтенд отримував success: true, але бот продовжував працювати зі старими налаштуваннями.
Код що НЕ працював:
|
1 2 3 4 5 6 7 8 |
// AIUserBotsController.php $userBot->update(['ai_personality' => $personality]); $syncResult = $syncService->updateBotPersonality($userBot); // Завжди повертали success незалежно від синхронізації! return response()->json(['success' => true]); |
Рішення: Queue + Retry механізм
Реалізували двоетапну синхронізацію:
- Immediate Sync – миттєва спроба
- Queue Fallback – додавання в чергу при невдачі
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Оновлений контролер $userBot->update([ 'ai_personality' => $personality, 'ai_sync_status' => 'pending' ]); // Миттєва спроба $immediateSync = $syncService->updateBotPersonality($userBot); if ($immediateSync) { return response()->json(['success' => true, 'sync_status' => 'immediate']); } // Додаємо в queue з retry SyncBotWithAIGateway::dispatch($userBot->id, 'update') ->delay(now()->addSeconds(5)); return response()->json([ 'success' => true, 'sync_status' => 'queued_for_retry' ]); |
Job з exponential backoff:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class SyncBotWithAIGateway implements ShouldQueue { public $tries = 5; public $backoff = [10, 30, 60, 120, 300]; // секунди public function handle(BotSyncService $syncService) { $success = $syncService->updateBotPersonality($this->userBot); if (!$success) { throw new \Exception("Sync failed on attempt {$this->attempts()}"); } } } |
Проблема 2: Queue Worker в Docker
Проблема
Laravel queue:work в Docker контейнері “завмирав” через 1-2 години роботи. Завдання накопичувалися, але не оброблялися.
Рішення: Окремий контейнер + Supervision
docker-compose.yml:
|
1 2 3 4 5 6 7 8 9 10 |
ai-queue-worker: build: ./images/php83 container_name: ai-queue-worker restart: unless-stopped command: > bash -c " echo 'Starting AI Queue Worker...' && php artisan queue:work --queue=ai-sync --sleep=3 --tries=1 --max-time=3600 --memory=512 " |
Також додали автоматичний retry через cron:
|
1 2 3 4 5 6 7 |
laravel-cron: command: > bash -c " echo '*/30 * * * * php artisan ai:retry-sync' >> /etc/cron.d/laravel-cron && cron -f " |
Проблема 3: UserBot Polling vs Real-time
Проблема
UserBots не мають webhook’ів як звичайні боти. Довелося реалізувати polling через MadelineProto, що призвело до затримок у відповідях.
Неефективний підхід:
|
1 2 3 |
// Кожні 5 хвилин перевіряти ВСІ діалоги $schedule->command('userbot:ai-check')->everyFiveMinutes(); |
Рішення: Розумний polling
Оптимізований cron job:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class UserBotAIChecker extends Command { private function getNewMessagesForUserBot($client, $userBot): array { $lastCheckTime = Cache::get("userbot_last_check:{$userBot->id}", time() - 300); $dialogs = $client->messages->getDialogs(['limit' => 50]); foreach ($dialogs['dialogs'] as $dialog) { $history = $client->messages->getHistory([ 'peer' => $dialog['peer'], 'limit' => 10, ]); foreach ($history['messages'] as $message) { if ($message['date'] > $lastCheckTime) { $updates[] = ['_' => 'updateNewMessage', 'message' => $message]; } } } Cache::put("userbot_last_check:{$userBot->id}", time(), 3600); return $updates; } } |
Проблема 4: Мовні налаштування ігноруються
Проблема
Бот зі встановленою мовою "language": "ru" відповідав українською, незалежно від налаштувань.
Рішення: Посилені мовні інструкції
Було (слабкі інструкції):
|
1 2 |
const prompt = `Відповідай російською мовою. ${userMessage}`; |
Стало (посилені інструкції):
|
1 2 3 4 5 6 7 |
private getStrongLanguageInstruction(language: string): string { return `🚨 КРИТИЧЕСКИ ВАЖНО: Отвечай ТОЛЬКО НА РУССКОМ ЯЗЫКЕ! Используй русскую грамматику, русские слова, русские выражения. НЕ используй украинские слова как "працюєш", "знаєш". ПРАВИЛЬНО по-русски: "работаешь", "знаешь", "расскажи".`; } |
Проблема 5: Гендерні особливості для знайомств
Проблема
Всі боти спілкувалися однаково, незалежно від гендеру та призначення (знайомства vs загальне спілкування).
Рішення: Адаптивні personality
Структура даних:
|
1 2 3 4 5 6 7 |
interface DatingPersonality { gender: 'female' | 'male'; flirtLevel: 'subtle' | 'moderate' | 'playful'; responseStyle: 'shy' | 'confident' | 'mysterious'; location: string; } |
Генерація промптів:
|
1 2 3 4 5 6 7 8 9 10 |
private getGenderInstructions(personality: DatingPersonality): string { if (personality.gender === 'female') { return `🎀 ТЫ ДЕВУШКА - общайся как девушка: • Используй больше эмоциональных слов: "мило", "интересно" • Задавай встречные вопросы: "а ты?", "правда?" • Используй ) чаще чем точки • Показывай заинтересованность к собеседнику`; } } |
Проблема 6: TypeScript типи не синхронізувалися
Проблема
При додаванні нових полів в Laravel API, TypeScript фронтенд видавав помилки про невідомі властивості.
Рішення: Синхронізація інтерфейсів
AIService.tsx:
|
1 2 3 4 5 6 7 8 9 10 11 |
export interface AIPersonality { name: string; age?: number; traits: string[]; // НОВІ поля gender?: 'female' | 'male'; flirtLevel?: 'subtle' | 'moderate' | 'playful'; responseStyle?: 'shy' | 'confident' | 'mysterious'; location?: string; } |
Проблема 7: Персональна пам’ять не працювала
Проблема
personalFacts: 0 в логах, факти про користувачів не зберігалися.
Код з помилкою:
|
1 2 3 |
const saved = await this.prisma.message.findFirst({...}); if (saved.length > 0) { // ❌ saved - об'єкт, не масив! |
Рішення: Виправлення логіки пам’яті
|
1 2 3 4 5 |
const saved = await this.prisma.message.findFirst({...}); if (saved && saved.metadata?.personalMemory) { // ✅ Правильно return saved.metadata.personalMemory as PersonalMemory; } |
Практичні висновки
Що працює добре:
- Queue система – надійно обробляє синхронізацію
- Docker контейнери – ізоляція процесів
- Exponential backoff – автоматичне відновлення
- Status tracking – завжди знаємо стан системи
Що потрібно покращити:
- Real-time polling – UserBot API обмежений
- Monitoring – потрібно більше метрик
- Scaling – при рості навантаження
Уроки для інших проектів:
- Завжди тестуйте повний workflow – не окремі компоненти
- Додавайте retry механізми відразу, не після проблем
- Логируйте все що стосується синхронізації між сервісами
- TypeScript типи повинні відповідати API структурам
- Docker queue workers потребують спеціального налаштування
Підсумок
Розробка AI Gateway для UserBots виявилася складнішою за очікування через специфіку Telegram API та необхідність надійної синхронізації між сервісами. Основні проблеми вирішилися через queue систему з retry механізмом та детальне логування всіх операцій.
Результат: стабільна система AI-ботів для знайомств з гендерними особливостями, яка обробляє 1000+ повідомлень на день з 99% uptime.