Трасса LLM-запроса обрывается после отправки задачи в очередь

API принимает запрос на длинный LLM-отчёт и ставит задачу в очередь. В интерфейсе трассировки виден быстрый HTTP-ответ, а отдельный worker показывает дорогой вызов модели без связи с исходным пользователем. При ошибке приходится искать по времени и job ID. Причина обычно на границе очереди: контекст трассировки живёт внутри процесса API и не переходит в новое сообщение сам собой.

Передайте контекст вместе с задачей

При публикации сообщения OpenTelemetry propagator сериализует текущий контекст в метаданные сообщения. В стандартном W3C Trace Context это поле traceparent; при необходимости передают tracestate. Worker извлекает этот контекст из metadata до начала обработки и создаёт span обработки, связанный с контекстом создания сообщения. Дальше инструментированный вызов LLM становится частью наблюдаемой цепочки. Не копируйте только текстовый trace ID в новый span: так в логах появится похожий номер, но корректной связи между span не будет.

Для очередей есть нюанс: одна задача может быть получена повторно, а batch может включать несколько сообщений. Семантические соглашения OpenTelemetry рекомендуют передавать контекст создания на каждом сообщении и связывать consumer span с ним. В зависимости от инструмента это может быть parent либо span link; насильно делать один HTTP-span родителем всех повторных обработок не всегда отражает реальную структуру. Обязательно добавьте независимый job_id как атрибут задачи, чтобы поиск работал даже при отдельной трассе или sampling.

Проверьте путь из трёх участков

Отправьте один тестовый запрос: API создаёт job rpt-17, очередь передаёт сообщение, worker вызывает модель. В трассировке найдите span HTTP-приёма, публикации сообщения, обработки worker и LLM-вызова. У worker должна быть связь с контекстом сообщения; у LLM-вызова — с обработкой worker. Затем принудительно повторите доставку: два worker-attempt должны быть различимы по span и попытке, хотя job_id один. Отдельно проверьте сообщение без trace context: worker создаёт новую трассу, но не падает и всё равно пишет job ID.

Не помещайте в baggage промпт, токен API или текст ответа. Контекст трассировки — перенос идентификаторов, а не место для чувствительных данных. Если очередь или прокси меняет headers, проверьте, что traceparent действительно сохраняется и что входные метаданные не позволяют внешнему пользователю навязать произвольную доверенную трассу без принятой политики.

У async-запуска бывает ещё одна ловушка: HTTP-ответ «задача принята» завершается до работы worker. Поэтому длительность исходного HTTP-span не равна времени готовности отчёта. Для пользовательской задержки отдельно измеряйте интервал от принятия job до состояния done, а трассу используйте, чтобы разложить его на ожидание в очереди и выполнение. Если очередь задерживает сообщение на десять минут, один красивый span LLM-вызова на три секунды не объяснит жалобу пользователя.

В платном курсе LLMOps & Evaluation: из песочницы в продакшн вы отслеживаете качество, задержку и стоимость LLM-сервиса после релиза. Связанная трасса позволяет найти, какая часть задержки пришлась на очередь, worker и вызов модели. Откройте программу курса, чтобы соединить наблюдаемость с quality-гейтами и эксплуатационным runbook.

Продолжить на практике

  • LLMOps & Evaluation: из песочницы в продакшн ↗

    Практический курс по LLMOps. Соберёте продакшн-контур для LLM: eval-фреймворк (LangSmith/Ragas/OpenAI Evals), quality-гейты в CI/CD, алёрты на дрейф, монитринг скорости и стоимости, управление промпт-версиями и безопасность. Плюс профессиональные блоки: synthetic data для тестов, LLM-based judges, отчётность для релиз-комитета и runbook’и инцидентов.