GEMINI LABEN
VIDEO — 9月1日、エージェント型の動画理解が 3.7 Flash・3.6 Flash・3.5 Flash-Lite に届きました。モデルが動画のタイムラインを自分で辿りますTOKENS — 必要なときだけ字幕・フレーム・音声を取りに行く方式で、長尺では従来処理に比べて最大88%のトークン削減とされていますSCOPE — 対応は Interactions API と GenerateContent API の両方です。長い動画を扱う見積もりは、この変更で前提が変わりますMUSIC — 9月3日、Lyria 3.5 がパブリックプレビューへ。フルレングスの楽曲生成に対応し、44.1kHz ステレオを出力しますCONTROL — Lyria 3.5 はテキストと画像を入力に取り、音楽的な一貫性、自然なボーカル、尺と構成の細かい制御が改善されましたROBOTICS — gemini-robotics-er-2-streaming-preview は Live API 上のリアルタイム配信向けで、物理動作に対するブロッキング挙動つきの function calling に対応しますVIDEO — 9月1日、エージェント型の動画理解が 3.7 Flash・3.6 Flash・3.5 Flash-Lite に届きました。モデルが動画のタイムラインを自分で辿りますTOKENS — 必要なときだけ字幕・フレーム・音声を取りに行く方式で、長尺では従来処理に比べて最大88%のトークン削減とされていますSCOPE — 対応は Interactions API と GenerateContent API の両方です。長い動画を扱う見積もりは、この変更で前提が変わりますMUSIC — 9月3日、Lyria 3.5 がパブリックプレビューへ。フルレングスの楽曲生成に対応し、44.1kHz ステレオを出力しますCONTROL — Lyria 3.5 はテキストと画像を入力に取り、音楽的な一貫性、自然なボーカル、尺と構成の細かい制御が改善されましたROBOTICS — gemini-robotics-er-2-streaming-preview は Live API 上のリアルタイム配信向けで、物理動作に対するブロッキング挙動つきの function calling に対応します
記事一覧/API / SDK
API / SDK/2026-08-29中級

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

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

Gemini API233gemini-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-09-07
Lyria 3.5 が来た日に、音源フォルダの置き方を変えました
Lyria 3.5 のフルレングス生成が来たとき、生成した音と配信する音が同じフォルダに並んでいました。名前ではなくハッシュで線を引く、四十数行の台帳ゲートの作り方をお伝えします。
API / SDK2026-09-02
9月で止まる Gemini の標準 API キーを、CI とサーバーとローカルの三層で棚卸しする
標準 API キーは制限付きのものも含めて9月中に拒否されるようになります。差し替えではなく出どころの移行になるため、コード・デプロイ設定・手元の三層に分けた棚卸しと、両対応クライアントを挟んでから切り替える手順をまとめました。
API / SDK2026-09-01
Gemini の API キーを差し替えても旧キーが勝つ。決め手は読み込み順ではありませんでした
同じ環境変数名を複数の経路が供給していると、キーを差し替えても古い値が残ります。読み込み順を四通り入れ替えた実測と、供給と読み取りを台帳に残す監査シムの実装をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます