Четыре формы observability для агентов: Gemini на клиенте, Claude Code в BigQuery, CI — через pull

Когда агент в Cursor или Claude Code лезет в production-логи без заранее заданной формы, контекстное окно забивается шумом, а вопрос «сколько мы тратим на LLM прямо сейчас» остаётся без ответа — для соло-разработчика с агентами в ежедневном цикле это разница между расследованием за минуты и бесконечным перебором GitHub API. Ryan Tsuji, CTO airCloset, в разборе на Dev.to описывает, как внутренняя AI-платформа cortex строит observability под четыре типа вопросов — и почему единый стек «всё в Loki через OTel» здесь проигрывает.
Почему одна схема логов не кормит и дашборд, и агента
Предыдущая серия Tsuji про code-graph была про переформатирование статического графа анализа так, чтобы ИИ мог его запрашивать. Та же дисциплина переносится на observability: сначала форма данных, потом потребление — будь то Grafana или MCP-запрос вроде {service_name="…"} |~ "error".
Сырые production-логи без reshaping создают четыре типичные боли: объём забивает контекстное окно, шум маскирует ошибки, метрики и traces не связаны, а вопросы о тратах на модели в реальном времени остаются без ответа. Автор намеренно разводит четыре оси — application, infrastructure, CI, LLM — и даёт каждой свою форму данных:
| Ось | Вопрос | Форма |
|---|---|---|
| Application | что происходит в production прямо сейчас | log + trace |
| Infrastructure | хватает ли ресурсов, что упало | metric |
| CI | что сломалось и с какого момента | log + alert |
| LLM | траты и кто сколько использует | metric + structured records |
Тезис жёсткий: «просто всё через OTel в Loki» заставляет один backend отвечать на несовместимые типы вопросов — real-time spend и monthly SQL-агрегации по команде. Одна из сторон неизбежно проиграет.
Application и infrastructure: стандартный стек как фундамент для MCP
Две «классические» оси автор держит кратко — они не несут AI-эпохных сюрпризов, но без них агент не сможет расследовать production.
Application: OpenTelemetry → traces в Tempo, logs в Loki, metrics в Mimir (Grafana Cloud). Единообразная форма логов и traces у всех сервисов cortex — предпосылка для AI-расследований через MCP.
Infrastructure: метрики GCP (Cloud Run, Cloud Run Jobs, BigQuery, Pub/Sub, Cloud Tasks и др.) через Cloud Monitoring → Mimir. Одно место для запросов вроде «какой сервис ел больше всего CPU на прошлой неделе».
Вывод для разработчика с агентами: если traces и logs не унифицированы, MCP-инструмент для логов вернёт кашу, а не ответ. AI-эпохные решения Tsuji сосредоточены в CI и LLM — туда и смещается фокус.
Gemini: client-side стоимость вместо Billing API
Вызовы Gemini в cortex идут через общий wrapper traceGeminiCall, который на каждый вызов эмитит четыре метрики:
gemini.tokens.total— накопительные токены (labels:model/service/type=prompt|completion)gemini.requests.total— счётчик запросов (labels:model/service/status)gemini.request.duration— гистограмма латентностиgemini.cost.usd— оценочная стоимость (labels:model/service)
Выбор B (client-side): таблица цен в константе GEMINI_PRICING для моделей gemini-3-flash и gemini-3-pro; обновляется вручную при смене тарифов Google. Альтернатива A — Cloud Billing API: точнее, но с лагом в часы и сутки и без гранулярности по call-site context.
Мотивация B — атрибуция по контексту вызова (service / model / call-site), а не только SKU в billing. Real-time — бонус. Счётчик в Prometheus: gemini_cost_usd_USD_total (удвоение usd_USD — артефакт OTel meter gemini.cost.usd + unit USD при экспорте). Пример PromQL: sum(increase(gemini_cost_usd_USD_total[1h])). Алерт при $1/hour, severity info → Slack.
Паттерн переносим напрямую: если вы оборачиваете вызовы Gemini или другой pay-per-use модели в своём коде, inline-метрики с ценами в константе дают мгновенную картину трат по сервису, тогда как billing API ответит завтра и без привязки к конкретному вызову.
Claude Code: ledger в BigQuery, а не traces в Loki
Snippet поста говорит «OTel straight to BigQuery» — но в теле Part 1 пайплайн устроен иначе: structured ledger в четыре стадии.
- Emit — bundled analyzer в Claude Code POST-ит
UsageInput(только token info, без email) на внутренний endpoint - Auth proxy — Cloudflare Edge Router валидирует
CORTEX_API_KEY, проставляет email пользователя вX-Cortex-User-Email - Ingest — Cloud Run API дедуплицирует и публикует в Pub/Sub
- Persist — Cloud Run worker из Pub/Sub валидирует схему и streaming-insert в BigQuery
Поля записи: email, repository, timestamp, input_tokens / output_tokens, cache_creation_input_tokens / cache_read_input_tokens. SQL-агрегации отвечают на вопросы «команда A за неделю», «repo X за месяц», hit ratio prompt-cache.
Loki отклонён из-за болезненности LogQL aggregation и joins для таких запросов. BigQuery с DAY partition и email как primary key «пишется естественно». Структурные гарантии: identity authority на Edge Router (emit не держит email); Pub/Sub разделяет приём записей и worker; при сбое — DLQ с retry до 5 раз.
За 30 дней на портале cortex: 78.0B tokens, 384K messages, 47 users, 79 repositories. Cache Read Input — 75.1B (96% от total); prompt-cache «dramatically effective». На subscription нет прямого $, но cache read ~1/10 effective input rate → автор оценивает ~7× эффективнее blended input vs counterfactual без cache.
Логи MCP tool calls тоже в BigQuery (cortex.mcp_tool_calls), но проще: каждый MCP server пишет записи напрямую, без OTel.
Различие двух LLM-инструментов в одной платформе показательно: Gemini — свой calling code → wrapper + inline metrics → Prometheus (real-time); Claude Code — внешний CLI → usage как records post-factum → BigQuery (SQL aggregation). Для команды с агентами в IDE это урок: внешний CLI не обязан вписываться в ваш OTel-пайплайн — иногда structured ledger в аналитическое хранилище дешевле и гибче, чем натягивать LogQL на вопросы про usage по email и repo.
CI: post-hoc pull вместо push из тестового workflow
Третий design judgment — логи CI доставляются через post-hoc pull, а не webhook push из самого test run.
Механика:
- По завершении Test job срабатывает событие
workflow_run - Отдельный workflow для log shipping
- Pull логов из GitHub API (
/repos/.../actions/jobs/.../logs) - Отправка в Grafana Cloud как structured JSON (job / status / ref / pr / commit / output) через OTLP
/v1/logs
Фильтр примера: {service_name="ci", ref="main", status="failure"}.
Три причины выбора pull:
- CI и observability развязаны: сбой shipping не ломает тест; можно retry/replay shipping отдельно
- Нет пути для PR-кода к API key: shipping workflow в default-branch context, base-repo secrets; test workflow не трогает Grafana API key
- Сбой shipping становится наблюдаемым — иначе «тихая смерть» observability
При failure на main — LogQL alert → Slack; триггер для Self-Healing (обещан в Part 2).
Для соло-разработчика с агентами в CI/CD это практичный паттерн: агент может спросить «что сломалось на main с такого-то коммита» через LogQL, не дергая GitHub API на каждое расследование — если логи уже лежат в едином хранилище в structured JSON.
Что дальше: Part 2 и перенос в свой workflow
Part 1 закрывает write-side четырёх осей. Part 2 — «Reconciling PII Protection With AI Searchability, and Driving Self-Healing» — обещан через неделю после Part 1: PII, integration surface (web dashboards + MCP для AI), Self-Healing. Связка с code-graph series: статический граф vs динамика production; 46-repo codebase, semantic search для AI.
Статья подготовлена с помощью Claude; технический контент, решения и скриншоты — из production-систем cortex (внутреннее кодовое имя AI-платформы airCloset).
Перенос в типовой соло-workflow с Cursor и агентами — не прямая инструкция автора, а вывод из паттернов: разные формы observability под разные вопросы AI; wrapper с inline-метриками для pay-per-use моделей; structured ledger для внешних CLI вроде Claude Code; CI-логи в едином хранилище для ad-hoc запросов без API на каждое расследование. Начинать стоит с вопроса, который вы реально задаёте агенту чаще всего — и под него выбирать форму данных, а не наоборот.
Источники
- Ryan Tsuji — «Observability Design for the AI Era — Application / Infrastructure / CI / LLM, Each in Its Own Shape (Part 1)», Dev.to, 6 июля 2026: Dev.to