Жизненный цикл запроса
Этот документ прослеживает полный путь пользовательского запроса — от отправки сообщения в мобильном приложении, через инфраструктуру и оркестратор, внутрь Agent Runtime и обратно к клиенту в виде события реального времени.
От клиента к оркестратору
Когда пользователь отправляет сообщение из мобильного приложения, запрос проходит через несколько инфраструктурных компонентов, прежде чем достигает Go-оркестратора.
Простыми словами, этот раздел отвечает на вопрос: «Что находится между пользователем и бэкендом, и что делает каждый слой?»
Каждый переход выполняет одну главную задачу: найти нужный адрес, принять интернет-трафик, расшифровать его, проверить авторизацию и передать запрос дальше в бэкенд.
Путь запроса
Resolve DNS in Cloudflare
Мобильное приложение отправляет HTTPS-запрос на dev.api.crawbl.com. Cloudflare отвечает на простой вопрос: «Какой публичный IP должен использоваться для этого хоста?»
Crawbl использует external-dns, чтобы автоматически поддерживать актуальность этих DNS-записей.
Hit the cloud load balancer
Облачный балансировщик нагрузки автоматически подготавливается Envoy Gateway.
Его задача проста: предоставить стабильную публичную точку входа и перенаправить трафик к подам шлюза. Он не проверяет тело запроса и детали авторизации.
Terminate TLS and enforce auth at Envoy
Envoy Gateway — это место, где зашифрованный интернет-трафик становится читаемым HTTP внутри кластера.
Здесь же происходят проверки безопасности:
- Валидация
HMACдля мобильных запросов сX-Token - Валидация
JWTдля запросов сAuthorization: Bearer
Если любая из проверок не проходит, запрос отклоняется здесь и никогда не достигает бэкенда.
Match the HTTPRoute
Envoy использует ресурсы HTTPRoute из Kubernetes Gateway API, чтобы решить, какой бэкенд-сервис должен получить запрос.
Простыми словами, это слой маршрутизации, который говорит: «Запросы с этим хостом и путём идут в этот сервис».
Hand off to the orchestrator
Go-оркестратор получает обычный HTTP-запрос с уже прикреплёнными заголовками доверенной идентификации.
Отсюда запрос проходит через многоуровневую архитектуру: обработчик → сервис → репозиторий → база данных.
Ключевые компоненты инфраструктуры
| Компонент | Роль |
|---|---|
| API Gateway (Envoy) | Реализация Gateway API, завершение TLS, фильтры авторизации |
| Управление сертификатами | Автоматические TLS-сертификаты через DNS-валидацию |
| Автоматизация DNS | Поддерживает актуальность публичных DNS-записей в соответствии с хостами шлюза |
Зачем такая архитектура
Ты можешь задаться вопросом, почему для простого API-вызова так много переходов. У каждого компонента есть своя чётко выраженная задача:
- Cloudflare даёт глобальный DNS с защитой от DDoS
- LoadBalancer предоставляет стабильный публичный IP, который не меняется при перезапуске подов
- Envoy Gateway обрабатывает TLS и авторизацию на границе, поэтому оркестратор видит только уже проверенные запросы
- HTTPRoutes позволяют добавлять новые сервисы, не трогая балансировщик нагрузки
Такое разделение означает, что любой компонент можно заменить независимо. Меняешь облачного провайдера? Замени тип LoadBalancer. Переключаешь провайдера авторизации? Обнови SecurityPolicy. Код оркестратора при этом не меняется.
От оркестратора к Runtime
После того как оркестратор получает сообщение пользователя, ему нужно доставить это сообщение в Agent Runtime пользователя и вернуть ответ.
Простыми словами, этот раздел объясняет передачу управления от «главный бэкенд принял запрос» до «agent runtime пользователя выполнил работу и ответил».
Это основной путь запроса и ответа для каждого взаимодействия пользователя с агентами.
gRPC-поток
Шаг за шагом
Ensure the runtime exists
Перед тем как что-либо пересылать, оркестратор проверяет, что runtime пользователя существует и готов к работе. Если runtime ещё не существует, платформа создаёт его, и оператор начинает согласовывать дочерние ресурсы. Если runtime существует, но ещё не верифицирован, оркестратор ждёт.
Проще говоря, это шаг «убедись, что агент пользователя действительно там, прежде чем отправлять ему работу».
Именно поэтому первое сообщение после создания аккаунта может занять несколько лишних секунд.
Broadcast status
Как только оркестратор начинает обработку, он отправляет события реального времени — например, состояние «агент занят/печатает» — через Socket.IO, чтобы мобильное приложение могло показать немедленную обратную связь.
Open the gRPC stream
Оркестратор открывает двунаправленный gRPC-поток к ClusterIP-сервису runtime на порту 42618. Поды runtime недоступны из публичного интернета.
Главная идея проста: пользователи никогда не обращаются к поду runtime напрямую. Бэкенд всегда стоит посередине.
Запрос включает сообщение пользователя и опциональный agent_id, когда оркестратор маршрутизирует к конкретному агенту рабочего пространства. Runtime загружает идентификацию агента из блюпринта, полученного при запуске — оркестратору не нужно пересылать системные промпты.
Process the agent work
Внутри пода Agent Runtime происходит несколько вещей:
- Активируется агент Manager (или целевой агент-делегат)
- Вызывается настроенный LLM-провайдер
- Могут выполняться вызовы инструментов — локально или через MCP обратно к оркестратору
- Частичные токены и итоговый ответ стримятся обратно в оркестратор по открытому gRPC-потоку
Handle the response
Когда оркестратор получает ответ от runtime, он:
- Сбрасывает состояние «печатает/занят»
- Сохраняет сообщение пользователя и ответ агента в PostgreSQL
- Рассылает события
message.newчерез Socket.IO - Возвращает API-ответ вызывающей стороне
Текущая реализация сохраняет сообщения до их рассылки.
Вызовы инструментов MCP
В процессе работы агента Agent Runtime может вызывать инструменты, предоставляемые встроенным MCP-сервером оркестратора. Так агенты получают доступ к возможностям платформы — пуш-уведомлениям, данным пользователя и истории разговоров — без хранения отдельных учётных данных приложения:
Каждый вызов инструмента MCP аутентифицируется с помощью токенов с ограниченным доступом, привязанных к вызывающему пользователю и рабочему пространству.
Если ты не знаком с MCP, практический смысл таков: «runtime может попросить бэкенд выполнить действия, принадлежащие платформе, не храня у себя «сырые» учётные данные приложения».
Почему gRPC Bidi Streaming
Оркестратор взаимодействует с Agent Runtime через двунаправленный gRPC-поток на порту 42618, используя ClusterIP-сервис runtime через внутреннюю сеть кластера.
Этот транспорт был выбран потому что:
- Стриминг ответов: частичные токены возвращаются в оркестратор по мере их генерации LLM, что обеспечивает обновления UI в реальном времени
- Строгая типизация: контракт gRPC определён в proto-файлах, что позволяет выявлять несовместимые изменения на этапе компиляции
- Нет публичного доступа: нет публичного эндпоинта, который нужно защищать; ClusterIP-сервис доступен только внутри кластера
- Встроенные вызовы инструментов: запросы вызовов инструментов MCP и результаты передаются по тому же потоку без необходимости отдельного HTTP-обращения
Оркестратор определяет ClusterIP-сервис runtime по рабочему пространству, открывает поток, отправляет сообщение пользователя с опциональным agent_id и считывает обратно частичные токены и итоговый ответ. Аутентификация использует HMAC Bearer-токен в метаданных gRPC, привязанный к пользователю и рабочему пространству.
События реального времени
Когда агент заканчивает обработку сообщения, пользователь должен увидеть ответ мгновенно, а не при следующем опросе API.
Простыми словами, этот раздел отвечает на вопрос: «Как приложение узнаёт, что что-то изменилось, не спрашивая бэкенд постоянно?»
Crawbl использует Socket.IO на основе Redis pub/sub для отправки событий подключённым клиентам в реальном времени.
Обзор архитектуры
Как это работает
Оркестратор запускает Socket.IO-сервер в пространстве имён /v1. Когда мобильный клиент открывает приложение, он подключается через WebSocket и аутентифицируется с теми же заголовками, что и REST-запросы (X-Token или Authorization: Bearer). После успешной аутентификации клиент автоматически вступает в комнату рабочего пространства (workspace:{workspaceId}).
Если термин «комната» непривычен, считай её приватной группой рассылки для одного рабочего пространства. События, отправленные в эту комнату, достигают каждого подключённого клиента, который в данный момент просматривает это рабочее пространство.
Когда сервису нужно уведомить клиентов — например, пришло новое сообщение от агента — он вызывает интерфейс Broadcaster. Broadcaster отправляет событие в комнату рабочего пространства, и каждый подключённый клиент в этой комнате его получает.
Масштабирование на несколько подов через Redis
В продакшене несколько подов оркестратора работают за балансировщиком нагрузки. Клиент 1 может быть подключён к Поду A, а Клиент 2 — к Поду B. Без координации событие, отправленное на Поде A, достигнет только Клиента 1.
Redis pub/sub решает эту проблему. Когда Под A отправляет событие, Redis-адаптер публикует его в Redis-канал. Под B подписан на тот же канал и повторно отправляет событие своим локальным клиентам. В результате каждый подключённый клиент получает каждое событие, независимо от того, к какому поду он подключён.
Плавный откат
Если Redis не настроен, оркестратор переключается в локальный режим, который молча отбрасывает broadcast-события. Система продолжает работать — клиенты просто не будут получать обновления в реальном времени и будут вынуждены опрашивать бэкенд для получения новых данных.
Типы событий
| Событие | Данные | Когда срабатывает |
|---|---|---|
message.new | Полный объект сообщения | Создано новое сообщение (пользователя или агента) |
message.updated | Полный объект сообщения | Изменилось содержимое или статус сообщения |
agent.typing | {conversationId, agentId, isTyping} | Агент начал или прекратил обработку |
agent.status | {agentId, status} | Агент перешёл в состояние онлайн, занят или офлайн |
Эти четыре события охватывают полный жизненный цикл хода разговора. Мобильное приложение использует agent.typing и agent.status для отображения индикатора печати, а message.new — для добавления сообщений в чат в реальном времени.