Архитектура
Под капотом: Nx monorepo, Next.js + ElysiaJS, Drizzle, BullMQ, Qdrant. Для разработчиков и интеграторов.
Раздел для разработчиков и интеграторов. Если вы бизнес-пользователь — можно пропустить.
TL;DR
Runtime
Bun + ElysiaJS + PostgreSQL + Redis + Qdrant
Frontend
Next.js 16 App Router, FSD, Eden Treaty
Schemas
Zod 4 в @agonts/contracts — общие backend + frontend
Workers
BullMQ — индексация, flow-exec, voice, chat-completion
Структура monorepo
Backend
Паттерн модуля
Каждый модуль в apps/api/src/modules/<feature>/ — controller / service / model:
Пример:
export const agents = new Elysia({ prefix: '/agents' })
.use(requireWorkspace)
.get('/', async ({ workspaceId }) => ({
items: await AgentsService.list(workspaceId),
}), {
response: AgentsModel.list,
})
.post('/', ({ workspaceId, body }) => AgentsService.create(workspaceId, body), {
body: AgentsModel.create,
response: AgentsModel.item,
});Все эндпоинты workspace-scoped через плагин requireWorkspace, читающий
X-Workspace-Id.
Модули
Prop
Type
Frontend
Feature-Sliced Design
Правило зависимостей: только вниз (views → widgets → features → entities → shared).
Eden Treaty — типобезопасный клиент
// packages/eden/src/index.ts
import type { App } from '@agonts/api';
export function createApiClient(baseUrl: string) {
return treaty<App>(baseUrl);
}// использование в UI
const { items } = unwrap(await api.agents.get(), 'agents.list');Добавили endpoint в Elysia → типы сразу в UI. Ни кодогенерации, ни guard-манёвров.
База данных
Prop
Type
Все workspace-scoped таблицы имеют tenant_id uuid references tenants(id) on delete cascade. Индексы tenant_id + <sort> для быстрых page-query.
Миграции
Drizzle миграции в packages/db/drizzle/NNNN_<name>.sql. Кастомный runner
packages/db/src/run-migrate.ts (обходит баг Drizzle #5316 с out-of-order
timestamps).
Фоновые воркеры
Prop
Type
Голосовой runtime
- Клиент → WS
/voice/ws?key=<API-ключ>. modules/voice/index.tsаутентифицирует ключ.- Открывается
VoiceSessionState. - Runtime берёт
default STTиdefault TTSизvoice_providers. modules/voice/stt.tsсоздаётSttStream.- PCM → STT → conversation-handler → flow → LLM.
- Ответ →
modules/voice/tts.ts→ чанки клиенту. closeVoiceSession→voice_minuteвusage_events.
- Клиент → ваш SIP-номер → АТС.
- АТС форкает μ-law 8 кГц на
/telephony/sip-stream/ws. - μ-law → PCM16 через
codecs/mulaw.ts. - Дальше как «Входящий веб».
- Flow или API →
POST /telephony/dial. - Runtime →
default telephonyпровайдера. POST {baseUrl}/callsсpstn-rest-apiдрайвером.- Оператор дозванивается → status-callback.
- Подняли трубку → медиа форкается в SIP-мост.
Контракты (Zod 4)
Все схемы в packages/contracts/src/schemas/. Только Zod 4 — новые API:
z.email() // не z.string().email()
z.uuid() // не z.string().uuid()
z.url()
z.object({ ... }).passthrough()Zod схемы идут прямо в Elysia body/params/query/response — Elysia поддерживает Standard Schema. Типы инферятся автоматически.
События (pub-sub)
packages/events — лёгкий pub-sub поверх Redis:
- live-обновления очереди операторов (SSE);
- инвалидация кешей при мутациях;
- flow-триггеры по событию.
Деплой
docker-compose.prod.yml в корне. Поднимает Postgres, Redis, Qdrant,
MinIO, API, Web, Docs.
- API — горизонтально за балансировщиком (stateless; активные WS-сессии per-instance, Redis — источник правды для общих счётчиков).
- Postgres — managed (RDS / Yandex Cloud) с репликой.
- Redis — managed, persistence включён.
- Qdrant — отдельный узел с persistence.
- Object storage — любой S3-совместимый.
Тестирование
Prop
Type
Где что искать
Prop
Type