GEMINI LABEN
GA — Gemini Omni Flash が8月27日に一般提供へ入りました。モデル ID は gemini-omni-1.1-flash で、会話形式の動画生成と編集を担いますEXTEND — 既存クリップの末尾に続きを生成してつなげられます。extend タスクでもプロンプトの直接指示でも指定でき、短い尺の制約を継ぎ足しで越えていく設計ですRESOLUTION — video_config に resolution が加わり 360p・720p・1080p・4k を選べます。1080p と 4K はアップスケール出力のため、素材によって仕上がりに差が出ますDEPRECATION — gemini-omni-flash-preview は9月30日に廃止されます。プレビュー版のエンドポイントを本番で参照しているコードは差し替えが必要ですTRANSCRIBE — Gemini 3.5 Transcribe と Transcribe Live が8月26日に GA となりました。85言語以上の言語検出、話者分離、単語単位のタイムスタンプに対応しますSHUTDOWN — gemini-robotics-er-1.6-preview が8月31日に停止します。残り2日で、移行先は7月30日から公開プレビューの ER 2 系ですGA — Gemini Omni Flash が8月27日に一般提供へ入りました。モデル ID は gemini-omni-1.1-flash で、会話形式の動画生成と編集を担いますEXTEND — 既存クリップの末尾に続きを生成してつなげられます。extend タスクでもプロンプトの直接指示でも指定でき、短い尺の制約を継ぎ足しで越えていく設計ですRESOLUTION — video_config に resolution が加わり 360p・720p・1080p・4k を選べます。1080p と 4K はアップスケール出力のため、素材によって仕上がりに差が出ますDEPRECATION — gemini-omni-flash-preview は9月30日に廃止されます。プレビュー版のエンドポイントを本番で参照しているコードは差し替えが必要ですTRANSCRIBE — Gemini 3.5 Transcribe と Transcribe Live が8月26日に GA となりました。85言語以上の言語検出、話者分離、単語単位のタイムスタンプに対応しますSHUTDOWN — gemini-robotics-er-1.6-preview が8月31日に停止します。残り2日で、移行先は7月30日から公開プレビューの ER 2 系です
記事一覧/API / SDK
API / SDK/2026-08-29中級

gemini-3.5-transcribe の smart モードでは、話者ラベルも単語タイムスタンプも返ってきません

gemini-3.5-transcribe の smart モードは diarization_mode とも timestamp_granularities とも併用できません。mode が文字列とオブジェクトの両方を取る点と、機能を足すと音声の上限が半分になる点を整理します。

Gemini API224gemini-3.5-transcribe音声認識文字起こし話者ダイアライゼーション

公式ドキュメントの「Transcription modes」の節を読み進めていて、末尾の注記で手が止まりました。smart は timestamp_granularities とも diarization_mode とも併用できない、と書かれています。

この注記の置かれている位置に、少し引っかかりました。話者ダイアライゼーションの節も、単語タイムスタンプの節も、モードの節より にあります。上から順に読むと「話者ラベルが使える」「単語タイムスタンプも使える」「読みやすく整形する smart もある」と受け取ったあとで、最後にその組み合わせが成立しないと知る順序になっています。

私自身、設定値はコードに直書きせず外部ファイルへ切り出す癖があります。個人開発では、あとから自分がどこを触ればよいか分からなくなるのが一番こわいからです。ただ、今回の transcription_config に関しては、その癖がそのまま落とし穴になりました。理由は次の節に書きます。

mode は文字列とオブジェクトの両方を受け取ります

gemini-3.5-transcribe の設定は、generation_config の中の transcription_config に集約されています。ここの mode が少し変わっていて、文字列とオブジェクトのどちらも受け取ります

from google import genai
 
client = genai.Client()
audio_file = client.files.upload(file="path/to/sample.mp3")
 
# 文字列で渡す形(smart)
interaction = client.interactions.create(
    model="gemini-3.5-transcribe",
    input=[{"type": "audio", "uri": audio_file.uri,
            "mime_type": audio_file.mime_type}],
    generation_config={
        "transcription_config": {"mode": "smart"}
    },
)
 
# オブジェクトで渡す形(verbatim + 話者ラベル + 単語タイムスタンプ)
interaction = client.interactions.create(
    model="gemini-3.5-transcribe",
    input=[{"type": "audio", "uri": audio_file.uri,
            "mime_type": audio_file.mime_type}],
    generation_config={
        "transcription_config": {
            "mode": {
                "type": "verbatim",
                "diarization_mode": "speaker",
                "timestamp_granularities": ["word"],
            }
        }
    },
)

同じキーが2つの型を取ります。手で書いている間は気になりません。問題は、設定を JSON や YAML へ外出しして「モードだけ差し替えられるように」した瞬間に起きます。

mode: smart と書いていた行を、話者ラベルを足すためにオブジェクトへ書き換える。逆に「読みやすくしたい」と mode: smart へ戻すと、オブジェクトの中に置いていた diarization_mode が設定ごと消えます。どちらの向きでも、消えたことに気づく手がかりがありません。エラーとして返ってこないからです。

さらに紛らわしいのが、diarization_modetimestamp_granularities置き場所 です。この2つは transcription_config の直下ではなく、mode オブジェクトの中に入ります。custom_vocabularylanguage_codes は直下です。階層が一段ずれます。

smart を選んだ時点で外れるもの、verbatim を選んだ時点で背負うもの

両モードで何が使えるのかを、先に一枚で持っておくと迷いません。

機能verbatim(既定)smart
話者ダイアライゼーション(diarization_mode使える(最大8話者)使えない
単語タイムスタンプ(timestamp_granularities使える使えない
フィラー語の除去・整形されない(発話どおり)される
言い直しのインライン解決されないされる
カスタム語彙(custom_vocabulary使える使える
言語コード指定(language_codes使える使える

verbatim 側にも代償があります。単語タイムスタンプを有効にすると、文字起こし全体の精度が落ちる可能性がある、と公式に明記されています。時刻が要らない用途で「念のため」有効にしておく設計は、精度を捨てているだけになります。

エンドポイントによる差も別にあります。非ストリーミングの gemini-3.5-transcribe と、Live API 上の gemini-3.5-transcribe-live では、使える機能が同じではありません。

項目gemini-3.5-transcribegemini-3.5-transcribe-live
話者ダイアライゼーション対応(3話者以上は実験的)非対応
単語タイムスタンプ対応非対応
カスタム語彙最大1,000語最大1,000語
音声の長さ最大1時間1セッション10分

「リアルタイムで話者を分けて表示する」画面を先に設計してしまうと、ここで手戻りが出ます。話者ラベルが要るなら、ストリーミングではなくファイル処理側に寄せる判断が先に来ます。

verbatim を選ぶ前に、もう一点だけ知っておきたいことがあります。増えた情報は、思っている場所には返ってきません。文字起こしの本文は従来どおり interaction.output_text に入りますが、話者ラベルと単語の時刻は word_info という注釈として、インタラクションのコンテンツ側にぶら下がります。取り出すには階層をたどる必要があります。

def extract_word_annotations(interaction):
    words = []
    for step in getattr(interaction, "steps", []) or []:
        for content in getattr(step, "content", []) or []:
            for annotation in getattr(content, "annotations", []) or []:
                if getattr(annotation, "type", None) == "word_info":
                    words.append(annotation)
    return words

各注釈は textspeakerstart_offsetend_offset を持ちます。話者ダイアライゼーションを有効にしたまま output_text だけを読んでいると、話者情報のない普通の文字起こしが返ってきたように見えます。そして「設定が効いていない」と結論づけてしまいます。同じ種類の静かな失敗が、ここにもう一度現れます。

送る前に弾く仕組みを、80行だけ書いておきます

組み合わせの制約は覚えておけるほど少なくありません。私は、設定を組み立てた直後に検証する小さな関数を挟むようにしました。API を呼ぶ前に落ちるので、音声のアップロードも課金も発生しません。

"""transcription_config を送信前に検証する。"""
from typing import Any
 
VERBATIM_ONLY = ("timestamp_granularities", "diarization_mode")
MAX_VOCAB = 1000
RECOMMENDED_VOCAB = 100
 
 
def normalize_mode(mode: Any) -> dict:
    """mode は文字列とオブジェクトの両方を取る。辞書へ寄せる。"""
    if mode is None:
        return {"type": "verbatim"}
    if isinstance(mode, str):
        return {"type": mode}
    if isinstance(mode, dict):
        m = dict(mode)
        m.setdefault("type", "verbatim")
        return m
    raise TypeError(f"mode は str か dict です: {type(mode).__name__}")
 
 
def validate(tc: dict) -> list[str]:
    errors = []
    mode = normalize_mode(tc.get("mode"))
    mtype = mode.get("type")
 
    if mtype not in ("verbatim", "smart"):
        errors.append(f"mode.type が不明です: {mtype!r}")
 
    if mtype == "smart":
        for key in VERBATIM_ONLY:
            if key in mode or key in tc:
                errors.append(f"{key} は smart と併用できません(verbatim 用の設定です)")
 
    for key in VERBATIM_ONLY:
        if key in tc:
            errors.append(f"{key} は transcription_config 直下ではなく mode の中に置きます")
 
    vocab = tc.get("custom_vocabulary") or []
    if len(vocab) > MAX_VOCAB:
        errors.append(f"custom_vocabulary が {len(vocab)} 件です(上限 {MAX_VOCAB} 件)")
    elif len(vocab) > RECOMMENDED_VOCAB:
        errors.append(f"custom_vocabulary が {len(vocab)} 件です({RECOMMENDED_VOCAB} 件までを推奨)")
 
    return errors
 
 
def audio_limit_minutes(tc: dict) -> int:
    mode = normalize_mode(tc.get("mode"))
    heavy = any(k in mode for k in VERBATIM_ONLY)
    return 30 if heavy else 60

normalize_mode を挟んでいるのは、文字列とオブジェクトの両方が来る前提を、検証側で吸収するためです。呼び出し側に「必ずオブジェクトで書いてください」と強いると、mode: smart と1行で済む書き味が失われます。

手元の6パターンに当てて、実際に出た結果がこちらです。

OK  A 既定のまま  上限 60 分
OK  B smart だけ  上限 60 分
NG  C smart + 話者ラベル  上限 30 分
      - diarization_mode は smart と併用できません(verbatim 用の設定です)
OK  D verbatim + 話者 + 単語  上限 30 分
NG  E 置き場所を間違えた  上限 60 分
      - diarization_mode は smart と併用できません(verbatim 用の設定です)
      - diarization_mode は transcription_config 直下ではなく mode の中に置きます
NG  F 語彙 240 件  上限 60 分
      - custom_vocabulary が 240 件です(100 件までを推奨)

E のケースが、外部ファイル運用で実際に起きやすい形です。mode を文字列へ戻したときに diarization_mode だけが直下へ取り残される。API はこれを受け取っても止まりません。検証側で2行のメッセージが出たので、原因の場所がその場で分かりました。

F はエラーではなく推奨からの逸脱です。語彙は最大1,000語まで渡せますが、公式の推奨は100語程度までです。固有名詞を思いつく限り詰め込む設計は、上限に届く前に効きが鈍る可能性があります。

機能を有効にすると、扱える音声の長さが半分になります

見落としやすいのが、尺の上限です。gemini-3.5-transcribe は1リクエストあたり最大1時間の音声を受け取ります。ただし 話者ダイアライゼーションか単語タイムスタンプを有効にすると、30分に下がります

これは設定の互換性より影響が大きい制約です。「50分の対談を、話者ラベル付きで一度に投げる」という設計は、そもそも成立しません。分割して投げ、話者ラベルをチャンクをまたいで突き合わせる工程が要ります。そして分割した各チャンクの spk_1 が、別のチャンクの spk_1 と同一人物である保証はありません。

先ほどの audio_limit_minutes() を検証と一緒に呼んでおくと、この判断を設計段階へ引き上げられます。音源の秒数が分かっているなら、上限を超える組み合わせをその場で弾けます。3話者以上の割り当ては実験的と明記されている点も、対談やインタビューを扱うなら頭に入れておきたいところです。

決める順序は、成果物から逆算します

設定を先に触ると、この記事のような衝突に何度も当たります。順序を逆にすると迷いません。

  1. 出力として何が必要か を先に決めます。読み物としての議事録か、検索用のインデックスか、話者ごとの発言録か。
  2. そこから モードが決まります。時刻や話者が要るなら verbatim、要らないなら smart です。両方は取れません。
  3. モードが決まってから 尺の設計 に降ります。verbatim で機能を足したなら30分が上限です。分割が要るかどうかがここで確定します。
  4. 最後に カスタム語彙 を積みます。固有名詞や社内用語が多い音源ほど効きます。言語が分かっているなら language_codes も併せて指定するほうが、語彙の指示も通りやすくなります。

この順序は、設定が増えるほど効いてきます。設定値の互換性をあとから足していく設計がどう壊れるかは、Gemini API 構造化出力スキーマを本番アプリで安全に進化させる設計記録でも別の角度から書いています。エラーとして返ってこない失敗の拾い方については、Gemini API 本番運用ノート — 429・500・503 に静かに耐えるエラーハンドリングとレート制限の設計が近い話題です。

音声を扱う実装をリアルタイム側まで持っていく場合は、電話を受けるAIを本番投入する:Gemini Live API × Twilio Media Streams で構築する電話応答エージェントにストリーミング前提の設計をまとめてあります。話者ラベルが Live 側で使えないことを踏まえた構成が必要になる領域です。

まず手元の設定ファイルを開いて、mode の行と diarization_mode の階層だけ確かめてみてください。ずれていても、いまは何のエラーも出ていないはずです。

小さな確認の話にお付き合いいただき、ありがとうございました。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-08-27
Gemini に渡す前に表が壊れる場所は結合セルと2行ヘッダ
スプレッドシートの表をそのまま Gemini に渡すと、結合セルと2行ヘッダで行の所属が失われます。抽出時点で何が消えるかを実測し、平坦化と集計行の検出までを Python で組みます。
API / SDK2026-08-27
temperature が効いているかを測るより渡した値を記録する方が先です
非推奨になったサンプリングパラメータは 200 が返るまま無視されます。grep と AST が取りこぼした箇所を実行時に拾い、設定の組み立て元まで記録して CI の合否に変えるまでを、実際に動かした結果とともにまとめます。
API / SDK2026-08-23
Gemini TTS を streamGenerateContent で受け取り、先頭のチャンクから鳴らす
Gemini の TTS をストリーミングで受け取ると、音声は「ファイル」ではなく PCM の断片で届きます。チャンクごとに WAV ヘッダを付けた場合に何が起きるかを手元で測り、連結とヘッダの正しい順序を実装まで示します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →