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>
This commit is contained in:
@@ -0,0 +1,574 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user