Generative AIAI evalsTools & frameworks

Langfuse overview

4 minutes read

In production, observability platforms help you answer three important questions: “What’s happening right now?” “Why is it happening?” and “How can I improve my system?” For applications powered by large language models (LLMs), this goes beyond merely tracking request volume and latency. It also requires tracing individual prompts, API and tool calls, responses, and any relevant context.

Langfuse provides the visibility you need to understand how your LLM-powered applications are performing. This allows you to debug issues, monitor performance, and implement improvements within a unified workflow.

Setup

Langfuse offers both a cloud solution with paid and free plans and an open-source version that can be deployed locally. The managed offering is the easiest to get started with. Just head over to cloud.langfuse.com and set up an instance — you don’t have to install anything. If you prefer the local version, the easiest way to get started is via Docker. Just clone the git repo and start the services:

git clone https://github.com/langfuse/langfuse.git
cd langfuse

docker compose up

In this case, you need to have Docker installed. This option is best for development and testing. For production-ready options, you can deploy Langfuse to your self-managed Kubernetes cluster or leverage managed clusters on AWS, Azure, or GCP. This provides high availability, but with a more complex setup.

Once installed, you need to create an account for your instance, which you will use to manage your projects. Then, create a new organization and project. Next, create API keys that your applications will use to connect to your instance. These credentials include a public key, a secret key, and the hostname. Here is an example:

LANGFUSE_SECRET_KEY="sk-lf-dfe3ffa9-9514-41b8-9f99-69c9920dc280"
LANGFUSE_PUBLIC_KEY="pk-lf-cff73d3c-1fea-4e25-a52f-76d9d12c6f86"
LANGFUSE_BASE_URL="http://localhost:3000" # or https://cloud.langfuse.com or the DNS of a cloud load balancer

The LANGFUSE_BASE_URL varies based on how you installed Langfuse. You can always generate new keys or delete existing ones in your project settings.

Great. Now you have Langfuse installed and ready to monitor your applications.

Core concepts

Your LLM-powered applications complete various steps before returning a response. First, the user’s input is preprocessed and combined with the necessary context. Then, it is sent to a model provider for processing. The model may call tools to perform various actions before returning the final response. This complete cycle is represented as a trace in Langfuse.

Steps within a trace are represented as observations. They are the core building blocks in Langfuse. There are various observation types, such as events, spans, generation logs, tool calls, guardrails, and more. If you are using a native integration, the observation type is automatically set. We’ll see how you can do it manually with the as_type parameter when creating observations.

Traces can then be grouped into sessions. A session represents a group of traces that form the same interaction. This is useful for multi-turn interactions. This can be done easily by adding a sessionId attribute to observations, as we’ll see shortly.

Besides a sessionId, you can give your traces and observations additional attributes to help you filter and analyze them easily. Here are some useful attributes:

  • Environment — useful for deployment contexts like dev and prod.

  • Tags — these allow you to categorize traces using labels.

  • User — track end-users associated with traces.

  • Metadata — key-value pairs for arbitrary information.

  • Releases and versions — for change history.

Your first trace

You can use various native integrations for popular libraries such as OpenAI, LangChain, and LlamaIndex. If an integration for your use case does not exist, you can use the Langfuse SDK directly. If you already use OpenTelemetry, a CNCF observability framework, you can send traces to Langfuse at /api/public/otel. Langfuse is built on top of OpenTelemetry, so the integration is seamless.

Here is an example for the OpenAI Python SDK:

# install packages: pip install langfuse openai
from dotenv import load_dotenv
from langfuse.openai import openai # use this instead of from openai import OpenAI

load_dotenv() # load API keys from your environment

response = openai.responses.create(
    model="gpt-5.4-mini",
    input="Explain the concept of recursion"
)
print(response.output_text)

The langfuse.openai wrapper takes care of everything for you automatically. For LangChain/LangGraph, you can use the Langfuse CallbackHandler. Callbacks in LangChain/LangGraph allow you to tap into key LLM and agent execution phases. This helps you perform tasks such as logging, token tracking, and interrupting agent execution.

# install packages: pip install langfuse langchain-openai python-dotenv langchain-core

from langchain_openai import ChatOpenAI
from langfuse.langchain import CallbackHandler
from dotenv import load_dotenv

load_dotenv() # load API keys from your environment

langfuse_handler = CallbackHandler() 

llm = ChatOpenAI(model="gpt-5.4-mini")
response = llm.invoke(
    input="Explain the concept of recursion.",
    config={"callbacks": [langfuse_handler]}
)

print(response.content)

Once you run this code, the traces should appear in Langfuse:

Traces collected by the LangChain callback handler.

Explore the documentation to learn more about using various integrations.

Using Langfuse SDKs

If you are using the Langfuse SDKs, there are three ways to instrument your application. First, you can use a context manager. Context managers automate the setup and cleanup of resources, such as files and connections. Here’s how you can create an observation:

# install packages: pip install langfuse openai python-dotenv

from openai import OpenAI
from dotenv import load_dotenv
import os
from langfuse import get_client

load_dotenv() # load API keys from your environment

langfuse = get_client() # automatically picks up your LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY and LANGFUSE_BASE_URL

client = OpenAI() # automatically picks up your OPENAI_API_KEY and OPENAI_BASE_URL. You must set the base URL if you're using a proxy

with langfuse.start_as_current_observation(name="main", as_type="span") as span:
    with langfuse.start_as_current_observation(as_type="generation", name="recursion_explanation") as generation:
        user_input = "Explain the concept of recursion"
        generation.update(input=user_input)
        response = client.responses.create(
            model="gpt-5.1",
            input=user_input
        )

        print(response.output_text)
        generation.update(output=response.output_text)

We are using start_as_current_observation to create observations within a particular context. As you can see, we can create child observations within other observations. This helps us build a hierarchy of observations for various steps in the execution cycle. Once the code finishes execution, you can see the traces in Langfuse:

Traces collected "with" the context manager in Langfuse.

The second way is to use the observe() decorator for functions. This automatically captures inputs, outputs, and other information about a function without changing the function itself. You can also use the as_type parameter here, depending on what the function does. Each observed function will be recorded as an individual trace.

import os

from dotenv import load_dotenv
from langfuse import get_client, observe
from openai import OpenAI

load_dotenv()

langfuse = get_client()

client = OpenAI()

@observe(name="database-lookup", as_type="span", capture_output=False)
def perform_db_query(query_id: str):
    print({"id": query_id, "data": "Sample Database Record"})

@observe(name="generate-text-completion", as_type="generation", capture_input=True, capture_output=True)
def generate_text():
    response = client.responses.create(
        model="gpt-5.1",
        input="Explain the concept of recursion"
    )

    print(response.output_text)

perform_db_query("123")
generate_text()

You can also manually create observations as follows:

from dotenv import load_dotenv
from langfuse import get_client

load_dotenv()

langfuse = get_client()

root = langfuse.start_observation(name="data-processing-pipeline", input="initial-data")

generation = root.start_observation(name="llm-transformation", as_type="generation")

evaluation = generation.start_observation(name="post-processing", as_type="evaluator")

generation.end()
evaluation.end()

root.end()

In this case, we must manually nest observations, manage context, and manage their lifecycle using the .end() method. Once you run this code, your traces appear in the Langfuse UI:

Manually collected observations in Langfuse.

Adding attributes

As noted earlier, attributes help you add useful information to your traces and observations. You can set such attributes as follows:

import os

from dotenv import load_dotenv
from langfuse import get_client, observe, propagate_attributes
from openai import OpenAI

load_dotenv()

langfuse = get_client()

client = OpenAI()

@observe()
def process_request():
    with propagate_attributes(
        session_id="super-awesome-session",
        user_id="super-awesome-user",
        metadata={"source": "web"},
        trace_name="web-request"
    ):
        response = client.responses.create(
            model="gpt-5.1",
            input="Explain the concept of recursion"
        )

        print(response.output_text)

process_request()

As you can see below, we were able to add attributes to observations using propagate_attributes:

Attributes such as session and user added to a Langfuse trace.

Enriching your traces with attributes like session_id, user_id, and environment provides critical context to your observation hierarchy. A user_id groups logs into clear, individual customer interactions. Meanwhile, the environment tag safely separates development from live production analytics. Once execution finishes, these attributes become searchable filters within your Langfuse dashboard.

Langfuse only tracks and aggregates analytics for end-users or sessions via the custom IDs you provide, but it does not authenticate or create them.

Conclusion

We have examined the features of the Langfuse observability platform. You learned how to set up an instance and instrument your application code. Additionally, we discussed the core concepts of traces, observations, and multi-turn sessions. By utilizing native framework integrations and adding custom attributes and metadata, you gain full control over your telemetry.

How did you like the theory?
Report a typo