Шлюз и движок обработки запросов
Сердце AiHummer — это единый сервис-шлюз (gateway). Он одновременно и административный API (настройки, подключение каналов, маркетплейс), и движок обработки запросов (цикл вызова функций, который порождает ответ). Поэтому типовое развёртывание — это всего один сервис плюс PostgreSQL: отдельный слой воркеров запускать не обязательно.
Коротко: шлюз принимает запрос, движок выполняет шаги, а очередь доставки возвращает результат в канал.
Один сервис, две роли
По умолчанию шлюз слушает публичный порт :8780, что задаётся
AIHUMMER_GATEWAY_ADDR — этот порт несёт весь внешний трафик (API, привязку
устройств, WS/SSE, входящие вебхуки, прокси приложения/pocket и пробы
работоспособности). Веб‑интерфейс администратора работает на отдельном
приватном слушателе (по умолчанию :8781,
AIHUMMER_WEBUI_ADDR), раздаётся по корневому пути / и в идеале привязан к
внутреннему интерфейсу. В любом случае процесс всегда один и тот же единый сервис.
# gateway.env — единственная обязательная настройка
AIHUMMER_DATABASE_URL=postgres://user:pass@localhost:5432/aihummer?sslmode=disable
# Веб-интерфейс администратора после старта — на http://localhost:8781/ (приватный слушатель)
Единственная жёсткая зависимость — PostgreSQL. Postgres — единый источник истины для агентов, настроек, диалогов, памяти, состояния доставки и аудита. Всё остальное — дополнительные сервисы, векторное хранилище и провайдеры моделей — опционально и подключается только тогда, когда вы это настроите.
[!NOTE] Без базы данных шлюз стартует в health-only режиме: он отвечает на
GET /healthz, чтобы оркестратор или балансировщик видели, что процесс работает, но запросы обслуживать не будет.GET /readyzпроверяет PostgreSQL и возвращает503, пока база недоступна.
Что происходит при старте
При старте шлюз выполняет несколько шагов в строгом порядке:
- открывает пул соединений с базой данных,
- применяет ожидающие миграции под advisory-локом PostgreSQL,
- разрешает конфигурацию (значение из БД → переменная окружения → встроенное значение по умолчанию) и
- связывает сервисы — маршрутизатор, оркестратор, каналы, инструменты, память, доставку — в работающий шлюз.
Важное следствие: большинство возможностей подключаются опционально через ключ
настройки. Ненастроенная возможность просто не активируется, и поэтому среда выполнения по
умолчанию остаётся компактной и предсказуемой. Вы включаете нужное из
веб-интерфейса или переменной AIHUMMER_*, и шлюз учитывает это при следующем
старте (или на горячую — для тех параметров, которые это поддерживают).
Движок обработки запросов
Когда сообщение доходит до шлюза, за дело берётся движок обработки запросов (turn engine). Он ведёт цикл вызова функций: модели передаются системный промпт и диалог, она может вызывать инструменты (или порождать суб-агентов), результат каждого инструмента возвращается обратно, и цикл продолжается, пока модель не выдаст финальный ответ. Этот ответ затем передаётся слою доставки.
входящее сообщение
└─▶ движок обработки запросов
├─ сборка слоистого системного промпта
├─ вызов модели ─▶ вызовы инструментов / суб-агенты ─▶ результаты ─┐
│ ▲ │
│ └─────────────────────────────────────────────────────-──┘
└─ финальный ответ ─▶ надёжная доставка ─▶ канал-источник
Поскольку цикл строго отслеживает, откуда пришёл каждый вход, ответы формируются из истории диалога и результатов инструментов, а не за счёт внедрения недоверенного текста в инструкции. Именно это свойство делает описанную ниже слоистость промпта не только быстрой, но и безопасной.
Слоистый, дружелюбный к кэшу системный промпт
Системный промпт — не единый блок. Он собирается слоями, намеренно упорядоченными так, чтобы стабильные части шли первыми, а изменчивые — последними. Это важно, потому что провайдеры моделей кэшируют промпт по его префиксу: пока начало промпта байт-в-байт идентично, кэшированный префикс переиспользуется и заново обрабатывается только хвост.
| Зона | Слои (по порядку) | Меняется… |
|---|---|---|
| Стабильный префикс (кэшируемый) | базовая идентичность + гид по инструментам/памяти → арендатор → профиль агента → навыки | редко — на агента/арендатора |
| Изменчивая часть (добавляется последней) | состояние настройки → подгрузка памяти → текущая дата | каждый запрос |
Стабильный префикс несёт всё, что определяет, кто такой агент: встроенную идентичность и гид по тому, как работают инструменты и память, затем слой арендатора, профиль агента и отрендеренный блок навыков. Ничего из этого не меняется между двумя соседними запросами одного агента, поэтому это и образует переиспользуемый кэшируемый префикс.
Изменчивая часть — данные текущего шага, которые могут меняться при повторном запуске, — добавляется после стабильного префикса именно для того, чтобы не делать кэш недействительным: состояние настройки, память, подгруженная под конкретный диалог, и текущая дата меняются от запроса к запросу, но раз они стоят в конце, то стоят ровно столько, сколько добавляют.
[!TIP] Этот порядок — причина того, почему меняющиеся данные вроде сегодняшней даты могут присутствовать в каждом запросе, не оплачивая повторное кодирование всей идентичности каждый раз. Держите свой контент на агента в стабильных слоях (профиль, навыки), а изменчивую часть оставьте движку.
Куда дальше
- Как изолируются арендаторы — в Мультиарендности и идемпотентности.
- Как возвращаются ответы — в Надёжной доставке и восстановлении.
- Опциональные возможности работают как дополнительные сервисы.