МАТЕРИАЛЫ КУРСА
СИИ • НАВИГАЦИЯ

Langfuse: трейсы LLM-вызовов

Зачем отдельный инструмент для LLM, self-hosting в compose, интеграция SDK, что смотреть и как связать с evals

Langfuse: трейсы LLM-вызовов

Обычный трейс говорит: «вызов модели занял 4.2 секунды, HTTP 200». Этого достаточно для инфраструктуры и бесполезно для качества — потому что главные вопросы AI-продукта звучат иначе: какой промпт ушёл, что вернулось, сколько стоило, стало ли хуже после вчерашней правки промпта.

Langfuse — открытая (MIT) платформа, которая закрывает именно это. В курсе она стоит в контуре с лабы 3 и работает на роль AI Engineer и роль Quality одновременно.

Что даёт

  • Трейс каждого вызова: промпт целиком, ответ, модель, параметры, токены на вход и выход, стоимость, задержка.
  • Вложенность. Цепочка «ретривер → промпт → модель → парсер» видна деревом: понятно, какой шаг сожрал время и на каком испортились данные.
  • Версии промптов. Промпт хранится и версионируется в Langfuse, приложение тянет нужную версию — правка промпта перестаёт быть деплоем кода, а изменения качества привязываются к версии.
  • Датасеты и эксперименты. Реальные трейсы отправляются в датасет, на нём прогоняются прогоны при смене промпта или модели — сравнение «было/стало» на одних и тех же входах.
  • Оценки. Оценка человеком, кодом или моделью-судьёй пишется прямо на трейс, и её видно в общей статистике.

Поднять у себя

Langfuse ставится в тот же compose-контур, что и продукт. Учти, что это не один контейнер: web, воркер, PostgreSQL для метаданных, ClickHouse для аналитики трейсов, Redis для очереди и S3-совместимое хранилище для сырых событий.

git clone https://github.com/langfuse/langfuse.git
cd langfuse
docker compose up -d      # UI на http://localhost:3000

Дальше в UI создаётся проект и пара ключей (LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY) — они уезжают в .env продукта, как любые другие секреты. На слабой машине честнее взять облачный бесплатный тариф: шесть контейнеров рядом с вашим стеком заметно греют ноутбук.

Подключить к коду

Минимальный вариант — декоратор поверх функции и обёртка над клиентом модели:

from langfuse import observe, get_client

langfuse = get_client()

@observe()
def answer_question(question: str, trace_id: str) -> str:
    langfuse.update_current_trace(
        session_id=trace_id,          # тот же id, что в логах и метриках
        tags=["uc-02", "questions"],  # какой use case обслуживаем
    )
    ...

Две строчки, которые превращают инструмент из красивого в полезный:

  1. Общий идентификатор с логами и метриками — иначе Langfuse живёт отдельной жизнью и три столпа не сходятся.
  2. Тег use case на трейсе. Тогда качество считается не «в среднем по продукту», а по каждому UC отдельно — и сразу видно, какой сценарий тянет статистику вниз.

Что смотреть каждую неделю

  • Стоимость на пользователя и на UC — растёт ли, и за счёт чего: длины промпта, числа ретраев, выбранной модели.
  • P95 задержки вызова модели — отдельно от задержки API.
  • Доля вызовов с ошибкой формата — сколько раз ответ не прошёл валидацию структуры.
  • Оценки по свежим трейсам — берётся выборка реального трафика и прогоняется через evals; расхождение с результатами на golden-наборе означает, что набор устарел.

Связь с лабами прямая: golden dataset из лабы 2 живёт в CI, а Langfuse показывает, что происходит на настоящем трафике. Evals ловят регрессию до деплоя, Langfuse — деградацию после. Нужны оба: набор из 30–50 примеров никогда не покроет того, что придумают живые пользователи.