AI Engineering Academy · Lektion

Langfuse til modeluafhængig observability

Integrér Langfuse som et open source-alternativ, der fungerer med enhver LLM-udbyder, opsaml brugerdefinerede spans til søgning og værktøjskald, og opsæt dashboards til omkostningssporing.

Lektion 3 af 413 trin

Langfuse til modeluafhængig observability er en gratis AI Engineering Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i AI Engineering Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Langfuse: Open source-observerbarhed for LLM'er

Langfuse er en open source-platform til observerbarhed for LLM-applikationer, som fungerer med enhver modeludbyder: OpenAI, Anthropic, Mistral, lokale modeller via Ollama eller din egen finjusterede model. I modsætning til LangSmith, som binder dig til LangChain, integreres Langfuse med al Python-kode via et enkelt SDK. Du kan selv hoste Langfuse gratis eller bruge den administrerede cloudtjeneste på cloud.langfuse.com.

# pip install langfuse
from langfuse import Langfuse

langfuse = Langfuse(
    public_key='pk-lf-...',
    secret_key='sk-lf-...',
    host='https://cloud.langfuse.com'  # or your self-hosted URL
)

print('Langfuse connected:', langfuse.auth_check())

Spor, delspor og genereringer

Langfuse bruger en hierarkisk datamodel med tre niveauer. Et spor repræsenterer én brugerforespørgsel fra start til slut. I et spor repræsenterer delspor de enkelte behandlingstrin (hentning, forbehandling og værktøjskald). Genereringer er en særlig type delspor, der specifikt bruges til LLM-kald: De registrerer modellen, promptens tokens, færdiggørelsens tokens og omkostningen på en struktureret måde, som muliggør omkostningsdashboard og kvalitetsmålinger.

from langfuse import Langfuse

langfuse = Langfuse()

# Create a trace for one user request
trace = langfuse.trace(
    name='rag-query',
    user_id='user_123',
    session_id='session_abc',
    tags=['production', 'rag']
)

# Add a retrieval span
retrieval_span = trace.span(
    name='vector-retrieval',
    input={'query': 'What is RAG?'}
)
chunks = vector_db.search('What is RAG?')
retrieval_span.end(output={'chunks': [c['text'][:100] for c in chunks]})

# Add an LLM generation
generation = trace.generation(
    name='answer-generation',
    model='gpt-4o',
    model_parameters={'temperature': 0.0},
    input=[{'role': 'user', 'content': 'Context: ...\nQuestion: What is RAG?'}]
)
response = openai_client.chat.completions.create(model='gpt-4o', messages=[...])
generation.end(
    output=response.choices[0].message.content,
    usage={'input': response.usage.prompt_tokens, 'output': response.usage.completion_tokens}
)

Integrationsmønsteret med dekoratorer

Langfuse leverer funktionsdekoratorer, der automatisk omslutter dine funktioner med sporingsdelspor. Dekoratoren @observe() registrerer input og output, tidsforbrug og eventuelle undtagelser. Det er den enkleste måde at instrumentere eksisterende kode på uden at omstrukturere den.

from langfuse.decorators import observe, langfuse_context

# @observe wraps the function as a span automatically
@observe()
def retrieve_chunks(query: str) -> list[dict]:
    return vector_db.search(query, top_k=5)

@observe()
def generate_answer(query: str, context: str) -> str:
    response = openai_client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': 'Answer using the context.'},
            {'role': 'user', 'content': f'Context: {context}\nQuestion: {query}'}
        ]
    )
    # Attach LLM usage data to the current span
    langfuse_context.update_current_observation(
        usage={'input': response.usage.prompt_tokens, 'output': response.usage.completion_tokens},
        model='gpt-4o'
    )
    return response.choices[0].message.content

@observe(name='rag-pipeline')  # top-level trace
def rag_pipeline(query: str) -> str:
    chunks = retrieve_chunks(query)  # becomes a nested span
    context = '\n'.join([c['text'] for c in chunks])
    return generate_answer(query, context)  # becomes another nested span

Integration med enhver LLM-udbyder

I modsætning til LangSmiths dybe integration med LangChain fungerer Langfuse med enhver LLM-udbyder ved hjælp af den samme dekoratorbaserede tilgang. Uanset om du kalder Anthropic API, en lokal Ollama-model, et Hugging Face-inferensendepunkt eller en tilpasset model, du selv har finjusteret, sporer Langfuse kaldet på samme måde. Denne udbyderuafhængighed er afgørende, når du kører flere modeller i den samme applikation.

from langfuse.decorators import observe, langfuse_context
import anthropic
from openai import OpenAI

anthropic_client = anthropic.Anthropic()
openai_client = OpenAI()

@observe()
def call_claude(prompt: str) -> str:
    response = anthropic_client.messages.create(
        model='claude-3-5-sonnet-20241022',
        max_tokens=1024,
        messages=[{'role': 'user', 'content': prompt}]
    )
    langfuse_context.update_current_observation(
        model='claude-3-5-sonnet-20241022',
        usage={'input': response.usage.input_tokens, 'output': response.usage.output_tokens}
    )
    return response.content[0].text

@observe()
def call_gpt4(prompt: str) -> str:
    response = openai_client.chat.completions.create(
        model='gpt-4o',
        messages=[{'role': 'user', 'content': prompt}]
    )
    langfuse_context.update_current_observation(model='gpt-4o',
        usage={'input': response.usage.prompt_tokens, 'output': response.usage.completion_tokens})
    return response.choices[0].message.content

Dashboard til omkostningssporing

Langfuse beregner automatisk omkostninger ud fra modelnavn og tokenantal ved hjælp af en indbygget pristabel, der dækker OpenAI, Anthropic, Mistral og snesevis af andre udbydere. Omkostningsdashboardet viser: samlet forbrug pr. tidsperiode, omkostninger opdelt efter model, omkostninger opdelt efter funktion eller bruger (ved hjælp af mærker og metadata) samt daglige og ugentlige forbrugstendenser. Denne synlighed forhindrer uventet høje regninger og hjælper med at identificere usædvanligt dyre forespørgsler.

# Cost data is automatically computed - no manual config
# Langfuse knows: gpt-4o input = $0.005/1K tokens, output = $0.015/1K tokens

# Add metadata to enable cost breakdown by feature
@observe(name='rag-query')
def handle_rag_query(query: str, feature: str, user_id: str) -> str:
    langfuse_context.update_current_trace(
        user_id=user_id,
        tags=[feature, 'rag'],
        metadata={'feature': feature, 'query_length': len(query)}
    )
    return rag_pipeline(query)

# In Langfuse dashboard you can now filter costs by:
# - feature: 'document_qa', 'chat', 'summarization'
# - user_id: to see which users are your most expensive
# - model: to compare gpt-4o vs gpt-4o-mini costs
# - date range: to see daily/weekly/monthly trends

Tilføjelse af brugerfeedbackscorer

Langfuse giver dig mulighed for at knytte brugerfeedback til spor efterfølgende. Når en bruger klikker på tommelfinger op eller ned på et svar, kan du registrere det som en score på det tilsvarende spor. Det forbinder signaler om reel brugertilfredshed med hele sporingskonteksten, så du kan analysere, hvad der gør højt vurderede svar anderledes end dårligt vurderede svar.

from langfuse.decorators import observe, langfuse_context

@observe()
def generate_response(query: str) -> dict:
    answer = rag_pipeline(query)
    # Get the current trace ID to link feedback later
    trace_id = langfuse_context.get_current_trace_id()
    return {'answer': answer, 'trace_id': trace_id}

# Later, when user submits feedback:
def record_user_feedback(trace_id: str, score: int, comment: str):
    langfuse.score(
        trace_id=trace_id,
        name='user_satisfaction',  # score name
        value=score,               # 1 (thumbs up) or 0 (thumbs down)
        comment=comment,
        data_type='BOOLEAN'
    )

# Now in Langfuse: filter traces where user_satisfaction = 0
# to find the exact prompts and contexts that users rated negatively

Automatiske scorer med LLM som bedømmer

Ud over brugerfeedback understøtter Langfuse automatisk scoring ved hjælp af evalueringsmodeller, hvor en LLM fungerer som bedømmer. Du kan definere evaluatorer, der kører asynkront på udvalgte spor og scorer dem efter kriterier som relevans, troværdighed, toksicitet eller korrekt format. Disse automatiske scorer udfylder det samme scoredashboard som menneskelig feedback og giver dig løbende kvalitetsovervågning uden menneskelig annotering i stor skala.

from langfuse import Langfuse

langfuse = Langfuse()

def auto_score_traces():
    # Get recent unscored traces
    traces = langfuse.fetch_traces(tags=['production'], limit=50)
    
    for trace in traces.data:
        question = trace.input.get('query', '')
        answer = trace.output.get('answer', '') if trace.output else ''
        
        if not question or not answer:
            continue
        
        # LLM-as-judge scoring
        score = evaluate_relevance(question, answer)  # returns 0.0-1.0
        
        langfuse.score(
            trace_id=trace.id,
            name='auto_relevance',
            value=score,
            data_type='NUMERIC',
            comment='Automated relevance score from LLM judge'
        )

# Run this as a scheduled job every hour

Promptstyring i Langfuse

Langfuse indeholder en funktion til promptstyring, der gemmer dine prompts i Langfuse-cloudtjenesten og lader dig hente dem under kørsel. Det adskiller promptversioner fra kodeudrulninger — du kan opdatere en prompt i Langfuses brugerflade, og ændringen træder straks i kraft uden en kodeudrulning. Langfuse registrerer også, hvilken promptversion hvert spor brugte, så du kan sammenligne ydeevnen på tværs af promptversioner.

from langfuse import Langfuse

langfuse = Langfuse()

# Fetch the current production prompt by name
# The prompt lives in Langfuse UI, not in your code
prompt = langfuse.get_prompt('rag-system-prompt', version='production')

# Use it in your pipeline
messages = [
    {'role': 'system', 'content': prompt.compile(context_limit=4000)},
    {'role': 'user', 'content': query}
]

response = openai_client.chat.completions.create(model='gpt-4o', messages=messages)

# The trace is automatically linked to the prompt version
# In Langfuse you can filter: show me traces using prompt v3 vs v4
# and compare their quality scores

Selvhosting af Langfuse

Langfuse kan selvhostes med én Docker Compose-kommando og bruge PostgreSQL til lagring. Selvhosting betyder, at dine sporingsdata aldrig forlader din infrastruktur — det er afgørende for applikationer, der håndterer personhenførbare oplysninger, medicinske data eller beskyttet indhold. Den selvhostede version har de samme funktioner som den administrerede cloudtjeneste, men kræver, at du administrerer infrastrukturen (sikkerhedskopier, skalering og opgraderinger).

# Self-host Langfuse with Docker Compose
# docker-compose.yml (simplified)
# version: '3'
# services:
#   langfuse:
#     image: langfuse/langfuse:2
#     ports:
#       - '3000:3000'
#     environment:
#       - DATABASE_URL=postgresql://langfuse:password@postgres/langfuse
#       - NEXTAUTH_SECRET=your-random-secret
#       - SALT=your-random-salt
#   postgres:
#     image: postgres:15
#     environment:
#       - POSTGRES_DB=langfuse
#       - POSTGRES_PASSWORD=password

# After docker-compose up, point your SDK to:
langfuse = Langfuse(
    public_key='pk-lf-your-key',
    secret_key='sk-lf-your-key',
    host='http://localhost:3000'  # your self-hosted instance
)

Langfuse eller LangSmith: Hvornår skal du vælge hvad

Vælg LangSmith, når du bruger LangChain intensivt og ønsker automatisk sporing uden konfiguration, dyb integration med LangChain-evalueringer, og når du er tryg ved at være afhængig af en leverandør. Vælg Langfuse, når du bruger flere LLM-udbydere, har brug for selvhosting for at overholde krav til databeskyttelse, ønsker open source-gennemsigtighed eller bygger med andre rammeværker end LangChain. Begge er klar til produktion, og begge tilbyder generøse gratis niveauer.

OpenTelemetry-integration til LLM'er

Til team, der allerede bruger OpenTelemetry til distribueret sporing, understøtter Langfuse indlæsning via OTLP (OpenTelemetry Protocol). Du kan sende LLM-sporingsdata fra dine eksisterende OTel-eksportører direkte til Langfuse uden at ændre din instrumentering. Det muliggør en samlet observerbarhedsstak, hvor LLM-spor, databaseforespørgselsdelspor og HTTP-forespørgselsspor alle findes i det samme system med ensartede korrelations-id'er.

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Configure OTel to send to Langfuse OTLP endpoint
exporter = OTLPSpanExporter(
    endpoint='https://cloud.langfuse.com/api/public/otel/v1/traces',
    headers={
        'Authorization': 'Basic ' + base64.b64encode(b'pk-lf-xxx:sk-lf-xxx').decode()
    }
)

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

# Now create spans as usual - they appear in Langfuse automatically
tracer = trace.get_tracer('my-llm-app')
with tracer.start_as_current_span('rag-query') as span:
    span.set_attribute('llm.model', 'gpt-4o')
    span.set_attribute('llm.prompt_tokens', 500)
    result = rag_pipeline(query)

Hurtigt tjek

Test din forståelse af modeluafhængig observerbarhed med Langfuse fra denne lektion.

Opsummering af lektionen

I denne lektion har du lært: Langfuse leverer open source- og modeluafhængig LLM-observerbarhed ved hjælp af en hierarkisk datamodel med spor, delspor og genereringer, dekoratoren @observe() instrumenterer eksisterende kode med minimale ændringer, og omkostningssporing, brugerfeedbackscorer og automatisk scoring med LLM som bedømmer gør Langfuse til en komplet platform til kvalitetsovervågning. Dernæst opsætter vi alarmer for latenstid, omkostninger og forringelse af kvaliteten.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Langfuse til modeluafhængig observability” gratis?

Ja — hele teksten til “Langfuse til modeluafhængig observability” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af AI Engineering Academy-kurset, skal du opgradere til CoddyKit PRO. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Langfuse til modeluafhængig observability”?

Integrér Langfuse som et open source-alternativ, der fungerer med enhver LLM-udbyder, opsaml brugerdefinerede spans til søgning og værktøjskald, og opsæt dashboards til omkostningssporing. Du øver dig i AI Engineering Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på AI Engineering Academy?

Der kræves ingen tidligere erfaring. AI Engineering Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Langfuse til modeluafhængig observability”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne AI Engineering Academy-lektion?

Ja. Alle AI Engineering Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Hvorfor LLM-apps er svære at fejlfinde
  2. Tracing med LangSmith
  3. Langfuse til modeluafhængig observability
  4. Alarmer ved latenstid, omkostninger og kvalitetsforringelse
← Tilbage til AI Engineering Academy