# Output Validation, Grounding Verification, and Fact-Checking

**Last updated:** 2026-02
**Status:** GA
**Category:** AI Security Engineering

---

## Introduksjon

Output validation, grounding verification og fact-checking er fundamentale sikkerhetsteknikker for å sikre at LLM-genererte svar er faktisk korrekte, basert på kildemateriale, og ikke inneholder hallusinasjoner eller fabricerte fakta. Disse teknikkene er spesielt kritiske i RAG-systemer (Retrieval Augmented Generation) der modellen skal basere sine svar på hentet dokumentasjon.

**Groundedness** refererer til i hvilken grad en modells output er basert på faktisk tilgjengelig informasjon fra pålitelige kilder. Et "grounded" svar holder seg tett til gitt informasjon og unngår spekulasjon eller fabrikasjon. **Ungroundedness** er det motsatte – når LLM-er produserer informasjon som er ikke-faktisk eller unøyaktig sammenlignet med kildematerialet.

Azure AI Content Safety tilbyr dedikert **Groundedness Detection API** som automatisk detekterer og kan korrigere tekst som avviker fra kildematerialet, noe som sikrer at generert innhold er i tråd med faktiske eller intenderte referanser.

## Kjernekomponenter

### 1. Groundedness Detection API (Azure AI Content Safety)

Azure AI Content Safety tilbyr et dedikert API for groundedness-deteksjon med følgende kapabiliteter:

**Deteksjonsmoduser:**
- **Non-reasoning mode:** Rask deteksjon, optimalisert for online-applikasjoner
- **Reasoning mode:** Detaljerte forklaringer på detekterte ugrunnede segmenter (krever Azure OpenAI GPT-4o)

**Domenestøtte:**
- `MEDICAL` – Medisinsk domene med spesialisert deteksjon
- `GENERIC` – Generisk domene for de fleste use cases

**Oppgavetyper:**
- `QnA` – Question & Answer-oppgaver
- `Summarization` – Sammendragsoppgaver

**API-respons:**
```json
{
  "ungroundedDetected": true,
  "ungroundedPercentage": 1.0,
  "ungroundedDetails": [
    {
      "text": "12/hour.",
      "offset": { "utf8": 0, "utf16": 0, "codePoint": 0 },
      "length": { "utf8": 8, "utf16": 8, "codePoint": 8 },
      "reason": "None. The premise mentions '10/hour' but not '12/hour'."
    }
  ]
}
```

### 2. Grounding Correction Feature

API-et kan automatisk korrigere detektert ungroundedness:

**Request:**
```json
{
  "domain": "Medical",
  "task": "Summarization",
  "text": "The patient name is Kevin.",
  "groundingSources": ["The patient name is Jane."],
  "correction": true,
  "llmResource": {
    "resourceType": "AzureOpenAI",
    "azureOpenAIEndpoint": "<endpoint>",
    "azureOpenAIDeploymentName": "<deployment>"
  }
}
```

**Response:**
```json
{
  "correctedText": "The patient name is Jane."
}
```

### 3. Citation Verification

I RAG-systemer med Azure AI Search eller Microsoft Foundry Agents:

**Citation format:**
- `[message_idx:search_idx†source]` – Standard citation-format
- `url_citation` annotations – URL-baserte referanser i streaming-responser

**Verifiseringsprosess:**
1. Spør spørsmål som du vet besvares i et spesifikt indeksert dokument
2. Bekreft at responsen inkluderer citations i korrekt format
3. Ved streaming, bekreft `url_citation` annotations med gyldige URLer
4. Verifiser at sitert innhold matcher kildedokumenter i søkeindeksen

### 4. Source Attribution i Agents

Microsoft Foundry Agents og Bing Grounding-tools følger en firetrinns prosess:

1. **Query formulation:** Agenten identifiserer informasjonsgap og konstruerer søkespørringer
2. **Search execution:** Grounding-tool sender spørringer til søkemotorer og henter resultater
3. **Information synthesis:** Agenten prosesserer søkeresultater og integrerer funn i svar
4. **Source attribution:** Agenten gir transparens ved å sitere søkekilder med URLer

### 5. Evaluation Frameworks

**Azure AI Evaluation SDK:**
```python
from azure.ai.evaluation import GroundednessEvaluator

groundedness_eval = GroundednessEvaluator(
    azure_ai_project=azure_ai_project,
    credential=credential,  # gitleaks:allow
    threshold=3.0  # 1-5 skala
)

result = groundedness_eval(
    query="What shape has 4 equilateral sides?",
    response="Rhombus",
    context="Rhombus is a shape with 4 equilateral sides."
)
```

**MLflow GenAI Scorers (Databricks):**
```python
from mlflow.genai.scorers import retrieval_groundedness
import mlflow

trace = mlflow.get_trace("<trace-id>")
feedback = retrieval_groundedness(trace=trace)
```

**Evaluator-output:**
```json
{
  "groundedness": 5.0,
  "gpt_groundedness": 5.0,
  "groundedness_threshold": 3.0,
  "groundedness_reason": "The response accurately answers the query...",
  "groundedness_result": "pass"
}
```

## Arkitekturmønstre

### Mønster 1: Inline Groundedness Validation (Real-time)

**Bruk når:** Du trenger sanntidsvalidering i produksjonsapplikasjoner.

**Arkitektur:**
```
User Query → LLM Generation → Groundedness API → [Pass/Fail + Correction] → User
                ↓
         Grounding Sources (Azure AI Search, Database)
```

**Fordeler:**
- Umiddelbar deteksjon av hallusinasjoner
- Automatisk korreksjon av ungrounded innhold
- Høy brukertillit gjennom verifisert output

**Ulemper:**
- Latency overhead (spesielt med reasoning mode)
- Ekstra Azure OpenAI-kostnader ved reasoning/correction
- Krever rate limiting-håndtering

**Implementering:**
```python
from azure.ai.contentsafety import ContentSafetyClient
from azure.core.credentials import AzureKeyCredential

client = ContentSafetyClient(endpoint, AzureKeyCredential(key))

response = client.text_detect_groundedness(
    domain="GENERIC",
    task="QnA",
    qna={"query": user_query},
    text=llm_response,
    grounding_sources=retrieved_docs,
    reasoning=True,
    correction=True,
    llm_resource={
        "resourceType": "AzureOpenAI",
        "azureOpenAIEndpoint": aoai_endpoint,
        "azureOpenAIDeploymentName": deployment
    }
)

if response.ungrounded_detected:
    final_response = response.corrected_text
else:
    final_response = llm_response
```

### Mønster 2: Post-Generation Evaluation Pipeline

**Bruk når:** Du evaluerer kvalitet i utvikling/testing eller batch-prosessering.

**Arkitektur:**
```
Dataset → LLM → Response Log → Evaluation Pipeline → Metrics Dashboard
                                      ↓
                              [Groundedness Evaluator]
                              [Factuality Evaluator]
                              [Citation Validator]
```

**Fordeler:**
- Ingen produksjonslatency
- Mulighet for A/B-testing av grounding-strategier
- Omfattende metrikker for kvalitetssporing

**Ulemper:**
- Ikke sanntids – feil oppdages etter utlevering (i dev/test)
- Krever separat pipeline-infrastruktur

**Implementering:**
```python
from azure.ai.evaluation import evaluate, GroundednessEvaluator

groundedness = GroundednessEvaluator(evaluator_model)

result = evaluate(
    data="evaluation_dataset.jsonl",
    target=chat_application,
    evaluators={"groundedness": groundedness},
    evaluator_config={
        "default": {
            "column_mapping": {
                "query": "${data.queries}",
                "context": "${outputs.context}",
                "response": "${outputs.response}"
            }
        }
    }
)

# Resultat inkluderer per-turn groundedness scores
print(result.metrics["groundedness"])  # Aggregert score
print(result.evaluation_per_turn["groundedness"])  # Per-spørsmål
```

### Mønster 3: Agentic Retrieval med Built-in Verification

**Bruk når:** Du bygger agenter med Azure AI Foundry eller Semantic Kernel.

**Arkitektur:**
```
User → Agent (with Azure AI Search tool) → Query Planning → Retrieval → Synthesis
                                                                           ↓
                                                                  Citation Generation
                                                                           ↓
                                                                     Verified Response
```

**Fordeler:**
- Built-in citation tracking
- Transparent kildeattribusjon
- Automatisk grounding gjennom tool-design

**Ulemper:**
- Avhengig av agent-framework
- Begrenset kontroll over grounding-logikk

**Implementering:**
```python
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import AzureAISearchTool

with AIProjectClient.from_connection_string(conn_str) as project:
    # Azure AI Search tool gir automatisk grounding
    search_tool = AzureAISearchTool(
        index_name="knowledge-base",
        index_connection_id=search_connection.id
    )

    agent = project.agents.create_agent(
        model=model_deployment,
        name="grounded-agent",
        instructions="Answer using only indexed documents. Cite sources.",
        tools=[search_tool]
    )

    # Responses inkluderer automatisk citations
    response = project.openai.responses.create(
        input=user_query,
        extra_body={"agent": {"name": agent.name}}
    )

    # Verifiser citations
    for annotation in response.annotations:
        if annotation.type == "url_citation":
            print(f"Source: {annotation.url}")
```

## Beslutningsveiledning

### Når bruke hvilken teknikk?

| Use Case | Groundedness API | Citation Verification | Evaluation Pipeline | Agentic Retrieval |
|----------|------------------|----------------------|---------------------|-------------------|
| **Medisinsk rådgivning** | ✅ Obligatorisk (Medical domain) | ✅ Recommended | ✅ Pre-prod | ⚠️ Vurder custom |
| **Kundesupport chatbot** | ✅ Real-time validation | ✅ Yes | ✅ Kontinuerlig | ✅ Preferred |
| **Oppsummeringer** | ✅ With correction | ⚠️ Hvis RAG | ✅ A/B testing | 🚫 Mindre relevant |
| **Offentlig sektor FAQ** | ✅ Generic domain | ✅ Mandatory | ✅ Compliance audit | ✅ Preferred |
| **Forskningsassistent** | ⚠️ Latency-cost tradeoff | ✅ Critical | ✅ Quality metrics | ✅ With Academic Search |

### Beslutningstabell: Non-reasoning vs Reasoning Mode

| Faktor | Non-reasoning | Reasoning |
|--------|---------------|-----------|
| **Latency** | ~200-500ms | ~1-3s |
| **Kostnad** | Kun Content Safety | Content Safety + Azure OpenAI |
| **Output** | Boolean + percentage | Boolean + percentage + explanation |
| **Use case** | Prod filtering | Debugging/audit trail |

### Vanlige feil

| Problem | Symptom | Løsning |
|---------|---------|---------|
| **Manglende grounding sources** | API error eller lav accuracy | Sørg for å sende relevante `groundingSources` array |
| **Feil domain-valg** | Lav precision | Bruk `MEDICAL` for helsedata, `GENERIC` for resten |
| **For generisk query** | Mange false positives | Vær spesifikk i QnA-task `query`-felt |
| **Citations ikke validert** | Brudd på compliance | Implementer citation validation i test-suite |
| **Ingen correction-handling** | Brukere ser ungrounded svar | Bruk `correction: true` eller fallback til "I don't know" |

### Røde flagg (stopp og revurder)

- ❌ Du har ikke implementert groundedness-sjekk i medisinske/juridiske applikasjoner
- ❌ Du stoler på LLM citations uten å verifisere mot faktiske kilder
- ❌ Du har ikke rate limiting for Groundedness API-kall
- ❌ Du bruker ikke reasoning mode i dev/test før prod-deploy
- ❌ Du har ingen metrikker for groundedness i produksjon

## Integrasjon med Microsoft-stakken

### Azure AI Content Safety

**Endpoint:**
```
POST https://<resource>.cognitiveservices.azure.com/contentsafety/text:detectGroundedness?api-version=2024-09-15-preview
```

**Headers:**
```http
Ocp-Apim-Subscription-Key: <key>
Content-Type: application/json
```

**Body:**
```json
{
  "domain": "Generic",
  "task": "QnA",
  "qna": { "query": "..." },
  "text": "<LLM output>",
  "groundingSources": ["<doc1>", "<doc2>"],
  "reasoning": false,
  "correction": false
}
```

**Begrensninger:**
- Kun engelsk språk (garantert kvalitet)
- Tekst: maks 7500 tegn
- Grounding sources: se input requirements
- Regional availability: Sjekk [dokumentasjon](https://learn.microsoft.com/en-us/azure/ai-services/content-safety/overview#region-availability)

### Azure AI Foundry

**Groundedness som del av Content Filters:**

I Azure AI Foundry kan groundedness detection kjøres som del av content filtering pipeline:

```python
# I AI Foundry portal: Guardrails + controls → Try it out → Groundedness detection

# Via SDK:
from azure.ai.evaluation import GroundednessEvaluator

evaluator = GroundednessEvaluator(
    azure_ai_project={"subscription_id": "...", "project_name": "..."},
    credential=DefaultAzureCredential(),
    threshold=2  # 1-5 skala (lavere = strengere)
)
```

### Azure OpenAI (RAG med On Your Data)

**Konfigurasjon for groundedness:**

Når du bruker Azure OpenAI "On Your Data"-feature:

1. **Strictness-parameter:** Juster hvor strengt retrieval matcher query (1-5)
2. **Limit responses to data content:** Tvinger modellen til kun å svare basert på hentet data
3. **Number of retrieved documents:** Balansér mellom kontekst og presisjon

**Anbefaling for offentlig sektor:**
- Strictness: 4-5 (høy)
- Limit to data: ✅ Enabled
- Retrieved docs: 3-5

### Copilot Studio

**Generative Answers med grounding:**

Copilot Studio har innebygd grounding via:
- **Dataverse-integrasjon:** Automatisk grounding mot organisasjonsdata
- **SharePoint/Web search:** Konfigurerbare kildefiltre
- **Citation tracking:** Synlige kilder i chatbot-svar

**Best practice:**
- Aktiver "Show sources" i Generative Answers-node
- Konfigurer "Grounding" setting til "High" for offentlig sektor
- Bruk "Content moderation" sammen med groundedness

### Power Platform AI Builder

**Ingen native groundedness API**, men kan integreres via:
- Custom connector til Azure AI Content Safety
- Power Automate flow som kaller Groundedness API post-generation

## Offentlig sektor (Norge)

### Forvaltningsloven og veiledningsplikt

**§ 11. Veiledningsplikt:**
> Forvaltningsorganet skal på en hensynsfull måte påse at saken er så godt opplyst som mulig før vedtak treffes.

**Groundedness-krav:**
- Offentlige AI-systemer som gir veiledning **må** kunne dokumentere faktabaserte svar
- Hallusinasjoner i veiledningskontekst kan være lovstridig mangelfull saksbehandling
- **Anbefaling:** Groundedness detection med `reasoning: true` for audit trail

### Dokumentasjonsplikt (Arkivlova)

AI-genererte svar som er del av saksbehandling må dokumenteres:
- Lagre groundedness-score per respons
- Lagre grounding sources som ble brukt
- Lagre correction events hvis detektert ungroundedness

**Teknisk løsning:**
```python
# Log til Azure Monitor eller Application Insights
logger.info("AI Response", extra={
    "query": user_query,
    "response": final_response,
    "grounding_sources": [doc.id for doc in sources],
    "groundedness_score": result.groundedness,
    "ungrounded_detected": result.ungrounded_detected,
    "correction_applied": correction_applied
})
```

### DPIA-krav (GDPR Art. 35)

Groundedness-validering er relevant for DPIA hvis:
- AI-system fatter eller foreslår automatiserte beslutninger
- System gir råd som påvirker rettigheter (NAV, Skatteetaten, etc.)

**DPIA-punkt:**
- Beskriv groundedness validation som risikoreduserende tiltak
- Dokumenter threshold-valg og reasoning for false positive/negative-balanse
- Inkluder cost-benefit av correction-feature

### EIF (European Interoperability Framework)

**Semantic interoperability:**
- Groundedness sikrer at AI-svar er semantisk konsistente med authoritative sources
- Viktig for cross-border AI-tjenester i offentlig sektor

## Kostnad og lisensiering

### Azure AI Content Safety Groundedness API

**Prismodell (per 1000 text records):**
- **Non-reasoning mode:** ~0.75 USD per 1K requests
- **Reasoning mode:** Content Safety fee + Azure OpenAI GPT-4o inference
- **Correction mode:** Content Safety fee + Azure OpenAI GPT-4o generation

**Estimat for chatbot med 10K queries/dag:**
- Non-reasoning: ~225 USD/måned
- Reasoning (10% av queries for audit): ~300-400 USD/måned

### Azure OpenAI (for correction/reasoning)

**GPT-4o pricing (når brukt med Groundedness API):**
- Input tokens: ~0.0025 USD per 1K tokens
- Output tokens: ~0.010 USD per 1K tokens

**Grounding sources overhead:**
- Gjennomsnittlig grounding source: 500-2000 tokens
- Med 3 sources: ~1500-6000 tokens input per request

**Cost optimization:**
- Bruk non-reasoning i prod, reasoning i dev/test
- Implementer caching av groundedness-sjekker for identiske query+source-kombinasjoner
- Rate limit API-kall per bruker

### Lisensiering

**Inkludert i:**
- Azure AI Services commitment (Foundry-lisenser)
- Consumption-based (pay-as-you-go)

**Ikke inkludert i:**
- Microsoft 365 Copilot-lisenser (de har egne groundedness-mekanismer)

**Grounding with Bing Search:**
- Eget prisnivå (se [Bing Grounding pricing](https://www.microsoft.com/bing/apis/grounding-pricing))
- Ikke dekket av Azure Data Protection Addendum (dataflyt utenfor Azure compliance boundary)

## For arkitekten (Cosmo)

### Spørsmål å stille kunden

1. **Domene og kritikalitet:**
   - Er dette medisinsk, juridisk eller annen høy-risiko domene?
   - Hva er konsekvensen av en hallusinasjon i produksjon?
   - Trenger dere audit trail av groundedness-sjekker?

2. **RAG-arkitektur:**
   - Hvilke grounding sources skal brukes? (Azure AI Search, SharePoint, Dataverse?)
   - Hvor mange dokumenter er typisk relevante per query?
   - Har dere allerede embeddings og vector search?

3. **Latency-toleranse:**
   - Kan dere akseptere 1-3s ekstra latency for reasoning mode?
   - Er dette en real-time chatbot eller batch-prosessering?

4. **Budsjettering:**
   - Hva er query-volumet per dag/måned?
   - Hvor stor andel trenger reasoning/correction? (100% er kostbart)

5. **Compliance:**
   - Er dette offentlig sektor med dokumentasjonsplikt?
   - Trenger dere DPIA-dokumentasjon av groundedness-validering?

6. **Eksisterende arkitektur:**
   - Bruker dere allerede Azure AI Content Safety for andre filters?
   - Er Azure AI Foundry evaluation SDK i bruk?

### Fallgruver å unngå

1. **Over-reliance på groundedness API som eneste sikkerhet:**
   - Groundedness != faktualitet mot eksterne sannheter
   - API sjekker kun consistency mot oppgitte sources
   - **Løsning:** Kombiner med faktasjekk mot autoritative databaser

2. **Glemme rate limiting:**
   - Groundedness API har query rate limits
   - **Løsning:** Implementer exponential backoff og queueing

3. **Feil expectation om language support:**
   - Kun engelsk er garantert kvalitet
   - **Løsning:** For norsk: vurder oversettelse til engelsk før API-kall (overhead)

4. **Ikke teste reasoning mode før prod:**
   - Reasoning gir forklaringer som kan avsløre svakheter
   - **Løsning:** Alltid kjør reasoning i dev/test-fase

5. **Undervurdere grounding source quality:**
   - "Garbage in, garbage out" gjelder også for groundedness
   - **Løsning:** Valider at grounding sources faktisk er authoritative

6. **Manglende citation validation:**
   - Agents kan generere citations som ikke finnes
   - **Løsning:** Valider at citerte URLer/document IDs faktisk eksisterer

### Arkitekturanbefalinger

**For høy-risiko domener (medisinsk, juridisk, offentlig saksbehandling):**
```
1. Groundedness API med reasoning=true (audit trail)
2. Citation verification (valider at kilder eksisterer)
3. Human-in-the-loop for final approval
4. Logging til Azure Monitor med retention
```

**For medium-risiko (kundesupport, intern FAQ):**
```
1. Groundedness API med non-reasoning (real-time)
2. Correction feature enabled
3. Evaluation pipeline i dev/test
4. Basic citation tracking
```

**For lav-risiko (generell informasjon, ikke-kritisk):**
```
1. Agentic retrieval med built-in citations
2. Post-generation evaluation (sampling)
3. User feedback loop
```

### Tekniske tips

**Optimalisering av grounding sources:**
```python
# Prioriter de mest relevante kildene
ranked_sources = rerank_documents(query, retrieved_docs)
top_sources = ranked_sources[:3]  # Begrens til topp 3 for cost

# Send kun nødvendig context
grounding_texts = [extract_relevant_passage(doc, query) for doc in top_sources]
```

**Retry-logikk:**
```python
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10)
)
def check_groundedness(text, sources):
    return client.text_detect_groundedness(
        domain="GENERIC",
        task="QnA",
        text=text,
        grounding_sources=sources
    )
```

**Caching strategy:**
```python
import hashlib
from functools import lru_cache

def cache_key(text, sources):
    content = text + "".join(sources)
    return hashlib.sha256(content.encode()).hexdigest()

@lru_cache(maxsize=1000)
def cached_groundedness_check(key):
    # Implementer actual API call
    pass
```

## Kilder og verifisering

### Microsoft Learn-ressurser (Verified via MCP)

1. **Groundedness Detection Concept:**
   https://learn.microsoft.com/en-us/azure/ai-services/content-safety/concepts/groundedness
   [Verified: 2026-02]

2. **Groundedness Detection Quickstart:**
   https://learn.microsoft.com/en-us/azure/ai-services/content-safety/quickstart-groundedness
   [Verified: 2026-02]

3. **Content Filter Groundedness (Azure OpenAI):**
   https://learn.microsoft.com/en-us/azure/ai-foundry/openai/concepts/content-filter-groundedness
   [Verified: 2026-02]

4. **Azure AI Evaluation SDK (Groundedness Evaluator):**
   https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/develop/evaluate-sdk
   [Verified: 2026-02]

5. **Azure AI Search Grounding (Transparency Note):**
   https://learn.microsoft.com/en-us/azure/ai-foundry/responsible-ai/search/transparency-note
   [Verified: 2026-02]

6. **Bing Grounding Tools for Agents:**
   https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/tools/bing-tools
   [Verified: 2026-02]

7. **Security Planning for LLM Applications (Output Validation):**
   https://learn.microsoft.com/en-us/ai/playbook/technology-guidance/generative-ai/mlops-in-openai/security/security-plan-llm-application
   [Verified: 2026-02]

### Konfidensnivå

| Seksjon | Kilde | Konfidens |
|---------|-------|-----------|
| Groundedness Detection API | Microsoft Learn (MCP-verified) | ✅ Verified |
| Citation Verification | Microsoft Learn (MCP-verified) | ✅ Verified |
| Evaluation Frameworks | Microsoft Learn (MCP-verified) | ✅ Verified |
| Arkitekturmønstre | Baseline (modellkunnskap) + MCP-grunnlag | 🟡 Baseline |
| Offentlig sektor Norge | Baseline (modellkunnskap) + kjent lovverk | 🟡 Baseline |
| Kostnadsestimater | Baseline (modellkunnskap av prismodeller) | 🟡 Baseline |

**MCP-kall utført:** 4 (2x docs_search, 1x code_sample_search, 2x docs_fetch)
**Kilder hentet:** 7 Microsoft Learn-artikler
**Sist oppdatert:** 2026-02-05
