LLM Observability с Langfuse: зачем нужно и как настроить
Что такое LLM observability?
LLM observability — это практика мониторинга, трейсинга и оценки вызовов языковых моделей в production по четырём направлениям: трейсинг запросов, контроль стоимости, версионирование промптов и оценка качества выходов. В отличие от традиционного APM, она учитывает недетерминированность ответов и стоимость каждого вызова — то, что стандартные метрики вроде HTTP-статусов не отражают.
TL;DR
- -Langfuse — MIT-лицензия, self-hosted без ограничений по трейсам, 22 000+ звёзд на GitHub, 23 млн установок SDK в месяц
- -Один неудачный промпт против хорошего может стоить сотни долларов в месяц при 1000 активных пользователях — трекинг затрат обязателен
- -Self-hosted разворачивается за 15 минут через Docker Compose; минимальные требования — Docker и 2 ГБ RAM
- -SDK v3 построен на OpenTelemetry — нативная совместимость с любой OTEL-инструментацией, без vendor lock-in
- -Покрывает все четыре столпа LLM observability: трейсинг, трекинг стоимости, управление промптами и оценку качества
LLM-вызовы в production нужно трейсить, промпты — версионировать, расходы — контролировать. Стандартные APM-инструменты здесь не помогут: ответы недетерминированы, каждый вызов стоит денег, а 200 OK ничего не говорит о качестве результата. Это отдельная категория observability.
Langfuse — open-source платформа (MIT) для LLM observability. Self-hosted без ограничений, 22 000+ звёзд на GitHub, 23 миллиона установок SDK в месяц. В этом руководстве — установка, трейсинг, prompt management, cost tracking и evaluations.
LLM Observability: четыре компонента
Если вы работали с APM (Application Performance Monitoring), LLM observability покажется знакомой. Но есть три важных отличия:
Недетерминизм. Один и тот же промпт при temperature > 0 даёт разные ответы. Нельзя просто сравнить expected vs actual — качество приходится оценивать статистически.
Стоимость за вызов. HTTP-запрос к собственному API стоит доли цента. Один вызов GPT-4o с контекстом на 10 000 токенов — 3-5 центов. При 1 000 пользователей в день разница между хорошим и плохим промптом — сотни долларов в месяц.
Качество вывода. 200 OK ничего не говорит о качестве ответа. Модель может вернуть грамматически правильный, но фактически бесполезный текст. Нужны метрики качества, а не только метрики доступности.
Четыре компонента LLM observability:
┌─────────────────────────────────────────────────────┐
│ LLM Observability │
├──────────┬──────────┬──────────────┬────────────────┤
│ Tracing │ Cost │ Prompt │ Evaluation │
│ │ Tracking │ Management │ │
├──────────┼──────────┼──────────────┼────────────────┤
│ Что │ Сколько │ Какой промпт │ Насколько │
│ произошло│ стоило │ в продакшене │ хороший ответ │
└──────────┴──────────┴──────────────┴────────────────┘
Langfuse покрывает все четыре. Можно начать с одного (обычно tracing) и добавлять остальные по мере роста.
Почему Langfuse
На рынке LLM observability пять-шесть платформ. Вот чем выделяется Langfuse:
Open-source (MIT). Код на GitHub, self-hosted версия без ограничений. Нет vendor lock-in — исходники и данные всегда у вас.
Self-hosted бесплатно. Никаких лимитов на количество трейсов. Для небольших и средних команд self-hosted на одной VM — дешевле любого SaaS.
Не привязан к фреймворку. LangSmith заточен под LangChain — с другими стеками работать сложнее. Langfuse работает с любым LLM-провайдером: OpenAI, Anthropic, open-source модели через LiteLLM, Vercel AI SDK.
Активно развивается. За последний год: SDK v3 на OpenTelemetry (нативная совместимость с любой OTEL-инструментацией), встроенный MCP-сервер (управление промптами прямо из IDE), observation-level evaluations, dataset versioning. Рост с 10 000 до 22 000 звёзд за 10 месяцев.
Сравнение (кратко):
| Langfuse | LangSmith | Helicone | |
|---|---|---|---|
| Open-source | MIT | Нет | Apache 2.0 |
| Self-hosted | Бесплатно, без лимитов | Enterprise only | Да |
| Framework lock-in | Нет | LangChain | OpenAI-first |
| Prompt management | Да + MCP | Да | Да (beta) |
| Free tier (cloud) | 50k obs/мес | 5k traces/мес | 100k req/мес |
Установка за 15 минут: self-hosted
Минимальные требования: Docker и 2 ГБ RAM. Langfuse упаковывает всё в один docker-compose.
# docker-compose.yml
services:
langfuse:
image: langfuse/langfuse:2
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://postgres:postgres@db:5432/langfuse
NEXTAUTH_SECRET: your-secret-key-change-me
SALT: your-salt-change-me
NEXTAUTH_URL: http://localhost:3000
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: langfuse
volumes:
- langfuse_data:/var/lib/postgresql/data
volumes:
langfuse_data:
docker compose up -d
Через минуту Langfuse доступен на localhost:3000. Создайте аккаунт, проект, скопируйте API-ключи.
Первые трейсы через Python SDK
pip install langfuse
from langfuse import Langfuse
langfuse = Langfuse(
public_key="pk-lf-...",
secret_key="sk-lf-...",
host="http://localhost:3000" # ваш self-hosted URL
)
# Создаём трейс
trace = langfuse.trace(name="chat-response", user_id="user-123")
# Логируем LLM-вызов
generation = trace.generation(
name="gpt-4o-response",
model="gpt-4o",
input=[{"role": "user", "content": "Порекомендуй кафе в Москве"}],
output="Вот несколько вариантов...",
usage={"input": 42, "output": 128},
)
langfuse.flush()
Откройте Langfuse UI — трейс появится в списке с данными о модели, токенах и latency.
Автоматическая инструментация через LiteLLM
Ручное логирование — для понимания механики. В продакшене лучше автоматизировать. Если вы используете LiteLLM как прокси к LLM-провайдерам, Langfuse подключается одной строкой:
import litellm
litellm.success_callback = ["langfuse"]
# Каждый вызов автоматически попадёт в Langfuse
response = litellm.completion(
model="gpt-4o",
messages=[{"role": "user", "content": "Привет"}],
metadata={
"trace_name": "chat-response",
"trace_user_id": "user-123",
"generation_name": "greeting",
"tags": ["production", "chat"],
}
)
LiteLLM сам отправит в Langfuse: модель, токены, стоимость, latency, input/output. Вы пишете обычный LLM-вызов, Langfuse заполняется автоматически.
Для OpenAI SDK есть drop-in replacement:
from langfuse.openai import openai
# Используйте как обычный OpenAI SDK
# Все вызовы автоматически трейсятся
client = openai.OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Привет"}],
)
Трейсинг: что внутри LLM-вызова
Трейс в Langfuse — это дерево операций. На верхнем уровне — trace (один запрос пользователя). Внутри — generations (LLM-вызовы) и spans (промежуточные шаги: retrieval, preprocessing, postprocessing).
Trace: "generate-itinerary"
│
├── Span: "validate-input" (12ms)
│
├── Generation: "analyze-request" (GPT-4o, 340 tokens, $0.003)
│
├── Span: "search-places" (Foursquare API, 800ms)
│
├── Generation: "build-itinerary" (GPT-4o, 2100 tokens, $0.018)
│
└── Generation: "validate-result" (GPT-4o-mini, 450 tokens, $0.001)
Что видно в UI для каждой generation:
- Input/Output — полный промпт и ответ
- Model — какая модель использовалась
- Tokens — input, output, total
- Cost — стоимость в долларах (автоматически по модели)
- Latency — время ответа
- Metadata — произвольные поля (user ID, feature, session)
Три вещи, которые трейсинг показывает сразу:
Паттерн 1: скрытые повторные вызовы. Retry-логика в коде может вызывать модель 2-3 раза на один запрос пользователя. Без трейсинга вы видите только финальный ответ. С трейсингом — каждый вызов со стоимостью.
Паттерн 2: model mismatch. Предполагается GPT-4o-mini для быстрых ответов, но один эндпоинт тянет GPT-4o. В трейсинге модель видна по каждому вызову — фильтр по полю model сразу покажет, где что-то не так.
Паттерн 3: latency bottleneck. В цепочке из четырёх LLM-вызовов один занимает 80% времени. Без spans вы знаете только общее время. С ними — точно видно, куда оптимизировать.
Prompt Management: версионирование без деплоя
Стандартный подход: промпт зашит в код. Изменение требует PR → code review → merge → deploy. A/B тест двух промптов — два деплоя.
Langfuse выносит промпты из кода: код загружает их по имени через API. Изменение промпта в UI применяется к следующему запросу без деплоя.
# Загрузка промпта из Langfuse
prompt = langfuse.get_prompt("travel-assistant")
# Компиляция с переменными
compiled = prompt.compile(
destination="Москва",
preferences="вегетарианская кухня"
)
# Использование в LLM-вызове
response = litellm.completion(
model="gpt-4o",
messages=compiled,
metadata={"langfuse_prompt_name": "travel-assistant"}
)
Каждое изменение промпта — новая версия. Langfuse хранит всю историю: кто, когда, что изменил. Можно откатить одним кликом.
Fallback-паттерн
Langfuse — внешний сервис. Он может быть недоступен. Промпт нужен всегда.
try:
prompt = langfuse.get_prompt("travel-assistant", label="production")
messages = prompt.compile(destination=city)
except Exception:
# Если Langfuse недоступен — fallback на хардкод
messages = [
{"role": "system", "content": FALLBACK_SYSTEM_PROMPT},
{"role": "user", "content": f"Порекомендуй кафе в {city}"},
]
Для production этот паттерн обязателен. Langfuse ускоряет итерацию над промптами, но не должен быть single point of failure.
MCP-сервер: промпты из IDE
С ноября 2025 Langfuse поддерживает MCP (Model Context Protocol). Подключив MCP-сервер к Claude Code, Cursor или другому AI-ассистенту, вы можете читать и редактировать промпты прямо из IDE.
{
"mcpServers": {
"langfuse": {
"type": "http",
"url": "https://your-langfuse.com/api/public/mcp",
"headers": {
"Authorization": "Basic base64(publicKey:secretKey)"
}
}
}
}
Зачем: вместо переключения между IDE и браузером, AI-ассистент видит текущие промпты, предлагает изменения и может применить их через MCP — всё в одном рабочем потоке.
Cost Tracking: сколько стоит каждая фича
Langfuse автоматически считает стоимость каждого LLM-вызова по модели и количеству токенов. Встроенная таблица pricing обновляется с каждым релизом (GPT-5.2, Claude Opus 4 — поддержка в день выхода).
В дашборде вы видите:
- Total cost за период
- Cost per trace — средняя стоимость одного запроса
- Cost per user — сколько тратит конкретный пользователь
- Cost per model — распределение расходов между моделями
Паттерн: per-feature cost tracking
Добавляя теги или metadata к трейсам, вы группируете расходы по фичам:
trace = langfuse.trace(
name="itinerary-generation",
tags=["feature:itinerary", "tier:premium"],
user_id="user-123",
)
Фильтр по тегу feature:itinerary в дашборде покажет, сколько стоит именно генерация маршрутов. Отдельно — чат, отдельно — рекомендации, отдельно — саммаризация.
Паттерн: синхронизация расходов в свою БД
Langfuse API позволяет программно получить стоимость:
# Получить все трейсы пользователя
traces = langfuse.fetch_traces(user_id="user-123")
# Суммировать стоимость
total_cost = sum(t.total_cost or 0 for t in traces.data)
# Записать в свою БД для лимитов, биллинга, аналитики
db.update_user_spending(user_id="user-123", amount=total_cost)
Это нужно, если у вас есть лимиты на LLM-расходы per user (freemium, credits) или вы хотите показывать пользователю его потребление.
Evaluations: качество ответов в числах
Трейсинг показывает, что произошло. Evaluations показывают, насколько хорошо.
LLM-as-a-Judge
Автоматическая оценка через LLM: одна модель оценивает ответы другой. Langfuse поддерживает это из коробки.
Настраивается в UI: выбираете evaluator template (relevance, helpfulness, toxicity), целевой набор трейсов — Langfuse прогоняет оценку по каждому. Результат — score от 0 до 1, привязанный к трейсу.
Observation-Level Evaluations (новинка 2026)
Раньше оценка привязывалась только к trace целиком. Но в цепочке из четырёх LLM-вызовов проблема может быть в одном конкретном шаге. С февраля 2026 можно оценивать каждый observation (generation, span) отдельно.
Пример: в pipeline “analyze → search → generate → validate” evaluator для generate проверяет фактическую корректность, а для validate — соответствие формату. Разные evaluators для разных шагов.
Datasets: регрессионное тестирование промптов
Dataset в Langfuse — набор пар input/expected_output. Изменили промпт — прогоняете dataset, сравниваете scores с предыдущей версией. Если качество упало — откатываете.
С декабря 2025 поддерживается версионирование datasets: каждое изменение (добавление, удаление, обновление item) создаёт новую версию. Можно запустить эксперимент на конкретной исторической версии для воспроизводимости.
# Создаём или обновляем dataset
langfuse.create_dataset(name="travel-queries")
langfuse.create_dataset_item(
dataset_name="travel-queries",
input={"query": "Кафе в центре Москвы"},
expected_output="Список из 5+ кафе с адресами",
)
# Запуск эксперимента
dataset = langfuse.get_dataset("travel-queries")
for item in dataset.items:
response = run_my_pipeline(item.input)
item.link(
trace_id=response.trace_id,
run_name="prompt-v3-test",
)
В UI Langfuse вы увидите сравнение run’ов: prompt v2 vs v3, с scores по каждому item.
Production Checklist
Self-hosted vs Cloud
| Критерий | Self-hosted | Cloud |
|---|---|---|
| Стоимость | Только инфраструктура (~$10-20/мес VPS) | От $59/мес (Pro) |
| Данные | Остаются у вас | На серверах Langfuse |
| Обслуживание | Вы обновляете, бекапите, мониторите | Всё включено |
| Лимиты | Нет | 50k obs/мес (бесплатно), далее по тарифу |
| Подходит для | Команды с DevOps, compliance-требования | Быстрый старт, маленькие команды |
Для большинства production-проектов self-hosted выгоднее: $15/мес за VPS vs $59+/мес за cloud. Но self-hosted = ваша ответственность за uptime и бекапы.
Мониторинг самого Langfuse
Langfuse — внешняя зависимость для ваших LLM-вызовов (prompt management). Если он падает — промпты не грузятся. Два паттерна:
- Health check.
/api/public/healthвозвращает статус. Добавьте в мониторинг (Zabbix, Uptime Robot, Grafana). - Fallback промпты. Каждый
get_prompt()обёрнут в try/catch с fallback на хардкод. Langfuse может лежать — приложение продолжает работать.
Retention и cleanup
Трейсы занимают место в PostgreSQL. 1 000 трейсов в день — ~1 ГБ в месяц (зависит от размера промптов и ответов). Настройте автоматическое удаление старых трейсов:
-- Удалять трейсы старше 90 дней
DELETE FROM traces WHERE created_at < NOW() - INTERVAL '90 days';
Или используйте встроенные настройки retention в Langfuse Cloud.
Минимальный production-сетап
┌─────────────────────────────────────────┐
│ Ваше приложение │
│ │
│ LLM-вызов → LiteLLM → OpenAI/Anthropic │
│ │ │
│ │ metadata (trace_name, │
│ │ user_id, tags) │
│ ▼ │
│ LiteLLM callback → Langfuse │
└─────────────────────────────────────────┘
│
▼
┌──────────────────┐
│ Langfuse (Docker) │
│ │
│ • Трейсы │ ← автоматически
│ • Стоимость │ ← автоматически
│ • Промпты │ ← управление в UI
│ • Evaluations │ ← настройка в UI
└──────────────────┘
Промпты загружаются из Langfuse при каждом вызове (с кешем). Трейсы отправляются асинхронно — не блокируют основной поток. Стоимость считается автоматически по модели и токенам.
Итог
Langfuse закрывает четыре задачи одним инструментом: трейсинг, cost tracking, prompt management и evaluations. Self-hosted версия бесплатна и разворачивается за 15 минут. Интеграция через LiteLLM или OpenAI drop-in — одна строка кода.
Минимальный путь: подключить трейсинг к одному endpoint, посмотреть данные за неделю. Этого достаточно, чтобы оценить полезность инструмента для конкретного проекта.
Ссылки:
- Langfuse GitHub — исходный код и self-hosted установка
- Langfuse Docs — документация
- LiteLLM + Langfuse — интеграция
- Langfuse Changelog — все обновления
FAQ
Какой реальный объём хранилища занимают трейсы в PostgreSQL при долгосрочном хранении?
Один трейс с промптом на 1 000 токенов и ответом на 500 токенов занимает примерно 8–12 КБ в PostgreSQL с учётом индексов. При 1 000 трейсов в день это около 300–400 МБ в месяц. Официальная рекомендация по retention — 90 дней, устойчивое состояние хранилища около 1–1.5 ГБ — без проблем помещается на VPS за $15/мес. При больших контекстах (RAG с длинными документами) умножайте на 3–5x и планируйте заранее.
Поддерживает ли Langfuse мультитенантные сценарии с изолированными данными разных команд?
Да. Langfuse использует иерархию Organizations → Projects. У каждого проекта свои API-ключи, пространство имён промптов и датасетов. Один self-hosted инстанс обслуживает несколько команд с полной изоляцией данных на уровне проекта. RBAC доступен в облачном тарифе Pro и в Enterprise-версии self-hosted.
Как параметр cache_ttl_seconds взаимодействует с провайдерским кешированием промптов?
cache_ttl_seconds управляет клиентским кешированием объекта промпта в памяти — он предотвращает лишние запросы к серверу Langfuse при каждом LLM-вызове. Это не влияет на провайдерское кеширование промптов (Anthropic или OpenAI). Два механизма независимы: можно иметь TTL кеша Langfuse 5 минут и при этом использовать кеширование Anthropic для самого LLM-вызова, если приложение передаёт нужные cache-control заголовки дальше по цепочке.