0Pricing
AI Agents · 课时

使用 Whisper 和 Deepgram 进行语音转文本

实时转写与批量转写、语言检测和标点处理。

使用 Whisper 和 Deepgram 进行语音转文本 是 CoddyKit 上的免费 AI Agents 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Agents 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Agents 课程共包含 4 节课。

语音代理中的语音转文本

语音代理必须先将语音音频转换为文本,LLM 才能进行处理。此步骤称为语音转文本(STT)或自动语音识别(ASR)。

目前有两种主流选择:OpenAI Whisper(基于文件、批量处理)和Deepgram(流式、实时,并支持说话人分离和词级时间戳)。

OpenAI Whisper:基础转写

通过 OpenAI 接口使用 Whisper 可以转写音频文件。支持的格式包括:mp3、mp4、wav、webm、m4a 和 flac。文件大小上限为 25MB。

import openai
import os

client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

def transcribe_file(audio_path, language=None):
    with open(audio_path, 'rb') as audio_file:
        params = {
            'model': 'whisper-1',
            'file': audio_file,
            'response_format': 'json'  # or 'text', 'srt', 'vtt', 'verbose_json'
        }
        if language:
            params['language'] = language  # e.g., 'en', 'fr', 'de'

        transcript = client.audio.transcriptions.create(**params)

    return transcript.text

# Basic usage
text = transcribe_file('meeting_recording.mp3')
print('Transcribed:', text[:200])

带词级时间戳的 Whisper

使用 response_format='verbose_json' 可获取每个单词和片段的时间戳。这对于查找录音中的特定时刻、实现卡拉 OK 式高亮,以及将转写文本与音频播放对齐都很有用。

import openai
import os

client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

def transcribe_with_timestamps(audio_path):
    with open(audio_path, 'rb') as f:
        result = client.audio.transcriptions.create(
            model='whisper-1',
            file=f,
            response_format='verbose_json',
            timestamp_granularities=['word', 'segment']  # get both word and segment times
        )

    segments = []
    for seg in result.segments:
        segments.append({
            'start': seg.start,
            'end':   seg.end,
            'text':  seg.text
        })

    words = []
    if hasattr(result, 'words'):
        for word in result.words:
            words.append({'word': word.word, 'start': word.start, 'end': word.end})

    return {'text': result.text, 'segments': segments, 'words': words}

Deepgram SDK:异步流式传输

Deepgram 针对实时流式转写进行了优化。音频会在录制过程中以分块形式发送;说话人尚未说完一句话时,就可以先返回部分转写结果。

使用 pip install deepgram-sdk 安装。

import asyncio
import os
from deepgram import DeepgramClient, PrerecordedOptions

client = DeepgramClient(os.getenv('DEEPGRAM_API_KEY'))

async def transcribe_with_deepgram(audio_path):
    with open(audio_path, 'rb') as f:
        audio_data = f.read()

    options = PrerecordedOptions(
        model='nova-2',            # Deepgram's best accuracy model
        language='en',
        smart_format=True,         # auto-add punctuation and formatting
        utterances=True,           # segment by speaker turns
        punctuate=True,
        diarize=True               # speaker identification
    )

    response = await client.listen.asyncrest.v('1').transcribe_file(
        {'buffer': audio_data},
        options
    )

    return response.results.channels[0].alternatives[0].transcript

说话人分离

说话人分离用于识别每个时刻是谁在说话,并将片段标记为说话人 0、说话人 1等。这对于会议转写和多人对话至关重要。

async def transcribe_with_diarization(audio_path):
    from deepgram import DeepgramClient, PrerecordedOptions
    import os

    dg_client = DeepgramClient(os.getenv('DEEPGRAM_API_KEY'))

    options = PrerecordedOptions(
        model='nova-2',
        diarize=True,
        utterances=True,
        smart_format=True
    )

    with open(audio_path, 'rb') as f:
        audio_data = f.read()

    response = await dg_client.listen.asyncrest.v('1').transcribe_file(
        {'buffer': audio_data}, options
    )

    utterances = []
    for utt in response.results.utterances:
        utterances.append({
            'speaker': f'Speaker {utt.speaker}',
            'start':   round(utt.start, 2),
            'end':     round(utt.end, 2),
            'text':    utt.transcript
        })

    return utterances

# Output:
# [{'speaker': 'Speaker 0', 'start': 0.0, 'end': 3.2, 'text': 'Hello everyone.'},
#  {'speaker': 'Speaker 1', 'start': 3.5, 'end': 6.1, 'text': 'Good morning!'}]

语言检测

当不知道用户使用哪种语言时,Whisper 和 Deepgram 都可以自动检测所说的语言。Whisper 会根据音频开头的 30 秒来检测语言。

import openai
import os

client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))

def detect_language(audio_path):
    with open(audio_path, 'rb') as f:
        # Use translations endpoint to get verbose JSON with language detection
        result = client.audio.transcriptions.create(
            model='whisper-1',
            file=f,
            response_format='verbose_json'
        )
    return {
        'detected_language': result.language,
        'text': result.text
    }

result = detect_language('user_audio.wav')
print(f"Language: {result['detected_language']}")
print(f"Text: {result['text'][:100]}")

# Deepgram: set language='auto' in options
from deepgram import PrerecordedOptions
options = PrerecordedOptions(model='nova-2', language='auto', detect_language=True)

长音频文件分块

由于 Whisper 的大小限制为 25MB,较长的录音(例如 1 小时的会议)需要进行分块。使用 pydub 根据静音切分音频,然后分别转写每个分块并将结果拼接起来。

使用 pip install pydub 安装。需要 ffmpeg。

from pydub import AudioSegment
from pydub.silence import split_on_silence
import io

def transcribe_long_audio(audio_path, chunk_length_ms=60000):
    audio = AudioSegment.from_file(audio_path)

    # Split on silence
    chunks = split_on_silence(
        audio,
        min_silence_len=1000,   # 1 second of silence
        silence_thresh=-40,     # dBFS
        keep_silence=500        # keep 500ms at each end
    )

    # If no silence splitting worked, use fixed-length chunks
    if len(chunks) <= 1:
        chunks = [
            audio[i:i + chunk_length_ms]
            for i in range(0, len(audio), chunk_length_ms)
        ]

    full_transcript = []
    for i, chunk in enumerate(chunks):
        print(f'Transcribing chunk {i+1}/{len(chunks)}')
        buf = io.BytesIO()
        chunk.export(buf, format='mp3')
        buf.seek(0)
        buf.name = f'chunk_{i}.mp3'
        result = client.audio.transcriptions.create(model='whisper-1', file=buf)
        full_transcript.append(result.text)

    return ' '.join(full_transcript)

Deepgram 实时流式传输

对于实时语音对话,请使用 Deepgram 的 WebSocket 实时流式传输接口。音频分块会在录制过程中发送;中间转写结果和最终转写结果会在数毫秒内到达。

import asyncio
import os
from deepgram import DeepgramClient, LiveTranscriptionEvents, LiveOptions

async def live_transcribe(on_transcript_callback):
    dg_client = DeepgramClient(os.getenv('DEEPGRAM_API_KEY'))

    connection = dg_client.listen.asynclive.v('1')

    async def on_message(self, result, **kwargs):
        sentence = result.channel.alternatives[0].transcript
        is_final = result.is_final
        if sentence:
            await on_transcript_callback(sentence, is_final)

    connection.on(LiveTranscriptionEvents.Transcript, on_message)

    options = LiveOptions(
        model='nova-2',
        language='en',
        smart_format=True,
        interim_results=True,  # get partial results before sentence ends
        endpointing=500        # ms of silence before finalizing
    )

    await connection.start(options)
    return connection  # caller sends audio chunks via connection.send(audio_chunk)

错误处理与重试

音频转写接口可能因网络问题、不支持的格式或速率限制而失败。请使用指数退避实现重试,并在发送前验证音频。

import time
import os

MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024  # 25MB
SUPPORTED_FORMATS = ('.mp3', '.mp4', '.wav', '.webm', '.m4a', '.flac', '.ogg')

def validate_audio_file(audio_path):
    if not os.path.exists(audio_path):
        raise FileNotFoundError(f'Audio file not found: {audio_path}')
    size = os.path.getsize(audio_path)
    if size > MAX_FILE_SIZE_BYTES:
        raise ValueError(f'File too large: {size / 1024 / 1024:.1f}MB (max 25MB)')
    ext = os.path.splitext(audio_path)[1].lower()
    if ext not in SUPPORTED_FORMATS:
        raise ValueError(f'Unsupported format: {ext}. Supported: {SUPPORTED_FORMATS}')

def transcribe_with_retry(audio_path, max_retries=3):
    validate_audio_file(audio_path)
    for attempt in range(max_retries):
        try:
            return transcribe_file(audio_path)
        except Exception as e:
            if attempt < max_retries - 1:
                wait = 2 ** attempt
                print(f'Retry {attempt + 1} after error: {e}. Waiting {wait}s')
                time.sleep(wait)
            else:
                raise

在 Whisper 与 Deepgram 之间进行选择

这两种工具分别适用于不同场景。请使用下面的决策矩阵,为您的语音代理选择合适的工具。

COMPARISON = '''
Whisper (OpenAI API):
  - Best for: batch transcription, podcast processing, meeting notes
  - Latency: file upload latency + ~1-5 seconds processing
  - Strengths: excellent multilingual, high accuracy, cheap
  - Word timestamps: yes (verbose_json)
  - Diarization: NO (must use separate tool)
  - Use when: accuracy > speed, offline processing

DeeGram:
  - Best for: real-time voice agents, call center transcription
  - Latency: <300ms for streaming, ~1s for file upload
  - Strengths: streaming, diarization, custom vocabulary
  - Word timestamps: yes, with confidence scores
  - Diarization: YES (built-in, up to 10 speakers)
  - Use when: speed > accuracy, real-time required

Rule of thumb:
  Agent voice conversation -> Deepgram live streaming
  Batch audio files -> Whisper API
  Meeting transcripts with speakers -> Deepgram with diarize=True
'''
print(COMPARISON)

音频格式转换

Whisper 和 Deepgram 支持常见的音频格式,但用户音频可能以不常见的格式到达(例如浏览器生成的 ogg、opus、webm,以及来自 iOS 的 m4a)。在发送到接口之前,请将其转换为标准 WAV 或 MP3。

from pydub import AudioSegment
import os

SUPPORTED_FORMATS = {'.mp3', '.wav', '.flac', '.m4a', '.ogg', '.webm', '.opus'}

def convert_to_wav(input_path, output_path=None):
    ext = os.path.splitext(input_path)[1].lower()
    if ext not in SUPPORTED_FORMATS:
        raise ValueError(f'Unsupported audio format: {ext}')

    if output_path is None:
        output_path = input_path.rsplit('.', 1)[0] + '.wav'

    # pydub handles format detection automatically
    audio = AudioSegment.from_file(input_path)

    # Normalize to 16kHz mono (optimal for Whisper)
    audio = audio.set_frame_rate(16000).set_channels(1)

    audio.export(output_path, format='wav')
    print(f'Converted {input_path} -> {output_path} ({len(audio) / 1000:.1f}s)')
    return output_path

def ensure_compatible_audio(audio_path):
    ext = os.path.splitext(audio_path)[1].lower()
    if ext in {'.mp3', '.wav', '.m4a', '.flac'}:
        return audio_path  # already compatible
    return convert_to_wav(audio_path)

知识检查

在语音转文本的语境中,说话人分离是什么?

回顾:使用 Whisper 和 Deepgram 进行语音转文本

Whisper(OpenAI 接口)适合批量转写:准确率高,支持多种语言,并可通过 verbose_json 提供词级时间戳。当延迟不是关键因素时,可以使用它进行基于文件的处理。

Deepgram专为实时流式传输而设计:通过 WebSocket 实时转写,提供中间结果、内置说话人分离,并将延迟控制在 300 毫秒以内。对于交互式语音代理,请使用 Deepgram。这两种工具都支持语言检测,在生产环境中都需要重试逻辑和文件验证。

常见问题解答

「使用 Whisper 和 Deepgram 进行语音转文本」课时是免费的吗?

是的 — 「使用 Whisper 和 Deepgram 进行语音转文本」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Agents 课程的其余内容,请升级到 CoddyKit PRO。 AI Agents 课程共包含 4 节课。

「使用 Whisper 和 Deepgram 进行语音转文本」这节课中我会学到什么?

实时转写与批量转写、语言检测和标点处理。 你通过在浏览器中直接运行的动手代码来练习 AI Agents,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Agents 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Agents 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「使用 Whisper 和 Deepgram 进行语音转文本」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Agents 课中编写并运行代码吗?

能。每节 AI Agents 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 Whisper 和 Deepgram 进行语音转文本
  2. 代理回复中的文本转语音
  3. 构建语音对话循环
  4. 优化语音代理的延迟
← 返回 AI Agents