# Audio and Video Transcription Workflow Architecture **Last updated:** 2026-02 **Status:** GA **Category:** Multi-Modal AI **Type:** reference --- ## Innhold - [Introduksjon](#introduksjon) - [Kjernekomponentar](#kjernekomponentar) - [Batch Transcription at Scale](#batch-transcription-at-scale) - [Speaker Attribution og Diarization](#speaker-attribution-og-diarization) - [Automatic Translation with Context Preservation](#automatic-translation-with-context-preservation) - [Quality Assurance og Human-in-the-Loop Workflows](#quality-assurance-og-human-in-the-loop-workflows) - [Implementeringsmønstre](#implementeringsmønstre) - [Norsk offentleg sektor](#norsk-offentleg-sektor) - [Beslutningsrammeverk](#beslutningsrammeverk) - [For Cosmo](#for-cosmo) ## Introduksjon Azure Speech Services tilbyr omfattande kapabilitetar for transkribering og oversettelse av audio- og videoinnhald. Tenesta støttar sanntids- og batch-transkribering med funksjonar som taleridentifisering (diarization), automatisk språkdeteksjon, ordnivå-tidsstempel og tilpassa talemodeller. For norsk offentleg sektor er dette relevant for møtetranskribering, arkivering av telefonsamtalar, tekstforming av video, og tilgjengeleggjering av audioinnnhald. Batch Transcription API er designa for å transkribere store mengder lydfilar lagra i Azure Blob Storage eller tilgjengelege via URL. Prosesseringa skjer asynkront og er optimal for arkivtranskribering, callcenter-analyse og untertekstproduksjon. Fast Transcription API (preview) tilbyr synkron prosessering med lågare latency for kortare lydfiler. Sanntidstranskribering via Speech SDK er eigna for live-scenario. Azure Video Indexer (tidlegare Azure Media Services Video Indexer) tilbyr AI-driven analyse av videoinnhald, inkludert automatisk transkribering, omsetjing, emnedeteksjon, ansiktsdeteksjon og scene-segmentering. For heilskaplege audio/video-transkripsjonsworkflowar bør ein vurdere å kombinere Speech Services, Video Indexer og Azure OpenAI for oppsummering og innsikt. --- ## Kjernekomponentar | Komponent | Formål | Teknologi | |-----------|--------|-----------| | Real-time STT | Sanntids tale-til-tekst | Azure Speech SDK | | Batch Transcription | Volumbasert asynkron transkribering | Speech to text REST API v3.1+ | | Fast Transcription | Synkron rask transkribering | Speech REST API (preview) | | Diarization | Taleridentifisering og -separering | Speech Services | | Speech Translation | Sanntids taleoversettelse | Translation SDK | | Custom Speech | Tilpassa talegjenkjenning | Azure Speech Studio | | Video Indexer | AI-driven videoanalyse | Azure Video Indexer | | Batch Processing Kit | Open-source container for volum-transkribering | GitHub/Docker Hub | --- ## Batch Transcription at Scale ### Batch Transcription API ```python import requests import json import time speech_key = os.environ["SPEECH_KEY"] speech_region = os.environ["SPEECH_REGION"] base_url = (f"https://{speech_region}.api.cognitive.microsoft.com" "/speechtotext/v3.1") headers = { "Ocp-Apim-Subscription-Key": speech_key, "Content-Type": "application/json" } # Opprett batch-transkribering transcription_payload = { "contentUrls": [ "https://storage.blob.core.windows.net/audio/meeting1.wav", "https://storage.blob.core.windows.net/audio/meeting2.wav", "https://storage.blob.core.windows.net/audio/meeting3.wav" ], "locale": "nb-NO", "displayName": "Kommunestyremøte Q4 2025", "properties": { "wordLevelTimestampsEnabled": True, "diarizationEnabled": True, "diarization": { "speakers": { "minCount": 2, "maxCount": 15 } }, "punctuationMode": "DictatedAndAutomatic", "profanityFilterMode": "Masked", "destinationContainerUrl": ( "https://mystorage.blob.core.windows.net/transcripts" "?sp=rwl&st=...&sig=..." ) } } # Start transkribering response = requests.post( f"{base_url}/transcriptions", headers=headers, data=json.dumps(transcription_payload) ) transcription_url = response.headers["Location"] print(f"Transkribering starta: {transcription_url}") # Poll for ferdigstilling while True: status = requests.get(transcription_url, headers=headers).json() if status["status"] in ["Succeeded", "Failed"]: break print(f"Status: {status['status']}") time.sleep(30) # Hent resultat if status["status"] == "Succeeded": files_url = f"{transcription_url}/files" files = requests.get(files_url, headers=headers).json() for file in files["values"]: if file["kind"] == "Transcription": result = requests.get( file["links"]["contentUrl"], headers=headers ).json() for phrase in result["recognizedPhrases"]: speaker = phrase.get("speaker", "Ukjent") text = phrase["nBest"][0]["display"] offset = phrase["offsetInTicks"] / 10_000_000 print(f"[{offset:.1f}s] Taler {speaker}: {text}") ``` ### Batch Processing Kit (open-source) For store volum med fleire Speech-containers: ```yaml # docker-compose.yml for batch processing kit version: "3" services: batch-kit: image: batchkit/speech-batch-kit:latest environment: - SPEECH_ENDPOINT=http://speech-container:5000 - INPUT_FOLDER=/input - OUTPUT_FOLDER=/output - DIARIZATION=true - LANGUAGE=nb-NO volumes: - ./audio-files:/input - ./transcripts:/output speech-container: image: mcr.microsoft.com/azure-cognitive-services/speechservices/speech-to-text environment: - EULA=accept - Billing=https://northeurope.api.cognitive.microsoft.com - ApiKey=${SPEECH_KEY} ports: - "5000:5000" ``` ### Skaleringsstrategiar | Volum | Strategi | Gjennomstrøyming | |-------|----------|-----------------| | < 100 filer/dag | Batch Transcription API direkte | Standard quota | | 100-1000 filer/dag | Batch API med auka quota | Kontakt Microsoft | | > 1000 filer/dag | Batch Processing Kit + fleire containers | Lineær skalering | | Sanntid + batch | Hybrid: SDK for live, API for arkiv | Ulike endpoints | --- ## Speaker Attribution og Diarization ### Sanntids diarization ```python import azure.cognitiveservices.speech as speechsdk speech_config = speechsdk.SpeechConfig( subscription=os.environ["SPEECH_KEY"], region=os.environ["SPEECH_REGION"] ) # Konfigurer for diarization speech_config.speech_recognition_language = "nb-NO" speech_config.set_property( speechsdk.PropertyId.SpeechServiceConnection_LanguageIdMode, "Continuous" ) audio_config = speechsdk.audio.AudioConfig( filename="meeting_recording.wav" ) # Opprett conversation transcriber conversation_transcriber = speechsdk.transcription.ConversationTranscriber( speech_config=speech_config, audio_config=audio_config ) transcript = [] done = False def transcribed_cb(evt): if evt.result.reason == speechsdk.ResultReason.RecognizedSpeech: transcript.append({ "speaker": evt.result.speaker_id, "text": evt.result.text, "offset": evt.result.offset, "duration": evt.result.duration }) print(f"Taler {evt.result.speaker_id}: {evt.result.text}") def canceled_cb(evt): nonlocal done done = True def stopped_cb(evt): nonlocal done done = True conversation_transcriber.transcribed.connect(transcribed_cb) conversation_transcriber.canceled.connect(canceled_cb) conversation_transcriber.session_stopped.connect(stopped_cb) # Start transkribering conversation_transcriber.start_transcribing_async() while not done: time.sleep(0.5) conversation_transcriber.stop_transcribing_async() ``` ### Diarization i batch-modus Batch Transcription API støttar opp til 35 talarar: ```json { "locale": "nb-NO", "properties": { "diarizationEnabled": true, "diarization": { "speakers": { "minCount": 2, "maxCount": 35 } }, "wordLevelTimestampsEnabled": true } } ``` ### Resultatformat med talarinformasjon ```json { "recognizedPhrases": [ { "speaker": 1, "offsetInTicks": 15000000, "durationInTicks": 35000000, "nBest": [{ "confidence": 0.92, "display": "Eg vil starte med å gå gjennom sakslista.", "words": [ {"word": "Eg", "offset": "PT1.5S", "duration": "PT0.2S"}, {"word": "vil", "offset": "PT1.7S", "duration": "PT0.15S"} ] }] }, { "speaker": 2, "offsetInTicks": 52000000, "durationInTicks": 28000000, "nBest": [{ "confidence": 0.88, "display": "Takk. Eg har eit spørsmål til sak nummer tre.", "words": [] }] } ] } ``` --- ## Automatic Translation with Context Preservation ### Sanntids taleoversettelse ```python import azure.cognitiveservices.speech as speechsdk translation_config = speechsdk.translation.SpeechTranslationConfig( subscription=os.environ["SPEECH_KEY"], region=os.environ["SPEECH_REGION"] ) # Gjenkjenn norsk, omset til fleire språk translation_config.speech_recognition_language = "nb-NO" translation_config.add_target_language("en") # Engelsk translation_config.add_target_language("ar") # Arabisk translation_config.add_target_language("so") # Somali translation_config.add_target_language("pl") # Polsk audio_config = speechsdk.audio.AudioConfig( filename="info_meeting.wav" ) recognizer = speechsdk.translation.TranslationRecognizer( translation_config=translation_config, audio_config=audio_config ) def recognized_cb(evt): if evt.result.reason == speechsdk.ResultReason.TranslatedSpeech: print(f"NORSK: {evt.result.text}") for lang, translation in evt.result.translations.items(): print(f" → {lang}: {translation}") recognizer.recognized.connect(recognized_cb) recognizer.start_continuous_recognition() import time time.sleep(300) # Køyr i 5 minutt recognizer.stop_continuous_recognition() ``` ### Post-transkribering AI-powered oversettelse For betre kontekstuell kvalitet, kombiner transkribering + Azure OpenAI: ```python def translate_transcript_with_context( transcript: list[dict], target_language: str ) -> list[dict]: """Omset transkripsjon med kontekst via GPT-4o.""" # Samle heile transkripsjonen for kontekst full_text = "\n".join( f"[Taler {t['speaker']}]: {t['text']}" for t in transcript ) response = openai_client.chat.completions.create( model="gpt-4o", messages=[ { "role": "system", "content": f"""Omset følgjande møtereferat frå norsk til {target_language}. Bevar: - Talaridentifikasjon ([Taler X]) - Fagterminologi (omset korrekt eller behold) - Formell tone (dette er offentleg forvaltning) - Kontekst mellom ytringane""" }, {"role": "user", "content": full_text} ], max_tokens=4000 ) return response.choices[0].message.content ``` --- ## Quality Assurance og Human-in-the-Loop Workflows ### Confidence-basert QA ```python def qa_transcript(transcript_result: dict, confidence_threshold: float = 0.7) -> dict: """QA av transkriberingsresultat med flagging.""" qa_report = { "total_phrases": 0, "high_confidence": 0, "low_confidence": 0, "flagged_phrases": [], "average_confidence": 0.0 } confidences = [] for phrase in transcript_result["recognizedPhrases"]: qa_report["total_phrases"] += 1 conf = phrase["nBest"][0]["confidence"] confidences.append(conf) if conf >= confidence_threshold: qa_report["high_confidence"] += 1 else: qa_report["low_confidence"] += 1 qa_report["flagged_phrases"].append({ "text": phrase["nBest"][0]["display"], "confidence": conf, "offset": phrase["offsetInTicks"] / 10_000_000, "speaker": phrase.get("speaker", "?"), "alternatives": [ n["display"] for n in phrase["nBest"][1:3] ] }) qa_report["average_confidence"] = ( sum(confidences) / len(confidences) if confidences else 0 ) return qa_report ``` ### Human-in-the-Loop workflow med Power Automate ``` Batch Transcription API → Azure Blob Storage (transcript.json) → Azure Function: QA-analyse → IF avg_confidence >= 0.85: → Automatisk godkjenning → Arkivsystem → IF avg_confidence 0.6-0.85: → Power Automate: Send flagga segment til korrekturles → Teams Adaptive Card til saksbehandlar → Manuell korrigering → Arkivsystem → IF avg_confidence < 0.6: → Varsling: Lydkvalitet for låg → Anbefal ny innspeling eller manuell transkribering ``` ### Custom Speech for betre norsk gjenkjenning ```python # Tren tilpassa modell med norsk fagterminologi # Steg 1: Førebu treningsdata training_data = { "plain_text": [ "Reguleringsplan for Stortorvet", "Detaljreguleringsplan med konsekvensutgreiing", "Klage på vedtak etter plan- og bygningslova" ], "structured_text": [ {"phrase": "bygningslova", "pronunciation": "BYG-NINGS-LO-VA"}, {"phrase": "detaljreguleringsplan", "pronunciation": "DE-TALJ-RE-GU-LE-RINGS-PLAN"} ] } # Steg 2: Opplasting via Speech Studio eller REST API # Steg 3: Tren modell # Steg 4: Deploy som custom endpoint # Steg 5: Bruk endpoint-ID i transkribering ``` --- ## Implementeringsmønstre ### Mønster 1: Møtetranskribering for kommunestyre ``` Teams-møte → Opptak (MP4/WAV) → Azure Blob Storage → Batch Transcription API (diarization ON) → QA-funksjon (confidence check) → Azure OpenAI: Oppsummering + vedtaksliste → Power Automate: Distribusjon → Saksbehandlingssystem (møteprotokoll) → Offentleg nettside (med tidskoda tekst) → Arkiv (eInnsyn-kompatibelt) ``` ### Mønster 2: Callcenter-analyse ``` Telefonopptak → Event Grid trigger → Azure Function → Batch Transcription → Cosmos DB (transcript + metadata) → Azure OpenAI: Sentiment + kategorisering → Power BI: Dashboard med KPI-ar → Gjennomsnittleg behandlingstid → Innbyggjartilfredsheit (sentiment) → Hyppigaste henvendelsestypar ``` ### Mønster 3: Untertekstproduksjon for offentleg video ``` Video → Azure Media Services (encode) → Speech Services: Transkribering (nb-NO) → Speech Translation: Omsetjing (en, ar, so) → VTT/SRT-filgenerering per språk → Azure CDN: Distribusjon → Videospelar med fleirspråklege untertekstar ``` --- ## Norsk offentleg sektor ### Lovkrav - **Offentlegheitslova**: Møteprotokoll skal vere tilgjengelege - **Likestillings- og diskrimineringslova**: Lydopptak må tekstast for tilgjengelegheit - **Arkivlova**: Transkripsjoner er arkivverdig materiale - **WCAG 2.1 AA**: Video skal ha teksting/untertekstar ### Språkstøtte for norsk - Norsk bokmål (`nb-NO`) fullt støtta for STT og TTS - Custom Speech med fagterminologi for betre resultat - Speech Translation frå norsk til 30+ målspråk - Diarization fungerer med norsk tale (opp til 35 talarar) ### Personvern - Lydopptak er personopplysingar — krev rettsleg grunnlag - Batch Transcription: Resultat kan lagrast i eigen Azure Storage - Ikkje lagring hos Microsoft etter prosessering (stateless) - Custom Speech-treningsdata: Kontroller plassering og tilgang - Anbefaling: Sletting av lydfilar etter transkribering om ikkje arkivpliktig ### DPIA-vurdering - Transkribering av møte/telefonar krev DPIA - Vurder: Samtykke, informasjonsplikt, lagringsbegrensing - Diarization identifiserer talarar med generisk ID — ikkje biometrisk identifikasjon - Container-deployment for ekstra datakontroll --- ## Beslutningsrammeverk | Scenario | Anbefaling | Grunngjeving | |----------|------------|--------------| | Live møtetranskribering | Speech SDK med ConversationTranscriber | Sanntid + diarization | | Arkiv-transkribering (100+ filer) | Batch Transcription API | Asynkron, skalerbar | | Kort lydfil (< 5 min) | Fast Transcription API | Synkron, låg latency | | Fleirspråkleg teneste | Speech Translation SDK | Sanntid omsetjing | | Kontekstuell omsetjing | Transkribering + GPT-4o | Betre kvalitet | | Callcenter-analyse | Batch + sentiment + oppsummering | End-to-end innsikt | | Video-untertekstar | Batch + VTT-generering | WCAG-kompatibelt | | On-premises krav | Speech containers + Batch Kit | Ingen data ut av nettverk | --- ## For Cosmo - **Batch Transcription API er standard for volumbasert transkribering** — asynkron prosessering av store lydarkiv med diarization (opp til 35 talarar), ordnivå-tidsstempel og automatisk interpunksjon, resultat til eigen Azure Storage. - **Diarization er tilgjengeleg i både sanntid og batch** — ConversationTranscriber (SDK) for live-møte, og `diarizationEnabled` + `diarization.speakers` i Batch API for opptak, med speaker-ID per frase i output. - **Norsk bokmål (`nb-NO`) er fullt støtta for STT** — Custom Speech med plain text og structured text-treningsdata forbetrar gjenkjenning av fagterminologi (t.d. "detaljreguleringsplan", "konsekvensutgreiing"). - **Kombiner transkribering med Azure OpenAI for verdiskaping** — GPT-4o kan oppsummere møtereferat, trekke ut vedtakspunkt, kategorisere henvendelsestypar og utføre kontekstuell omsetjing med betre kvalitet enn direkte taleoversetting. - **Human-in-the-loop basert på confidence scores** er påkravd for juridisk bindande transkripsjoner — automatisk godkjenning over 0.85, manuell korrekturlesing for 0.6-0.85, og re-innspeling under 0.6.