# Streaming and Real-Time RAG Responses

**Last updated:** 2026-02
**Status:** GA
**Category:** RAG Architecture & Semantic Search

---

## Introduksjon

Streaming av RAG-responser innebærer å returnere generert innhold token-for-token eller i små chunks mens modellen prosesserer, fremfor å vente på hele svaret. Dette gir dramatisk redusert opplevd latency og bedre brukeropplevelse ved at tekst vises progressivt på samme måte som ChatGPT og andre moderne AI-grensesnitt.

I tradisjonelle RAG-implementeringer sender klienten en forespørsel, systemet søker i vektordatabasen, augmenterer prompten med kontekst, og LLM-en genererer et komplett svar før noe returneres. Dette kan ta flere sekunder eller minutter for komplekse spørsmål. Med streaming begynner teksten å vises umiddelbart etter første token er generert, noe som gir brukeren tilbakemelding om at systemet arbeider og lar dem begynne å lese svaret mens resten genereres.

Azure OpenAI Service støtter streaming via Server-Sent Events (SSE) i både Chat Completions API og den nyere Responses API. SSE er en HTTP-basert protokoll som holder en langvarig forbindelse åpen og sender datachunks til klienten etterhvert som de blir tilgjengelige. For RAG-applikasjoner betyr dette at retrieval-fasen skjer først (vanligvis ikke-streamet), deretter starter streaming av generert tekst umiddelbart når LLM-en begynner å produsere output.

## Kjernekomponenter

### Server-Sent Events (SSE)

SSE er den primære teknologien for streaming fra Azure OpenAI:

| Komponent | Beskrivelse |
|-----------|-------------|
| **Protokoll** | Enveiskommunikasjon over HTTP (server → klient) |
| **Content-Type** | `text/event-stream` |
| **Event format** | `data: <JSON>\n\n` per event |
| **Avslutning** | `data: [DONE]` signal når streaming er ferdig |
| **Forbindelse** | Langvarig HTTP-tilkobling med `Connection: keep-alive` |
| **Chunk-kodning** | `Transfer-Encoding: chunked` uten Content-Length |

### Azure OpenAI Streaming API

**Chat Completions API** (`stream: true`):
- Enkleste implementasjon for streaming
- Returnerer `ChatCompletionChunk` objekter
- Hver chunk inneholder delta med ny tekst (`choices[0].delta.content`)
- Støtter function calling (argumenter streams også)
- Eksempel response: `{"choices":[{"delta":{"content":"Hello"},"index":0}]}`

**Responses API** (`stream: true`):
- Nyere API som kombinerer Chat Completions og Assistants
- Støtter både synkron og asynkron (background) streaming
- Event-basert format: `response.output_text.delta`
- Kan gjenoppta streaming fra en `sequence_number` ved avbrudd
- Støtter stateful conversations med `previous_response_id`

### Streaming Content Types

I Semantic Kernel Agent Framework brukes spesialiserte content-typer:

| Type | Formål |
|------|--------|
| `StreamingChatMessageContent` | Container for streaming-meldinger |
| `StreamingTextContent` | Token-by-token tekstchunks |
| `StreamingFileReferenceContent` | Filreferanser under streaming |
| `StreamingAnnotationContent` | Metadata og citations i stream |

### Client-side Buffering

Progressive rendering krever håndtering av:
- **Partial tokens** — inkomplette ord eller JSON-strukturer
- **UI updates** — effektiv re-render av chunks uten flimmering
- **Function calls** — pause i streaming når modellen kaller en funksjon
- **Error handling** — reconnect-logikk ved avbrudd

## Arkitekturmønstre

### Mønster 1: Standard RAG med Chat Completions Streaming

**Bruk når:** Enkel RAG-applikasjon med Azure Cognitive Search eller annen vektordb.

**Fordeler:**
- Enklest å implementere (én API-kall med `stream=true`)
- Lavest latency fra første token til bruker
- Kompatibel med alle Azure OpenAI modeller

**Ulemper:**
- Retrieval-fasen er ikke streambar (må vente på søkeresultater før streaming starter)
- Ingen innebygd støtte for gjenopptagelse ved avbrudd

**Arkitektur:**
```
User Query → Azure Cognitive Search (vector search)
          → Prompt augmentation (inject context)
          → Azure OpenAI Chat Completions (stream=true)
          → SSE stream → Client (progressive display)
```

**Implementering:**
```python
from openai import OpenAI

client = OpenAI(
    base_url="https://YOUR-RESOURCE.openai.azure.com/openai/v1/",
    api_key=os.getenv("AZURE_OPENAI_API_KEY")
)

# 1. Retrieval phase (ikke streamet)
search_results = azure_search_client.search(query, top=5)
context = "\n".join([doc['content'] for doc in search_results])

# 2. Augmentation + streaming generation
messages = [
    {"role": "system", "content": f"Answer based on: {context}"},
    {"role": "user", "content": query}
]

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end='', flush=True)
```

### Mønster 2: Responses API med Background Streaming

**Bruk når:** Lange prosesser (reasoning modeller, kompleks research) som kan ta minutter.

**Fordeler:**
- Unngår timeouts på langvarige operasjoner
- Kan gjenoppta streaming fra `sequence_number` ved avbrudd
- Støtter async workflows (brukeren kan komme tilbake senere)

**Ulemper:**
- Høyere time-to-first-token latency enn synkron streaming
- Krever polling-logikk eller WebSocket for status-oppdateringer

**Arkitektur:**
```
User Query → RAG retrieval → Responses API (background=true, stream=true)
          → Poll status (in_progress) → Resume stream fra sequence_number
          → Display chunks progressivt → Final status (completed)
```

**Implementering:**
```python
# Start background streaming response
response = client.responses.create(
    model="o3",
    input=augmented_prompt,
    background=True,
    stream=True
)

cursor = None
for event in response:
    if event.type == 'response.output_text.delta':
        print(event.delta, end='', flush=True)
        cursor = event["sequence_number"]

# Ved avbrudd: gjenoppta fra cursor
# GET /responses/{response_id}?stream=true&starting_after={cursor}
```

### Mønster 3: Semantic Kernel Agent med Callback

**Bruk når:** RAG med function calling, multi-turn conversations, behov for å håndtere intermediate steps.

**Fordeler:**
- Håndterer function call results i streaming-flyten
- Callback for intermediate messages (tool calls, partial results)
- Innebygd support i Semantic Kernel Agent Framework

**Ulemper:**
- Mer kompleks setup (krever agent framework)
- Overhead fra framework kan øke latency marginalt

**Arkitektur:**
```
User → Agent (invoke_stream) → RAG retrieval (via function)
    → Streaming response + callback for function results
    → Progressive display + intermediate step logging
```

**Implementering:**
```python
from semantic_kernel.agents import AzureResponsesAgent

async def handle_intermediate(message: ChatMessageContent) -> None:
    for item in message.items:
        if isinstance(item, FunctionResultContent):
            print(f"Retrieved: {item.result[:100]}...")

async for response in agent.invoke_stream(
    messages=user_input,
    thread=thread,
    on_intermediate_message=handle_intermediate
):
    print(response.content, end='', flush=True)
```

## Beslutningsveiledning

### Velg Streaming-mønster

| Scenario | Anbefalt mønster | Begrunnelse |
|----------|------------------|-------------|
| RAG med Azure Cognitive Search, < 10s responstid | Chat Completions streaming | Enklest, lavest latency |
| RAG med reasoning modeller (o3, o1-pro), > 1 min | Responses API background streaming | Unngår timeouts |
| Agentic RAG med function calling | Semantic Kernel Agent | Callback-støtte for intermediate steps |
| RAG med flere søk (multi-hop) | Responses API stateful | Reuse context med `previous_response_id` |
| Real-time RAG med WebSocket | Custom med SSE → WebSocket bridge | Bedre browser-support, bidireksjonell kommunikasjon |

### Vanlige feil

| Feil | Symptom | Løsning |
|------|---------|---------|
| **Buffer timeout** | Stream stopper midt i token | Sett `request_timeout > 120s` i backend settings |
| **UI flimmering** | Tekst hopper eller blinker | Bruk React/Vue streaming-biblioteker (react-markdown streaming) |
| **Connection drop** | Stream mister forbindelse | Implementer reconnect med `sequence_number` resume |
| **Partial JSON** | Function call argumenter er inkomplette | Buffer chunks til `finish_reason == "function_call"` |
| **Cache interference** | CDN cacher SSE-responser | Sett `Cache-Control: no-cache` header |

### Røde flagg

- **Streaming før retrieval er ferdig** — vises som tomme eller irrelevante første tokens
- **Ingen progress indicator** — brukeren tror systemet har hengt seg
- **Manglende error streaming** — errors bør også vises progressivt (ikke bare status 500)
- **Synkron retrieval i async context** — blokkerer streaming-start

## Integrasjon med Microsoft-stakken

### Azure OpenAI Service

| Feature | Support | Notes |
|---------|---------|-------|
| **Chat Completions streaming** | GA | `stream=true` parameter |
| **Responses API streaming** | GA | Event-based format |
| **Background streaming** | GA | For o3, o1-pro modeller |
| **Resume streaming** | GA | Via `sequence_number` |
| **Function calling streaming** | GA | Arguments streams token-by-token |

### Azure Application Gateway

For SSE-trafikk gjennom Application Gateway:

**Kritiske innstillinger:**
- **Response buffers** — må være `disabled` (ellers bufres hele responsen)
- **Request timeout** — sett > streaming-forventet tid (f.eks. 300s)
- **Backend headers** — `Connection: keep-alive`, `Transfer-Encoding: chunked`

**Konfigurasjon:**
```json
{
  "backendHttpSettings": {
    "requestTimeout": 300,
    "responseBuffering": false
  }
}
```

### Azure API Management

SSE-konfigurasjon krever:
- **Route timeout** — øk fra standard 30s til 300s+
- **No caching** — disable cache policies for SSE endpoints
- **Passthrough mode** — ikke transformer SSE events

### Semantic Kernel

**Agent Framework** (C#, Python):
```python
from semantic_kernel.agents import ChatCompletionAgent

agent = ChatCompletionAgent(...)

async for response in agent.invoke_stream(message, thread):
    # response er StreamingChatMessageContent
    print(response.content, end='', flush=True)
```

**ChatHistory oppdateres** kun etter full response er mottatt (ikke per chunk).

### Power Platform

**Power Automate** støtter ikke native SSE-streaming. Workaround:
- Bruk polling-flow (sjekk status hver 2s)
- Eller WebSocket via Azure Functions bridge

**Copilot Studio** støtter streaming via SSE for custom connectors (preview).

## Offentlig sektor (Norge)

### Network Policies

SSE krever langvarige HTTP-forbindelser som kan trigge firewall-regler:

| Policy | Anbefaling |
|--------|------------|
| **Idle timeout** | Min. 300s for SSE-forbindelser |
| **Connection limits** | Whitelist Azure OpenAI FQDN for lengre forbindelser |
| **Proxy compatibility** | Test med organisasjonens proxy (noen buffer SSE) |
| **TLS requirements** | TLS 1.2+ (SSE over HTTPS) |

**Kompetanse Norges nettverk:** Hvis proxy buffer disabler SSE, bruk Azure API Management som mellommann med custom buffering.

### Tilgjengelighet (WCAG)

Streaming text må være skjermleser-vennlig:

- **ARIA live regions** — `aria-live="polite"` på streaming-container
- **Focus management** — ikke flytt focus mens tekst streams
- **Pause/resume** — brukeren må kunne pause streaming (tilgjengelighetskrav)
- **Skip to end** — shortcut for å hoppe til ferdig svar

**Eksempel:**
```html
<div role="log" aria-live="polite" aria-atomic="false">
  <!-- Streaming text appends here -->
</div>
```

### Personvern (GDPR)

Streaming påvirker logging:

- **Ikke logg partial responses** — vent til `[DONE]` før logging
- **Redaction** — PII-filtrering må skje server-side før streaming starter
- **Audit trail** — logg `sequence_number` for replay-evne

## Kostnad og lisensiering

### Prismodell

Streaming koster **det samme per token** som ikke-streaming:

| Modell | Input (NOK/1K tokens) | Output (NOK/1K tokens) |
|--------|----------------------|------------------------|
| **gpt-4o** | ~0.28 | ~1.12 |
| **gpt-4o-mini** | ~0.014 | ~0.056 |
| **o3** (reasoning) | ~1.68 | ~6.72 |

**Kostnadsoptimalisering:**
- Stream kun når bruker venter (ikke for background jobs)
- Bruk `max_tokens` for å begrense lange responses
- Cache retrieval-resultater (reduserer total tokens ved re-prompts)

### Code Interpreter (Responses API)

Ved bruk av streaming med `code_interpreter` tool:

- **Tilleggskostnad:** 0.28 NOK/session (utover tokens)
- **Session lifetime:** 1 time (idle timeout 20 min)
- **Simultane sessions:** Hver parallel streaming med code_interpreter = ny session

### Lisenskrav

| Tjeneste | Lisenskrav |
|----------|------------|
| **Azure OpenAI** | Azure subscription + OpenAI resource |
| **Semantic Kernel** | Open source (MIT) — ingen lisenskostnad |
| **Application Gateway** | Azure-kostnad (per gateway-time) |
| **API Management** | Per call/måned tier (Consumption for lavt volum) |

## For arkitekten (Cosmo)

### Spørsmål å stille kunden

1. **Forventet responstid?** — Hvis > 30s, vurder background streaming.
2. **Brukerkontekst?** — Chatbot (streaming obligatorisk) vs. batch report (streaming unødvendig).
3. **Network environment?** — Proxy, firewall, idle timeouts kan blokkere SSE.
4. **Function calling?** — Krever callback-logikk for å håndtere intermediate steps.
5. **Mobile vs. web?** — Mobile app kan ha ustabil forbindelse → trenger resume-logikk.
6. **WCAG-krav?** — Streaming må være pause-bar og skjermleser-kompatibel.
7. **Error handling?** — Hvordan skal partial failures vises? (stream error chunks vs. abort)
8. **Token budget?** — Streaming gjør det vanskelig å stoppe ved token-limit (pre-calculate max_tokens).

### Fallgruver

| Fallgruve | Konsekvens | Unngå ved |
|-----------|------------|-----------|
| **Stream før retrieval** | Tomme/irrelevante første tokens | Buffer retrieval-resultater før streaming starter |
| **Ingen reconnect-logikk** | Brukeren ser halvferdig svar | Implementer `sequence_number` resume |
| **Synkron I/O i async context** | Blokkerer streaming | Bruk `asyncio` i Python, `async/await` i C# |
| **Manglende `Cache-Control`** | CDN cacher SSE | Sett `no-cache` header på backend |
| **Ingen progress feedback** | Brukeren venter uten tilbakemelding | Vis spinner til første chunk arrives |

### Anbefalinger per modenhetsnivå

**Nivå 1 (POC):**
- Bruk Chat Completions API med `stream=true`
- Ignorer error handling (fail fast)
- Bruk `print()` for debugging (console output)

**Nivå 2 (Pilot):**
- Implementer reconnect-logikk med timeout
- Legg til ARIA live regions for tilgjengelighet
- Test med organisasjonens proxy/firewall

**Nivå 3 (Produksjon):**
- Bruk Responses API med background streaming for lange prosesser
- Implementer full observability (logg sequence_number, latency per chunk)
- A/B-test streaming vs. ikke-streaming for UX-gevinst
- Set opp Application Gateway med optimale SSE-innstillinger

**Nivå 4 (Skalering):**
- Implementer custom WebSocket-bridge for bedre mobile support
- Bruk Azure SignalR for multi-region streaming med fallback
- Implementer adaptive streaming (juster chunk-størrelse basert på nettverkshastighet)

## Kilder og verifisering

### Verified (fra MCP microsoft-learn)

- Azure OpenAI Responses API streaming: https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/responses (Verified: 2026-02)
- Semantic Kernel Agent streaming: https://learn.microsoft.com/en-us/semantic-kernel/frameworks/agent/agent-streaming (Verified: 2026-02)
- SSE med Application Gateway: https://learn.microsoft.com/en-us/azure/application-gateway/use-server-sent-events (Verified: 2026-02)
- Azure OpenAI REST API reference: https://learn.microsoft.com/en-us/azure/foundry/openai/reference (Verified: 2026-02)
- Chat Completions API streaming: https://learn.microsoft.com/en-us/azure/foundry/openai/reference#chat-completions (Verified: 2026-02)

### Baseline (modellkunnskap)

- SSE standard (W3C EventSource): Baseline (standard protocol knowledge)
- WebSocket vs. SSE tradeoffs: Baseline (industry best practices)
- React streaming patterns: Baseline (frontend framework knowledge)
- WCAG streaming requirements: Baseline (accessibility standards)

**Konfidensnivå per seksjon:**
- Introduksjon: High (Verified via Responses API docs)
- Kjernekomponenter: High (Verified via REST API reference + Semantic Kernel docs)
- Arkitekturmønstre: High (Verified code samples from microsoft-learn)
- Beslutningsveiledning: Medium (Kombinasjon av verified patterns + experience-based)
- Integrasjon med Microsoft-stakken: High (Verified via Application Gateway + API Management docs)
- Offentlig sektor: Medium (Verified network policies + baseline tilgjengelighet)
- Kostnad: High (Verified via Azure OpenAI pricing 2026-02)
- For arkitekten: Medium (Experience-based best practices)

---

*Denne kunnskapsreferansen er generert med MCP microsoft-learn search/fetch (2026-02) og representerer siste tilgjengelige dokumentasjon fra Microsoft Learn.*
