Как устроен AI-продукт изнутри - архитектура, компромиссы и то, что реально ломалось по пути, включая продакшн.
Я хотел принимать решения про AI-продукты, опираясь на собственное техническое понимание - не на слайд из презентации. Стоимость токена, бюджет задержки, grounding против галлюцинаций, риск вендор-лока - эти темы всплывают в любом обсуждении AI-роадмапа, и мне не хотелось кивать, не прочувствовав компромиссы на себе.
Поэтому я построил полноценный продукт: AI-ассистент для планирования путешествий, в одиночку, от начала до конца - аутентификация, streaming-чат, retrieval-augmented generation (RAG), деплой на собственный сервер с доменом и HTTPS, CI/CD. Не обёртка над чат-API, а система с реальными архитектурными решениями за ней - и реальной эксплуатацией после запуска.
Рабочая демка доказывает, что ты умеешь доставлять результат. Решения за ней доказывают, что ты умеешь думать как продакт. Вот четыре, за которые я готов ответить на любом интервью.
«Правильный» выбор для RAG - Qdrant/Weaviate/Pinecone. Я использовал pgvector - расширение поверх Postgres, который и так был нужен для реляционных данных. На масштабе MVP вторая база данных - это операционная стоимость (ещё один сервис, который нужно поднимать, мониторить, бэкапить) без соразмерной выгоды. Это паттерн, который я бы отслеживал в любом ревью AI-роадмапа: команда тянется к специализированному инструменту раньше, чем простой инструмент реально исчерпал себя.
Первая версия ждала полного ответа AI и только потом показывала его. Технически работало нормально - но ощущалось медленным, хотя задержка была той же, просто не было обратной связи во время генерации. Перешёл на Server-Sent Events, чтобы токены появлялись по мере генерации. Тот же бэкенд, ощутимо лучший UX - то, что невидимо в спеке и очевидно в первую же секунду использования продукта.
Начал на OpenAI, перешёл на бесплатный тариф Gemini, чтобы не жечь платные кредиты на ранней итерации. Вся интеграция с провайдером живёт в одном модуле - смена провайдера - это правка конфига, а не рефакторинг.
Это решение окупилось не один раз, а три. Дважды ещё во время разработки - новый формат API-ключей Google, тихо ломающий стандартную интеграцию, и модель, которую отключили для новых аккаунтов прямо во время работы над проектом. А затем - уже в проде: Gemini API оказался недоступен из региона, где физически находится мой сервер (геоблокировка на уровне провайдера, не связанная с кодом или аккаунтом). Решением стал полный переход на self-hosted LLM (Ollama) - локальная модель, без единого внешнего запроса, а значит без риска повторной блокировки. Миграция заняла часы, а не дни, именно потому что весь AI-слой был изолирован в одном модуле с самого начала.
В стартовой схеме Intent Router, Prompt Builder и AI Gateway были отдельными сервисами. Я объединил всё это в ответственности внутри одного FastAPI-приложения. Разбивать на микросервисы раньше, чем появилась реальная причина (размер команды, разная скорость деплоя, независимое масштабирование) - это добавление координационных издержек без компенсирующей выгоды. Знать, когда не строить более сложную версию - тот же навык, что и знать, когда не добавлять лишнюю фичу в бэклог.
Когда RAG был готов, я не просто посмотрел на правдоподобный ответ и закрыл задачу. Я добавил в тестовый документ выдуманный факт (несуществующее кафе с придуманным фирменным блюдом) и напрямую спросил AI про него. Если модель не могла знать эту деталь ниоткуда, кроме моего документа, и ответила правильно - это доказательство, что retrieval-пайплайн реально работает от начала до конца, а не что модель просто хорошо угадывает про путешествия в целом.
Проект не остался на localhost - поднят на собственном VDS, со своим доменом и автоматическим HTTPS (Caddy сам получает и обновляет сертификат Let's Encrypt, без ручной возни с certbot). За этим стоит ещё один слой решений, отдельный от собственно продуктовой логики: