Steg 9 (R4): unified migrate-corpus.mjs --write over engineering/governance/ infrastructure/security. 327 filer mutert, verified=null, prosa byte-identisk (fra første ## seksjon), advisor urørt (0 endringer). To applier-fixes oppdaget under kjøring (TDD, RED→GREEN): - insertHeaderFields: anker faller nå tilbake når en meta-linje selv passerer 500B (2 filer pakket et avsnitt i **Status:** → Type/Source landet utenfor scan-vinduet, applierens post-write-assertion fanget + restaurerte). - normalizeStaleVerified: fjerner nå ALLE stale non-date **Verified:** i 500B-vinduet, inkl. stray body-dup rett under --- (9 mlops-genaiops-filer var ellers falskt "verified"/fresh, droppet fra worklist). Operatør-godkjent utvidelse av carve-out; kun stray metadata-linjer, aldri prosa. test-transform-criterion: precondition oppdatert til post-migrasjons-sannhet (fila bærer nå Source). Suite 728/728 grønn.
449 lines
17 KiB
Markdown
449 lines
17 KiB
Markdown
# Streaming and Real-Time RAG Responses
|
|
|
|
**Last updated:** 2026-06-24
|
|
**Status:** GA
|
|
**Category:** RAG Architecture & Semantic Search
|
|
**Type:** reference
|
|
**Source:** https://learn.microsoft.com/azure/foundry/openai/reference
|
|
|
|
---
|
|
|
|
## Innhold
|
|
|
|
- [Introduksjon](#introduksjon)
|
|
- [Kjernekomponenter](#kjernekomponenter)
|
|
- [Arkitekturmønstre](#arkitekturmønstre)
|
|
- [Beslutningsveiledning](#beslutningsveiledning)
|
|
- [Integrasjon med Microsoft-stakken](#integrasjon-med-microsoft-stakken)
|
|
- [Offentlig sektor (Norge)](#offentlig-sektor-norge)
|
|
- [Kostnad og lisensiering](#kostnad-og-lisensiering)
|
|
- [For arkitekten (Cosmo)](#for-arkitekten-cosmo)
|
|
- [Kilder og verifisering](#kilder-og-verifisering)
|
|
|
|
## 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.*
|