SIM: Движок цифрового симулятора (Simulation Engine)

Published

June 11, 2026

WarningОграничение публичной документации

В открытом доступе представлена демонстрационная версия метода. В настоящей публичной документации отображены не все шаги, технические сценарии и приватные эндпоинты для системы цифровых симуляторов бизнес-процессов.

  • Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).

1. Концепция Движка (Simulation Engine)

Движок симулятора — это изолированный архитектурный компонент (микросервис на языке Go), который обеспечивает жизненный цикл симуляции, оркестрацию потоков времени, управление конкурентностью, интеграцию с базами данных и шинами сообщений.

Движок спроектирован по принципу Pluggable Architecture (подключаемые модули). Он ничего не знает о специфике бизнес-логики (будь то кухня, логистика или финансы) и оперирует абстрактными интерфейсами процессов.

Общая топология системы:

Simulation_Process cluster_control API & КОНТРОЛЬ СИМУЛЯЦИИ cluster_engine СИМУЛЯЦИОННЫЙ ДВИЖОК cluster_workers РАСПРЕДЕЛЕННОЕ ВЫПОЛНЕНИЕ (FAN-OUT) PG PostgreSQL (Data Store) Redis Redis Cache (1. Hydrate State) Kafka Apache Kafka Broker (3. Emit Intents & History) Client HTTP API Клиент Ctrl EngineController Client->Ctrl POST /start Loader DBProfileLoader Ctrl->Loader Динамическое приведение Engine SimulationEngine Ctrl->Engine Асинхронный запуск Loader->PG Bulk Query Tick Временной шаг: +1 Час (Цикл Виртуального Времени) Engine->Tick Workers Worker Pool: Bounded Concurrency Tick->Workers Параллельный Fan-Out Workers->Redis 1 Workers->Kafka 3 BizPlugin Абстрактный Плагин Процесса (2. Advance & Evaluate) Workers->BizPlugin 2


2. API Управление и Жизненный цикл Симуляции

Управление симуляциями осуществляется через EngineController посредством HTTP REST API. Контроллер обеспечивает неблокирующий запуск и безопасную принудительную остановку процессов.

2.1 Асинхронный запуск (StartHandler)

При получении запроса POST /start движок выполняет следующие шаги:

  1. Декодирование и валидация: Извлекается ProcessID, ProcessType и глубина симуляции в часах (Hours).

  2. Динамическое извлечение возможностей (Interface Assertion): Движок берет абстрактный процесс из реестра (processRegistry) и приводит его к интерфейсу DBProfileLoader. Это позволяет изолировать логику чтения параметров конкретного процесса от ядра симулятора.

  3. Инициализация контекста: Создается отменяемый контекст выполнения:

    ctx, cancel := context.WithCancel(context.Background())
    ec.activeSims.Store(req.ProcessID, cancel)
  4. Offloading в бэкграунд: Тяжелый цикл вычислений запускается в отдельной горутине, а клиент мгновенно получает ответ 202 Accepted с числом активированных цифровых двойников.

2.2 Принудительная остановка (StopHandler)

Позволяет досрочно погасить симуляцию по process_id. Из потокобезопасной карты sync.Map извлекается соответствующий context.CancelFunc, вызов которого каскадно останавливает все связанные горутины воркеров.


3. Масштабирование и Конкурентность (High-Throughput Execution)

Для обеспечения одновременной симуляции десятков тысяч цифровых двойников (агентов) в рамках одного виртуального часа, SimulationEngine реализует паттерны Fan-Out и Concurrency Throttling.

3.1 Ограничение пула через Семафор (Bounded Concurrency)

Прямой запуск горутины под каждого агента без контроля ресурсов может привести к исчерпанию дескрипторов ОС, перегрузке планировщика Go и падению RAM. Движок блокирует это за счет буферизированного канала-семафора внутри каждого временного тика:

var wg sync.WaitGroup
sem := make(chan struct{}, 5000) // Жесткий лимит на 5000 параллельных воркеров

for _, userID := range userIDs {
    wg.Add(1)
    go func(id int64) {
        defer wg.Done()
        sem <- struct{}{}        // Захват слота
        defer func() { <-sem }() // Освобождение слота

        se.processAgentTick(ctx, id, staticProfiles[id], currentVirtualTime)
    }(userID)
}
wg.Wait() // Барьер синхронизации: время не пойдет вперед, пока все не завершат тик

4. Двухуровневое управление состоянием (State Hydration & Fallback)

Метод processAgentTick выполняет сборку (гидратацию) состояния агента перед передачей его в математическую модель. Движок использует гибридную схему: Локальный кэш (Redis) -> Внешняя сеть (gRPC) -> Безопасный дефолт.

flowchart TD
    A[Начало тика агента] --> B{Запрос в Redis}
    B -->|Найдено| C[Передача стейта в Модель]
    B -->|Cache Miss / Пусто| D{Бизнес-логика реализует StateWarmer?}
    D -->|Да| E[gRPC вызов Login + GetFridgeState]
    E -->|Успешно| F[Сохранить в Redis] --> C
    E -->|Сбой сети| G[Загрузить Default State] --> C
    D -->|Нет| G

4.1 Пакетный конвейер (Redis Pipeline) и Оптимизация памяти

  • Lazy Loading: Состояние прогревается только в момент реальной необходимости.
  • Zero-Allocation приведение типов: Вместо использования рефлексии (reflect), движок применяет конструкцию switch-case для сырых интерфейсов из Redis, преобразуя их в примитивы без аллокаций в куче (Heap).
  • Пакетная запись: При инициализации больших процессов (InitStateInRedis) используется rdb.Pipeline(), группирующий команды записи для минимизации сетевых задержек (RTT).

5. Схемы интеграционных данных (Примеры Payloads)

Движок не сохраняет результаты деятельности агентов в доменные базы напрямую. Взаимодействие со смежными сервисами и собственным контуром планирования происходит асинхронно через топики Apache Kafka.

После выполнения расчета поведения, движок отправляет два типа событий в Apache Kafka. Разработчикам транспортного уровня необходимо закладываться на следующие JSON-схемы:

5.1 Топик twin-history-events (Снимок состояния для аналитики)

Отправляется на каждый тик каждого агента (даже если он бездействует). Служит для сквозного логирования динамических параметров и верификации гипотез в аналитических хранилищах (например, ClickHouse).

{
  "user_id": 48291,
  "process_id": "proc-food-2026-v1",
  "virtual_time": "2026-09-28T14:00:00+03:00",
  "action": "COOKING",
  "metrics": {
    "current_state": "COOKING",
    "current_hunger": 0.65,
    "current_stress": 0.21,
    "time_since_last_shop": 2,
    "cached_fridge": "{\"items\":[{\"item_id\":\"a9c2-4bbf\",\"name\":\"Молоко\",\"category\":\"Молочные продукты\",\"portions\":1.5,\"weight_grams\":300,\"price\":90}]}"
  }
}

5.2 Топик agent-intents (Транзакционное намерение совершить действие)

Генерируется только тогда, когда действие агента отличается от IDLE. Содержит смещенное виртуальное время (Virtual Time + Delay), указывающее, когда именно в будущем действие будет совершено. Данный топик вычитывается внутренним планировщиком симулятора. Когда маркер глобального цикла currentVirtualTime догоняет время интента, Движок извлекает его и инициирует физический вызов сначала ИИ-сервиса, затем продуктового микросервиса (например, отправляет чек с продуктами в FoodTracker)

{
  "user_id": 48291,
  "process_id": "proc-food-2026-v1",
  "action_type": "COOKING",
  "virtual_time": "2026-09-28T14:45:00+03:00"
}