Что такое «actor model» для агентов (Akka, Orleans)?

Краткий тезис

Actor model — это модель параллельных вычислений, в которой каждая единица выполнения («actor») обладает собственным состоянием, обрабатывает сообщения асинхронно и может создавать других акторов. Для AI-агентов эта модель даёт стройную архитектуру: каждый агент — это actor, его mailbox хранит входящие запросы, а коммуникация идёт через message bus. Фреймворки вроде Akka (JVM) и Orleans (.NET) предоставляют готовые решения для масштабируемых, отказоустойчивых систем, что идеально подходит для agentic RAG с множеством взаимодействующих агентов.


1. Определение actor model

Actor model (модель акторов) — математическая модель параллельных вычислений, предложенная Карлом Хьюиттом в 1973 году. В этой модели всё является актором:

  • Actor — независимая вычислительная единица, которая может:

    • получать сообщения;
    • выполнять локальные действия (менять своё состояние);
    • отправлять сообщения другим акторам;
    • создавать новых акторов.
  • Mailbox (почтовый ящик) — очередь входящих сообщений, упорядоченная по времени поступления. Каждый actor обрабатывает сообщения последовательно, одно за другим.

  • Сообщения — неизменяемые объекты (immutable), которыми обмениваются акторы. Отправка сообщения — единственный способ взаимодействия.

  • Асинхронность — отправитель не ждёт ответа, а продолжает работу. Ответ приходит как отдельное сообщение.

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


2. Как работает actor model

Процесс взаимодействия:

  1. Actor A создаёт сообщение (например, ProcessQuery(query)) и отправляет его актору B.
  2. Сообщение попадает в mailbox актора B.
  3. Когда B готов, он достаёт следующее сообщение из очереди.
  4. B обрабатывает сообщение — меняет своё внутреннее состояние, отправляет ответы другим акторам или порождает новых акторов.
  5. После обработки B переходит к следующему сообщению.

Все операции — асинхронные и неблокирующие. Акторы никогда не блокируют друг друга ожиданием ответа; вместо этого они связываются через сообщения.

Пример с кодом (псевдокод на Python с библиотекой pykka):

import pykka

class SearchActor(pykka.ThreadingActor):
    def __init__(self, index):
        super().__init__()
        self.index = index  # внутреннее состояние

    def on_receive(self, message):
        query = message['query']
        results = self.index.search(query)  # локальное действие
        return results  # ответ отправляется как сообщение отправителю

class OrchestratorActor(pykka.ThreadingActor):
    def __init__(self):
        super().__init__()
        self.search_actor = SearchActor.start()  # создаём актора

    def on_receive(self, message):
        if message['type'] == 'search':
            # отправка сообщения и ожидание ответа (через future)
            future = self.search_actor.ask({'query': message['query']})
            results = future.get()  # блокируется только этот actor
            # создаём нового актора для реранжирования
            reranker = RerankerActor.start()
            reranker.tell({'results': results})  # fire-and-forget

Здесь каждый actor живёт в своём потоке (или корутине), а коммуникация идёт только через сообщения.


3. Преимущества actor model

ПреимуществоОписание
Изоляция состоянияНет глобальных блокировок — каждый actor отвечает только за своё состояние. Ошибка в одном actor не разрушает всю систему.
АсинхронностьСообщения не блокируют отправителя, что позволяет легко масштабировать систему.
Отказоустойчивость (fault tolerance)Супервизорные иерархии: если actor падает, его «supervisor» перезапускает его или возвращает к последнему корректному состоянию.
Прозрачное распределениеActor может быть на другом узле сети — сообщения доставляются одинаково, что делает распределённые системы проще.
Упрощение многопоточностиРазработчик не думает о блокировках, только о логике обработки сообщений.

Fault tolerance — способность системы продолжать работу после сбоя. В Akka реализована через стратегии супервизии (OneForOne, AllForOne).


4. Популярные фреймворки (Akka, Orleans и другие)

ФреймворкЯзык/ПлатформаОсобенности
AkkaJVM (Scala, Java)Классический actor model, поддержка кластеризации, потоки данных (Akka Streams), модуль для HTTP (Akka HTTP). Широко используется в финтехе и IoT.
Orleans.NET (C#)Абстракция «virtual actor» — актор всегда существует логически, физически создаётся при необходимости. Встроенная персистентность, автоматическое распределение.
ActixRustВысокая производительность, модель акторов с лёгкими «actor» поверх tokio. Часто используется для веб-сервисов (Actix Web).
CAF (C++ Actor Framework)C++Для высоконагруженных систем реального времени. Поддерживает распределённые и встраиваемые сценарии.
Pykka / ThespianPythonОбёртки над акторами с поддержкой многопоточности и многопроцессности. Подходят для прототипирования AI-агентов.

Различие между Akka и Orleans Akka — классическая реализация, требует явного создания акторов; Orleans — «virtual actor», акторы прозрачно активируются по мере необходимости, что упрощает разработку крупных распределённых систем.


5. Actor model для AI-агентов

В контексте agentic RAG (систем, где несколько AI-агентов координируют выполнение задач) actor model даёт естественную архитектуру:

  • Каждый AI-агент (например, search agent, summarize agent, validation agent) — это отдельный actor.
  • Mailbox — очередь запросов от пользователей или других агентов.
  • Сообщения — это вызовы инструментов (tool use), запросы к LLM, результаты поиска, команды планирования.
  • Cупервизор (supervisor) — специальный actor, который перезапускает упавшего агента, логирует ошибки, управляет жизненным циклом.

Преимущества для agentic RAG:

  • Масштабирование — можно добавить несколько копий одного агента (например, несколько search-агентов для шардирования базы знаний).
  • Асинхронная координация — планировщик отправляет задачу агенту и не ждёт ответа, а продолжает обрабатывать другие запросы.
  • Отказоустойчивость — если один агент упал (например, из-за ошибки LLM), супервизор перезапускает его, не затрагивая остальных.
  • Прозрачность распределения — агенты могут быть развёрнуты на разных серверах, что критично для больших RAG-инсталляций.

6. Пример архитектуры agentic RAG на actor model

[Пользовательский запрос] → [Router Actor] → [Search Actor] → [Rerank Actor] → [Summarize Actor] → [Response]
                                    |
                             [Supervisor Actor] (логирует, перезапускает)
  1. Router Actor принимает запрос, определяет, какие агенты нужны (поиск, суммаризация, генерация).
  2. Search Actor ищет релевантные документы в векторном индексе.
  3. Rerank Actor переупорядочивает результаты с помощью более точного реранкера.
  4. Summarize Actor передаёт контекст LLM и генерирует ответ.
  5. Supervisor Actor отслеживает статус каждого актора, при сбое перезапускает, собирает метрики.

Все взаимодействия — асинхронные сообщения, что позволяет выполнять шаги параллельно (например, несколько Search Actor'ов ищут в разных шардах).


7. Когда использовать actor model для AI-агентов

Подходит, когда:

  • Система состоит из множества слабо связанных агентов, которые должны общаться асинхронно.
  • Требуется высокая отказоустойчивость (один агент может упасть без остановки всей системы).
  • Планируется распределённое развёртывание (несколько серверов, облачные среды).
  • Нужно изолировать состояние каждого агента (например, контекст сессии пользователя).

Не подходит, когда:

  • Вся логика укладывается в один-два вызова LLM (избыточно).
  • Требуется строгая синхронность (actor model асинхронен по своей природе).
  • Очень высокие требования к реальному времени (задержки на передачу сообщений могут быть значительными).

8. Ограничения и альтернативы

ОграничениеПояснениеАльтернатива
Сложность отладкиАсинхронные цепочки сообщений трудно трассировать.Использовать распределённую трассировку (Zipkin, Jaeger).
Нет гарантий доставки сообщенийВ некоторых реализациях сообщения могут быть потеряны при сбое узла.Orleans предоставляет «at‑most‑once»; Akka — «at‑most‑once» по умолчанию, но можно настроить.
Избыточность для простых сценариевЕсли агентов меньше 3, overhead от сообщений перевешивает выгоду.Использовать простой REST или gRPC вызовы.
Потребление памятиКаждый actor занимает память (стек, mailbox).Использовать лёгкие акторы (Akka Typed) или virtual actors (Orleans).

Альтернативы actor model для AI-агентов

  • Event‑driven architecture (через Kafka/RabbitMQ) — похоже на сообщения, но без встроенной супервизии.
  • Graph‑based DAG (например, LangGraph) — явные направленные графы вызовов, без actor model.
  • Serverless functions — каждый агент как отдельная функция (AWS Lambda), коммуникация через очереди.

9. Интеграция с большими языковыми моделями (LLM)

Actor model отлично сочетается с вызовами LLM:

  • Вызов LLM — это асинхронная операция с задержкой. Actor может отправить запрос к LLM и продолжить обработку других сообщений, пока ответ не придёт.
  • Для этого часто используют Future или Promise внутри actor: actor отправляет запрос, сохраняет ссылку на future, и когда ответ приходит, обрабатывает его.
  • В Akka есть ask pattern, возвращающий Future[Response].
  • В OrleansGrain (actor) может вызывать другой grain асинхронно.

Пример (псевдокод на Scala с Akka):

class TextAnalysisActor extends Actor {
  def receive: Receive = {
    case Analyze(text) =>
      val llmFuture = (llmActor ? GenerateSummary(text)).mapTo[Summary]
      llmFuture.foreach { summary =>
        context.parent ! AnalysisResult(text, summary)
      }
  }
}

10. Пет-проект для закрепления

Задача Реализовать мини-систему из трёх AI-агентов для обработки вопроса пользователя: QueryRouter, SearchAgent и SummarizeAgent, используя actor model.

Инструменты

  • Python с библиотекой pykka (или thespian).
  • Любая простая библиотека для эмбеддингов (например, sentence-transformers) и локальный векторный индекс (FAISS).
  • Вызов LLM через OpenAI API (или эмуляция).

Шаги:

  1. Определите актора SearchAgent:
    • При получении сообщения Search(query) он ищет top‑5 чанков в FAISS и возвращает SearchResults(texts).
  2. Определите актора SummarizeAgent:
    • При получении Summarize(texts) он формирует промпт, вызывает LLM и возвращает FinalAnswer(answer).
  3. Определите актора QueryRouter:
    • При получении UserQuery(query) отправляет его SearchAgent, получает результаты, затем отправляет их SummarizeAgent, получает ответ и выводит.
  4. Добавьте SupervisorActor, который логирует все сообщения и перезапускает актора в случае сбоя.
  5. Напишите тест, отправляя несколько запросов параллельно.

Ожидаемый результат Вы увидите, как асинхронность позволяет обрабатывать несколько запросов без блокировок; при падении одного из агентов (например, из-за тайм-аута LLM) supervisor перезапускает его, и остальные запросы продолжают выполняться.

Расширение Добавьте CacheActor, который хранит результаты повторяющихся запросов — state внутри actor.


11. Связь с другими вопросами

ВопросТема
811Определение AI-агента
812Архитектура RAG agent
814Tool use в агентах
815Планирование (planning) в multi-agent systems
816Memory в агентах
817Multi-agent coordination (например, через actor model)

Эти вопросы углубляют понимание того, как actor model вписывается в общую картину построения интеллектуальных агентов и их взаимодействия.


12. Навигация


Навигация