Трейсинг¶
Eval AI Library содержит легковесную подсистему трейсинга для продакшн-мониторинга LLM-вызовов и работы агентов. Она собирает span-данные (время, токены, стоимость, вход/выход, вызовы инструментов, шаги рассуждений) и асинхронно отправляет их в коллектор. Десять фреймворк-интеграций подключаются к трейсеру без клеевого кода.
Легковесная установка¶
Для трейсинга в продакшне без полного фреймворка оценки:
Lite-экстра тянет только pydantic и aiohttp, без ML-зависимостей.
Конфигурация¶
TracingConfig читает три env-переменные для базовой установки и несколько опциональных для тонкой настройки:
| Env-переменная | По умолчанию | Описание |
|---|---|---|
TRACING_ENABLED | false | Главный переключатель. Установите true для включения трейсинга |
TRACING_URL | "" | HTTP-эндпоинт вашего trace-receiver-а |
TRACING_PROJECT | default | ID проекта для разделения трейсов на receiver-е |
TRACING_API_KEY | — | Опциональный Bearer-токен для receiver-а |
TRACING_SINK | http | Дефолтный sink -- http, memory или file |
TRACING_SINK_PATH | traces.jsonl | Путь при TRACING_SINK=file |
TRACING_STRICT | false | При true ошибки отправки бросают исключение вместо лога (для CI) |
TRACING_STREAM | false | При true каждый end_span() сразу флашит span как partial_span -- долгие сессии выживают падение |
from eval_lib.tracing import TracingConfig
TracingConfig.is_enabled() # bool
TracingConfig.get_url() # str
TracingConfig.get_project() # str
Все методы TracingConfig статические -- просто читают окружение. Никакого конструктора вызывать не нужно.
Типы спанов¶
SpanType | Значение |
|---|---|
LLM_CALL | Вызов LLM API (сообщения, ответ, токены, стоимость) |
TOOL_CALL | Вызов инструмента / функции |
AGENT_STEP | Шаг рассуждений / планирования агента |
REASONING | Шаг chain-of-thought |
RETRIEVAL | Поиск документов / векторов |
EVALUATION | Запуск метрики оценки |
CUSTOM | Всё остальное |
Основной трейсер¶
Синглтон tracer собирает спаны и рассылает их через sink:
from eval_lib.tracing import tracer, SpanType
# Явный жизненный цикл спана
trace_id = tracer.start_trace("my-pipeline")
span = tracer.start_span("generate-answer", SpanType.LLM_CALL, input_data={"prompt": "..."})
# ... ваш LLM-вызов ...
tracer.end_span(span, output="Ответ: 42")
tracer.end_trace()
# Или через контекст-менеджер
with tracer.trace("retrieval", SpanType.RETRIEVAL) as span:
results = retrieve_documents(query)
Спаны вкладываются автоматически через context-variables -- ручного проброса parent_span_id не требуется.
Декораторы¶
Автоматическая инструментация функций для написанных вручную пайплайнов:
from eval_lib.tracing import trace_llm, trace_tool, trace_step
@trace_llm()
async def call_openai(prompt: str) -> str:
# Записывает вход, выход, длительность, стоимость как LLM_CALL span
...
@trace_tool()
async def search_database(query: str) -> list:
# Записывает как TOOL_CALL span
...
@trace_step()
async def process_request(request):
# Записывает как AGENT_STEP span
...
Фреймворк-интеграции¶
Все callback-и lazy-loaded -- импорт из eval_lib.tracing требует SDK соответствующего фреймворка только тогда, когда вы реально им пользуетесь. Установите нужную экстра рядом с eval-ai-library[lite].
LangChain¶
from eval_lib.tracing import EvalLibCallbackHandler, callback_handler
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(callbacks=[callback_handler]) # глобальный экземпляр
# или: llm = ChatOpenAI(callbacks=[EvalLibCallbackHandler()])
LlamaIndex¶
from eval_lib.tracing import install_llamaindex_tracing
install_llamaindex_tracing() # патчит Settings.callback_manager глобально
Низкоуровневые классы: EvalLibEventHandler, EvalLibSpanHandler.
CrewAI¶
from eval_lib.tracing import CrewAITraceCollector
collector = CrewAITraceCollector()
crew.step_callback = collector
AutoGen¶
from eval_lib.tracing import AutoGenTraceHandler
handler = AutoGenTraceHandler()
# подключить согласно runtime-API AutoGen
Haystack¶
Низкоуровневый класс: EvalLibHaystackTracer.
Semantic Kernel¶
Claude Agent SDK¶
from eval_lib.tracing import ClaudeAgentTraceCollector
collector = ClaudeAgentTraceCollector()
# передайте collector.on_event в message-stream SDK
smolagents¶
from eval_lib.tracing import smolagents_step_callback
agent = CodeAgent(..., step_callbacks=[smolagents_step_callback])
phidata¶
OpenAI Assistants¶
from eval_lib.tracing import OpenAIAssistantsTraceCollector
collector = OpenAIAssistantsTraceCollector()
# передайте run-events ассистента
OpenTelemetry¶
Отправляйте спаны eval-lib через OTel (Jaeger, Tempo, DataDog, любой OTel-совместимый бэкенд):
from eval_lib.tracing import EvalLibSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(EvalLibSpanExporter()))
Sinks¶
TraceSender принимает любую реализацию Sink. Встроено три:
HTTPSink -- дефолтный¶
Отправляет батчи трейсов на TRACING_URL по HTTP (Bearer-auth через TRACING_API_KEY).
from eval_lib.tracing import TraceSender, HTTPSink
sender = TraceSender(sink=HTTPSink(url="https://collector.example.com", api_key="..."))
FileSink -- JSONL append¶
Удобен для локальной отладки или air-gapped развёртываний.
from eval_lib.tracing import FileSink, TraceSender
sender = TraceSender(sink=FileSink(path="traces.jsonl"))
Или через env: TRACING_SINK=file TRACING_SINK_PATH=./out/traces.jsonl.
InMemorySink -- для тестов¶
Буферизует всё в памяти; удобно в unit-тестах.
from eval_lib.tracing import InMemorySink, TraceSender
sink = InMemorySink()
sender = TraceSender(sink=sink)
# ... запуск пайплайна ...
assert len(sink.spans) == 3
Receiver-сторона¶
Приёмная сторона живёт в eval_lib.connector -- trace_routes (FastAPI-роуты) и trace_receiver (storage + runtime-eval loop). Монтируйте их за собственной аутентификацией / БД. См. tracing-hardening-tz.md в репозитории для контракта pluggable-storage.
Модель данных¶
Спаны -- это dataclass-ы в eval_lib.tracing.types:
TraceSpan содержит: span_id, trace_id, parent_span_id, span_type, name, start_time, end_time, input_data, output_data, metadata, status, плюс LLM-специфичные поля (tokens, cost, model). Все поля чисто сериализуются в JSON.