Программный код летучий

в 8:39, , рубрики: actors model, python

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

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

Еще одно преимущество динамической загрузки — возможность заменять акторы «на ходу».

С другой стороны, изолированность пакетов позволяет объектам, в них описанным, выступать в роли «атомов» или «кирпичиков» из которых можно собрать «бесконечное» множество вариантов, как в количественном, так и функциональном смысле.

Введение

Люди склонны впадать в крайности. У кого‑то все ставки на объекты, другой обожает функции, жизнь не мила без микросервисов третьему, ИИ‑агенты стали объектом религиозного поклонения. Не обошла стороной сия участь (впадать в крайности) и меня. А пусть все будет плагинами, приложение целиком будет состоять из плагинов, а ядро будет обеспечивать только загрузку плагинов и взаимодействие их между собой.

Отправной точкой послужил проект, состоящий из ядра и набора подключаемых модулей. В процессе разработки возникали сомнения относительно архитектуры ядра. В конечном итоге было реализовано несколько вариантов его организации, а выбор конкретной схемы осуществлялся через конфигурационный файл. Однако сама ситуация породила более радикальную идею: если плагины легко заменяемы, почему бы не сделать заменяемыми и части ядра?

Дополнительным источником вдохновения стала акторная модель. Она привлекает своей наглядностью и простотой восприятия, а также изолированностью акторов.

Совпадение трёх факторов — плагинов, акторов и желания избавиться от монолитного ядра — привело к идее формировать приложения целиком из динамически загружаемых акторов.

Архитектура

Актор

Кратко, и немного упрощенно, архитектуру приложения на основе фреймворка можно описать следующим образом: множество акторов, каждый в своей корутине, в бесконечном цикле, асинхронно читают, каждый из своей очереди, сообщения и обрабатывают их. Актор — объект, конструктор которого не содержит параметров, должен обязательно содержать поле adaptor, должен обязательно содержать асинхронный метод handle с единственным параметром (за исключением self), через который будет передаваться сообщение для обработки актором, возвращающий True, если сообщение обработано.

Адаптер

Как видим, актор не содержит ни очереди, ни механизма чтения из нее. Все это обеспечивает Адаптер — объект, который создается для каждого актора и ссылка на который записывается в поле adaptor актора. Также адаптер обеспечивает для актора доступ к множеству сервисных функций, таких как сериализация, создание таймеров и тикеров, выполнение внешних программ и тому подобное.

Процесс

Фреймворк предусматривает возможность создания множества процессов, в каждом из которых функционирует свой цикл событий и свой набор акторов, обмен между процессами по TCP каналам, адресация по имени актора. В процессе обработки каждый актор может отсылать сообщения другим акторам, записывая создаваемые сообщения через API ядра в соответствующие очереди.

Администратор

Загрузкой, созданием, удалением, перемещением акторов, связыванием акторов и адаптеров, маршрутизацией сообщений, а также предоставлением ряда сервисных функций (через адаптер), например, таких как послать сообщение или выполнить запрос, занимается Администратор. Здесь и далее запросом будем называть сообщение, которое подразумевает обязательный ответ. Администратор содержит множество дочерних объектов, каждый из которых отвечает за свой участок работы, но их совокупность мы можем трактовать как один комплексный объект.

Администратор является актором, в том смысле, что ему тоже можно отправлять сообщения. Разделение интерфейса на локальный (через адаптер) и удаленный (через сообщения) продиктован характером задач. Например, создание тикера происходит локально (нет большого смысла запускать тикер где‑то «далеко» от потребителя последовательности), а создание акторов обязано быть доступно на всем поле вычислительных единиц, в противном случае мы не сможем утилизировать их вычислительные мощности, и поэтому выполняется через запрос.

В каждом из процессов создается свой администратор, но только один администратор, тот который создается при запуске приложения в главном процессе, содержит объект для управления процессами.

Распределенность

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

Однопоточность

Ввиду однопоточного характера асинхронного ядра, «тяжелые» вычисления в акторе могут блокировать всех остальных акторов на время своего выполнения.

Система не обеспечивает автоматического вытеснения, поэтому стратегия планирования полностью вынесена на уровень приложения: актор обязан самостоятельно управлять передачей вычислительно сложных задач в фоновые потоки или процессы.

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

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

Зависимости

В Python существует известная особенность: одновременно загрузить несколько версий одного пакета практически невозможно, поскольку импорт основан только на имени пакета. В peacepie конкурирующие (ссылающиеся на разные версии одного и того же пакета) акторы могут размещаться в разных процессах, каждый из которых использует собственный путь поиска и собственный набор зависимостей. Такой набор формируется для каждого процесса в отдельной папке с помощью символьных ссылок на объекты файловой системы из единого хранилища пакетов, которое представляет собой множество папок с именами образованными как из имени пакета, так и его версии, внутрь которых установлены соответствующие пакеты.

Практика

В разделе Ссылки приведена ссылка на репозиторий проекта, содержащий, в том числе, файл README.md. В указанном файле присутствует описание плана ознакомления с функционалом пакета через практические действия.

Готовность к эпохе ИИ

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

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

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

Подобный подход хорошо соответствует современной практике разработки с участием искусственного интеллекта. Языковые модели наиболее эффективно работают над локализованными, относительно небольшими задачами, имеющими четко определенные границы. Именно таким объектом и является отдельный актор. Более того, возможность динамической замены пакетов позволяет быстро экспериментировать с различными реализациями одного и того же поведения, сравнивать их эффективность и при необходимости откатываться к предыдущим версиям без перестройки всего приложения.

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

Еще одной интересной теоретической возможностью является интеграция искусственного интеллекта непосредственно в акторную систему. Языковая модель может быть оформлена как обычный актор или доступ к ней может осуществляться через специализированный актор‑шлюз (gateway), инкапсулирующий взаимодействие с внешним сервисом. С точки зрения остальных участников системы такой актор ничем не отличается от любого другого: он получает сообщения, обрабатывает их и возвращает результаты. В сочетании с возможностью динамической загрузки пакетов это открывает перспективу создания систем, способных самостоятельно генерировать новые акторы, тестировать их и включать в свою архитектуру без участия разработчика. Иными словами, система получает возможность не только выполнять прикладные задачи, но и развивать собственную функциональность. Впрочем, для большинства практических приложений такой уровень автономности представляется скорее любопытной демонстрацией возможностей архитектуры, чем действительно необходимой функцией.

Более того, при наличии соответствующих прав доступа и доверенной инфраструктуры система может самостоятельно расширять область своего функционирования: подключаться к новым узлам по каналам связи (как вариант по ssh), подготавливать на них окружение (например, устанавливать требуемую версию Python), развертывать себя в качестве службы или запускать контейнер с ядром внутри и загружать необходимый набор акторов. Таким образом, архитектура допускает не только саморазвитие в функциональном отношении, но и контролируемое самораспространение на новые вычислительные узлы. Что еще раз свидетельствует о гибкости.

Low/No‑code

Изоляция акторов, их динамическая загрузка и взаимодействие исключительно через сообщения делают peacepie подходящей основой для построения Low/No‑code платформ. Каждый актор можно рассматривать как готовый функциональный блок с четко определенным интерфейсом, а прикладное решение — как граф взаимодействующих компонентов.

В этом случае разработка приложения сводится не к написанию кода, а к выбору, настройке и связыванию акторов между собой. Такой подход близок к системам визуальной автоматизации, например n8n, однако между ними существует принципиальное различие. Если n8n ориентирован главным образом на интеграцию внешних сервисов и автоматизацию бизнес‑процессов, то в peacepie строительными блоками являются полноценные программные компоненты, способные реализовывать произвольную прикладную логику.

Благодаря оформлению акторов в виде независимо публикуемых пакетов библиотека доступных блоков может непрерывно расширяться без изменения ядра, что открывает путь к созданию специализированных Low/No‑code сред для самых разных предметных областей.

Mobilis in mobile

На первый взгляд peacepie может показаться альтернативой контейнерным технологиям, однако в действительности эти подходы решают задачи разных уровней и не конкурируют, а дополняют друг друга. Контейнер обеспечивает изоляцию среды выполнения приложения, его зависимостей и системного окружения, тогда как актор представляет собой минимальную функциональную единицу внутри этой среды.

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

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

Похожая аналогия применима к живому организму: контейнер можно сравнить с телом или системой жизнеобеспечения, создающей условия для работы, а акторы — с органами, выполняющими специализированные функции. Органы могут заменяться, адаптироваться и даже пересаживаться в другую систему, тогда как тело обеспечивает среду, в которой эти функции могут существовать. Так же и в программной архитектуре peacepie позволяет менять и перестраивать функциональные компоненты без необходимости менять всю инфраструктурную основу.

Заключение

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

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

С каждым шагом разработки становится всё очевиднее: горизонт задач не сужается, а расширяется подобно кругу познания. То, что начиналось как учебно‑тренировочный пет‑проект, постепенно обрастает новыми вопросами — распределённое взаимодействие, механизмы отказоустойчивости, инструменты визуального проектирования. Продолжать движение в одиночку становится всё сложнее, а во многих направлениях — и вовсе невозможно без коллективного участия.

Остаётся надеяться, что идеи, изложенные в статье, найдут отклик у разработчиков, интересующихся акторными системами, динамической композицией приложений или созданием Low/No‑code платформ. Впрочем, признавая всю сложность и неочевидность предложенного подхода, очень вероятно, что таких людей может оказаться немного (вероятнее всего 0! — либо эмоционально окрашенный ноль, либо единица, если трактовать как факториал нуля).

Но даже если статья просто вызовет размышления или породит критику — «будет уже хорошо» (Винни‑Пух).

Ссылки

Пакет доступен на PyPi https://pypi.org/project/peacepie.

Исходный код на GitHub https://github.com/vnmol/peacepie.

Автор: vmol

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js