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.
531 lines
20 KiB
Markdown
531 lines
20 KiB
Markdown
# Speech-to-AI Integration Pipelines
|
|
|
|
**Last updated:** 2026-02
|
|
**Status:** GA
|
|
**Category:** Multi-Modal AI
|
|
**Type:** reference
|
|
|
|
---
|
|
|
|
## Innhold
|
|
|
|
- [Introduksjon](#introduksjon)
|
|
- [Speech Recognition og Language Detection](#speech-recognition-og-language-detection)
|
|
- [Audio Preprocessing og Quality Assessment](#audio-preprocessing-og-quality-assessment)
|
|
- [Low-Latency Streaming Architectures](#low-latency-streaming-architectures)
|
|
- [Error Handling og Confidence Scoring](#error-handling-og-confidence-scoring)
|
|
- [Pipeline-arkitekturar for norsk offentleg sektor](#pipeline-arkitekturar-for-norsk-offentleg-sektor)
|
|
- [For Cosmo](#for-cosmo)
|
|
|
|
## 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
|
|
|
|
```python
|
|
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 |
|
|
|
|
```python
|
|
# Phrase list for forbetra norsk gjenkjenning
|
|
phrase_list = speechsdk.PhraseListGrammar.from_recognizer(speech_recognizer)
|
|
phrase_list.addPhrase("Direktoratet for digital tjenesteutvikling")
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
```
|
|
|
|
```python
|
|
# 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 |
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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.
|