Capra Consulting Gjensidige
Hvem er vi?
Magnus Rødseth
Magnus Rødseth
Utvikler i Capra Consulting
Håvard Opheim
Håvard Opheim
Utvikler i Capra Consulting

Utleid hos Gjensidige, hvor vi bygger agentiske AI-applikasjoner

Capra Consulting Gjensidige
Intro
Dagens tema

Forutsigbar agentisk oppførsel med LangChain og MLflow

Agentiske applikasjoner kan oppleves som uforutsigbare og vanskelig å forholde seg til som utviklere.

Vi viser hvordan vi bruker MLflow og LangChain for å observere og guide adferden.

Capra Consulting Gjensidige
01
Lynkurs: LangChain, agenter
og samtaledesign
Hva er LangChain?
📝
Prompts
Prompt-maler
🤖
Modell
LLM-abstraksjoner
Output
Strukturert respons

LangChain er et bibliotek som gjør det enklere å bygge applikasjoner med store språkmodeller. I stedet for rå API-kall gir LangChain deg byggeblokker: prompt-maler, modellabstraksjoner, verktøy og minnehåndtering.

LangGraph er et søsterbibliotek som lar deg bygge agenter.

Men hva er egentlig en agent?

Hva er en agent?
Vanlig LLM-integrasjon
Bruker → Spørsmål ↓ Modell → Svar

Én runde. Ferdig.

Agent
  • 🧠 Resonnere over spørsmålet og planlegge neste steg
  • 🛠️ Bruke verktøy — hente data, søke i dokumenter, kalle API-er
  • 🔄 Iterere — sjekke svaret sitt, prøve på nytt
  • 🤝 Delegere — sende deloppgaver til andre agenter
I praksis: Du gir en LLM tilgang til verktøy og lar den selv bestemme om og når den skal bruke dem.
Tre nøkkelprinsipper for agentisk samtaledesign
1. Intensjonsklassifisering

Hva prøver brukeren å oppnå? Vi klassifiserer hvert spørsmål for å velge riktig behandling.

2. Kontekstinjeksjon

Vi henter relevant informasjon (fra dokumenter, databaser) og gir det til modellen før den svarer.

3. Guardrails

Vi definerer hva agenten ikke skal svare på. Holdningen er: «Gjør én ting, gjør den bra.»

Hvordan koble på LangChain — i tre steg
# 1. Lag en modell from langchain_openai import AzureChatOpenAI model = AzureChatOpenAI(model="gpt-4.1") # 2. Lag en prompt from langchain_core.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_messages([ ("system", "Du er en hjelpsom assistent."), ("human", "{question}"), ]) # 3. Koble sammen og kjør chain = prompt | model response = chain.invoke({"question": "Hva er hovedpunktene?"})
Hva bør man se etter og benchmarke?
⏱️
Latency
Hvor lang tid tar hvert steg?
🎭
Hallusinering
Finner modellen opp ting?
🛠️
Verktøybruk
Valgte agenten riktig verktøy?
🎯
Kontekstrelevans
Hentet RAG riktige dokumenter?
📉
Regresjoner
Dårligere etter siste endring?
Dette er nøyaktig det vi skal vise dere hvordan vi måler — med MLflow.
02
Hva bygger vi?
Caset: En agentisk dokumentassistent

Tenk deg at du bygger en chatbot der brukere kan:

  • Laste opp komplekse dokumenter (rapporter, kontrakter, manualer)
  • Stille spørsmål og få svar basert på innholdet
  • Få AI-genererte oppsummeringer og anbefalinger

Utfordringen:

  • Hvordan vet du at agenten fungerer?
  • At den ikke hallusinerer?
  • At RAG henter riktig kontekst?
Applikasjonen
Teknisk arkitektur — helt overordnet
Frontend
React Router Frontend
Backend (FastAPI)
Guardrails
RAG
Conversation Memory
Tools
Infrastructure
PostgreSQL + pgvector
Azure OpenAI
MLflow

Tech-stacken: Python, LangGraph + LangChain, Azure OpenAI, PostgreSQL med pgvector, MLflow for observabilitet

03
Observabilitet og sporbarhet
Hvorfor er observabilitet viktig?

Vi vite:

👥
Brukeratferd
Hvordan bruker brukere AI-delene? Hva lurer de faktisk på?
🤖
Agentoppførsel
Svarer den bra? Eller bare slop?
📄
Ekstraksjonskvalitet
Finner AI-en all relevant informasjon?
Vi bruker MLflow til å teste og iterere etterhvert som vi lærer mer om brukerne våre
MLflow UI — Traces-oversikten
MLflow UI — Traces-oversikten
Slik setter vi opp MLflow-tracing

init_mlflow():

# api/src/observability/tracing.py import mlflow from src.config import get_settings def init_mlflow() -> None: """Initialize MLflow tracking.""" settings = get_settings() # Sett tracking URI — peker på MLflow-serveren mlflow.set_tracking_uri(settings.mlflow_tracking_uri) # Sett eksperiment — alle traces havner her mlflow.set_experiment(settings.mlflow_experiment_name) # Én linje — dette er magien mlflow.langchain.autolog(log_traces=True)
Hva tracer vi — og hva ser vi?
Chat-agenten: En LangGraph-graf
🟢
validate_input
classify_intent
retrieve_context
↑ trenger kontekst
generate_response
🔴

Med mlflow.langchain.autolog() kan vi i MLflow UI se hvert steg som en span i tracet.

MLflow UI — Utvidet trace
MLflow UI — Utvidet trace
Agenttilstanden — hva som flyter gjennom grafen

Tilstanden mellom nodene er definert som en TypedDict:

# api/src/agent/state.py class AgentState(TypedDict, total=False): messages: Annotated[list[BaseMessage], add_messages] conversation_id: uuid.UUID | None resolved_context: dict | None # Bruker-/dokumentkontekst retrieved_docs: list[Document] # RAG-resultater sources: list[dict] # Kildemetadata for siteringer current_step: str # Observabilitet intent: str | None # Klassifisert intensjon needs_retrieval: bool # Trenger vi RAG? is_rejected: bool # Avvist av guardrails?
Alt dette er synlig i hvert trace og vi kan se nøyaktig hva grafen «tenkte» i hvert steg.
Ekstraheringsagenten: Deep Agents med subagenter

For dokumentanalyse bruker vi Deep Agents som abstraksjonslag over LangChain og LangGraph.

from deepagents import create_deep_agent def create_extraction_agent(...) -> Runnable: model = get_extraction_model() tools = [...] # verktøy for parsing, lagring, fremdrift subagent = [...] # subagent for ekstraksjon av strukturerte funn return create_deep_agent( model=model, tools=tools, subagents=subagent, system_prompt=ORCHESTRATOR_SYSTEM_PROMPT, )
Fordi Deep Agents bygger på LangChain, får vi automatisk tracing av hele orkestreringsagenten og alle subagent-kall via den samme autolog()-linjen.
MLflow UI — Ekstraheringsagent trace
MLflow UI — Ekstraheringsagent trace
Evaluering — Custom scorers

Tracing gir oss innsikt i hva som skjer. Men vi trenger også å måle hvor bra det fungerer.

Vi har bygget fire tilpassede scorers med MLflow GenAI Evaluation:

🎯
GroundednessScorer
Er svaret forankret i konteksten? Fanger opp hallusinasjoner.
💡
HelpfulnessScorer
Adresserer svaret brukerens spørsmål?
📚
RetrievalRelevanceScorer
Er den hentede konteksten relevant for spørsmålet?
🇳🇴
NorwegianLanguageScorer
Svarer agenten på norsk?
GroundednessScorer — Kodeeksempel
from mlflow.genai import scorer from mlflow.entities import Feedback def GroundednessScorer(): @scorer def groundedness(*, inputs, outputs, context=None, **kwargs): if intent in ["REJECTED", "GENERAL_CHAT"]: return None # hopp over irrelevante intensjoner result = _groundedness_judge(...) # LLM-dommer vurderer forankring return Feedback( value=..., # 1.0 = forankret, 0.5 = delvis, 0.0 = hallusinert rationale=..., # forklaring fra dommeren ) return groundedness
Kjøre evalueringer — CLI

Vi har en CLI for å kjøre evalueringer mot testdatasett eller produksjons-traces:

# Evaluer mot utvalgt testdatasett (17 testtilfeller) uv run --directory api python -m src.evaluation.cli conversation
# Evaluer de siste 100 produksjons-tracene uv run --directory api python -m src.evaluation.cli conversation \ --from-traces --trace-count 100
# Evaluer med 20% sampling fra siste uke uv run --directory api python -m src.evaluation.cli conversation \ --from-traces --start-date 2026-01-01 --sample-rate 0.2
MLflow UI — Evaluation results
MLflow UI — Evaluation results
Utvalgte testdatasett

Vi har definert testtilfeller som dekker ulike scenarioer:

# api/src/evaluation/datasets/conversation.py DOCUMENT_TEST_CASES = [ ConversationTestCase( id="doc_001", question="Hva er hovedfunnene i rapporten?", expected_intent="DOCUMENT_QUESTION", context="Rapport fra 2024. Tre kritiske funn identifisert.", reference_answer="Rapporten identifiserer tre kritiske funn.", ), ] OUT_OF_SCOPE_TEST_CASES = [ ConversationTestCase( id="oos_001", question="Kan du hjelpe meg med matlagning?", expected_intent="REJECTED", ), ]
Disse testtilfellene kan kjøres som regresjonstester — etter hver endring i prompts eller agentkode kan vi verifisere at kvaliteten holder seg.
Produksjons-traces → Evaluering — Flyt
Produksjons-traces
MLflow
Filtrer / sample
Evalueringsdatasett
Scorers
Resultater
tilbake i MLflow UI
04
Utvikling og hosting
Lokal utvikling — Docker Compose
# docker-compose.yml services: postgres: image: pgvector/pgvector:pg16 ports: ["5432:5432"] mlflow: build: dockerfile: docker/mlflow.Dockerfile command: > mlflow server --host 0.0.0.0 --port 5000 --backend-store-uri postgresql://...@postgres:5432/mlflow --serve-artifacts ports: ["5000:5000"] depends_on: postgres: { condition: service_healthy }
Produksjon — Hosting-alternativer

For produksjon kan MLflow hostes på flere måter.

Databricks (vårt valg)
  • MLflow har native integrasjon med Databricks
  • Skalerer automatisk
  • Managed service — ingen infrastruktur å vedlikeholde
Alternativer for produksjonsskala

(fra MLflows dokumentasjon)

  • AWS SageMaker
  • Azure Machine Learning
  • Nebius (🇪🇺)
  • GCP (GKE)
05
Oppsummering og veien videre
Den store idéen
Ved å dumpe all denne innsikten — hvert agentresonnement, hvert RAG-oppslag, hvert evalueringsresultat — inn i MLflow, kan vi gjøre dataanalyse på bruker- og agentatferd.
Vi kan tweake systemet vårt, og skape en brukeropplevelse som brukerne faktisk elsker.
Feedback-loopen
AI-assistent
Basert på tilstandsrapporten din anbefaler jeg at du sjekker dreneringen rundt grunnmuren, spesielt...
👍
👎
Var dette svaret nyttig?

Par dette med et tilbakemeldingssystem — og vi har en transparent måte å korrigere agentenes oppførsel i produksjon.

Bruker
Feedback 👍/👎
Database
MLflow Assessment
Evaluering
Prompt-forbedring
Bedre svar ↩
Oppsummert
HvaHvordanVerktøy
Automatisk tracingmlflow.langchain.autolog()MLflow + LangChain
Agent-orkestreringLangGraph StateGraphLangGraph
DokumentekstraksjonDeep Agents med subagenterdeepagents
KvalitetsmålingCustom scorers + mlflow.genai.evaluate()MLflow GenAI
ProduksjonsmonitoreringTrace-basert evaluering med samplingMLflow traces API
Bruker-feedbackThumbs up/down → MLflow assessmentsFastAPI + MLflow
Én linje kode gir oss full observabilitet. Et sett med scorers gir oss systematisk kvalitetsmåling. Og muligheten til å evaluere produksjons-traces gir oss den tryggheten vi trenger for å skalere.
Takk for oppmerksomheten!
Spørsmål?

Magnus Rødseth og Håvard Opheim

Capra Consulting @ Gjensidige

Capra Consulting Gjensidige