Скрытая проблема масштабирования
При внедрении Agentic AI в продакшен команда Planck столкнулась с парадоксом: добавление новых LLM-агентов в систему приводило к непропорциональному росту задержек (latency). Изначально подозрение пало на провайдеров (OpenAI, Anthropic), так как их API действительно могли быть перегружены. Однако анализ логов показал, что запросы к LLM обрабатываются быстро, но агент возвращает ответ с огромным опозданием. Причина крылась не в сети, а в коде.
Архитектура и ожидаемая производительность
Система Planck построена на Python с использованием asyncio и aiohttp. Логика проста: один входящий запрос запускает N агентов, каждый из которых делает M параллельных вызовов к под-агентам (LLM). Поскольку работа с LLM — это I/O-bound задача, теоретически время ответа должно определяться самым медленным вызовом, а не их количеством. Однако на практике при росте числа агентов латентность линейно росла, вплоть до таймаутов.
Диагностика: GIL и Event Loop Lag
Профилирование выявило критическую проблему: Event Loop Lag. Python’s GIL (Global Interpreter Lock) позволяет выполнять только один поток кода одновременно. Когда LLM возвращает ответ, событийный цикл должен выполнить небольшую CPU-bound задачу (обработка результата), но если цикл занят другими задачами, он не может обработать ответ мгновенно. Это создавало очередь задержек.
Ключевые узкие места, выявленные при первом оптимизационном цикле:
- Сериализация JSON: Стандартная библиотека
jsonоказалась слишком медленной для больших объемов данных. - Лимиты соединений:
aiohttpпо умолчанию ограничивает пул соединений 100 штуками, что блокирует истинный параллелизм при большом числе агентов.
Результаты оптимизации
Замена json на orjson и тонкая настройка лимитов aiohttp дали значительный прирост скорости, но не устранили проблему полностью. Даже при симуляции чистых I/O задач с помощью asyncio.sleep и имитации CPU-задач через time.sleep, рост числа агентов продолжал увеличивать задержку. Это доказало, что проблема не в библиотеках, а в природе обработки событий в Python при высокой конкуренции за CPU-время.
| Параметр | Исходное состояние | Оптимизация | Результат |
|---|---|---|---|
| JSON парсинг | Стандартный json |
orjson |
Значительное ускорение сериализации/десериализации |
| Лимит соединений aiohttp | 100 (по умолчанию) | Оптимизированный пул | Лучший параллелизм, но не решение проблемы GIL |
| Основной瓶颈 (Bottleneck) | Неизвестен | Профилирование | Event Loop Lag из-за CPU-задач после получения I/O ответа |
Вывод для инженеров
Асинхронность в Python не дает истинного параллелизма для CPU-задач. Любая микроскопическая работа с данными (парсинг, валидация, логирование), выполняемая в событийном цикле после получения ответа от LLM, становится узким горлышком при масштабировании сотен агентов. Решение требует либо выноса CPU-тяжелых задач в отдельные процессы, либо использования более эффективных библиотек для обработки данных, либо пересмотра архитектуры взаимодействия агентов.
Источник: Towards Data Science ↗
