ktg-plugin-marketplace/plugins/ms-ai-architect/skills/ms-ai-engineering/references/multi-modal/speech-to-ai-pipelines.md
Kjell Tore Guttormsen 6a7632146e feat(ms-ai-architect): add plugin to open marketplace (v1.5.0 baseline)
Initial addition of ms-ai-architect plugin to the open-source marketplace.
Private content excluded: orchestrator/ (Linear tooling), docs/utredning/
(client investigation), generated test reports and PDF export script.
skill-gen tooling moved from orchestrator/ to scripts/skill-gen/.

Security scan: WARNING (risk 20/100) — no secrets, no injection found.
False positive fixed: added gitleaks:allow to Python variable reference
in output-validation-grounding-verification.md line 109.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 17:17:17 +02:00

19 KiB

Speech-to-AI Integration Pipelines

Last updated: 2026-02 Status: GA Category: Multi-Modal AI


Introduksjon

Speech-to-AI-integrasjon handlar om å kople talegjenkjenning med downstream AI-tenester for å skape ende-til-ende-pipelines som konverterer tale til handlingsorienterte innsikter. Azure Speech Service dannar grunnlaget, med støtte for sanntids talegjenkjenning, batchtranskribering, språkdeteksjon, talardiarisering og tilpassa talemodular.

For norsk offentleg sektor er tale-til-AI-pipelines avgjerande for tilgjengelegheit (automatisk teksting), møtetranskribering (kommunestyre, utval), klageblandling per telefon, og innbyggardialog via talebaserte brukargrensesnitt. Azure Speech Service støttar norsk bokmål (nb-NO) og kan kombinerast med Azure OpenAI for intelligente samtaleagenter.

Denne referansefila dekkjer arkitekturmønster for sanntidsstreaming, batchprosessering, språkdeteksjon og feilhandtering — med fokus på robuste produksjonsklare implementeringar.


Speech Recognition og Language Detection

Azure Speech Service oversikt

Funksjon Tilgjengelegheit Brukstilfelle
Real-time STT GA Samtaleagenter, live teksting
Batch transcription GA Arkivprosessering, møtereferat
Fast transcription GA Rask transkribering av filer
Language identification GA Fleirspråklege samtalar
Speaker diarization GA Møtetranskribering
Custom speech GA Bransjespecifikk terminologi
Real-time TTS GA Talebaserte assistentar
Text streaming GA Sanntids TTS frå GPT-output
gpt-4o-realtime Preview Direkte tale-til-tale

Språkgjenkjenning

Azure Speech Service tilbyr to typar språkdeteksjon:

At-Start Language Identification:

  • Detekterer språk i starten av audiostraumen
  • Støtta i C#, C++, Python, Java, JavaScript, Objective-C
  • Best for scenario med eitt hovudspråk per sesjon

Continuous Language Identification:

  • Detekterer språkbytter undervegs i samtalen
  • Støtta i C#, C++, Java, JavaScript, Python
  • Kritisk for fleirspråklege norske kommunar
import azure.cognitiveservices.speech as speechsdk

speech_config = speechsdk.SpeechConfig(
    subscription=os.environ["SPEECH_KEY"],
    region=os.environ["SPEECH_REGION"]
)

# Konfigurer automatisk språkdeteksjon
auto_detect_config = speechsdk.languageconfig.AutoDetectSourceLanguageConfig(
    languages=["nb-NO", "nn-NO", "en-US", "se-NO"]  # Bokmål, nynorsk, engelsk, nordsamisk
)

speech_recognizer = speechsdk.SpeechRecognizer(
    speech_config=speech_config,
    auto_detect_source_language_config=auto_detect_config,
    audio_config=audio_config
)

def recognized_handler(evt):
    result = evt.result
    auto_detect_result = speechsdk.AutoDetectSourceLanguageResult(result)
    detected_language = auto_detect_result.language
    print(f"[{detected_language}] {result.text}")

speech_recognizer.recognized.connect(recognized_handler)
speech_recognizer.start_continuous_recognition()

Tilpassa talemodular (Custom Speech)

For norsk offentleg sektor med spesialisert terminologi:

Tilpasning Brukstilfelle Eksempel
Phrase list Forbetra gjenkjenning av spesifikke ord Stadnamn, fagtermar
Custom model Trenar ny modell med eigne data Vegsektoren, helsesektor
Display format Tilpass visning av gjenkjend tekst Tal-til-siffer, dato-format
# Phrase list for forbetra norsk gjenkjenning
phrase_list = speechsdk.PhraseListGrammar.from_recognizer(speech_recognizer)
phrase_list.addPhrase("Statens vegvesen")
phrase_list.addPhrase("E6 Megården-Mørsvikbotn")
phrase_list.addPhrase("Utredningsinstruksen")
phrase_list.addPhrase("Forvaltningsloven")
phrase_list.addPhrase("personvernforordningen")

Audio Preprocessing og Quality Assessment

Audioformat og kvalitetskrav

Parameter Tilrådde verdi Minimum Kommentar
Samplingsrate 16 kHz 8 kHz 16 kHz gir best nøyaktigheit
Bit depth 16-bit 8-bit Mono PCM tilrådde
Kanalar Mono Mono Stereo splittar automatisk
Format WAV (PCM) WAV, MP3, OGG WAV gir minst komprimeringstap
SNR >20 dB >10 dB Signal-to-noise ratio

Audio preprocessing pipeline

import numpy as np
from scipy.io import wavfile
from scipy.signal import butter, lfilter

class AudioPreprocessor:
    """Preprosessering av audio for optimal talegjenkjenning."""

    def __init__(self, target_sample_rate=16000):
        self.target_sample_rate = target_sample_rate

    def assess_quality(self, audio_data: np.ndarray, sample_rate: int) -> dict:
        """Vurder audiokvalitet før talegjenkjenning."""
        # Berekn SNR
        signal_power = np.mean(audio_data ** 2)
        noise_floor = np.percentile(np.abs(audio_data), 5) ** 2
        snr = 10 * np.log10(signal_power / max(noise_floor, 1e-10))

        # Berekn varigheit
        duration = len(audio_data) / sample_rate

        # Detekter stille
        silence_threshold = np.percentile(np.abs(audio_data), 10)
        silence_ratio = np.sum(np.abs(audio_data) < silence_threshold) / len(audio_data)

        # Berekn peak level
        peak_level = 20 * np.log10(np.max(np.abs(audio_data)) / 32768)

        return {
            "snr_db": round(snr, 1),
            "duration_seconds": round(duration, 1),
            "silence_ratio": round(silence_ratio, 3),
            "peak_level_db": round(peak_level, 1),
            "sample_rate": sample_rate,
            "quality_score": self._calculate_quality_score(snr, silence_ratio, peak_level),
            "recommendation": self._get_recommendation(snr, silence_ratio)
        }

    def _calculate_quality_score(self, snr, silence_ratio, peak_level):
        score = 0
        if snr > 20: score += 40
        elif snr > 15: score += 30
        elif snr > 10: score += 20
        else: score += 10

        if silence_ratio < 0.3: score += 30
        elif silence_ratio < 0.5: score += 20
        else: score += 10

        if -6 < peak_level < -1: score += 30
        elif -12 < peak_level < 0: score += 20
        else: score += 10

        return min(score, 100)

    def _get_recommendation(self, snr, silence_ratio):
        if snr < 10:
            return "Låg SNR — vurder støyreduksjon eller betre mikrofon"
        if silence_ratio > 0.7:
            return "Mykje stille — sjekk at audio faktisk inneheld tale"
        return "Kvaliteten er akseptabel for talegjenkjenning"

    def apply_noise_reduction(self, audio_data: np.ndarray, sample_rate: int) -> np.ndarray:
        """Enkel bandpassfiltrering for å redusere støy."""
        low_freq = 80   # Hz — under menneskeleg tale
        high_freq = 8000  # Hz — over dei fleste talefrekvensane

        nyquist = sample_rate / 2
        low = low_freq / nyquist
        high = high_freq / nyquist

        b, a = butter(4, [low, high], btype='band')
        return lfilter(b, a, audio_data).astype(np.int16)

Quality gates for produksjon

Gate Terskel Handling
SNR-sjekk < 10 dB Avvis, be om ny innspeling
Varighetssjekk < 1 sekund Avvis, for kort
Stillesjekk > 80% stille Åtvar, be om verifikasjon
Peak clipping > -0.5 dB Åtvar, mogleg klipping
Format-validering Støtta format Konverter automatisk

Low-Latency Streaming Architectures

Sanntids talegjenkjenning

Mikrofon → Audio stream → Azure Speech SDK
                              ↓
                     ┌── Recognizing event (interim)
                     │   "Eg trur at veg..."
                     ├── Recognized event (final)
                     │   "Eg trur at vegsektoren bør investere meir."
                     │   Offset: 1800000 ticks
                     │   Duration: 30500000 ticks
                     └── SessionStopped event

Speech SDK streaming-arkitektur

import azure.cognitiveservices.speech as speechsdk
import asyncio
import json

class StreamingSpeechPipeline:
    """Sanntids tale-til-AI pipeline med låg latency."""

    def __init__(self, speech_key, speech_region, openai_client):
        self.speech_config = speechsdk.SpeechConfig(
            subscription=speech_key,
            region=speech_region
        )
        self.speech_config.speech_recognition_language = "nb-NO"
        self.speech_config.request_word_level_timestamps()
        self.speech_config.set_property(
            speechsdk.PropertyId.SpeechServiceResponse_StablePartialResultThreshold,
            "3"  # Reduser flimring i delresultat
        )

        self.openai_client = openai_client
        self.transcript_buffer = []

    async def start_streaming(self, audio_config=None):
        """Start sanntids talegjenkjenning med AI-prosessering."""
        if audio_config is None:
            audio_config = speechsdk.audio.AudioConfig(use_default_microphone=True)

        recognizer = speechsdk.SpeechRecognizer(
            speech_config=self.speech_config,
            audio_config=audio_config
        )

        # Interim-resultat for live visning
        recognizer.recognizing.connect(self._on_recognizing)

        # Endelege resultat for AI-prosessering
        recognizer.recognized.connect(self._on_recognized)

        # Start gjenkjenning
        recognizer.start_continuous_recognition()
        return recognizer

    def _on_recognizing(self, evt):
        """Handter interim-resultat (vis til brukar)."""
        print(f"\r  [interim] {evt.result.text}", end="", flush=True)

    def _on_recognized(self, evt):
        """Handter endeleg resultat (send til AI)."""
        if evt.result.reason == speechsdk.ResultReason.RecognizedSpeech:
            text = evt.result.text
            offset = evt.result.offset
            duration = evt.result.duration

            self.transcript_buffer.append({
                "text": text,
                "offset_ticks": offset,
                "duration_ticks": duration,
                "timestamp": offset / 10_000_000  # Konverter til sekund
            })

            print(f"\n[{offset/10_000_000:.1f}s] {text}")

Speech-to-Speech med Azure OpenAI

For samtaleagenter med direkte tale-til-tale:

Brukar tale → Speech SDK (STT) → Tekst
                                    ↓
                            Azure OpenAI (GPT-4o)
                                    ↓
                                  Svar-tekst
                                    ↓
                      Speech SDK (TTS) med text streaming
                                    ↓
                              Syntetisert tale → Brukar
# Text streaming for låg-latency TTS
speech_config.set_speech_synthesis_output_format(
    speechsdk.SpeechSynthesisOutputFormat.Raw24Khz16BitMonoPcm
)

tts_endpoint = (
    f"wss://{region}.tts.speech.microsoft.com"
    f"/cognitiveservices/websocket/v2"
)

# Stream GPT-output direkte til TTS
async def stream_response_to_speech(gpt_stream, synthesizer):
    """Stream GPT-4o tokens direkte til TTS for minimal latency."""
    request = speechsdk.SpeechSynthesisRequest(
        input_type=speechsdk.SpeechSynthesisRequestInputType.TextStream
    )

    # Start TTS-syntese
    result_future = synthesizer.speak_async(request)

    # Stream tekst-chunks frå GPT
    async for chunk in gpt_stream:
        if chunk.choices[0].delta.content:
            request.input_stream.write(chunk.choices[0].delta.content)

    # Steng straumen
    request.input_stream.close()
    result = await result_future

GPT-4o Realtime API for direkte tale-til-tale

Brukar tale → WebSocket → GPT-4o Realtime API → Syntetisert tale
                (bidireksjonell audiostraum)
Eigenskap Verdi
Latency < 500ms
Protokoll WebSocket
Audio input PCM 24kHz 16-bit mono
Audio output PCM 24kHz 16-bit mono
Innhaldsmoderering Ja, innebygd
Norsk støtte Ja (nb-NO)

Error Handling og Confidence Scoring

Confidence scoring i talegjenkjenning

Azure Speech Service rapporterer confidence på fleire nivå:

Nivå Tilgjengeleg Brukstilfelle
Ytring-nivå Recognized event Filtrering av låg-kvalitets resultat
Ord-nivå Med word timestamps aktivert Identifisere usikre ord
speech_config.request_word_level_timestamps()
speech_config.output_format = speechsdk.OutputFormat.Detailed

def handle_detailed_result(evt):
    result = evt.result
    detailed = json.loads(result.json)

    # N-best alternatives
    for nbest in detailed.get("NBest", []):
        confidence = nbest.get("Confidence", 0)
        text = nbest.get("Display", "")

        if confidence < 0.6:
            print(f"  [LAV CONFIDENCE {confidence:.2f}] {text}")
        else:
            print(f"  [OK {confidence:.2f}] {text}")

        # Ord-nivå confidence
        for word in nbest.get("Words", []):
            word_confidence = word.get("Confidence", 0)
            if word_confidence < 0.5:
                print(f"    Usikkert ord: '{word['Word']}' ({word_confidence:.2f})")

Robuste feilhandteringsmønster

class ResilientSpeechPipeline:
    """Robust tale-pipeline med retry og fallback."""

    MAX_RETRIES = 3
    RETRY_DELAY = 1.0  # sekund

    def __init__(self, primary_config, fallback_config=None):
        self.primary_config = primary_config
        self.fallback_config = fallback_config
        self.error_counts = {"no_match": 0, "canceled": 0, "timeout": 0}

    async def recognize_with_retry(self, audio_data):
        """Gjenkjenn tale med retry-logikk."""
        for attempt in range(self.MAX_RETRIES):
            try:
                result = await self._attempt_recognition(audio_data, self.primary_config)

                if result.reason == speechsdk.ResultReason.RecognizedSpeech:
                    self.error_counts = {"no_match": 0, "canceled": 0, "timeout": 0}
                    return {"status": "success", "text": result.text, "confidence": self._get_confidence(result)}

                elif result.reason == speechsdk.ResultReason.NoMatch:
                    self.error_counts["no_match"] += 1
                    details = result.no_match_details
                    return {
                        "status": "no_match",
                        "reason": str(details.reason),
                        "recommendation": self._no_match_recommendation(details)
                    }

                elif result.reason == speechsdk.ResultReason.Canceled:
                    cancellation = result.cancellation_details
                    if cancellation.reason == speechsdk.CancellationReason.Error:
                        if "timeout" in str(cancellation.error_details).lower():
                            self.error_counts["timeout"] += 1
                            await asyncio.sleep(self.RETRY_DELAY * (attempt + 1))
                            continue
                        raise SpeechServiceError(cancellation.error_details)

            except Exception as e:
                if attempt == self.MAX_RETRIES - 1:
                    if self.fallback_config:
                        return await self._attempt_recognition(audio_data, self.fallback_config)
                    raise
                await asyncio.sleep(self.RETRY_DELAY * (attempt + 1))

    def _no_match_recommendation(self, details):
        if details.reason == speechsdk.NoMatchReason.InitialSilenceTimeout:
            return "Ingen tale detektert — sjekk mikrofon eller audiokjelde"
        elif details.reason == speechsdk.NoMatchReason.InitialBabbleTimeout:
            return "For mykje bakgrunnsstøy — forbetra audiokvalitet"
        return "Ukjend årsak — prøv på nytt"

Feilkategoriar og handtering

Feiltype Årsak Handling Retry?
NoMatch - InitialSilenceTimeout Ingen tale i starten Sjekk mikrofon, auk timeout Nei
NoMatch - InitialBabbleTimeout For mykje støy Forbetra audiokvalitet Nei
Canceled - AuthenticationError Ugyldig nøkkel/token Forny token Ja (etter fornyelse)
Canceled - ConnectionError Nettverksproblem Retry med exponential backoff Ja
Canceled - ServiceTimeout Tenesta overbelasta Retry med delay Ja
Canceled - RuntimeError Intern feil Retry Ja

Monitoring og observability

from azure.monitor.opentelemetry import configure_azure_monitor
from opentelemetry import metrics

# Konfigurer Azure Monitor
configure_azure_monitor(connection_string=os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"])

meter = metrics.get_meter("speech-pipeline")
recognition_counter = meter.create_counter("speech.recognition.count")
confidence_histogram = meter.create_histogram("speech.recognition.confidence")
latency_histogram = meter.create_histogram("speech.recognition.latency_ms")

def track_recognition(result, latency_ms):
    recognition_counter.add(1, {"status": result["status"], "language": "nb-NO"})
    if result.get("confidence"):
        confidence_histogram.record(result["confidence"])
    latency_histogram.record(latency_ms)

Pipeline-arkitekturar for norsk offentleg sektor

Møtetranskribering med diarisering

Møtelyd → Audio preprocessing → Quality gate
                                     ↓
                          Speaker diarization
                          "Talar 1: ..." | "Talar 2: ..."
                                     ↓
                          Continuous recognition
                          (nb-NO med phrase list)
                                     ↓
                          Strukturert transkripsjon
                          [tidskode, talar, tekst]
                                     ↓
                     GPT-4o: Oppsummering + handlingspunkt
                                     ↓
                     Dokument: Møtereferat + opptak

Innbyggardialog via telefon

Innringar → Azure Communication Services
                      ↓
              Real-time transcription
              + Language detection
                      ↓
              GPT-4o: Klassifisering
              + Intentdeteksjon
                      ↓
              Routing til rett etat/saksbehandlar
              ELLER
              Automatisk svar via TTS

For Cosmo

  • Azure Speech Service støttar norsk bokmål (nb-NO) fullt ut for STT og TTS. Nynorsk (nn-NO) og nordsamisk (se-NO) har meir avgrensa støtte. Bruk language identification for fleirspråklege scenario.
  • Word-level timestamps og confidence scoring er avgjerande for produksjonsbruk — aktiver request_word_level_timestamps() og filtrer resultat med confidence under 0.6 for kvalitetssikring.
  • Text streaming for TTS (websocket v2) reduserer opplevd latency dramatisk når du kombinerer GPT-4o med Speech Service. Stream GPT-tokens direkte til TTS i staden for å vente på fullstendig svar.
  • Custom Speech med phrase lists er eit låg-innsats, høg-verdi tiltak for norske offentlege scenario. Legg til stadnamn, fagtermar og organisasjonsnamn for vesentleg forbetra gjenkjenning.
  • Implementer quality gates før talegjenkjenning — sjekk SNR, varigheit og støynivå. Det reduserer feilrate og unødvendige API-kall mot Speech Service.