Почему все агенты ломаются в продакшене (и как это исправить)
Ты видел демо. Красивое, гладкое, где агент сам пишет код, ищет информацию, решает задачу. Потом ты берешь этот же код, запускаешь у себя, и он падает через пять минут. Не потому что ты глупый. А потому что между игрушечным прототипом и системой, которая работает 24/7, лежит пропасть. Курс Kaggle "Building AI Agents" — это попытка Google и NVIDIA засыпать эту пропасть бетоном. Не советами вроде "используй LangChain", а конкретными архитектурными решениями, которые не сломаются под нагрузкой.
Архитектура, которая не просит пощады
Забудь про цепочки (chains). В продакшене они бесполезны. Google в курсе делает упор на Reified Architecture — овеществленную архитектуру. Звучит сложно? На деле это просто: каждый компонент агента — планировщик, исполнитель, память — это независимый сервис с четким API. Их можно масштабировать, менять, мониторить по отдельности.
| Компонент | Задача в игрушке | Задача в продакшене (по версии курса) |
|---|---|---|
| Планировщик (Planner) | Разбить задачу на шаги один раз | Динамически перепланировать при ошибках, оценивать стоимость каждого шага |
| Исполнитель (Executor) | Вызвать один инструмент | Управлять состоянием, валидировать выходы, handle таймауты |
| Память (Memory) | Хранить историю чата | Векторный поиск + граф знаний + кэширование дорогих запросов |
NVIDIA приносит в курс свою боль — оптимизацию под GPU. Их лучшая практика: не гонять всю логику агента через одну большую модель. Планировщик можно запускать на маленькой, быстрой модели (например, Llama 3.1 8B), а сложные рассуждения — на большой (GPT-4 или Claude). Это режет затраты в 3-5 раз.
# Пример архитектуры из курса: Planner как отдельный сервис
# Это НЕ полный код, а схема
import httpx
from pydantic import BaseModel
class PlanRequest(BaseModel):
task: str
available_tools: list[str]
budget: float # в долларах или токенах
class PlanStep(BaseModel):
action: str
tool: str
expected_cost: float
async def call_planner_service(task: str) -> list[PlanStep]:
# Планировщик живет в отдельном контейнере
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"http://planner-service:8000/plan",
json=PlanRequest(
task=task,
available_tools=["search", "calculator", "code_executor"],
budget=0.10 # Не больше 10 центов на задачу
).dict()
)
return [PlanStep(**step) for step in response.json()["steps"]]
# Исполнитель потом берет этот план и выполняет
# Если шаг падает, он отправляет ошибку обратно планировщику для перепланированияГде все ломается: Разработчики кладут планировщик и исполнитель в один цикл while True. Потом при нагрузке в 100 RPS планировщик начинает тормозить, и весь агент встает. В курсе Google настаивает: планировщик должен иметь свой автономный пул реплик с автоскейлингом.
Планирование действий: не просто "думай шаг за шагом"
ReAct (Reasoning + Acting) — это мантра. Но в курсе Kaggle показывают его темную сторону. ReAct в наивной реализации делает 5-10 вызовов LLM для одной задачи. При цене GPT-4 Turbo $10 на 1M токенов, одна сложная задача пользователя может стоить тебе $0.50. И это без учета задержек.
Решение от Cohere (их модуль в курсе): Планирование с lookahead. Агент оценивает не только следующий шаг, но и потенциальные ветки развития. Это похоже на шахматы. Вместо цепочки: "Поищи в Google -> прочитай результат -> сформулируй ответ", агент сразу планирует: "Поищу в Google, если найду статью новее 2024 года, использую ее, иначе поищу в arXiv".
1 Шаг планирования с оценкой стоимости
Каждый инструмент в агенте должен иметь четкую цену. Поиск по базе — дешево. Вызов GPT-4 — дорого. Планировщик должен это знать. В курсе предлагают аннотировать инструменты метаданными:
tools_metadata = [
{
"name": "web_search",
"description": "Ищет в интернете",
"cost_per_call": 0.001, # $
"avg_latency_ms": 1200
},
{
"name": "ask_gpt4",
"description": "Задает сложный вопрос GPT-4",
"cost_per_call": 0.02,
"avg_latency_ms": 5000
}
]
# Планировщик выбирает инструменты не только по релевантности,
# но и чтобы уложиться в бюджет задачи.
# Это critical path для продакшена, о котором молчат 99% туториалов.Инструменты (Tools) — где агенты теряют лицо
Самый болезненный момент. Ты даешь агенту инструмент для работы с базой данных. Он пишет SQL-запрос с JOIN 10 таблиц и вешает продовую базу. В курсе NVIDIA предлагают радикальное решение: инструменты с sandbox.
- Чтение файлов: Агент не получает прямой доступ к файловой системе. Ему дают виртуальное окружение с копией файла.
- Выполнение кода: Не docker, а изолированные контейнеры с лимитами на CPU, память и время выполнения (например, через Firecracker microVMs).
- Работа с API: Все внешние вызовы идут через прокси с rate limiting и валидацией ответов.
Это не теория. В курсе есть пример с использованием Model Context Protocol (MCP) от NVIDIA для безопасного доступа к инструментам. Если ты хочешь запускать агентов локально без облаков, тебе сюда — мы разбирали это в LM Studio MCP.
Память, которая помнит всё (но не стоит миллион долларов)
Векторная база — это только первый слой. Google в курсе показывает трехслойную архитектуру памяти:
- Краткосрочная (Short-term): История текущего диалога. Хранится в оперативке, сбрасывается после сессии.
- Долгосрочная (Long-term): Векторная база (Chroma, Pinecone) для семантического поиска по прошлым разговорам.
- Структурированная (Structured): Граф знаний (например, Neo4j) для хранения фактов и отношений. Если агент узнал, что пользователь любит Python, он кладет это в граф, а не в векторную базу.
# Пример комбинированного запроса к памяти
# Псевдокод на основе курса
def query_memory(user_id: str, query: str):
# 1. Быстрый поиск в графе знаний
graph_facts = knowledge_graph.query(
f"""MATCH (u:User {{id: '{user_id}'}})-[:LIKES]->(t:Topic)
RETURN t.name"""
)
# 2. Семантический поиск в истории
vector_results = vector_db.similarity_search(
query,
filter={"user_id": user_id},
k=5
)
# 3. Объединяем контекст
context = f"""Пользователь предпочитает: {graph_facts}\nРанее обсуждали: {vector_results}"""
return context
# Это позволяет агенту не спрашивать "на каком языке ты пишешь код?" каждый раз.Ошибка №1: Сваливать всю память в один векторный индекс. Через месяц у тебя 100К эмбеддингов, поиск замедляется в 10 раз, а агент начинает возвращать нерелевантный контекст. Курс учит разделять.
Production deployment: что не пишут в README
Здесь курс Kaggle переходит от архитектуры к инженерии. И это самая ценная часть.
2 Мониторинг не по метрикам моделей, а по бизнес-целям
Не следи за accuracy или F1-score. Следи за:
- Cost per task: Средняя стоимость выполнения задачи. Если растет — где-то утечка.
- Completion rate: Сколько задач агент довел до конца без вмешательства человека.
- Tool error rate: Как часто инструменты падают. Если web_search падает в 30% случаев, пора менять провайдера.
3 Тестирование агентов — это не unit-тесты
Ты не можешь написать assert agent("напиши код") == "expected_code". В курсе предлагают подход property-based testing. Проверяй не конкретный вывод, а свойства:
def test_agent_code_generation(agent):
# Задача
task = "Напиши функцию сложения двух чисел на Python"
result = agent.run(task)
# Свойства, которые должны выполняться
assert "def add" in result # Содержит функцию
assert "return" in result # Что-то возвращает
assert "SyntaxError" not in result # Код синтаксически корректный
# Можно даже выполнить сгенерированный код в sandbox
with code_sandbox() as sandbox:
output = sandbox.execute(result + "\nprint(add(2, 3))")
assert "5" in output # И проверить его работуИ да, тестовый стенд должен быть изолирован от прода. Иначе твои тесты начнут слать реальные письма или удалять данные.
Что будет дальше? (Спойлер: суб-агенты)
Курс заканчивается, но тренды уже видны. Следующий шаг — иерархические агенты. Один главный агент-менеджер, который делегирует задачи специализированным суб-агентам: один для поиска информации, другой для анализа кода, третий для общения с пользователем. Это уменьшает сложность каждого агента и повышает надежность. Мы уже разбирали, как это работает, в статье про суб-агентов в AI-разработке.
Но главный вывод из курса Kaggle не технический. Он философский. AI-агент — это не модель, а система. Система из планировщиков, исполнителей, инструментов, памяти и мониторинга. Собирать их на коленке из скриптов Python — путь в никуда. Архитектура должна быть production-first с самого начала. И теперь у тебя есть карта, как это сделать.