Как мы развязали узел агентов: adapter pattern в боевых условиях

От паттерна к реальности: как мы завернули AI-агентов в красивую архитектуру
Полгода назад я столкнулся с классической проблемой: проект ai-agents рос как на дрожжах, но код превратился в сложный клубок зависимостей. LLM-адаптеры, работа с БД, поиск, интеграции с платформами — всё смешалось в одном месте. Добавить новый источник данных или переключиться на другую модель LLM стало настоящим квестом.
Решение было очевидным: adapter pattern и dependency injection. Но дьявол, как всегда, сидит в деталях.
Первым делом я создал иерархию абстрактных адаптеров. LLMAdapter с методами chat(), chat_stream() и управлением жизненным циклом, DatabaseAdapter для универсального доступа к данным, VectorStoreAdapter, SearchAdapter, PlatformAdapter — каждый отвечает за свой слой. Звучит скучно? Но когда ты реализуешь эти интерфейсы конкретно — начинает быть интересно.
Я написал AnthropicAdapter с полной поддержкой streaming и tool_use через AsyncAnthropic SDK. Параллельно сделал ClaudeCLIAdapter — суперсредство, позволяющее использовать Claude через CLI без затрат на API (пока это experimental). Для работы с данными подключил aiosqlite с WAL mode — асинхронность плюс надёжность. SearxNGAdapter с встроенным failover между инстансами. TelegramPlatformAdapter на базе aiogram. Всё это управляется через Factory — просто конфиг меняешь, и готово.
Но главная фишка — это AgentOrchestrator. Это сердце системы, которое управляет полным chat-with-tools циклом через адаптеры, не зная о деталях их реализации. Dependency injection через конструктор означает, что тестировать проще простого: подай mock’и — и программа думает, что работает с реальными сервисами.
Вторая часть истории — ProbabilisticToolRouter. Когда у агента сто инструментов, нужно понимать, какой из них нужен на самом деле. Я построил систему с четырьмя слоями scoring: regex-совпадения (вес 0,95), точное имя (0,85), семантический поиск (0,0–1,0), ключевые слова (0,3–0,7). Результат — ранжированный список кандидатов, который автоматически инжектится в system prompt. Никаких случайных вызовов функций.
А потом я подумал: почему бы не сделать это ещё и десктопным приложением? AgentTray с цветовыми индикаторами (зелёный — работает, жёлтый — обрабатывает, красный — ошибка). AgentGUI на pywebview, переиспользующий FastAPI UI. WindowsNotifier для уведомлений прямо в систему. И всё это — тоже адаптеры, интегрированные в ту же архитектуру.
Интересный факт: паттерн adapter родился в 1994 году в книге «Gang of Four», но в эру микросервисов и облачных приложений он переживает второе рождение. Его главная суперсила — не столько в самом коде, сколько в психологии: когда интерфейсы чётко определены, разработчики начинают думать о границах компонентов. Это спасает от копипасты и циклических зависимостей.
По итогам: 20 новых файлов, полностью переработанная config/settings.py, обновленные requirements. Система теперь масштабируется: добавить нового LLM-провайдера или переключиться на PostgreSQL — это буквально несколько строк конфига. Код более тестируемый, зависимости явные, архитектура дышит.
И главное — это работает. Действительно работает. 😄
Метаданные
- Session ID:
- grouped_ai-agents_20260211_0821
- Branch:
- HEAD
- Dev Joke
- Если npm работает — не трогай. Если не работает — тоже не трогай, станет хуже.
Часть потока:
Разработка: ai-agents