Перейти до основного вмісту

Архітектура

Огляд системи

AgentCore — це мультиканальна платформа AI-асистента, що зʼєднує messaging-канали (WhatsApp Cloud API, Telegram) з RAG-базою знань у межах відділу та HITL-воркфлоу схвалення.

┌─────────────┐   ┌─────────────┐
│ WhatsApp │ │ Telegram │
│ Cloud API │ │ (Grammy) │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌──────────────────────────────────┐
│ Fastify Server │
│ routes, auth, RBAC, dept scope │
└──────────────┬───────────────────┘

┌──────────┼──────────┬──────────────┐
▼ ▼ ▼ ▼
┌────────┐ ┌───────┐ ┌────────────┐ ┌────────────┐
│ Agent │ │ HITL │ │ Memory │ │ WebSocket │
│ Runner │ │Approve│ │ Extraction │ │ Events │
└───┬────┘ └───┬───┘ └─────┬──────┘ └─────┬──────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────┐
│ PostgreSQL 16 + pgvector │
│ Redis 7 (BullMQ queues + rate-limit store) │
└──────────────────────────────────────────────────┘

Конвеєр повідомлень

ADR-001 робить agent-tasks основною межею виконання для вхідних повідомлень. Канальні воркери — це транспортні адаптери: вони нормалізують вхідні повідомлення, зберігають записи user/conversation/message, створюють AgentTask і ставлять його в чергу. Вони більше не володіють RAG/HITL-логікою.

  1. Канал отримує повідомлення через WhatsApp Cloud API-webhook або Telegram polling/webhook.
  2. Inbound-черга каналу (wa-inbound або tg-inbound) нормалізує транспортний payload.
  3. Inbound-воркер знаходить або створює користувача й розмову, зберігає повідомлення користувача, створює AgentTask і ставить у чергу agent-tasks.
  4. Agent runner worker обробляє задачу:
    • inject_profile: завантажує system prompt простору імен і контекст employee profile.
    • rag_search: запускає OpenAiRagPipeline в api-режимі або fallback-адаптер, обраний простором імен у test/adapter-режимі.
    • generate: форматує відповідь для каналу.
    • confidence_check: керує injection-guard, тригерами ескалації персони, класифікацією намірів, confidence fallback та обходом матриці довіри.
  5. Результат маршрутизації зберігається як рядки AgentToolCall і один із:
    • автовідправка через wa-outbound або tg-outbound;
    • створення pending Approval і перехід розмови в awaiting_approval;
    • відправка persona-escalation-повідомлення і перехід розмови в escalated.
  6. Після схвалення approved/edited відповідь ставиться у outbound-чергу каналу.
  7. Memory extraction запускається після налаштованого інтервалу повідомлень.
  8. WebSocket-події бродкастять lifecycle задачі в реальному часі (agent-task.created, .started, .tool-call, .completed, .failed) із department-фільтрацією при кожній доставці.

Архітектура компонентів

Fastify-застосунок (src/app.ts)

App-фабрика реєструє компоненти в такому порядку:

  1. OpenAPI: Swagger і Swagger UI (/docs) з /api/v1 як префіксом API-сервера.
  2. Плагіни безпеки: Helmet, CORS по точних origin, глобальний rate limit, JWT-автентифікація та departmentScope.
  3. Middleware: audit-logger, idempotency і структурований error handler.
  4. Фонові воркери: ingestion, memory extraction та agent-runner черги.
  5. Маршрути: health, auth, knowledge, approvals, traces, namespaces, me, conversations, departments, users, roles, audit, RAG draft, employee profiles, intents, agent tasks, plugins, document templates і notifications — під /api/v1.
  6. Канали: WhatsApp Cloud API плагін і Telegram плагін — під /api/v1.
  7. Моніторинг: Bull Board admin UI (/admin/queues) і WebSocket event-бриджі (/ws/agent-tasks, /ws/notifications).

Плагінна система

Fastify-плагіни надають:

  • authenticate decorator — JWT verification hook (тільки Bearer-заголовок; WebSocket використовує first-message auth handshake);
  • departmentScope decorator — локальний для запиту DepartmentScope з forDepartment(request.user);
  • knowledgeIngestionQueue — BullMQ-черга обробки документів;
  • memoryExtractionQueue — BullMQ-черга екстракції профілів;
  • agentTasksQueue — BullMQ-черга обробки agent tasks;
  • channelService — абстракція конфігурації каналів, наразі на env-змінних;
  • broadcastAgentTaskEvent — WebSocket-бродкаст-хелпер, що повторно перевіряє namespace-department-доступ перед кожною відправкою;
  • pluginRegistry — реєстр namespace integration plugins з вбудованими OpenDataBot і webhook-плагінами;
  • notification-хелпери — персистентні per-user сповіщення та доставка через WebSocket.

Архітектура черг (BullMQ + Redis)

ЧергаПризначенняConcurrency
agent-tasksОбробка agent tasks через адаптериконфігурується
knowledge-ingestПарсинг документів, чанкінг, ембединги1
memory-extractionЕкстракція employee profile з чатів1
wa-inboundОбробка повідомлень WhatsAppконфігурується
wa-outboundВідправка повідомлень WhatsAppконфігурується
tg-inboundОбробка повідомлень Telegramконфігурується
tg-outboundВідправка повідомлень Telegramконфігурується

Черга agent-tasks використовує 3 retry з exponential backoff (base delay 2s). Таймаут для кожного адаптера: api — 30s, claude_local/codex_local — 300s, ollama — 120s. Stalled-job detection автоматично ретраїть задачі, якщо воркер помирає.

Усі черги використовують exponential-backoff retry. Воркери gracefully шатдаунять на SIGTERM/SIGINT.

Agent Runner та adapter layer

Agent runner — це канонічний pipeline обробки повідомлень. Він зберігає кожну задачу в AgentTask, пише прогрес по кроках у AgentToolCall і генерує чернетки через pluggable adapter-інтерфейс (AgentAdapter). Кожен простір імен вибирає свій адаптер через поле config.agentRunner.activeAdapter.

Доступні адаптери:

АдаптерБекендТаймаут
apiOpenAI SDK (chat.completions.create)30s
claude_localClaude CLI (claude --print)300s
codex_localCodex CLI (codex exec --json)300s
ollamaOllama HTTP API (OpenAI-сумісний)120s

Модель даних: кожна задача створює AgentTask з вкладеними AgentToolCall, що трекають inject_profile, rag_search, generate, confidence_check і всі adapter-level виклики з runner. Токени, вартість і тривалість трекаються по задачі.

Див. Конфігурація для per-namespace налаштування адаптера.

Ізоляція відділу

ADR-002 централізує department-доступ у src/lib/department-scope.ts.

  • forDepartment(user) повертає DepartmentScope.
  • scope.directWhere() застосовується до Prisma-моделей з прямим departmentId.
  • scope.nestedWhere('namespace') охоплює моделі на кшталт AgentTask через повʼязані простори імен.
  • scope.departmentId використовується у raw SQL-фільтрах RAG.
  • Admin-користувачі отримують крос-департаментний доступ; усі інші ролі обмежені departmentId з JWT.

Fastify-плагін у src/plugins/department-scope.ts декорує автентифіковані запити полем request.departmentScope. REST-маршрути, RAG-пошук, agent runner та WebSocket-бродкасти використовують той самий scope-примітив. Регресійний набір у tests/department-isolation.test.ts перевіряє list, detail, mutation, RAG, analytics та WebSocket isolation.

Потік даних: ingestion знань

Upload → Parse (PDF/DOCX/TXT/Image) → PII Scrub → Chunk → Embed → Synthetic Q&A → Store

Деталі — у Knowledge & RAG.

Потік даних: RAG-запит

User Query → Injection Guard → Intent Classify → Embed Query
→ Vector Search (chunks + questions) + Keyword Search
→ Hybrid Score + Rank → Top-K Assembly
→ LLM Generation (with system prompt + history + profile)
→ PII Restore → Confidence Check → Bypass or HITL

Деталі — у Knowledge & RAG.

Ключові дизайн-рішення

Namespace isolation

Кожен відділ володіє простором імен з власним system prompt, persona-конфігом, правилами ескалації та матрицями довіри. Неглобальні користувачі бачать тільки namespace-scoped дані у межах свого ефективного відділу.

Дворівневий захист PII

  • Ingestion time: незворотний scrub перед збереженням чанків та embeddings.
  • Conversation time: зворотне AES-256-GCM шифрування з PII_ENCRYPTION_KEY; LLM бачить плейсхолдери, у відповіді користувача вихідні значення відновлюються.

Матриця довіри

Per-intent tracking автономності. Після достатньої кількості успішних схвалень для наміру система автовідправляє відповіді (з налаштовуваним семплінгом для постійного контролю).

Гібридний retrieval

Поєднує vector і keyword search для recall. За замовчуванням 65% vector і 35% keyword; vector-бюджет розділений між content chunk embeddings та synthetic question embeddings.