English translation is not available yet. Showing Russian content.
Интегрировать OpenTelemetry в агента
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Интегрировать OpenTelemetry в агента
1. Цель задачи
Научиться инструментировать feedback|агентный цикл (thought → action → observation) с помощью OpenTelemetry для сквозной трассировки. После выполнения вы обеспечите полную наблюдаемость каждого шага агента в Jaeger UI, включая корректные parent-child связи и атрибуты (время мысли, вызванный инструмент, результат наблюдения). Ключевой результат В Jaeger виден полный граф трассировки одного запроса, где каждый этап (thought, action, observation) представлен отдельным спаном с корректными временными метками и связями.
2. Исходные данные
Перед началом необходимо иметь:
| Что нужно | Откуда взять |
|---|---|
| Агентный код (Python) на базе LangChain / llama-index / собственный цикл | Репозиторий pet-проекта (или пример из курса) |
| Docker (или podman) для запуска Jaeger | docker pull jaegertracing/all-in-one |
| Python 3.10+ с установленным pip | Локальное окружение |
| Доступ к сети для загрузки библиотек | pip install opentelemetry-* |
Если нет реального агента — симулируем:
- Склонируйте репозиторий-шаблон с простым агентом на LangChain (один инструмент search и один
calculator). - Убедитесь, что агент запускается и выполняет цепочку из 2–3 шагов на тестовом запросе («Сколько будет 2+2, а затем умножь на 3»).
- Подготовьте Python-виртуальное окружение и активируйте его.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Трассировка | OpenTelemetry Python SDK (opentelemetry-api, opentelemetry-sdk, opentelemetry-exporter-otlp) | Создание и экспорт спанов |
| Бэкенд трейсов | Jaeger (all-in-one) через OTLP gRPC | Приём и визуализация трейсов |
| Агентный фреймворк | LangChain / llama-index (или собственный) | Цикл thought-action-observation |
| Мониторинг | Docker Compose (опционально) | Управление Jaeger |
| Отладка | Jaeger UI (http://localhost:16686) | Просмотр графа трассировки |
4. Этапы выполнения
Этап 1: Запуск Jaeger и подключение OTLP-экспортера (30 минут)
Действия
-
Запустите Jaeger all-in-one в Docker
docker run -d --name jaeger \ -p 16686:16686 \ -p 4317:4317 \ -p 4318:4318 \ jaegertracing/all-in-one:latest -
Установите необходимые пакеты OpenTelemetry
pip install opentelemetry-api \ opentelemetry-sdk \ opentelemetry-exporter-otlp \ opentelemetry-instrumentation -
Создайте файл
tracing_setup.pyс конфигурацией трассировщика: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 def setup_tracer(service_name: str = "agent-service"): provider = TracerProvider() exporter = OTLPSpanExporter(endpoint="localhost:4317", insecure=True) processor = BatchSpanProcessor(exporter) provider.add_span_processor(processor) trace.set_tracer_provider(provider) return trace.get_tracer(__name__) -
Добавьте вызов
setup_tracer()в точку входа агента (например, вmain()или agent.run()).
Ожидаемый результат этапа Jaeger UI доступен по адресу http://localhost:16686, а при запуске агента в консоли не возникает ошибок подключения к OTLP.
Этап 2: Инструментирование цикла агента (1 час)
Действия
-
Определите структуру трейса
- Один span|root span на весь запрос (например,
agent.run). - Внутри root span: последовательные child span'ы для каждого шага:
- thought — генерация мысли;
- action — вызов инструмента (с атрибутами: имя инструмента, аргументы);
- observation — обработка результата инструмента.
- Один span|root span на весь запрос (например,
-
Оберните агентный цикл в root span:
tracer = setup_tracer() with tracer.start_as_current_span("agent.run") as root_span: while not done: step_result = agent.step() # ваш существующий цикл -
Внутри каждого шага создавайте child span:
# Пример для thought with tracer.start_as_current_span("thought", context=trace.get_current_span().get_span_context()) as thought_span: thought_span.set_attribute("thought.text", thought_output) # ... выполнение кода мыслиАналогично для action и observation.
-
Для action добавьте атрибуты
-
Для observation
- observation.result (строка)
-
Убедитесь, что контекст трассировки передаётся (используйте trace.get_current_span().get_span_context() при создании дочерних спанов).
Ожидаемый результат этапа Код агента модифицирован так, что каждый этап цикла порождает отдельный child span. Приложение не падает с ошибками OpenTelemetry.
Этап 3: Проверка экспорта и визуализации в Jaeger (30 минут)
Действия
- Запустите агента с тестовым запросом (например, «Сколько будет 20/4, умножь на 5»).
- Откройте Jaeger UI http://localhost:16686.
- В выпадающем списке Service выберите
agent-service. - Нажмите «Find Traces» — должен появиться один трейс (или несколько, если запускали многократно).
- Кликните на трейс Проверьте:
- Сделайте скриншот полного графа для отчёта.
Ожидаемый результат этапа В Jaeger UI виден полный граф трассировки с parent-child связями.
Этап 4: Обработка ошибок и дополнительных атрибутов (30 минут)
Действия
- Добавьте обработку исключений в spans: при возникновении ошибки установите span.set_status(Status(StatusCode.ERROR)) и запишите
exceptionв атрибуты. - Добавьте атрибут
error.messageпри сбое инструмента. - Проверьте сценарий с ошибкой (например, попросите агента вызвать несуществующий инструмент) — убедитесь, что в Jaeger отображается красный статус у спана action.
- Добавьте атрибут agent.user_query в root span (исходный запрос пользователя).
- Экспериментально попробуйте увеличить/уменьшить количество шагов (2-3 шага), чтобы убедиться, что все они корректно трассируются.
Ожидаемый результат этапа Трейсы корректно отображают ошибки, а root span содержит дополнительную информацию о запросе.
Этап 5: Документирование и рефакторинг (20 минут)
Действия
- Вынесите конфигурацию трассировки в отдельный модуль (используйте
tracing_setup.py). - Добавьте комментарии в код с пояснением, почему используется
trace.get_current_span(). - Напишите README с инструкциями:
- как запустить Jaeger;
- как включить/выключить трассировку через переменную окружения (например,
ENABLE_TRACING=True); - пример скриншота графа.
- Убедитесь, что код не содержит захардкоженных endpoint'ов — вынесите в переменные окружения
OTEL_EXPORTER_OTLP_ENDPOINT.
Ожидаемый результат этапа Чистый, документированный код, готовый к интеграции в production.
5. Критерии приемки (Definition of Done)
- Jaeger all-in-one запущен и UI отвечает на
localhost:16686. - При выполнении любого запроса к агенту в Jaeger появляется трейс с root span
agent.run. - Каждый этап цикла (thought, action, observation) представлен отдельным дочерним span с корректным родителем.
- Spans содержат атрибуты:
thought.text,action.tool_name,action.input,observation.result. - В случае ошибки у соответствующего span установлен статус ERROR и атрибут
error.message. - Трассировка не вызывает видимых slowdown (latency агента не увеличивается более чем на 10%).
- Конфигурация вынесена в переменные окружения (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME).
- README содержит инструкцию по запуску и скриншот графа из Jaeger.
6. Ожидаемый результат
Основной артефакт Модифицированный репозиторий агента с интегрированной OpenTelemetry трассировкой.
Содержимое
tracing_setup.py— инициализация tracer provider и экспортера.- Обновлённый файл агента (например,
agent.py) с вызовамиstart_as_current_span. docker-compose.yml(опционально) для запуска Jaeger.README.mdс документацией.
Дополнительно Скриншот Jaeger UI с полным графом трассировки по заданному запросу.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Jaeger не стартует из-за занятых портов | Смените порты при запуске (-p 16687:16686 и т.д.), обновите endpoint в коде |
| OTLP экспорт не работает (gRPC refused) | Убедитесь, что Jaeger слушает 4317. Проверьте docker logs jaeger. Используйте HTTP экспорт (порт 4318) как fallback |
| Spans в Jaeger отображаются без parent-child (все на одном уровне) | Проверьте, что контекст передаётся через trace.get_current_span().get_span_context() при создании child span |
| Атрибуты не отображаются | Убедитесь, что значения атрибутов — строки или числа; не используйте сложные объекты |
| Резкое замедление работы агента | Уменьшите частоту экспорта (BatchSpanProcessor параметр max_export_batch_size или переключитесь на SimpleSpanProcessor для отладки) |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Запуск Jaeger и подключение OTLP | 30 мин |
| Этап 2: Инструментирование цикла агента | 1 ч |
| Этап 3: Проверка экспорта и визуализации | 30 мин |
| Этап 4: Обработка ошибок и доп. атрибуты | 30 мин |
| Этап 5: Документирование и рефакторинг | 20 мин |
| Итого | ~3 ч |
Примечание Для первого раза может потребоваться до 4 часов, если потребуется отладка OTLP-подключения или исправление ошибок контекста.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 101 | Настройка OpenTelemetry Python |
| 102 | Создание пользовательских спанов |
| 103 | Текущая задача |
| 107 | Экспорт трейсов в Jaeger |
| 112 | Атрибуты спанов и лучшие практики |
| 145 | Обработка ошибок в трейсах |
| 201 | Инструментирование LangChain агентов |
| 303 | Работа с Docker для инструментов наблюдаемости |
| 410 | Построение графа зависимостей в Jaeger |
| 502 | Переменные окружения для OpenTelemetry |
10. Чек-лист самопроверки
- Я запустил Jaeger через Docker и проверил доступность UI.
- Я установил все необходимые opentelemetry-пакеты в виртуальное окружение.
- Я модифицировал агентный цикл так, что каждый шаг (thought/action/observation) создаёт дочерний span.
- Я протестировал сценарий с ошибкой и убедился, что в jaeger отображается ERROR статус.
- Я зафиксировал скриншот полного графа и добавил его в README.
- Я вынес настройки (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME) в переменные окружения.