Что такое «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
Процесс взаимодействия:
- Actor A создаёт сообщение (например, ProcessQuery(query)) и отправляет его актору B.
- Сообщение попадает в mailbox актора B.
- Когда B готов, он достаёт следующее сообщение из очереди.
- B обрабатывает сообщение — меняет своё внутреннее состояние, отправляет ответы другим акторам или порождает новых акторов.
- После обработки 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 и другие)
| Фреймворк | Язык/Платформа | Особенности |
|---|---|---|
| Akka | JVM (Scala, Java) | Классический actor model, поддержка кластеризации, потоки данных (Akka Streams), модуль для HTTP (Akka HTTP). Широко используется в финтехе и IoT. |
| Orleans | .NET (C#) | Абстракция «virtual actor» — актор всегда существует логически, физически создаётся при необходимости. Встроенная персистентность, автоматическое распределение. |
| Actix | Rust | Высокая производительность, модель акторов с лёгкими «actor» поверх tokio. Часто используется для веб-сервисов (Actix Web). |
| CAF (C++ Actor Framework) | C++ | Для высоконагруженных систем реального времени. Поддерживает распределённые и встраиваемые сценарии. |
| Pykka / Thespian | Python | Обёртки над акторами с поддержкой многопоточности и многопроцессности. Подходят для прототипирования 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] (логирует, перезапускает)
- Router Actor принимает запрос, определяет, какие агенты нужны (поиск, суммаризация, генерация).
- Search Actor ищет релевантные документы в векторном индексе.
- Rerank Actor переупорядочивает результаты с помощью более точного реранкера.
- Summarize Actor передаёт контекст LLM и генерирует ответ.
- 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 есть
askpattern, возвращающийFuture[Response]. - В Orleans —
Grain(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 (или эмуляция).
Шаги:
- Определите актора
SearchAgent:- При получении сообщения
Search(query)он ищет top‑5 чанков в FAISS и возвращаетSearchResults(texts).
- При получении сообщения
- Определите актора
SummarizeAgent:- При получении
Summarize(texts)он формирует промпт, вызывает LLM и возвращаетFinalAnswer(answer).
- При получении
- Определите актора
QueryRouter:- При получении
UserQuery(query)отправляет егоSearchAgent, получает результаты, затем отправляет ихSummarizeAgent, получает ответ и выводит.
- При получении
- Добавьте
SupervisorActor, который логирует все сообщения и перезапускает актора в случае сбоя. - Напишите тест, отправляя несколько запросов параллельно.
Ожидаемый результат Вы увидите, как асинхронность позволяет обрабатывать несколько запросов без блокировок; при падении одного из агентов (например, из-за тайм-аута LLM) supervisor перезапускает его, и остальные запросы продолжают выполняться.
Расширение Добавьте CacheActor, который хранит результаты повторяющихся запросов — state внутри actor.
11. Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 811 | Определение AI-агента |
| 812 | Архитектура RAG agent |
| 814 | Tool use в агентах |
| 815 | Планирование (planning) в multi-agent systems |
| 816 | Memory в агентах |
| 817 | Multi-agent coordination (например, через actor model) |
Эти вопросы углубляют понимание того, как actor model вписывается в общую картину построения интеллектуальных агентов и их взаимодействия.
12. Навигация
- Предыдущий: 812
- Следующий: 814
- Индекс: 00. Индекс разборов
Навигация
- Предыдущий: 812
- Следующий: 814
- Индекс: 00. Индекс разборов