Перейти к содержанию

14. Экосистема и Фреймворки

Зачем это нужно?

При создании AI-агентов вы сталкиваетесь с выбором: писать всё с нуля или использовать фреймворк? Оба подхода имеют плюсы и минусы, и понимание того, когда что выбирать, критично для долгосрочного успеха.

Эта глава поможет принимать архитектурные решения, избегать vendor lock-in и использовать готовые решения там, где они реально подходят.

Реальный кейс

Ситуация: Вам нужно создать DevOps-агента. Вы можете:

  • Использовать популярный фреймворк, который предоставляет всё из коробки
  • Построить собственный runtime, адаптированный под ваши нужды

Проблема:

  • Подход с фреймворком: Быстрый старт, но вы заперты в их абстракциях. Когда нужна кастомная логика, вы боретесь с фреймворком.
  • Кастомный подход: Полный контроль, но вы изобретаете велосипед. Каждая функция (tool execution, memory, planning) требует реализации.

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

Теория простыми словами

Что такое фреймворки для агентов?

Фреймворки для агентов — это библиотеки или платформы, которые предоставляют:

  • Инфраструктуру выполнения инструментов — обработка function calling, валидация, обработка ошибок
  • Управление памятью — контекстные окна, саммаризация, сохранение состояния
  • Паттерны планирования — ReAct циклы, оркестрация workflow, декомпозиция задач
  • Координация multi-agent — supervisor паттерны, изоляция контекста, маршрутизация

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

Кастомный Runtime vs Фреймворк

Кастомный Runtime:

  • Полный контроль над каждым компонентом
  • Нет vendor lock-in
  • Оптимизирован под ваш конкретный use case
  • Больше кода для написания и поддержки
  • Нужно реализовывать общие паттерны самостоятельно

Фреймворк:

  • Быстрая разработка, проверенные паттерны
  • Поддержка сообщества и примеры
  • Обрабатывает edge cases, которые вы можете пропустить
  • Меньше гибкости, сложнее кастомизировать
  • Потенциальный vendor lock-in
  • Может включать функции, которые вам не нужны

Как выбирать?

Критерии решения

Выбирайте кастомный Runtime когда:

  1. Уникальные требования — Ваш use case не вписывается в стандартные паттерны
  2. Критична производительность — Нужен тонкий контроль над latency/cost
  3. Минимум зависимостей — Хотите избежать внешних зависимостей
  4. Цель обучения — Хотите глубоко понять внутренности
  5. Долгосрочный контроль — Нужно независимо поддерживать и развивать систему

Выбирайте фреймворк когда:

  1. Стандартный use case — Ваши требования соответствуют общим паттернам
  2. Скорость выхода на рынок — Нужно быстро запустить
  3. Знакомство команды — Ваша команда уже знает фреймворк
  4. Быстрое прототипирование — Исследуете идеи и нужны быстрые итерации
  5. Поддержка сообщества — Выигрываете от примеров и знаний сообщества

Соображения портабельности

Vendor lock-in в Go решается знакомым паттерном — драйверы провайдеров, регистрируемые blank-импортом, как в database/sql. Приложение зависит от маленького фасада (Client.Chat / Client.Stream), а конкретный провайдер подключается импортом драйвера и выбирается строкой-именем в конфигурации. Сменить провайдера — сменить одну строку, а не переписывать код.

Что ещё помогает не запереть себя:

  • Стандартные протоколы — JSON Schema для tools (гл. 03), MCP/A2A для внешних интеграций (гл. 18), OpenTelemetry для observability
  • Минимальная связь с фреймворком — если берёте фреймворк, держите бизнес-логику отдельно от оркестрации
  • Постепенный путь миграции — проектируйте так, чтобы компоненты можно было менять позже

В реальном SDK. Коммерческие Go-SDK для LLM устроены ровно так: восемь драйверов протоколов (Anthropic, OpenAI, Google и другие), openai-compatible драйвер обслуживает сразу несколько провайдеров, а каталог моделей живёт отдельным пакетом. Приложение видит только фасад.

JSON Schema и инструменты

Схема инструмента — часть протокола function calling: LLM получает её в описании tool'а и по ней генерирует аргументы. Формат — стандартный JSON Schema (type, properties, required, enum); глава 03 показывает его целиком, здесь повторять не будем.

Практическое правило: не выписывайте схемы руками. SDK или ваш рантайм генерирует схему из Go-структуры параметров через reflection — структура остаётся единственным источником истины, рассинхрон схемы и кода невозможен:

type DeployParams struct {
    Service string `json:"service" jsonschema:"description=Имя сервиса"`
    Env     string `json:"env"     jsonschema:"enum=staging|production"`
}

// schema := reflector.Reflect(DeployParams{}) → готовый inputSchema для tool'а

Ручная валидация JSON Schema оправдана только на границе с внешними, недоверенными данными — например, когда аргументы приходят от чужого агента по сети. Для собственных tool'ов достаточно json.Unmarshal в Go-структуру: он сам сообщит о несоответствии типов и обязательных полей.

Общие паттерны во фреймворках

Все фреймворки под капотом реализуют одни и те же механизмы, которые вы уже построили в этом курсе:

Паттерн Что это Где в курсе
Tool Registry Словарь имя → инструмент + диспетчеризация вызовов Гл. 03
Agent Loop Цикл «LLM → tool call → результат → LLM» до финального ответа Гл. 04
История сообщений Линейный список сообщений как память агента Гл. 12

Отдельное предостережение — абстракция памяти (Memory с методами Store/Retrieve/Search), которую фреймворки нередко предлагают ради «портабельности». На практике это антипаттерн: память агента — это линейная история сообщений плюс condense при переполнении окна (гл. 12). Универсальный интерфейс памяти решает проблему смены фреймворка, которой у вас нет, и не решает проблему переполнения контекста, которая у вас есть. Если фреймворк навязывает такую абстракцию — проверьте его модель памяти до внедрения, а не после.

Фреймворки и экосистема

Обзор реальных фреймворков

Дисклеймер: агентные фреймворки эволюционируют быстро — мажорные релизы выходят раз в несколько месяцев, API ломается, сильные/слабые стороны меняются. Сводка ниже — ориентир на момент написания. Перед выбором проверяйте свежую документацию, последние релизы и реальные production-кейсы (issue tracker, downgrade-репорты).

LangGraph (Python, LangChain). Агенты на основе графов состояний: каждый шаг — узел графа, переходы определяются условиями. Подходит для сложных workflow с ветвлениями и циклами.

CrewAI (Python). Мульти-агентные системы: агенты объединяются в «команды» (crews) с ролями и задачами. Удобен для сценариев, где несколько агентов сотрудничают над общей целью.

AutoGen (Microsoft, Python). Мульти-агентные системы с упором на диалог между агентами. В v0.4 переписан с нуля (новое ядро + фреймворк AgentChat поверх него) — материалам и туториалам времён до v0.4 доверяйте с осторожностью: это фактически другой фреймворк.

Semantic Kernel (Microsoft, .NET/Python). Оркестратор, интегрирующий LLM с существующим кодом через «плагины». Ориентирован на enterprise и экосистему Microsoft.

OpenAI Agents SDK (Python/JS). Лёгкий SDK: агент как цикл с инструментами, handoff'ы между агентами, guardrails.

Google ADK (Python/Java). Agent Development Kit от Google, тесно связан с Vertex AI.

pydantic-ai (Python). Типизированные агенты: схемы инструментов генерируются из pydantic-моделей — та же идея, что генерация из Go-структуры в гл. 03.

Vercel AI SDK (TypeScript). Агентные примитивы для веб-стека.

Список не полный: рынок меняется быстрее книг — проверяйте актуальность на момент чтения.

Сравнительная таблица

Фреймворк Язык Сильные стороны Слабые стороны
LangGraph Python Граф-based workflow, гибкие состояния, стриминг Сложный API, крутая кривая обучения
CrewAI Python Простая мульти-агентная координация, роли Менее гибкий для нестандартных паттернов
AutoGen Python Диалоговые мульти-агентные системы, Microsoft backing Тяжеловесный, сложная отладка; v0.4 — фактически новый фреймворк
Semantic Kernel .NET, Python Enterprise-ready, интеграция с Azure Ориентирован на экосистему Microsoft

А в Go?

Доминирующего агентного фреймворка в Go нет — и это нормально. Рабочий выбор большинства Go-команд: маленькие библиотеки-фасады для LLM-провайдеров (паттерн драйверов описан выше) плюс собственный цикл агента на 300–500 строк, который вы пишете в главе 04. Это скучное и проверяемое решение: весь код агента — ваш, отладка обычная, зависимостей минимум. Фреймворк оправдан, когда вам нужны именно его готовые паттерны оркестрации, а не «чтобы не писать цикл руками».

Экосистема MCP

MCP (Model Context Protocol) — открытый протокол подключения инструментов к LLM-агентам: один протокол, и любой MCP-сервер работает с любым MCP-клиентом. Экосистема готовых серверов (базы данных, файловые системы, API, браузеры) активно растёт. Разбор протокола — в Главе 18: Протоколы Инструментов и Tool Servers.

Экосистема A2A

A2A (Agent-to-Agent) — открытый протокол взаимодействия агентов друг с другом: каждый агент публикует discovery-документ со своими возможностями, остальные находят его и делегируют задачи через стандартный HTTP API. Подробности — в Главе 18.

Skills

Отдельный слой экосистемы — навыки (Skills, agentskills.io): пакеты знаний в формате SKILL.md с progressive disclosure — агент подгружает инструкцию, только когда задача её касается. Поддерживаются Cursor, Claude Code, VS Code и др. Подробно — в Главе 09: Анатомия Агента.

Почему этот курс учит с нуля

Этот курс строит агента с нуля по нескольким причинам:

  1. Понимание фундамента. Фреймворк скрывает детали за абстракциями. Когда что-то ломается, вы не знаете, где искать. Написав agent loop, tool registry и историю сообщений своими руками, вы понимаете каждый компонент.

  2. Осознанный выбор. После реализации с нуля вы точно знаете, какие проблемы решает фреймворк. Решение «использовать LangGraph» или «написать кастом» становится обоснованным, а не случайным.

  3. Портабельность знаний. Фреймворки меняются. Знание принципов (agent loop, Function Calling, context management) переносится на любой фреймворк. Знание конкретного API — нет.

  4. Go как явный язык. Большинство фреймворков написаны на Python. Этот курс использует Go, что заставляет реализовывать паттерны явно — без «магии» декораторов и метапрограммирования.

  5. Простота вместо моды. Многие «продвинутые» паттерны из обзоров и постов про агентов на практике не нужны. Линейная история сообщений, один condense при переполнении окна, фиксированный system prompt, лимит итераций, защита от повторов — этого достаточно для большинства задач (см. гл. 12, гл. 13). Многоуровневые сжатия, скоринг важности сообщений, динамические промпты, тяжёлые «фреймворки памяти» обычно решают не ту проблему: они дороги, ломают prompt cache и усложняют отладку, при этом редко окупаются на реальных нагрузках. Это не значит, что они вредны всегда — но прежде чем их внедрять, стоит проверить базовые механизмы и убедиться, что простого решения действительно не хватает. Когда вы пишете runtime сами, эту разницу видно сразу.

Типовые ошибки

Ошибка 1: Vendor Lock-In

Симптом: Ваш код тесно связан с API фреймворка. Смена фреймворка требует переписывания всего.

Причина: Использование типов фреймворка напрямую вместо определения своих интерфейсов.

Решение:

// ПЛОХО: Прямая зависимость от фреймворка
func processRequest(frameworkAgent *FrameworkAgent) {
    result := frameworkAgent.Execute(userInput)
}

// ХОРОШО: На основе интерфейсов
type Agent interface {
    Execute(input string) (string, error)
}

func processRequest(agent Agent, userInput string) (string, error) {
    return agent.Execute(userInput)
}

// Адаптер фреймворка реализует ваш интерфейс
type FrameworkAdapter struct {
    agent *FrameworkAgent
}

func (a *FrameworkAdapter) Execute(input string) (string, error) {
    return a.agent.Execute(input)
}

Ошибка 2: Излишняя инженерия кастомного Runtime

Симптом: Вы тратите месяцы на построение функций, которые фреймворки предоставляют из коробки.

Причина: Не оцениваете, действительно ли нужна кастомная реализация.

Решение: Начните с фреймворка для прототипирования. Извлекайте в кастом только когда натыкаетесь на реальные ограничения.

Ошибка 3: Игнорирование ограничений фреймворка

Симптом: Вы постоянно боретесь с фреймворком, пытаясь заставить его делать то, для чего он не был предназначен.

Причина: Не понимаете дизайнерские решения и ограничения фреймворка.

Решение: Внимательно читайте документацию фреймворка. Если ограничения слишком сильные, рассмотрите кастомный runtime.

Ошибка 4: Нет пути миграции

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

Причина: Тесная связь делает миграцию невозможной без переписывания всего.

Решение: Проектируйте с интерфейсами с самого начала. Держите фреймворк как деталь реализации, а не основную зависимость.

Мини-упражнения

Упражнение 1: Интерфейс Tool

Определите интерфейс Tool, не зависящий от фреймворка. Схема генерируется из Go-структуры параметров — рукописный JSON не нужен:

type Tool interface {
    Name() string
    Description() string
    Schema() json.RawMessage // сгенерирована из структуры параметров (reflection)
    Execute(ctx context.Context, args map[string]any) (any, error)
}

type PingParams struct {
    Host string `json:"host" jsonschema:"description=Hostname or IP"`
}

// Схема — из структуры; в Execute аргументы распаковываются в неё же через json.Unmarshal

Ожидаемый результат:

  • Интерфейс не зависит от фреймворка; адаптер фреймворка может его реализовать
  • Схема — из Go-структуры, а не рукописный JSON (единый источник истины)
  • Валидация аргументов — через json.Unmarshal в структуру параметров

Упражнение 2: Адаптер фреймворка

Создайте адаптер, который оборачивает систему инструментов фреймворка для реализации вашего интерфейса Tool:

type FrameworkToolAdapter struct {
    frameworkTool FrameworkTool
}

func (a *FrameworkToolAdapter) Name() string {
    // Адаптируем имя инструмента фреймворка
}

func (a *FrameworkToolAdapter) Execute(ctx context.Context, args map[string]any) (any, error) {
    // Конвертируем ваш интерфейс в API фреймворка
}

Ожидаемый результат:

  • Фреймворк становится деталью реализации
  • Ваш код использует ваши интерфейсы
  • Легко менять фреймворки позже

Упражнение 3: Матрица решений

Создайте матрицу решений для выбора между кастомным runtime и фреймворком:

Критерий Кастомный Runtime Фреймворк
Скорость разработки ? ?
Гибкость ? ?
Бремя поддержки ? ?
Риск vendor lock-in ? ?

Заполните матрицу на основе ваших конкретных требований.

Ожидаемый результат:

  • Чёткое понимание компромиссов
  • Обоснованное решение для вашего use case

Критерии сдачи / Чек-лист

Сдано:

  • Понимаете, когда использовать фреймворки vs кастомный runtime.
  • Знаете, как избежать vendor lock-in: фасад провайдера в стиле database/sql и свои интерфейсы (Tool).
  • Можете оценить фреймворк против ваших требований и спокойно отнестись к громким обещаниям про «революционные» паттерны.
  • Понимаете общие паттерны, которые повторяются во всех фреймворках (Tool Registry, Agent Loop, история сообщений).
  • При выборе фреймворка проверяете свежесть данных (релизы, issues, реальные production-отзывы).

Не сдано:

  • Выбор фреймворка без оценки требований.
  • Тесная связь с API фреймворка (типы фреймворка везде в бизнес-логике).
  • Нет пути миграции, если фреймворк не подойдёт.
  • Игнорирование ограничений фреймворка («заставлю его делать то, для чего он не предназначен»).
  • Слепое копирование «продвинутых» паттернов из чужих репозиториев и блог-постов без понимания, какую конкретно проблему они решают.

Связь с другими главами

Что дальше?

После понимания экосистемы переходите к: