Files
Portfolio/blog-posts/19-deepgram-nova-german.md
damjan_savicandClaude Opus 4.5 43484c5023 Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics
- Add .env.example for environment configuration
- Add accessibility and lighthouse audit scripts
- Remove obsolete SEO reports and temporary files
- Remove dev-dist build artifacts and backup files
- Remove unused portrait images (moved/consolidated elsewhere)
- Update contact form and component improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:42:11 +01:00

575 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deepgram Nova-2 für deutsche Spracherkennung: Production Guide
**Meta-Description:** Optimierung von Deepgram Nova-2 für deutsche Sprache. Dialekterkennung, Fachvokabular-Boosting, Streaming-Integration und Produktions-Konfiguration.
**Keywords:** Deepgram, Nova-2, German ASR, Speech-to-Text, Deutsche Spracherkennung, STT API, Voice Recognition Germany
---
## Einführung
Deutsch ist eine **Tier-1-Sprache** bei Deepgram mit 5-10% Word Error Rate (WER). Doch für Production-Qualität braucht es Feintuning: Dialekte, Fachbegriffe, Komposita die deutsche Sprache hat ihre Eigenheiten.
---
## Baseline Performance
| Metrik | Nova-2 (Deutsch) | Whisper Large | Google STT |
|--------|------------------|---------------|------------|
| **WER** | 8-10% | 12-15% | 10-12% |
| **Latenz** | ~150ms | ~2000ms | ~300ms |
| **Preis/min** | $0.0043 | $0.006 | $0.016 |
| **Streaming** | Ja | Nein | Ja |
---
## Basis-Setup für Deutsch
```typescript
// src/services/deepgram-german.ts
import { createClient, LiveTranscriptionEvents } from '@deepgram/sdk';
interface GermanASRConfig {
model: 'nova-2' | 'nova-3';
language: 'de' | 'de-DE' | 'de-AT' | 'de-CH';
smartFormat: boolean;
punctuate: boolean;
diarize: boolean;
keywords: string[];
endpointing: number;
}
const defaultGermanConfig: GermanASRConfig = {
model: 'nova-2',
language: 'de',
smartFormat: true,
punctuate: true,
diarize: false,
keywords: [],
endpointing: 500
};
export class GermanASR {
private client = createClient(process.env.DEEPGRAM_API_KEY!);
async transcribeStream(
config: Partial<GermanASRConfig> = {}
) {
const finalConfig = { ...defaultGermanConfig, ...config };
const connection = this.client.listen.live({
model: finalConfig.model,
language: finalConfig.language,
smart_format: finalConfig.smartFormat,
punctuate: finalConfig.punctuate,
diarize: finalConfig.diarize,
keywords: finalConfig.keywords,
endpointing: finalConfig.endpointing,
// Deutsche Spezialoptionen
numerals: true, // "dreiundzwanzig" → "23"
profanity_filter: false, // Für vollständige Transkription
redact: false,
replace: [],
search: [],
utterance_end_ms: 1000,
interim_results: true
});
return connection;
}
async transcribeFile(
audioBuffer: Buffer,
config: Partial<GermanASRConfig> = {}
) {
const finalConfig = { ...defaultGermanConfig, ...config };
const result = await this.client.listen.prerecorded.transcribeFile(
audioBuffer,
{
model: finalConfig.model,
language: finalConfig.language,
smart_format: finalConfig.smartFormat,
punctuate: finalConfig.punctuate,
diarize: finalConfig.diarize,
keywords: finalConfig.keywords,
// Für Batch: Zusätzliche Optionen
paragraphs: true,
summarize: 'v2',
topics: true,
intents: true,
sentiment: true
}
);
return result;
}
}
```
---
## Keyword Boosting für Fachvokabular
### Das Problem mit deutschen Fachbegriffen
```
Standard ASR: "Der Patient hat Rücken Syndrom"
Mit Boosting: "Der Patient hat Rückensyndrom"
Standard ASR: "Wir nutzen Kühn a tees"
Mit Boosting: "Wir nutzen Kubernetes"
```
### Implementation
```typescript
// src/config/german-keywords.ts
// Medizinische Begriffe
const medicalKeywords = [
'Rückensyndrom',
'Bandscheibenvorfall',
'Computertomografie',
'Magnetresonanztomografie',
'Blutdruckmessung',
'Cholesterinspiegel',
'Elektrokardiogramm',
// ... weitere
];
// Tech-Begriffe
const techKeywords = [
'Kubernetes',
'TypeScript',
'PostgreSQL',
'WebSocket',
'Microservices',
'Containerisierung',
'Deployment',
'Repository',
// ... weitere
];
// E-Commerce
const ecommerceKeywords = [
'Mehrwertsteuer',
'Rechnungsstellung',
'Gewährleistung',
'Widerrufsrecht',
'Versandkosten',
'Zahlungsabwicklung',
// ... weitere
];
// Domain-spezifische Keywords zusammenstellen
export function getKeywordsForDomain(domain: string): string[] {
switch (domain) {
case 'medical':
return [...medicalKeywords, ...germanBaseKeywords];
case 'tech':
return [...techKeywords, ...germanBaseKeywords];
case 'ecommerce':
return [...ecommerceKeywords, ...germanBaseKeywords];
default:
return germanBaseKeywords;
}
}
// Basis-Keywords für alle Domains
const germanBaseKeywords = [
// Zahlen (für bessere Erkennung)
'einundzwanzig',
'zweiunddreißig',
'fünfundvierzig',
// Häufige Firmennamen
'Deutsche Bahn',
'Volkswagen',
'Siemens',
'Bosch',
// Abkürzungen
'GmbH',
'AG',
'KG',
'e.V.'
];
```
### Keyword Boosting mit Intensitäten
```typescript
// Deepgram unterstützt Keyword-Intensifier
const boostedKeywords = [
'Kubernetes:2', // Stark boosten
'PostgreSQL:2',
'Deployment:1', // Normal boosten
'Repository:1',
'GmbH:3' // Sehr stark boosten
];
const connection = deepgram.listen.live({
model: 'nova-2',
language: 'de',
keywords: boostedKeywords
});
```
---
## Deutsche Dialekte
### Regionale Anpassungen
```typescript
// src/config/german-dialects.ts
type GermanDialect =
| 'hochdeutsch' // Standard
| 'bayerisch'
| 'schwäbisch'
| 'sächsisch'
| 'plattdeutsch'
| 'österreichisch'
| 'schweizerdeutsch';
interface DialectConfig {
languageCode: string;
additionalKeywords: string[];
postProcessing: (text: string) => string;
}
const dialectConfigs: Record<GermanDialect, DialectConfig> = {
hochdeutsch: {
languageCode: 'de-DE',
additionalKeywords: [],
postProcessing: (text) => text
},
bayerisch: {
languageCode: 'de-DE',
additionalKeywords: [
'Servus', 'Grüß Gott', 'Pfiat di',
'Brezn', 'Semmel', 'Weißwurst'
],
postProcessing: (text) => {
// Bayrische Ausdrücke normalisieren wenn gewünscht
return text
.replace(/\bfei\b/g, 'wirklich')
.replace(/\bgell\b/g, 'nicht wahr');
}
},
österreichisch: {
languageCode: 'de-AT',
additionalKeywords: [
'Servus', 'Grüß Gott', 'Baba',
'Paradeiser', 'Erdapfel', 'Sackerl',
'Jänner', 'Feber'
],
postProcessing: (text) => text
},
schweizerdeutsch: {
languageCode: 'de-CH',
additionalKeywords: [
'Grüezi', 'Merci', 'Ade',
'Velo', 'Natel', 'Trottoir',
'Rüebli', 'Zmorge'
],
postProcessing: (text) => text
},
// ... weitere Dialekte
};
export function getDialectConfig(dialect: GermanDialect): DialectConfig {
return dialectConfigs[dialect] || dialectConfigs.hochdeutsch;
}
```
---
## Komposita-Handling
Deutsche Komposita sind eine Herausforderung:
```typescript
// src/processing/composita-handler.ts
// Problem: "Kraft fahrzeug haft pflicht versicherung"
// Gewünscht: "Kraftfahrzeughaftpflichtversicherung"
class CompositaProcessor {
// Häufige Komposita-Teile
private prefixes = [
'Kraft', 'Fahrzeug', 'Haft', 'Pflicht', 'Versicherung',
'Daten', 'Schutz', 'Verarbeitung', 'Einwilligung',
'Geschäfts', 'Führung', 'Bericht', 'Erstattung'
];
// Bekannte vollständige Komposita
private knownComposita = new Set([
'Kraftfahrzeughaftpflichtversicherung',
'Datenschutzgrundverordnung',
'Bundesausbildungsförderungsgesetz',
'Geschäftsführer',
'Einwilligungserklärung'
]);
process(text: string): string {
// Versuche aufeinanderfolgende Wörter zu verbinden
const words = text.split(' ');
const result: string[] = [];
let i = 0;
while (i < words.length) {
let combined = words[i];
let j = i + 1;
// Versuche mit nächsten Wörtern zu kombinieren
while (j < words.length) {
const potential = combined + words[j];
if (this.isLikelyCompositum(potential)) {
combined = potential;
j++;
} else {
break;
}
}
result.push(combined);
i = j;
}
return result.join(' ');
}
private isLikelyCompositum(word: string): boolean {
// Bekannt?
if (this.knownComposita.has(word)) return true;
// Beginnt mit bekanntem Prefix und ist lang genug?
const hasKnownPrefix = this.prefixes.some(p =>
word.startsWith(p) && word.length > p.length + 3
);
return hasKnownPrefix;
}
}
```
---
## Post-Processing Pipeline
```typescript
// src/processing/german-postprocessor.ts
import { CompositaProcessor } from './composita-handler';
interface PostProcessorConfig {
normalizeNumbers: boolean;
fixComposita: boolean;
correctCommonErrors: boolean;
dialect: GermanDialect;
}
class GermanPostProcessor {
private compositaProcessor = new CompositaProcessor();
process(text: string, config: PostProcessorConfig): string {
let result = text;
// 1. Häufige Fehler korrigieren
if (config.correctCommonErrors) {
result = this.correctCommonErrors(result);
}
// 2. Komposita zusammenfügen
if (config.fixComposita) {
result = this.compositaProcessor.process(result);
}
// 3. Zahlen normalisieren
if (config.normalizeNumbers) {
result = this.normalizeNumbers(result);
}
// 4. Dialekt-spezifische Anpassungen
const dialectConfig = getDialectConfig(config.dialect);
result = dialectConfig.postProcessing(result);
return result;
}
private correctCommonErrors(text: string): string {
const corrections: [RegExp, string][] = [
[/\beine mail\b/gi, 'eine E-Mail'],
[/\bwhats app\b/gi, 'WhatsApp'],
[/\bwifi\b/gi, 'WLAN'],
[/\bapp\b/gi, 'App'],
[/\bcloud\b/gi, 'Cloud'],
[/\bsmart phone\b/gi, 'Smartphone'],
[/\bhandy\b/gi, 'Handy'],
[/\bwebsite\b/gi, 'Webseite'],
[/\bhome office\b/gi, 'Homeoffice'],
];
let result = text;
for (const [pattern, replacement] of corrections) {
result = result.replace(pattern, replacement);
}
return result;
}
private normalizeNumbers(text: string): string {
// "drei komma fünf prozent" → "3,5 Prozent"
const numberWords: Record<string, string> = {
'null': '0', 'eins': '1', 'zwei': '2', 'drei': '3',
'vier': '4', 'fünf': '5', 'sechs': '6', 'sieben': '7',
'acht': '8', 'neun': '9', 'zehn': '10',
'elf': '11', 'zwölf': '12', 'dreizehn': '13',
'zwanzig': '20', 'dreißig': '30', 'vierzig': '40',
'fünfzig': '50', 'hundert': '100', 'tausend': '1000'
};
let result = text;
// Komma-Zahlen
result = result.replace(
/(\w+)\s+komma\s+(\w+)/gi,
(_, before, after) => {
const num1 = numberWords[before.toLowerCase()] || before;
const num2 = numberWords[after.toLowerCase()] || after;
return `${num1},${num2}`;
}
);
return result;
}
}
```
---
## Production-Konfiguration
```typescript
// src/config/production-german-asr.ts
export const productionConfig = {
// Deepgram Settings
deepgram: {
model: 'nova-2',
language: 'de',
smart_format: true,
punctuate: true,
diarize: false, // Nur wenn nötig (kostet extra)
endpointing: 500,
interim_results: true,
utterance_end_ms: 1000,
vad_events: true,
// Keywords für Domain
keywords: getKeywordsForDomain('tech')
},
// Post-Processing
postProcessing: {
normalizeNumbers: true,
fixComposita: true,
correctCommonErrors: true,
dialect: 'hochdeutsch' as GermanDialect
},
// Retry Logic
retry: {
maxRetries: 3,
backoffMs: 1000
},
// Monitoring
monitoring: {
logTranscripts: false, // DSGVO!
logLatency: true,
logErrors: true
}
};
```
---
## Metriken & Monitoring
```typescript
// src/monitoring/asr-metrics.ts
interface ASRMetrics {
requestId: string;
timestamp: Date;
// Performance
latencyMs: number;
audioLengthMs: number;
processingRatio: number; // audioLength / latency
// Quality
wordCount: number;
confidenceScore: number;
alternativesCount: number;
// Errors
errorType?: string;
errorMessage?: string;
}
class ASRMonitor {
async track(metrics: ASRMetrics) {
// Latenz-Anomalien erkennen
if (metrics.latencyMs > 500) {
console.warn(`High ASR latency: ${metrics.latencyMs}ms`);
}
// Niedrige Konfidenz flaggen
if (metrics.confidenceScore < 0.7) {
console.warn(`Low confidence: ${metrics.confidenceScore}`);
}
// Metriken speichern (für Dashboards)
await this.store(metrics);
}
}
```
---
## Fazit
Deutsche Spracherkennung mit Deepgram Nova-2 erfordert:
1. **Keyword Boosting**: Für Fachvokabular und Eigennamen
2. **Dialekt-Awareness**: de-DE, de-AT, de-CH unterscheiden
3. **Komposita-Handling**: Post-Processing für zusammengesetzte Wörter
4. **Domänen-Anpassung**: Keywords je nach Use Case
Mit diesen Optimierungen ist 5-8% WER auch für komplexe deutsche Fachsprache erreichbar.
---
## Bildprompts
1. "German language sound waves transforming into text, blue and gold colors, technical illustration"
2. "Map of Germany, Austria, Switzerland with different speech bubbles, dialect visualization"
3. "Long German compound word being assembled like building blocks, playful tech illustration"
---
## Quellen
- [Deepgram Speech Recognition Accuracy Guide](https://deepgram.com/learn/speech-recognition-accuracy-production-metrics)
- [Deepgram Multilingual STT Guide](https://deepgram.com/learn/multilingual-speech-to-text-guide)
- [Deepgram vs OpenAI vs Google Comparison](https://deepgram.com/learn/deepgram-vs-openai-vs-google-stt-accuracy-latency-price-compared)
- [Deepgram Nova-3 German Support](https://deepgram.com/learn/aura-2-now-speaks-dutch-french-german-italian-japanese)