GEOZ

如何将LINE Bot语音合成迁移到Gemini 3.1原生TTS?2026年台湾中文配置与避坑

2026/5/3
如何将LINE Bot语音合成迁移到Gemini 3.1原生TTS?2026年台湾中文配置与避坑

AIAI Summary (BLUF)

本文介绍如何将LINE Bot的语音合成功能升级至Google Gemini 3.1 Flash原生TTS,涵盖代码演进、需避免的关键异步等待错误,以及针对台湾中文的本地化配置调整与避坑指南。

Background (背景)

在之前的实践环节中,我们使用了 Gemini 3.1 Flash Live 实现了语音识别,并借助 Gemini 2.5 Live API 的“旁路”方法,勉强实现了文本转语音(TTS)功能。

然而在 2026 年 4 月,Google 正式发布了 Gemini 3.1 Flash TTS。这是一个专为音频输出设计的原生模型,不再需要 Live WebSocket,可以通过标准的 generate_content 流程直接输出高质量音频。

作为开发者,您自然希望立即跟进更优雅、更原生的解决方案。本文将分享如何将 LINE Bot 的语音总结功能升级到 Gemini 3.1 Native TTS,并揭示过程中遇到的“异步之坑”。


Technical Upgrade: From Live API to Native TTS (技术升级:从 Live API 到原生 TTS)

之前的阅读功能是使用 Gemini 2.5 Live API 模拟的。虽然可用,但存在几个不足:

Gemini 3.1 Flash TTS 的出现改变了这一切:

维度 (Aspect) Gemini 2.5 Live API Gemini 3.1 Flash TTS
复杂度 (Complexity) 需要管理 WebSocket 连接生命周期 (Requires managing WebSocket connection lifecycle) 使用熟悉的 generate_content_stream 接口 (Uses familiar generate_content_stream interface)
模型限制 (Model limitations) 必须使用特定 native-audio 模型,主要支持 us-central1 (Must use specific native-audio model, primarily us-central1) 模型名为 gemini-3.1-flash-tts-preview,全局可用 (Model name gemini-3.1-flash-tts-preview, globally available)
返回格式 (Return format) 固定 16kHz 采样率 (Fixed 16kHz sample rate) 动态参数,MIME 类型自动检测,通常提升至 24kHz (Dynamic parameters, auto-detected from MIME type, typically 24kHz)

The table above is self-contained; no further translation is needed as each cell already includes bilingual content.


Core Code Evolution — tools/tts_tool.py (核心代码演进)

新的实现更加简洁,关键在于 response_modalities=["audio"] 设置:

async def text_to_speech(text: str) -> tuple[bytes, int]:
    client = genai.Client(api_key=GOOGLE_AI_API_KEY, http_options={"api_version": "v1beta"})

    contents = [
        types.Content(
            role="user",
            parts=[
                # Add localization instructions to make the tone more natural
                types.Part.from_text(text=f"Please use Traditional Chinese with Taiwanese vocabulary, and read the following summary in a friendly and natural tone. ## Transcript:\n{text}"),
            ],
        ),
    ]

    config = types.GenerateContentConfig(
        response_modalities=["audio"],
        speech_config=types.SpeechConfig(
            voice_config=types.VoiceConfig(
                prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name="Zephyr")
            )
        ),
    )

    pcm_chunks = []
    sample_rate = 24000  # Default value

    try:
        # ⚠️ This is the big pit that almost made me stay up all night fixing it
        response_stream = await client.aio.models.generate_content_stream(
            model="gemini-3.1-flash-tts-preview",
            contents=contents,
            config=config,
        )
        async for chunk in response_stream:
            if chunk.parts:
                for part in chunk.parts:
                    if part.inline_data:
                        pcm_chunks.append(part.inline_data.data)
                        # Get the sampling rate dynamically from the MIME type (e.g. audio/L16;rate=24000)
                        if part.inline_data.mime_type:
                            sample_rate = parse_rate(part.inline_data.mime_type)
    except Exception as e:
        logger.error(f"TTS Error: {e}")
        raise

    pcm_bytes = b"".join(pcm_chunks)
    duration_ms = int(len(pcm_bytes) / (sample_rate * 2) * 1000)

    # Subsequently, it is also converted to m4a via ffmpeg and sent to LINE...

上述代码展示了核心逻辑:使用 client.aio.models.generate_content_stream 并以 "audio" 作为响应模态,配合 SpeechConfig 指定语音参数(如 Zephyr 音色)。关键改进包括动态采样率提取和区域化提示。


The Pitfall: The Missing await (陷阱:缺失的 await)

本次升级遇到了一个非常微妙的 TypeError,在远程部署后持续出现:

TypeError: 'async for' requires an object with __aiter__ method, got coroutine

❌ Incorrect Writing (错误写法)

当我按照示例编写时,直觉上认为可以直接对方法进行 async for

# This is wrong!
async for chunk in client.aio.models.generate_content_stream(...):
    pass

✅ Correct Solution (正确解法)

在 Google GenAI Python SDK 的异步版本中,generate_content_stream 本身是一个 async 函数,它会返回一个迭代器。因此你必须先 await 获取该迭代器,然后再对其执行 async for

# Correct approach: two steps
response_stream = await client.aio.models.generate_content_stream(...)
async for chunk in response_stream:
    pass

这个细节在普通的同步代码或某些旧版 SDK 中可能不存在,但在处理 3.1 Flash TTS 的异步流时,这正是能否成功运行的关键。


Localization Adjustment: Making the Bot Speak "Taiwanese" (区域化调整:让机器人说“台湾腔”)

虽然摘要本身已是繁体中文,但 TTS 模型在朗读时偶尔会带有非本土的腔调或词汇。我们通过提示工程解决了这个问题:

"Please use Taiwanese vocabulary in Traditional Chinese, and read it in a friendly and natural tone..."

加入这行指令后,Gemini 输出的音频在语调、断句上更贴近台湾用户的使用习惯,大大提升了“听摘要”的亲和力。


Summary: Changes Brought by Native TTS (总结:原生 TTS 带来的变化)

从 Live API 迁移到原生 TTS 后:

这次经验也提醒我,即使看似成熟的 SDK,在处理 async 模式时也应仔细检查返回值类型。

如果你也希望让 LINE Bot 开口说话,Gemini 3.1 Flash TTS 绝对是目前的最佳选择。

完整代码已更新至 GitHub,下次见!

常见问题(FAQ)

为什么需要从Live API升级到Gemini 3.1原生TTS?

Live API需要管理WebSocket连接生命周期,模型受限且采样率固定为16kHz。原生TTS使用标准generate_content_stream接口,支持动态采样率(24kHz),连接更稳定,代码量减少约30%。

Gemini 3.1 Flash TTS的模型名称和核心配置是什么?

使用异步版本SDK时最常见的错误是什么?如何解决?

晓婷深圳
本文由 晓婷 审核,最后更新于 2026年7月2日
联系编辑 →
← 返回文章列表
分享到:微博

版权与免责声明:本文仅用于信息分享与交流,不构成任何形式的法律、投资、医疗或其他专业建议,也不构成对任何结果的承诺或保证。

文中提及的商标、品牌、Logo、产品名称及相关图片/素材,其权利归各自合法权利人所有。本站内容可能基于公开资料整理,亦可能使用 AI 辅助生成或润色;我们尽力确保准确与合规,但不保证完整性、时效性与适用性,请读者自行甄别并以官方信息为准。

若本文内容或素材涉嫌侵权、隐私不当或存在错误,请相关权利人/当事人联系本站,我们将及时核实并采取删除、修正或下架等处理措施。也请勿在评论或联系信息中提交身份证号、手机号、住址等个人敏感信息。