Python и максимальный RPS для MAS
Байт-код Python в CPython в каждый момент времени исполняется строго в один поток. Один процесс Python не может использовать несколько ядер CPU параллельно для выполнения собственного байт-кода из-за GIL (Global Interpreter Lock). Потоки выполняются не одновременно, а поочередно: интерпретатор переключает контекст при операциях ввода-вывода или по таймеру (по умолчанию каждые 5 мс).
При I/O-bound задачах (сеть, файлы, БД, LLM API): Многопоточность и асинхронность ускоряют работу. Когда поток или таска блокируется в ожидании ответа от сети/диска, GIL освобождается, отдавая процессорное время другим задачам.
При CPU-bound задачах (вычисления, математика, обработка данных): Многопоточность не ускоряет код на многоядерных процессорах, а может замедлить его из-за накладных расходов на перехват GIL и постоянное переключение контекста.
Разделение агентов по типу нагрузки
Эффективность MAS зависит от того, как соотносится архитектура агента с ограничениями интерпретатора Python.
1. Role-Based Agents (Ролевые агенты) Концепция: Логика определяется ролями, контекстом и системными промптами Тип нагрузки: I/O-bound (99% времени — ожидание ответа от внешних API). Решение в Python: Одиночный асинхронный цикл событий (asyncio). Выделять отдельное ядро процессора или поток ОС под ролевого агента бессмысленно — он не нагружает CPU. Критическая ошибка: Использование синхронных HTTP-клиентов или библиотек внутри инструментов (Tools). Один блокирующий запрос останавливает весь Event Loop и замораживает всех остальных агентов.
2. Model-Based Agents (Модельно-ориентированные агенты) Концепция: Логика диктуется графом состояний, детерминированными алгоритмами или локальными моделями. Тип нагрузки: CPU/GPU-bound (матричные вычисления, векторизация, граф-аналитика). Решение в Python: Многопроцессность (multiprocessing / ProcessPoolExecutor). Каждому агенту (или группе) выделяется отдельный OS-процесс со своим адресным пространством и собственным GIL. Критическая ошибка: Запуск тяжелых вычислений напрямую внутри async def. Это мгновенно блокирует Event Loop. Для вызова CPU-задач из async-кода необходимо использовать loop.run_in_executor().
Как обходят GIL для настоящего параллелизма
Многопроцессность (multiprocessing): Создает отдельные процессы ОС. У каждого процесса свой интерпретатор Python, своя память и свой GIL, что позволяет задействовать все ядра процессора.
C/Rust-расширения и внешние сервисы (NumPy, PyTorch, vLLM, Triton): Низкоуровневые библиотеки отпускают GIL на время матричных операций, а вынесенные инференс-серверы выполняют расчеты на C++/CUDA вне процесса Python.
Free-Threading (No-GIL build): Начиная с Python 3.13+ доступен экспериментальный режим сборки интерпретатора без GIL, позволяющий потокам исполнять байт-код Python параллельно на разных ядрах CPU.