Langfuse перехватывает трейсы и накручивает счёт: как отключить | AiManual
AiManual Logo Ai / Manual.
12 Мар 2026 Гайд

Ловушка Langfuse: как SDK по умолчанию перехватывает чужие трейсы и накручивает счёт — инструкция по отключению

Подробное руководство по отключению скрытого перехвата всех трейсов в Langfuse SDK. Узнайте, как избежать неожиданных расходов и настроить фильтрацию.

Ваш счёт за Langfuse внезапно вырос в 10 раз? Теперь вы знаете, почему

Вы аккуратно настроили трейсинг для своего RAG-пайплайна. Запустили в прод. И через месяц получили счёт, где вместо ожидаемых $200 красуется $2000. Первая мысль — где-то ошибка в биллинге. Вторая — мы стали популярнее, чем думали. Третья, правильная, — вас обворовал собственный инструмент мониторинга.

Langfuse — отличная платформа для observability в AI-приложениях. Но её JavaScript и Python SDK (актуальные версии 3.8.1 и 3.5.2 на март 2026) содержат коварную настройку по умолчанию. После инициализации они начинают жадно слушать все трассировки OpenTelemetry в вашем приложении, включая те, что идут от сторонних библиотек, фреймворков и даже соседних микросервисов. Каждый такой спан — это деньги. Много денег.

Проблема не в баге, а в дизайне. Разработчики Langfuse считают такое поведение "удобным для onboarding", но для продакшена это финансовый сердечный приступ. Если у вас в одном процессе живёт, например, FastAPI-сервер с LangChain и отдельно клиент для базы данных — Langfuse соберёт трейсы отовсюду.

Как происходит перехват: техническая сторона катастрофы

При инициализации langfuse или langfuse-opentelemetry SDK автоматически регистрирует глобальные провайдеры трассировок OpenTelemetry. Вот код, который вы, скорее всего, написали, следуя официальной документации:

# ТИПИЧНАЯ ОПАСНАЯ КОНФИГУРАЦИЯ (НЕ ДЕЛАЙТЕ ТАК)
from langfuse import Langfuse
from langfuse.opentelemetry import LangfuseInstrumentor

# Инициализация клиента
langfuse = Langfuse(
    public_key="pk-lf-...",
    secret_key="sk-lf-...",
    host="https://cloud.langfuse.com"
)

# АКТИВАЦИЯ ПЕРЕХВАТА ВСЕГО
LangfuseInstrumentor().instrument()

# Дальше ваш код с вызовами LLM...

Метод .instrument() без аргументов — это приглашение к финансовому разорению. Он говорит: "Слушай все спаны в этом процессе и отправляй их все в Langfuse". Если в вашем проекте используется DeepEval для оценки, или какие-то фоновые задачи с собственным трейсингом — они тоже улетят в облако. И будут тарифицированы.

💡
Это напоминает историю с тихим OpenAI fallback в LlamaIndex, где локальный RAG мог незаметно стучаться в платный API. Тот же принцип: удобство разработчика оборачивается шоком от счёта.

Инструкция по обезвреживанию: три шага к контролю

Цель — заставить Langfuse слушать только те трейсы, которые вы явно ему поручили. Мы отключим автоинструментацию и будем вручную создавать спаны или точечно инструментировать нужные библиотеки.

1 Убираем глобальный перехват

Первым делом удалите или закомментируйте вызов LangfuseInstrumentor().instrument(). Если вам всё ещё нужна автоматическая инструментация для конкретных библиотек (например, только для OpenAI и LangChain), передайте явный список.

# БЕЗОПАСНАЯ КОНФИГУРАЦИЯ
from langfuse import Langfuse
from langfuse.opentelemetry import LangfuseInstrumentor

langfuse = Langfuse(
    public_key="pk-lf-...",
    secret_key="sk-lf-...",
    host="https://cloud.langfuse.com"
)

# ИНСТРУМЕНТИРУЕМ ТОЛЬКО ОПРЕДЕЛЁННЫЕ МОДУЛИ
instrumentor = LangfuseInstrumentor(
    instrumentations=[
        "openai",          # Только вызовы OpenAI API
        "langchain",       # Только цепочки LangChain
        "llama_index",     # Только индексы LlamaIndex
    ],
    # КРИТИЧЕСКИ ВАЖНЫЙ ФЛАГ
    skip_dep_check=True    # Игнорируем проверки зависимостей (актуально для версий >3.0)
)
instrumentor.instrument()

2 Переходим на ручное создание трейсов

Самый надёжный способ — вообще отказаться от автоматической инструментации OpenTelemetry и использовать нативный клиент Langfuse для создания трейсов. Это даёт полный контроль.

from langfuse import Langfuse

langfuse = Langfuse(...)

# Явное создание трейса
trace = langfuse.trace(
    name="user-query-processing",
    input={"question": user_question},
    metadata={"user_id": user_id}
)

# Создание спана внутри трейса
span = trace.span(
    name="llm-call",
    input={"prompt": formatted_prompt}
)

# ... ваш вызов LLM ...
span.end(output=llm_response)
trace.end()

Да, это требует больше кода. Но вы точно знаете, что платите только за эти явно созданные трейсы. Никаких сюрпризов. Это тот же принцип, что и при борьбе с раздуванием токенов — контроль над каждым элементом пайплайна.

3 Проверяем и аудитим

После внесения изменений необходимо убедиться, что лишние трейсы не утекают. Самый простой способ — использовать отладочный режим и локальный сервер Langfuse.

# Запускаем локальный Langfuse для тестирования
docker run -d -p 3000:3000 \
  -e LANGFUSE_SECRET_KEY="sk-lf-test" \
  -e LANGFUSE_PUBLIC_KEY="pk-lf-test" \
  langfuse/langfuse:latest

# В коде инициализируем клиента на локальный хост
langfuse = Langfuse(
    public_key="pk-lf-test",
    secret_key="sk-lf-test",
    host="http://localhost:3000"
)

Запустите ваше приложение, выполните несколько операций и откройте http://localhost:3000. Видите только ожидаемые трейсы? Отлично. Видите кучу спанов от PostgreSQL, Redis и фоновых воркеров? Возвращайтесь к шагу 1.

Где ещё спрятаны грабли: частые ошибки

Ошибка Последствие Как исправить
Инициализация SDK в глобальной области видимости модуля Инструментация применяется при импорте, перехватывает всё раньше, чем вы успеваете что-либо настроить. Инициализируйте Langfuse внутри функций или фабрик, после настройки окружения.
Использование instrument() в тестах или скриптах Тестовый прогон на 1000 запросов отправляет 1000 трейсов в продакшн-аккаунт. Всегда проверяйте LANGFUSE_ENABLED или аналогичный флаг. В тестах используйте заглушки (mocks).
Неявная зависимость от старой версии langfuse-opentelemetry Поведение могло измениться в новых версиях, но старый пакет всё ещё тянется как транзитивная зависимость. Явно зафиксируйте версии в requirements.txt: langfuse>=3.5.2, langfuse-opentelemetry>=1.2.0.

Вопросы, которые вы хотели задать

Это баг или фича?

Фича, но очень агрессивная. Разработчики Langfuse в документации на 2026 год мелким шрифтом упоминают, что instrument() без аргументов может привести к "неожиданному объёму данных". Они считают, что вы прочитаете это предупреждение прежде, чем скопируете код из quickstart. Мы все знаем, как это работает на практике.

Я использую несколько LLM-фреймворков одновременно. Как фильтровать?

Используйте параметр instrumentations, как показано выше. Если вашего фреймворка нет в списке поддерживаемых (например, какой-нибудь экзотический кастомный раннер для Llama), вам придётся вручную оборачивать его вызовы в спаны Langfuse.

Можно ли отфильтровать трейсы уже на стороне сервера Langfuse?

Нет. Фильтрация и выборка происходят на стороне SDK перед отправкой. Отправленные данные тарифицируются. Если вы отправили мусор — вы за него заплатили. Позже удалить его из базы можно, но деньги уже не вернуть.

Философия защищённого наблюдения

Инструменты observability в AI-стеке стали такими же критичными, как системы логирования десять лет назад. И так же опасны при неправильной настройке. История с Langfuse — частный случай общей проблемы: инструменты, стремящиеся к "zero-configuration", часто жертвуют безопасностью и предсказуемостью затрат.

Всегда задавайте себе вопросы: Что именно отслеживает этот SDK? Куда идут данные? По какому тарифу они тарифицируются? Такая же паранойя спасла многих от RCE в llama.cpp и от утечек в локальных сетапах VS Code.

Совет напоследок: раз в квартал проводите аудит исходящих подключений ваших AI-приложений. Запустите tcpdump или включите debug-логирование HTTP-клиента. Удивитесь, сколько "удобных" инструментов тихо стучатся домой или в облачные сервисы, пока вы думаете, что работаете полностью локально.

Ваш счёт за облачные сервисы должен быть предсказуемым. Не позволяйте инструментам делать его сюрпризом.

Подписаться на канал