Вы даете промпт. Агент "думает" три минуты. В ответ - тишина. Или код, который ломает продакшен. Или бесконечное "я понял, сейчас сделаю". Знакомо?
Работа с AI-агентами в Cursor и Claude Code похожа на управление черным ящиком. Внутри происходит какая-то магия, но когда что-то ломается - понять причину невозможно. Нет логов. Нет трейсов. Нет метрик.
Почему observability для AI-агентов - это не опция, а обязаловка
Представьте: у вас есть junior-разработчик, который:
- Никогда не говорит, что делает
- Не показывает промежуточные результаты
- При ошибке молчит и ждет следующей задачи
- Забывает контекст через 5 минут
Вы бы такого взяли на работу? Нет. Но именно так работают AI-агенты без observability.
Самый частый вопрос в чатах разработчиков: "Почему агент сделал это, а не то?" Ответа нет. Потому что вы не видите, как он принимал решения.
Hooks.json: конфиг, который превращает черный ящик в прозрачный
В Cursor и Claude Code есть скрытая фича - хуки. Это как middleware для вашего агента. Каждое событие в жизненном цикле агента можно перехватить, логировать, модифицировать.
1 Создаем hooks.json
Для Cursor создайте файл в папке настроек:
# Для macOS
~/Library/Application\ Support/Cursor/User/hooks.json
# Для Windows
%APPDATA%\\Cursor\\User\\hooks.json
# Для Linux
~/.config/Cursor/User/hooks.json
Для Claude Code - в корне проекта:
touch .claude/hooks.json
2 Базовый конфиг для логирования
Вот минимальный hooks.json, который покажет все события агента:
{
"hooks": [
{
"name": "log-everything",
"pattern": ".*",
"command": "echo '[HOOK] ${event.type} - ${event.timestamp}' >> /tmp/agent-hooks.log",
"runInBackground": true
}
]
}
Этот хук логирует все события в файл. Примитивно, но уже лучше, чем ничего.
Полный жизненный цикл агента: что можно перехватить
Агент в Cursor/Claude Code проходит через 12 этапов. Каждый этап - событие, которое можно ловить хуком.
| Событие | Когда срабатывает | Что содержит |
|---|---|---|
| agent.start | Агент запущен | prompt, context, model |
| agent.thinking | Начало "размышлений" | current_step, max_steps |
| agent.tool.call | Вызов инструмента | tool_name, arguments |
| agent.tool.result | Результат инструмента | tool_name, result, duration |
| agent.error | Ошибка агента | error_message, stack_trace |
| agent.complete | Задача завершена | final_output, total_tokens, duration |
Самое важное событие - agent.tool.call. Когда агент вызывает инструмент (читает файл, запускает команду, ищет в интернете), вы видите что именно и с какими аргументами.
Продвинутая настройка: трассировка с контекстом
Логировать события в файл - для новичков. Профессионалы отправляют данные в системы observability. Вот пример хука для OpenTelemetry:
{
"hooks": [
{
"name": "opentelemetry-trace",
"pattern": "agent.*",
"command": "python3 -c \"
import json
import sys
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
# Получаем данные события из stdin
event_data = json.load(sys.stdin)
# Настраиваем трейсер
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
# Создаем span
with tracer.start_as_current_span(event_data['type']) as span:
span.set_attributes({'agent.id': event_data.get('agent_id', 'unknown')})
span.set_attributes({'event.duration': event_data.get('duration', 0)})
# Логируем дополнительные данные
if 'prompt' in event_data:
span.add_event('prompt', {'text': event_data['prompt'][:100]})
print(f'Traced: {event_data["type"]}')
\"",
"runInBackground": true,
"passEventData": true
}
]
}
Этот хук:
- Передает все данные события в скрипт Python через stdin
- Создает span в OpenTelemetry для каждого события
- Добавляет атрибуты и события в span
- Отправляет данные в Jaeger/Tempo/Grafana
Ключевой параметр "passEventData": true передает полные данные события в команду хука. Без него вы получите только тип события.
Keywords AI: секретное оружие для контекстных подсказок
Keywords AI в Cursor - это система, которая автоматически добавляет релевантные файлы в контекст агента. Работает по принципу semantic search. Но как понять, какие файлы были добавлены?
Хук для мониторинга Keywords AI:
{
"hooks": [
{
"name": "keywords-ai-monitor",
"pattern": "keywords.*",
"command": "echo 'Keywords AI: ${event.type} - added ${event.added_files?.length || 0} files' >> /tmp/keywords.log",
"runInBackground": true
}
]
}
События Keywords AI:
keywords.search- поиск релевантных файловkeywords.context_update- обновление контекста агентаkeywords.cache_hit- использование кэшированных результатов
Это помогает понять, почему агент "видит" одни файлы и не видит другие. Особенно полезно при работе с большими проектами, где не все файлы должны быть в контексте.
Отладка промптов: почему агент делает не то
Самая частая проблема - промпт написан криво, но вы об этом не знаете. Агент пытается выполнить невыполнимое, зацикливается или делает что-то совершенно не то.
Хук для анализа промптов:
{
"hooks": [
{
"name": "prompt-analyzer",
"pattern": "agent.start",
"command": "python3 -c \"
import json
import sys
import re
event = json.load(sys.stdin)
prompt = event.get('prompt', '')
# Анализируем промпт
print('=== PROMPT ANALYSIS ===')
print(f'Length: {len(prompt)} chars')
print(f'Lines: {prompt.count(chr(10)) + 1}')
# Ищем типичные проблемы
if len(prompt) > 4000:
print('WARNING: Prompt too long, agent may truncate')
if re.search(r'делай.*и.*сразу', prompt, re.IGNORECASE):
print('WARNING: Multiple instructions without clear steps')
if not re.search(r'шаг.*\d+|сначала.*потом', prompt, re.IGNORECASE):
print('INFO: No clear step-by-step instructions')
# Сохраняем для дальнейшего анализа
with open('/tmp/prompt-analysis.json', 'a') as f:
json.dump({
'timestamp': event['timestamp'],
'prompt_preview': prompt[:200],
'analysis': {
'length': len(prompt),
'has_steps': bool(re.search(r'шаг|step', prompt, re.IGNORECASE))
}
}, f)
f.write('\n')
\"",
"runInBackground": true,
"passEventData": true
}
]
}
Этот скрипт проверяет:
- Длину промпта (если больше 4000 символов - могут быть проблемы)
- Наличие четких шагов
- Множественные инструкции в одном предложении
- Сохраняет метаданные для анализа трендов
Интеграция с существующими системами мониторинга
Observability бесполезна, если данные лежат в отдельных лог-файлах. Нужна интеграция с вашим стеком мониторинга.
Grafana + Prometheus
{
"hooks": [
{
"name": "prometheus-metrics",
"pattern": "agent.*",
"command": "python3 -c \"
import json
import sys
import requests
from datetime import datetime
event = json.load(sys.stdin)
# Отправляем метрики в Pushgateway
metrics = []
metrics.append(f'agent_events_total{{type=\"{event["type"]}\"}} 1')
if 'duration' in event:
metrics.append(f'agent_event_duration_seconds{{type=\"{event["type"]}\"}} {event["duration"]}')
if 'error_message' in event:
metrics.append(f'agent_errors_total{{type=\"{event["type"]}\"}} 1')
# Отправляем в Prometheus Pushgateway
try:
response = requests.post(
'http://localhost:9091/metrics/job/agent_hooks',
data='\n'.join(metrics)
)
except Exception as e:
print(f'Failed to send metrics: {e}')
\"",
"runInBackground": true,
"passEventData": true
}
]
}
Elasticsearch для полнотекстового поиска по логам
{
"hooks": [
{
"name": "elasticsearch-logger",
"pattern": "agent.(start|complete|error)",
"command": "python3 -c \"
import json
import sys
from datetime import datetime
import requests
event = json.load(sys.stdin)
# Формируем документ для Elasticsearch
doc = {
'@timestamp': datetime.utcnow().isoformat() + 'Z',
'event_type': event['type'],
'agent_id': event.get('agent_id', 'unknown'),
'duration_ms': event.get('duration', 0) * 1000 if 'duration' in event else None,
'has_error': 'error_message' in event
}
# Добавляем превью промпта для поиска
if 'prompt' in event:
doc['prompt_preview'] = event['prompt'][:500]
# Отправляем в Elasticsearch
try:
response = requests.post(
'http://localhost:9200/agent-events/_doc',
json=doc,
headers={'Content-Type': 'application/json'}
)
except Exception as e:
print(f'Elasticsearch error: {e}')
\"",
"runInBackground": true,
"passEventData": true
}
]
}
Типичные ошибки и как их избежать
Ошибка 1: Хуки замедляют работу агента. Решение: используйте "runInBackground": true и асинхронные вызовы в командах.
Ошибка 2: Хук падает и ломает работу агента. Решение: оборачивайте код в try-catch, логируйте ошибки хуков отдельно.
Ошибка 3: Слишком много данных, система observability тонет. Решение: фильтруйте события на уровне хуков, не логируйте все подряд.
Готовый production-конфиг
Вот полный hooks.json, который я использую в продакшене:
{
"hooks": [
{
"name": "critical-events-log",
"pattern": "agent.(start|error|complete)",
"command": "logger -t cursor-agent '[${event.type}] ${event.agent_id} - duration: ${event.duration || 0}s'"
},
{
"name": "prompt-sampling",
"pattern": "agent.start",
"command": "python3 /opt/scripts/sample_prompts.py",
"runInBackground": true,
"passEventData": true
},
{
"name": "performance-metrics",
"pattern": "agent.complete",
"command": "curl -X POST -H 'Content-Type: application/json' -d '{\"agent_id\": \"${event.agent_id}\", \"duration\": ${event.duration}, \"tokens\": ${event.total_tokens || 0}}' http://metrics-service/api/agent-metrics",
"runInBackground": true
},
{
"name": "error-alert",
"pattern": "agent.error",
"command": "python3 /opt/scripts/send_alert.py --level ERROR --message 'Agent ${event.agent_id} failed: ${event.error_message}'"
}
]
}
Этот конфиг:
- Логирует критические события в системный лог
- Сэмплирует промпты для анализа качества
- Отправляет метрики производительности
- Отправляет алерты при ошибках
Что дальше? Будущее observability для AI-агентов
Сейчас мы наблюдаем только события. Но следующий шаг - инструментирование самого процесса "мышления" агента. Claude обещает в будущих версиях дать доступ к цепочке мыслей (chain-of-thought). Когда это случится, хуки смогут перехватывать не только что агент сделал, но и почему он это сделал.
Пока этого нет, самый мощный инструмент - комбинация хуков и контекстных файлов. Если вы еще не читали про AGENTS.md и CLAUDE.md, сделайте это сейчас. Эти файлы + observability дают полный контроль над агентом.
И помните: агент, который нельзя отладить - это не инструмент, а головная боль. Настройте хуки сегодня, и завтра вы сэкономите часы на поиске "почему он сломал продакшен".
P.S. Если ваш агент все еще тупит после настройки observability, возможно, проблема в его навыках. Посмотрите как заставить ИИ-агента помнить инструкции.