Суть
Трассировка внутри одного процесса — задача несложная. Проблема начинается на границах: пользовательский запрос идёт через UI, HTTP-слой, оркестратор агента, ретривер, иногда наружу к чужому сервису. Каждый участок пишет свои логи, и без общего ключа они не склеиваются.
W3C Trace Context решает это одним HTTP-заголовком traceparent, который каждый участник цепочки обязан принять, использовать и передать дальше. Это стандарт консорциума W3C — он не привязан ни к вендору наблюдаемости, ни к языку.
Как работает
Заголовок состоит из четырёх полей через дефис:
00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
│ │ │ │
│ Trace ID — един для всей цепочки │ Trace Flags
Версия parent-id (сэмплирование)
- Trace ID не меняется от первого до последнего узла — по нему собирается вся история.
- parent-id (так поле названо в спецификации; в инструментах его обычно показывают как Parent Span ID) указывает на вызывающий узел, из этого строится дерево.
- Trace Flags говорят, сэмплировать этот трейс или пропустить. Флаг
sampled— рекомендация вызывающей стороны, а не приказ: принимающая может решить иначе.
Рядом со стандартом живёт заголовок tracestate — для вендор-специфичных данных, которые не помещаются в жёсткий формат traceparent.
Принимающая сторона извлекает контекст из заголовка и восстанавливает его для всех дочерних задач, включая асинхронные:
carrier = {"traceparent": request.headers.get("traceparent", "")}
context = TraceContextTextMapPropagator().extract(carrier=carrier)
После восстановления контекста каждый порождённый спан автоматически привязывается к общему дереву — даже если исполняется в другом потоке или корутине.
Зачем это агенту
У агента шаги не выстроены в линию: один запрос порождает ветвление на планирование, вызовы инструментов, обращения к ретриверу, иногда — делегирование чужому агенту. Сквозной Trace ID даёт три вещи, которых иначе нет:
- Поиск по инциденту. Пользователь жалуется на ответ — по одному идентификатору поднимается вся траектория, а не разрозненные записи.
- Атрибуция стоимости. Токены и латентность каждого узла суммируются по трейсу, и цену конкретного взаимодействия наконец можно посчитать.
- Локализация сбоя. Видно не «система вернула ошибку», а на каком именно узле ветка ушла не туда.
В событийно-ориентированных архитектурах именно это становится главной болью: протащить Trace ID через шину сообщений сложнее, чем через синхронный HTTP-вызов, и без дисциплины контекст теряется (см. Event Driven Agents).
Связано с
- OpenInference — что именно писать внутрь спанов, когда контекст уже проброшен
- Agent Observability — трейсинг шагов как основа наблюдаемости агента
- AgentOps — стандарт как обязательный пункт продакшен-чеклиста
- Agent Failure Modes — без сквозного трейса часть режимов отказа неотличима друг от друга
- Event Driven Agents — где проброс контекста ломается чаще всего
Открытые вопросы
- как надёжно пробрасывать
traceparentчерез Kafka или RabbitMQ, чтобы не терять контекст на асинхронных переходах - какую долю трейсов сэмплировать в проде, чтобы не утонуть в объёме, но поймать редкие сбои