Spracherkennung mit Whisper
whisper-large-v3-turbo ist über denselben /v1/audio/transcriptions-Endpunkt wie die OpenAI Whisper API zugänglich, sodass jeder für OpenAI geschriebene Code durch Änderung der base_url direkt übernommen werden kann. Unterstützte Formate, Sprachcodes und Inferenzparameter auf der Whisper-Modellseite.
Einrichtung
user@local $ pip install openai
user@local $ export OPENAI_API_KEY="sk-…"
Für das Aufteilen langer oder großer Dateien (über 10 Minuten oder über 25 MB):
user@local $ pip install pydub
# pydub benötigt ffmpeg
user@local $ brew install ffmpeg # macOS
user@local $ apt-get install ffmpeg # Debian/Ubuntu
Grundlegende Transkription
import os
from openai import OpenAI
client = OpenAI(base_url="https://llm.aihosting.mittwald.de/v1")
with open("aufnahme.mp3", "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language="de", # Immer explizit setzen — Standard ist "de"
response_format="json",
temperature=1.0,
)
print(result.text)
Mehrere Sprachen
dateien_und_sprachen = [
("meeting_de.mp3", "de"),
("interview_fr.wav", "fr"),
("podcast_en.ogg", "en"),
("vortrag_ja.flac", "ja"),
]
for dateiname, sprache in dateien_und_sprachen:
with open(dateiname, "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language=sprache,
response_format="json",
temperature=1.0,
)
print(f"[{sprache}] {result.text[:120]}")
Lange oder große Dateien (> 10 Minuten oder > 25 MB)
Die API akzeptiert Dateien bis zu 25 MB und bis zu 10 Minuten Audio. Beide Limits gelten unabhängig voneinander, bei Sprachaufnahmen greift üblicherweise das Längenlimit zuerst. Längere Aufnahmen mit pydub an Stille-Stellen aufteilen, damit jeder Abschnitt unter beiden Limits bleibt und keine Wörter mittendrin geschnitten werden:
from pydub import AudioSegment
from pydub.silence import split_on_silence
from io import BytesIO
MAX_CHUNK_MS = 9 * 60 * 1000 # Unter dem 10-Minuten-Limit bleiben
def grosse_datei_transkribieren(path: str, language: str = "de") -> str:
"""Datei beliebiger Größe und Länge transkribieren durch Aufteilen an Stille-Stellen."""
audio = AudioSegment.from_file(path)
# An Pausen von mehr als 700 ms mit Stille unter -40 dBFS aufteilen
chunks = split_on_silence(
audio,
min_silence_len=700,
silence_thresh=-40,
keep_silence=300, # 300 ms Stille an jeder Kante für Kontext beibehalten
)
# Fallback: wenn keine Stille gefunden, in feste 60-Sekunden-Abschnitte aufteilen
if not chunks:
chunk_ms = 60_000
chunks = [audio[i: i + chunk_ms] for i in range(0, len(audio), chunk_ms)]
# Fallback: eine Passage ohne Pausen kann das 10-Minuten-Limit weiterhin überschreiten
begrenzt: list[AudioSegment] = []
for chunk in chunks:
if len(chunk) <= MAX_CHUNK_MS:
begrenzt.append(chunk)
else:
begrenzt.extend(
chunk[i: i + MAX_CHUNK_MS] for i in range(0, len(chunk), MAX_CHUNK_MS)
)
transkripte: list[str] = []
for chunk in begrenzt:
buf = BytesIO()
chunk.export(buf, format="mp3")
buf.seek(0)
buf.name = "chunk.mp3" # openai SDK liest das .name-Attribut für den MIME-Typ
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=buf,
language=language,
response_format="json",
temperature=1.0,
)
transkripte.append(result.text)
return " ".join(transkripte)
volltext = grosse_datei_transkribieren("langes_interview.mp3", language="de")
print(volltext)
Transkription + Zusammenfassungs-Pipeline
Whisper mit einem Chat-Modell verknüpfen, um direkt von Audio zu einer schriftlichen Zusammenfassung zu gelangen:
def transkribieren_und_zusammenfassen(audio_path: str, language: str = "de") -> dict:
# Schritt 1: Transkribieren
with open(audio_path, "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language=language,
response_format="json",
temperature=1.0,
)
transkript = result.text
# Schritt 2: Zusammenfassen
zusammenfassung_resp = client.chat.completions.create(
model="Qwen3.6-35B-A3B-FP8",
messages=[
{
"role": "system",
"content": "Fasse das Transkript prägnant zusammen. Verwende Stichpunkte für die wichtigsten Themen.",
},
{"role": "user", "content": transkript},
],
temperature=0.7,
max_tokens=512,
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
return {
"transkript": transkript,
"zusammenfassung": zusammenfassung_resp.choices[0].message.content,
}
ausgabe = transkribieren_und_zusammenfassen("team_standup.mp3", language="de")
print("Transkript:\n", ausgabe["transkript"])
print("\nZusammenfassung:\n", ausgabe["zusammenfassung"])
Drop-in-Ersatz für OpenAI
Bestehender Code für die OpenAI Whisper API benötigt nur eine base_url-Änderung:
# Vorher (OpenAI)
client = OpenAI()
# Nachher (mittwald AI Hosting)
client = OpenAI(base_url="https://llm.aihosting.mittwald.de/v1")
Der gesamte übrige Code – Dateiverarbeitung, response_format, language, temperature – bleibt identisch.
Zeitstempel und Spracherkennung mit verbose_json
response_format="verbose_json" gibt segmentgenaue Zeitstempel, die erkannte Sprache und die Gesamtdauer zurück:
with open("aufnahme.mp3", "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language="de",
response_format="verbose_json",
temperature=1.0,
)
print(result.text)
print(f"Sprache: {result.language}")
print(f"Dauer: {result.duration}s")
for seg in (result.segments or []):
print(f" [{seg['start']:.1f}s–{seg['end']:.1f}s] {seg['text']}")