За прошлый год я запустил 5 сервисов с LLM под капотом. Каждый следующий сервис получался лучше предыдущего: мы оттачивали архитектуру, оптимизировали core микросервиса на FastAPI, быстрее выходили на MVP и ловили меньше багов.
Но довольно быстро стало понятно: LLM‑сервисы сложно интерпретировать. Для бизнес команды они выглядят как black box. Для инженеров — как набор плохо воспроизводимых состояний.
В этой статье я поделюсь практиками, которые:
упрощают интерпретацию поведения LLM;
делают работу сервиса прозрачной для Product Owners и SME;
ускоряют разработку и итерации без передеплоев.
Речь пойдёт про Langfuse — open‑source платформу для трейсинга, тестирования, отладки и версионирования промптов.
Почему именно Langfuse
Решение внедрять Langfuse в компанию было принято после анализа альтернатив: LangSmith, Arize Phoenix, Helicone и других.
Ключевые причины:
Полный контроль над данными (Self‑hosting и отсутствие жёсткой привязки к экосистеме LangChain);
Готовность к продакшену (Управление промптами, детальный cost tracking, нормальная работа с версиями);
Глубокий трейсинг (Поддержка сложных цепочек, агентов и workflow, а не просто логирование API‑вызовов).
Базовая инициализация Langfuse
Предположим, что базовые шаги вы уже сделали:
платформа развернута локально или используется Langfuse Cloud;
создан проект;
выпущены ключи доступа.
Для каждого проекта Langfuse предоставляет:
LANGFUSE_SECRET_KEY
LANGFUSE_PUBLIC_KEY
Инициализация клиента в коде может выглядеть так:
def get_llm_tracer() -> Langfuse:
return Langfuse(
secret_key=settings.langfuse_secret_key,
public_key=settings.langfuse_public_key,
host=settings.langfuse_host,
)Observation вместо россыпи спанов
В последних версиях SDK Langfuse унифицирован подход к трейсингу: теперь используется один метод, а тип наблюдения задаётся параметром as_type.
Для базового сценария достаточно обернуть вызов LLM в контекстный менеджер:
with llm_tracer.start_as_current_observation(
name=span_name,
as_type="generation",
input=user_input,
prompt=prompt_obj,
) as observation:
response = llm_client.chat.completions.parse(
model=settings.llm_model_base,
messages=messages,
temperature=settings.llm_temperature,
response_format=response_format,
)
response_content = response.choices[0].message.parsed
observation.update(output=response_content)Этот паттерн одинаково хорошо работает для:
обычных LLM‑вызовов;
structured output;
function calling;
любых функций без LLM (как часть цепочки).
Важно: Langfuse совместим с OpenTelemetry (OTel), поэтому вы можете связать LLM‑трейсы с обычными backend‑трейсами и получить сквозную наблюдаемость всего запроса.
Prompt Management: когда PO и SME перестают быть наблюдателями
Одна из сильных сторон Langfuse — визуальная платформа. Помимо observability, в ней есть полноценный Prompt Management.

Для инженера это:
версионирование промптов;
метрики и статистика использования;
контроль изменений.
Для PO и SME — инструмент конфигурирования поведения сервиса без погружения в код.
Ключевой момент: изменение промпта не требует передеплоя микросервиса. Инстанс сервиса подтягивает актуальный промпт по имени и тегу прямо из Langfuse.
Если нужно добавить динамический контекст — используется метод compile.
Пример:
def _get_prompt(
self,
name: str,
user_input: list[str],
label: str = "production",
*args,
**kwargs,
) -> tuple:
prompt = llm_tracer.get_prompt(name=name, label=label)
messages = prompt.compile(user_input=user_input, **kwargs)
return prompt, messagesСам объект prompt лучше возвращать и передавать в observation — так Langfuse корректно считает количество использований каждого промпта. Это особенно важно, если вы используете Schema‑Guided Reasoning или сложные цепочки рассуждений.
Evaluation и метрики для недетерминированных систем
Метрики важны для любого сервиса. Для LLM — критически важны. Мы использовали арсенал Evaluation в Langfuse: он позволяет запускать сервис на датасете «вопрос‑ответ» и агрегировать результаты.
Пример теста:
def test_with_dataset(
llm_tracer: Langfuse,
*args,
):
dataset = llm_tracer.get_dataset(settings.langfuse_dataset_name)
for item in dataset.items:
with item.run(
run_name=datetime.now().isoformat(),
run_description="Mud checks extraction",
) as root_span:
# run your LLM workflow
root_span.score_trace(
name="Score your LLM workflow",
value=accuracy,
)
# optional
assert accuracy >= settings.accuracy_toleranceМы запускали это как отдельную CI‑джобу через команду pytest. Можно добавить thresholdы и жёсткие проверки, но на уровне MVP это не обязательно. Главное — регулярный прогон и накопление статистики.
Кастомные дашборды — вишенка на торте
И напоследок — приятный бонус. Langfuse позволяет настраивать собственные дашборды и виджеты внутри проекта:
выводить кастомные метрики;
отслеживать ключевые показатели;
собирать обзор состояния сервиса в одном месте.
Для LLM‑сервисов, где поведение не всегда детерминировано, это сильно снижает тревожность команды и упрощает принятие решений.
