Слогер Создать блог
AI

5 уроков по observability для автономного AI-агента

Как я добавил логирование инструментов и перестал гадать, что на самом деле делает мой агент, пока я не смотрю.

Проблема: доверять агенту — значит не видеть правды

Мой агент работает сессиями по несколько часов без моего контроля. Он читает файлы, редактирует код, запускает команды и возвращает отчёт: «Рефакторинг модуля аутентификации выполнен, все тесты пройдены». Но проблема в том, что этот отчёт написан тем же агентом, который мог ошибиться. Если он молча пропустил шаг, неверно прочитал файл или «починил» не ту функцию, итоговый отчёт всё равно будет уверенным и чистым. У меня не было независимой записи того, что произошло на самом деле — только нарратив самого агента о самом себе.

Однажды это привело к серьёзной ошибке. Агент отчитался об успешном рефакторинге, тесты были зелёными. Но оказалось, что на более раннем шаге редактирование файла молча не применилось, и «проходящие тесты» проверяли старый код, который не изменился. Ничего не упало, ничего не выглядело неправильно. Агент просто сделал меньше, чем заявил. Тогда я понял: тихий частичный сбой хуже громкого. Падение указывает, где искать. Молча пропущенный шаг не даёт никаких зацепок, а итоговый отчёт активно врёт, умалчивая о проблеме.

Решение: логировать на границе, а не внутри агента

Я добавил слой логирования между агентом и его инструментами, а не внутри рассуждений агента. Это ключевое различие: если лог пишет сам агент, он может быть так же ошибочен, как и его отчёт. Если лог генерируется обвязкой при каждом реальном вызове инструмента — это факт, а не нарратив.

Что я логирую

Для каждого вызова инструмента я фиксирую:

  • Временная метка
  • Имя инструмента и краткое описание аргументов (без секретов и полного содержимого файлов — только чтобы понять, что произошло)
  • Статус результата: success, error или noop (последняя категория оказалась самой ценной)
  • Длительность
import json import time def log_tool_call(tool_name, args_summary, status, duration_ms): entry = { "ts": time.time(), "tool": tool_name, "args": args_summary, "status": status, # "success" | "error" | "noop" "duration_ms": duration_ms, } with open("agent_trace.jsonl", "a") as f: f.write(json.dumps(entry) + "\n")

Каждый запуск дописывается в файл .jsonl — один JSON-объект на строку. Этот формат оказался важен: он append-only, устойчив к сбоям (обрезанная последняя строка не портит предыдущие) и легко анализируется с помощью jq или Python, без базы данных.

Статус noop — настоящее спасение

Ошибка, с которой всё началось — молча пропущенное редактирование — проявилась сразу, как только я добавил статус noop. Инструмент редактирования, который выполняется, но ничего не меняет (потому что целевой текст не найден или файл уже в нужном состоянии) — это не то же самое, что успешное редактирование. Раньше оба случая показывались как «success» в моей голове. После разделения неудачный запуск выглядел бы так:

{"tool": "edit_file", "args": "auth.py: replace validate()", "status": "noop", "duration_ms": 4} {"tool": "run_tests", "args": "test_auth.py", "status": "success", "duration_ms": 812}

Noop, сразу за которым следует success на том, что он должен был исправить — огромный красный флаг, когда вы это видите. Но это было невидимо, когда у меня был только итоговый отчёт агента.

Визуализация сессии

Как только у меня появились структурированные логи, отследить сессию как последовательность стало просто. Например, диаграмма последовательности из пяти строк из сырого лога сразу показала проблему, которую я упускал в течение трёх сеансов отладки, читая прозаический отчёт агента.

Сэмплирование, а не всё подряд

Я не логирую полное содержимое файлов или полный вывод команд — это раздуло бы файл трассировки и вернуло проблему «стены текста, которую никто не читает». Вместо этого я логирую краткую сводку (первые ~100 символов diff, имя команды и код возврата, а не stdout). Если мне нужны полные детали для конкретного шага, я перезапускаю этот шаг вручную. Задача трассировки — сказать мне, где искать, а не быть полным повторением.

Запросы к логу вместо чтения

Когда логи стали структурированными, я перестал читать их сверху вниз и начал отправлять запросы. Однострочник вроде этого отвечает на вопрос «было ли сегодня что-то тихое?» за секунду:

cat agent_trace.jsonl | jq -r 'select(.status == "noop") | "\(.tool) \(.args)"'

После того как я начал запускать его после каждой сессии, выявилась закономерность, которую я раньше полностью упускал: примерно в 1 из 12 сессий был хотя бы один noop на записывающем инструменте. Большую часть времени это было безвредно — файл уже был в нужном состоянии. Но примерно четверть таких noop маскировали реальную проблему — тот же класс ошибок, с которого всё началось. До появления запроса у меня не было способа даже оценить эту пропорцию; я полагался на симптомы, которые замечал спустя дни.

Я также отслеживаю скользящее количество вызовов инструментов на сессию и помечаю всё, что выходит за пределы нормального диапазона. Сессия, которая обычно делает 20–40 вызовов, но внезапно делает 3 и останавливается, так же подозрительна, как и та, которая делает 200 — обычно это означает, что что-то сломалось выше по цепочке.

Честность в накладных расходах

Справедливый вопрос: не добавляет ли всё это логирование накладных расходов и сложности? На практике слой логирования — менее 40 строк кода, а сама запись занимает единицы миллисекунд — это один вызов open().write() на каждый вызов инструмента, без сетевых запросов и внешних сервисов. Стоимость ничтожна по сравнению с минутами (а иногда часами), которые я раньше тратил на восстановление сессии по памяти и скроллу после того, как что-то шло не так.

Выводы

  1. Логируйте на границе, а не внутри рассуждений. Лог, который пишет агент, наследует его слепые зоны. Лог, который генерирует обвязка при каждом реальном вызове инструмента, — это истина, независимая от того, что агент думает о произошедшем.
  2. «Ничего не сделано» заслуживает собственного статуса. Успех/неудача — недостаточно. Инструмент, который выполнился без ошибки, но ничего не изменил, — это отдельный важный случай. Именно там обычно прячутся настоящие баги, и объединение его с «успехом» — причина, по которой я упускал свою ошибку неделями.
  3. Append-only лучше, чем «умное» решение. Я сначала попробовал небольшую SQLite-базу, но она оказалась хрупкой — сбой во время записи мог повредить состояние, а для просмотра нужен был отдельный инструмент. Обычные JSON Lines файлы не требуют ничего, кроме cat и jq, устойчивы к сбоям, и их можно смотреть в реальном времени во время работы.
  4. Суммируйте, не выгружайте. Полный stdout/diff в каждой строке лога делает трассировку нечитаемой и дорогой для хранения. Логируйте достаточно, чтобы знать, где искать, и перезапускайте конкретный шаг для полных деталей, когда они действительно нужны.
  5. Сначала модель данных, потом визуализация. Мне хотелось с первого дня построить красивую панель. Но на самом деле важнее было сначала правильно определить схему лога — категории статусов, что считать одним «событием». Как только это стало надёжным, даже диаграмма последовательности из пяти строк из сырого лога оказалась полезнее той панели, которую я представлял.

Что дальше

Я работаю над тем, чтобы превратить детектор noop в активную проверку: вместо того чтобы я просматривал лог постфактум, обвязка будет помечать noop на любом инструменте редактирования/записи как предупреждение во время выполнения, чтобы тихий noop был обнаружен до того, как агент перейдёт к следующему шагу и выстроит ложный нарратив.

Также я хочу добавить флаги аномалий по длительности — если шаг, который обычно занимает 2 секунды, вдруг выполняется 40 секунд, это стоит проверить, даже если он технически «успешен». И я хочу вести небольшой скользящий базовый уровень (среднее количество вызовов инструментов, средняя длительность на инструмент) для каждого типа задач, чтобы «аномалия» измерялась относительно истории этой задачи, а не единого глобального порога, который сейчас даёт слишком много ложных срабатываний на действительно больших задачах.

Ничто из этого не должно быть сложным. Главный урок: observability не требует панели или вендора — она требует скучного, структурированного, append-only лога фактов, независимого от того, что вы пытаетесь наблюдать.

Заключение

Если вы запускаете любого автономного агента без присмотра, не доверяйте его собственному отчёту как единственной записи о произошедшем — логируйте вызовы инструментов независимо, дайте статус «ничего не сделал» и храните формат настолько простым, чтобы вы могли grep-нуть его в 2 часа ночи.

По материалам: dev.to. Текст переработан редакцией Слогера.

← На главную

Рекламное место — Конец поста
Реклама · Слогер

Комментарии (0)

Войдите, чтобы комментировать.

Пока нет комментариев. Будьте первым.

Маркетинг

Как запустить продукт на маленькой базе подписчиков: 5 писем, которые продают без фальшивого дефицита

Одно письмо-анонс почти никогда ничего не продаёт — даже на списке из 40 человек. Разбираем пятидневный ритм рассылки, где каждое письмо решает свою задачу, и даём готовый шаблон последнего письма.

Слогер 28.10.2026 ▲ 1
Разработка

Как выгрузить вакансии и зарплатные вилки из Greenhouse API: разбор подводных камней

Любая доска Greenhouse отдаётся публичным JSON без ключа — целиком, одним ответом. Но чтобы данные были пригодны, нужно знать пять вещей про флаги, экранирование и двойной счёт вакансий.

Слогер 28.10.2026 ▲ 0
Fluxs lenta
Реклама · fluxs.ru
Маркетинг

Переезд (миграция) с Битрикс24 и amoCRM без потери данных: в Fluxs появился модуль миграции

4 000 контактов в Битрикс24. Сотни сделок. Задачи за несколько лет. И всё это нужно перенести в новую CRM? Раньше такой переезд мог занять несколько дней. А иногда именно из-за переноса данных компании годами оставались в неудобной системе. Мы решили эту проблему. 🚀 В Fluxs появился модуль «Миграция данных». Теперь можно переносить контакты, сделки, задачи, проекты и базу знаний из Битрикс24, amoCRM, Jira, Яндекс Трекера и Confluence. А если вашей системы нет в списке — загрузить данные через Excel, CSV или JSON.

fluxs.ru 27.10.2026 ▲ 1
Карьера

Чем Cal-WARN отличается от федерального WARN: разбор порогов, уведомлений и штрафов

Калифорния требует предупреждать о массовых сокращениях за 60 дней — как и федеральный закон США. Только порог ниже, парт-тайм считается, а правило одной трети не действует. Разбираем, что это меняет для работника и работодателя.

Слогер 27.10.2026 ▲ 0
Карьера

WARN Act: кто считается работодателем при массовом сокращении — разбор для дочек, холдингов и франшиз

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

Слогер 27.10.2026 ▲ 0
Разработка

Разбор: почему профессию разработчика хоронят каждые 20 лет и как работать, когда всё меняется

Паника вокруг ИИ — не первая волна перемен в отрасли: похожие прогнозы звучали и в 1961 году, и в 2011-м. Разбираем, что в нынешней тревоге реально, а что стоит воспринимать как фоновый шум.

Слогер 27.10.2026 ▲ 0
Fluxs lenta
Реклама · fluxs.ru
Карьера

Страх провала и страх свидетелей: чем они отличаются и как начинать новое, когда страшно

«Я пока недостаточно хорош» — честная и бесполезная фраза. Обычно дело не в навыке, а в конкретных людях, которые увидят вашу первую попытку. Разбираем, как уменьшить ставку и всё-таки начать.

Слогер 26.10.2026 ▲ 0
Карьера

Почему удалёнка платит меньше — и в каких профессиях всё наоборот: разбор 8 049 вакансий

Медианная вилка на удалёнке ниже офисной на 6,5%. Но внутри функций разрыв меняет знак: безопасность и продажи получают премию за удалёнку, data/ML — самую большую скидку. Показываю, как читать эти цифры на переговорах.

Слогер 26.10.2026 ▲ 0
Личный опыт

Как заказать командную вышивку без минимальной партии: разбор и чек-лист

Малая команда редко может позволить себе стартовый тираж на 30 вещей. Разбираем схему, при которой вышивка делается по одной штуке — и при этом логотип у всех выглядит одинаково.

Слогер 26.10.2026 ▲ 0
Маркетинг

Автоматизация бизнеса за 15 000 или 170 000 рублей в год: в чём разница?

Автоматизация бизнеса за 15 000 или 170 000 ₽ в год. За что такая разница? 🤔 Представьте: компании нужно всего несколько вещей — принимать заявки, согласовывать счета и контролировать задачи сотрудников. А ей предлагают целую цифровую экосистему: CRM, телефонию, маркетинг, аналитику, десятки дополнительных функций… Но что, если половина из этого бизнесу вообще не нужна?

Слогер 25.10.2026 ▲ 1
Fluxs lenta
Реклама · fluxs.ru