Почему ваш AI-агент "тупит" и сжигает бюджет
Вы загружаете в промпт 15 файлов, отправляете запрос и ждете. Через 45 секунд получаете ответ, который на 80% состоит из воды. Стоимость вызова - $0.87. Вы пытаетесь уточнить - агент забывает, о чем шла речь в начале диалога. Знакомо?
Проблема не в том, что модели глупые. Проблема в том, как мы кормим их информацией. Контекстное окно - это не бездонная яма, куда можно сбрасывать все подряд. Это оперативная память с жесткими ограничениями.
Главное правило: чем больше контекста вы даете, тем хуже модель его обрабатывает. Она не умеет выделять главное - она просто усредняет все входные данные.
Техника 1: AGENTS.md - ваш единственный источник правды
Вспомните, как работает любой нормальный разработчик. Прежде чем писать код, он читает документацию проекта. Не всю сразу, а конкретные разделы, которые нужны для текущей задачи.
AGENTS.md - это именно такая документация, но написанная специально для AI. Не для людей. Разница принципиальная.
1 Что должно быть в AGENTS.md
Не нужно описывать всю архитектуру системы. Только то, что критично для агента прямо сейчас.
# AGENTS.md
## Ключевые правила
- Всегда используй TypeScript с strict mode
- Экспортируй только named exports
- Не используй any тип
- Пиши JSDoc для публичных функций
## Структура проекта
- src/api/ - эндпоинты API
- src/lib/ - утилиты
- src/types/ - TypeScript интерфейсы
## Конкретные паттерны
### Обработка ошибок
Всегда оборачивай async функции в try/catch
Используй кастомные классы ошибок из src/lib/errors.ts
### Логирование
Используй только winston
Конфигурация уже настроена в src/lib/logger.ts
Сравните с тем, как НЕ надо делать:
# Проект AwesomeApp
Этот проект создан для решения бизнес-задач компании...
Наша миссия - делать мир лучше...
Команда проекта состоит из 15 человек...
История проекта началась в 2020 году...
Агент прочитает все это, потратит токены и забудет 90% информации через три промпта.
Техника 2: Контекстные цепочки - разбиваем монолит
Представьте, что вам нужно объяснить сложную концепцию коллеге. Вы не вываливаете на него все знания сразу. Вы строите объяснение по цепочке: от простого к сложному, с постоянными проверками понимания.
С агентами работает та же логика. Одна большая промпт-инструкция на 5000 токенов - гарантия того, что модель пропустит важные детали.
2 Как строить цепочки правильно
Допустим, вам нужно написать аутентификацию. Не делайте так:
# ПЛОХО: Все в одном промпте
prompt = """
Напиши систему аутентификации на Node.js с Express.
Используй JWT токены, refresh tokens, валидацию входных данных,
хэширование паролей с bcrypt, rate limiting, логирование всех попыток входа,
поддержку двухфакторной аутентификации, отправку email для подтверждения,
и интеграцию с Google OAuth.
"""
Вместо этого разбейте на цепочку:
# Шаг 1: Базовая структура
prompt1 = """
Создай базовую структуру аутентификации:
- Модель User в MongoDB
- Роуты /api/auth/register и /api/auth/login
- Простейшая валидация email и пароля
"""
# Шаг 2: После получения кода из шага 1
prompt2 = """
Добавь к предыдущему коду:
1. JWT токены с expiresIn: '15m'
2. Middleware для проверки токена
3. Хэширование паролей с bcrypt
"""
# Шаг 3: И так далее...
# Каждый следующий промпт ссылается на результаты предыдущего
В статье про суб-агентов я подробно разбирал, как автоматизировать этот процесс. Но даже вручную цепочки работают в разы лучше монолитных промптов.
Техника 3: Структурирование - превращаем хаос в данные
Модели не умеют читать между строк. Они работают с паттернами. Чем более структурированы ваши данные, тем точнее будет ответ.
Возьмем пример из реальной жизни. Допустим, вам нужно, чтобы агент проанализировал ошибки в логах.
# ПЛОХО: Сырые логи
[2024-01-15 10:30:45] ERROR: Database connection failed
[2024-01-15 10:31:02] WARNING: High memory usage detected
[2024-01-15 10:32:15] ERROR: User authentication failed for ID 12345
[2024-01-15 10:33:00] INFO: Cache cleared successfully
А теперь структурируем:
{
"logs_analysis_request": {
"time_range": "2024-01-15 10:30:00 to 2024-01-15 10:35:00",
"errors": [
{
"timestamp": "2024-01-15 10:30:45",
"level": "ERROR",
"message": "Database connection failed",
"category": "infrastructure"
},
{
"timestamp": "2024-01-15 10:32:15",
"level": "ERROR",
"message": "User authentication failed for ID 12345",
"category": "authentication"
}
],
"warnings": [
{
"timestamp": "2024-01-15 10:31:02",
"level": "WARNING",
"message": "High memory usage detected",
"category": "performance"
}
]
}
}
Разница как между "найди иголку в стоге сена" и "иголка лежит в третьем стоге слева, на глубине 20 см".
| Что структурировать | Как структурировать | Выигрыш в точности |
|---|---|---|
| Требования к фиче | JSON с полями: цель, ограничения, примеры ввода/вывода | +40-60% |
| Ошибки и логи | Группировка по типам, приоритетам, временным отрезкам | +70-80% |
| Архитектурные решения | Таблица сравнения вариантов с плюсами/минусами | +50-70% |
Техника 4: Пересборка контекста - забыть, чтобы вспомнить
Самая контр-интуитивная техника. Иногда лучший способ улучшить понимание - это выкинуть старый контекст и начать заново.
Представьте диалог с агентом:
Вы: Напиши функцию для валидации email
Агент: Готово
Вы: Добавь валидацию телефона
Агент: Готово
Вы: Сделай так, чтобы можно было валидировать и email, и телефон одновременно
Агент: Готово
Вы: А теперь добавь поддержку международных форматов телефона
Агент: ... (тупит, предлагает странные решения)
Проблема в накоплении контекста. Каждый новый запрос добавляется к старому, создавая кашу из противоречивых инструкций.
3 Когда и как делать пересборку
- После каждого крупного изменения архитектуры. Закончили рефакторинг? Сбросьте контекст и начните с чистого листа с новым AGENTS.md.
- Когда агент начинает "плавать". Если ответы становятся менее точными с каждым новым запросом - это сигнал.
- При смене типа задачи. Перешли от написания API к настройке инфраструктуры? Полная пересборка.
Как это выглядит на практике:
# Вместо продолжения диалога
# Вы начинаете новый с агрегированным контекстом
old_context = """
Мы писали функцию validateEmail().
Потом добавили validatePhone().
Потом сделали validateContact() которая объединяет обе.
"""
# Агрегируем в структурированную форму
new_context = {
"current_state": {
"functions": ["validateEmail", "validatePhone", "validateContact"],
"purpose": "Валидация контактных данных пользователя",
"next_task": "Добавить поддержку международных форматов телефона"
}
}
# И начинаем новый диалог с этим структурированным контекстом
prompt = f"""
На основе текущего состояния проекта: {json.dumps(new_context)}
Реши следующую задачу: добавить поддержку международных форматов телефона.
"""
Собираем все вместе: рабочий пайплайн
Теперь давайте посмотрим, как эти техники работают в комплексе на реальном примере.
Задача: Добавить новую фичу в существующий проект - систему оповещений о ошибках в реальном времени.
4 Шаг 1: Подготовка AGENTS.md
# AGENTS.md для системы оповещений
## Контекст проекта
- Мониторинг: Prometheus + Grafana
- Оповещения сейчас: только email
- Язык: Go 1.21+
## Требования к новой фиче
1. Добавить Slack-уведомления
2. Поддержка приоритетов (Critical, Warning, Info)
3. Rate limiting: не более 5 уведомлений в минуту на канал
4. Логирование всех отправленных уведомлений
## Ограничения
- Не трогать существующую email-систему
- Использовать официальный Slack Go SDK
- Сохранить обратную совместимость API
5 Шаг 2: Контекстные цепочки для реализации
Первая цепочка: настройка Slack-клиента
// Промпт 1
"Создай структуру SlackNotifier на основе official Slack Go SDK.
Добавь базовую конфигурацию через environment variables.
Напиши метод Send(message string) error."
Вторая цепочка: интеграция с существующей системой
// Промпт 2 (после получения кода из промпта 1)
"Интегрируй SlackNotifier в существующую AlertDispatcher.
Добавь поддержку приоритетов через поле Priority в Alert struct.
Реализуй выбор канала Slack в зависимости от приоритета."
6 Шаг 3: Структурирование сложных частей
Вместо "добавь rate limiting" даем структурированное требование:
{
"rate_limiting": {
"requirement": "Ограничить количество уведомлений в Slack",
"limits": {
"per_channel": "5 сообщений в минуту",
"per_priority": {
"critical": "без ограничений",
"warning": "10 сообщений в минуту",
"info": "3 сообщения в минуту"
}
},
"implementation_notes": [
"Использовать golang.org/x/time/rate",
"Отдельный лимитер для каждого канала Slack",
"Логировать пропущенные из-за лимита уведомления"
]
}
}
7 Шаг 4: Пересборка при смене контекста
После реализации основной функциональности:
// Вместо продолжения в том же диалоге
// Начинаем новый с агрегированным контекстом
summary = `
Реализовано:
1. SlackNotifier с поддержкой official SDK
2. Интеграция с AlertDispatcher
3. Rate limiting на основе golang.org/x/time/rate
Следующая задача: добавить тесты и метрики.
`
Ключевой инсайт: не пытайтесь уместить весь проект в один промпт. Делите, структурируйте, пересобирайте. Каждая техника экономит 20-30% токенов и повышает точность на 40-50%.
Чего избегать: типичные ошибки
- Копирование всей документации проекта в промпт. Агент ее не запомнит, а токены потратит.
- Смешивание несвязанных контекстов. Не обсуждайте архитектуру БД и CSS-анимации в одном диалоге.
- Отсутствие версионирования AGENTS.md. Если файл устарел, он становится вредным.
- Игнорирование сигналов перегруза. Когда агент начинает "глючить" - это не баг модели, а ваша ошибка в управлении контекстом.
Что дальше?
Эти четыре техники - базовый набор. Когда освоите их, переходите к более продвинутым методам из статьи про production-ready агентов.
Самый важный навык, который нужно развивать: чувство контекста. Вы должны научиться чувствовать, когда агент "перегружен", когда ему не хватает информации, когда пора делать пересборку. Это не магия, это мышечная память, которая нарабатывается практикой.
Начните с малого. Возьмите одну маленькую задачу, примените AGENTS.md. Посмотрите на разницу. Потом добавьте контекстные цепочки. И так далее. Через месяц вы будете удивляться, как раньше жили без этого.
И последнее: не становитесь рабом оптимизации. Иногда проще потратить лишние $0.50 на токены, чем час на идеальную структуризацию. Знайте, где проходит граница разумного.