Gemini Live API로 라이브 전사

Gemini Live API로 라이브 전사 (Live transcription)

Gemini Live API는 gemini-3.5-transcribe-live 모델로 저지연·실시간 음성-텍스트 전사를 지원해요. WebSockets이나 Google GenAI SDK로 Live API에 연결하면 연속 오디오 입력을 스트리밍하고, 말이 진행되는 대로 점진적인 실시간 텍스트 전사를 받을 수 있어요.

출처: 원문

본문

Gemini Live API는 gemini-3.5-transcribe-live 모델로 저지연·실시간 음성-텍스트 전사를 지원해요. WebSockets이나 Google GenAI SDK로 Live API에 연결하면 연속 오디오 입력을 스트리밍하고, 말이 진행되는 대로 점진적인 실시간 텍스트 전사를 받을 수 있어요.

Google AI Studio에서 Live Transcription 시도 · Colab cookbook 열기 · 코딩 에이전트 스킬 사용

Gemini Live API를 활용해 Agora, Fishjam, LiveKit, Pipecat, Vercel, Vision Agents 같은 개발자 플랫폼이 고성능 음성 기반 인터페이스를 쉽게 구축·배포하게 해줘요. 이 플랫폼들은 복잡한 실시간 미디어 스트리밍 인프라를 뒤에서 관리해서, 개발자가 사용자 경험 구축에만 집중할 수 있게 해줘요.

Live 에이전트 vs Live 전사

둘 다 Live API 양방향 스트리밍 연결을 사용하지만, Live Transcription은 대화형 에이전트가 아니라 전용 저지연 음성 인식 파이프라인으로 동작해요.

기능 Live 에이전트 Live 전사
주요 역할 듣고·추론하고·말로 답하는 대화형 어시스턴트 들어오는 오디오를 전사하는 실시간 음성-텍스트 파이프라인
응답 양식 음성 오디오와 텍스트 ( response_modalities=["AUDIO"] ) 스트리밍 텍스트 전사 ( response_modalities=["TEXT"] )
상호작용 스타일 일시정지 감지와 인터럽션이 있는 턴 기반 대화 화자가 말하는 대로 연속 스트림 처리
지원 기능 함수 호출, Google 검색, 시스템 지침 음성 바이어싱 ( custom_vocabulary ), 언어 감지, 수동·하이브리드 VAD, Smart transcription
입력 스트림 멀티모달: 오디오, 비디오, 이미지, 텍스트 오디오 입력 (원시 16비트 PCM)

시작하기

다음 예시는 gemini-3.5-transcribe-live로 양방향 스트리밍 세션을 열고 실시간 전사를 받는 방법을 보여줘요.

Python

import asyncio
from google import genai
from google.genai import types

client = genai.Client()
model = "gemini-3.5-transcribe-live"

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=[],  # Automatic language detection
    ),
)

async def main():
    async with client.aio.live.connect(model=model, config=config) as session:
        print("Session established with Live Transcription")

        # Receive transcription events
        async for response in session.receive():
            server_content = response.server_content
            if server_content and server_content.input_transcription:
                print("Transcript:", server_content.input_transcription.text)

if __name__ == "__main__":
    asyncio.run(main())

JavaScript

import { GoogleGenAI, Modality } from '@google/genai';

const ai = new GoogleGenAI({});
const model = 'gemini-3.5-transcribe-live';

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: [], // Automatic language detection
  },
};

async function main() {
  const session = await ai.live.connect({
    model: model,
    config: config,
    callbacks: {
      onopen: () => console.log('Connected to Live Transcription'),
      onmessage: (message) => {
        const content = message.serverContent;
        if (content?.inputTranscription) {
          console.log('Transcript:', content.inputTranscription.text);
        }
      },
      onerror: (e) => console.error('Error:', e.message),
      onclose: (e) => console.log('Connection closed:', e.reason),
    },
  });
}

main();

WebSockets

const API_KEY = "YOUR_API_KEY";
const MODEL_NAME = "gemini-3.5-transcribe-live";
const WS_URL = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key=${API_KEY}`;

const websocket = new WebSocket(WS_URL);

websocket.onopen = () => {
  console.log('WebSocket connected');

  const setupMessage = {
    setup: {
      model: `models/${MODEL_NAME}`,
      generationConfig: {
        responseModalities: ['TEXT'],
      },
      inputAudioTranscription: {
        languageCodes: []
      }
    }
  };
  websocket.send(JSON.stringify(setupMessage));
};

websocket.onmessage = (event) => {
  const response = JSON.parse(event.data);
  const content = response.serverContent;
  if (content?.inputTranscription) {
    console.log('Transcript:', content.inputTranscription.text);
  }
};

중간·확정 전사

오디오가 Live API로 스트리밍되면 서버가 server_content 안에 두 가지 보완 전사 필드를 내보내요:

  • interim_input_transcription: 화자가 말하는 동안 빠르게 업데이트되는 저지연·추측성 부분 가설. 부분 업데이트가 최소 지연으로 빠르게 발생해요. 반응형 라이브 UI 자막이나 미리 보기 캡션을 렌더링하려면 interim_input_transcription을 사용하세요.
  • input_transcription: 화자가 멈추거나, 턴이 완료되거나, 말이 확정되면 내보내지는 확정된 전사. 한 번 내보내면 이 텍스트가 그 음성 세그먼트의 모델 권위 전사예요. Smart transcription 모드에서는 정리·포맷된 응답이 포함돼요.

다음 예시는 스트리밍 중간 부분을 표시하고 최종 전사를 커밋하는 방법을 보여줘요:

Python

async def receive_transcripts(session):
    async for response in session.receive():
        server_content = response.server_content
        if not server_content:
            continue

        # Real-time interim hypothesis (updates dynamically as user speaks)
        if server_content.interim_input_transcription:
            interim_text = server_content.interim_input_transcription.text
            print(f"\r[Interim] {interim_text}", end="", flush=True)

        # Finalized transcript (emitted on speech completion)
        if server_content.input_transcription:
            final_text = server_content.input_transcription.text
            print(f"\n[Final] {final_text}")

JavaScript

onmessage: (message) => {
  const content = message.serverContent;
  if (!content) return;

  if (content.interimInputTranscription) {
    // Update live subtitle preview on screen
    renderInterimPreview(content.interimInputTranscription.text);
  }

  if (content.inputTranscription) {
    // Append final committed transcript to chat history
    commitFinalTranscript(content.inputTranscription.text);
  }
};

WebSockets

websocket.onmessage = (event) => {
  const response = JSON.parse(event.data);
  const content = response.serverContent;
  if (content?.interimInputTranscription) {
    console.log('[Interim]:', content.interimInputTranscription.text);
  }
  if (content?.inputTranscription) {
    console.log('[Final]:', content.inputTranscription.text);
  }
};

오디오 보내기

활성 연결 위에 원시 16비트 PCM 오디오로 오디오 청크를 스트리밍하세요.

  • 오디오 형식: 16kHz 원시 16비트 PCM (모노, little-endian).
  • 청크 크기: 100ms 크기의 오디오 청크로 보내세요 (1,024~2,048 프레임).
  • MIME 타입: audio/pcm;rate=16000 (또는 일치하는 샘플 레이트).

Python

# Stream a raw PCM audio chunk
await session.send_realtime_input(
    audio=types.Blob(
        data=audio_chunk_bytes,
        mime_type="audio/pcm;rate=16000"
    )
)

# Signal the end of the audio stream when finished
await session.send_realtime_input(audio_stream_end=True)

JavaScript

// Send base64-encoded PCM audio chunk
session.sendRealtimeInput({
  audio: {
    data: audioChunkBase64,
    mimeType: 'audio/pcm;rate=16000'
  }
});

// Signal stream end
session.sendRealtimeInput({
  audioStreamEnd: true
});

WebSockets

// Send base64-encoded PCM audio chunk
websocket.send(JSON.stringify({
  realtimeInput: {
    audio: {
      data: audioChunkBase64,
      mimeType: 'audio/pcm;rate=16000'
    }
  }
}));

// Signal stream end
websocket.send(JSON.stringify({
  realtimeInput: {
    audioStreamEnd: true
  }
}));

전사 기능

자동 언어 감지

기본적으로 language_codes를 생략하거나 language_codes=[]로 설정하면 자동 언어 식별이 활성화돼요. 모델이 발화 전반의 음성 언어를 동적으로 감지하며, 다국어 대화와 코드 스위칭도 처리해요.

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=[],
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: [],
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      languageCodes: [],
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

특정 언어 힌트

명시적 BCP-47 언어 코드(예: 스페인어는 ["es-ES"], 프랑스어는 ["fr-FR"])를 제공해 인식을 특정 언어로 바이어스할 수 있어요 (참고: 지원 언어).

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=["es-ES"],
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: ['es-ES'],
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      languageCodes: ['es-ES'],
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

커스텀 어휘 바이어싱

custom_vocabulary에 최대 1,000개의 구, 고유 명사, 브랜드명, 기술 용어를 제공해 음성 인식을 특정 용어로 바이어스할 수 있어요 (보통 최대 100개 용어에서 최상의 결과를 얻어요).

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=[],
        custom_vocabulary=["Gemini", "Kubernetes", "BigQuery"],
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: [],
    customVocabulary: ['Gemini', 'Kubernetes', 'BigQuery'],
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      languageCodes: [],
      customVocabulary: ['Gemini', 'Kubernetes', 'BigQuery'],
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

Smart transcription

input_audio_transcription의 mode 파라미터로 전사 출력 포맷을 구성할 수 있어요:

  • VERBATIM (기본): 말한 모든 것을 정확히 문자 그대로의 전사로 생성하며, 원시 필러 단어("um", "uh", "like"), 반복, 잘못된 시작을 보존해요.
  • SMART (Smart transcription): 가독성을 위해 전사를 정리하고 구조화해요:
    • 불유창성 제거: 필러 단어, 더듬거림, 잘못된 시작 제거.
    • 인라인 자기 수정: 말로 한 수정을 자연스럽게 해결.
    • 구조화 포맷: 목록, 불릿, 숫자, 날짜, 문단 구분을 자동 포맷.
    • 문법·대소문자: 자연스러운 대문자화와 구두점 다듬기 적용.

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        mode="SMART",
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    mode: 'SMART',
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      mode: 'SMART',
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

음성 활동 감지 (VAD) 전략

자동 VAD (기본)

기본적으로 서버 측 자동 음성 활동 감지가 화자가 말하기 시작하고 멈추는 것을 감지해요.

하이브리드 VAD

하이브리드 VAD는 서버 측 자동 음성 시작 감지와 클라이언트 측 음성 끝 감지를 결합해 지연 없는 턴 확정을 제공해요:

  1. 서버 측 자동 VAD는 계속 활성화되어 프리픽스 오디오 패딩으로 음성 시작을 정확히 감지하고 앞 단어 잘림을 방지해요.
  2. 클라이언트 측 VAD가 침묵을 감지: 로컬 온디바이스 VAD가 화자가 말을 멈췄다고 감지하면 클라이언트가 즉시 audio_stream_end 신호를 보내요.
  3. 빠른 확정: 서버가 audio_stream_end를 즉시 턴 확정 프롬프트로 처리해, 기본 서버 측 침묵 대기 시간을 건너뛰고 최소 지연으로 확정 전사를 반환해요.
  4. 폴백: 클라이언트 VAD가 트리거되지 않으면 서버 측 VAD가 자동 폴백으로 동작해요.

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(),
)

async with client.aio.live.connect(model=model, config=config) as session:
    # Stream audio chunks...
    await session.send_realtime_input(
        audio=types.Blob(data=chunk, mime_type="audio/pcm;rate=16000")
    )

    # When client-side VAD detects end of speech, send audio_stream_end:
    await session.send_realtime_input(audio_stream_end=True)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {},
};

// Stream audio...
session.sendRealtimeInput({
  audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' }
});

// When client VAD detects end of speech, send audioStreamEnd:
session.sendRealtimeInput({
  audioStreamEnd: true
});

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {},
  },
};
websocket.send(JSON.stringify(setupMessage));

// Stream audio...
websocket.send(JSON.stringify({
  realtimeInput: {
    audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' }
  }
}));

// When client VAD detects end of speech, send audioStreamEnd:
websocket.send(JSON.stringify({
  realtimeInput: {
    audioStreamEnd: true
  }
}));

수동 VAD (푸시투톡)

워키토키 인터페이스나 푸시투톡 버튼의 경우 자동 VAD를 완전히 비활성화하고 activity_start와 activity_end로 턴 경계를 명시적으로 제어하세요:

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    realtime_input_config=types.RealtimeInputConfig(
        automatic_activity_detection=types.AutomaticActivityDetection(
            disabled=True
        )
    ),
    input_audio_transcription=types.AudioTranscriptionConfig(),
)

async with client.aio.live.connect(model=model, config=config) as session:
    # Button pressed: signal speech start
    await session.send_realtime_input(activity_start=types.ActivityStart())

    # Stream audio chunks...
    await session.send_realtime_input(audio=types.Blob(data=chunk, mime_type="audio/pcm;rate=16000"))

    # Button released: signal speech end
    await session.send_realtime_input(activity_end=types.ActivityEnd())

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  realtimeInputConfig: {
    automaticActivityDetection: {
      disabled: true,
    },
  },
  inputAudioTranscription: {},
};

// Signal speech start
session.sendRealtimeInput({ activityStart: {} });

// Stream audio...

// Signal speech end
session.sendRealtimeInput({ activityEnd: {} });

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    realtimeInputConfig: {
      automaticActivityDetection: {
        disabled: true,
      },
    },
    inputAudioTranscription: {},
  },
};
websocket.send(JSON.stringify(setupMessage));

// Button pressed: signal speech start
websocket.send(JSON.stringify({
  realtimeInput: {
    activityStart: {},
  },
}));

// Stream audio...
websocket.send(JSON.stringify({
  realtimeInput: {
    audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' },
  },
}));

// Button released: signal speech end
websocket.send(JSON.stringify({
  realtimeInput: {
    activityEnd: {},
  },
}));

클라이언트 애플리케이션의 임시 토큰

클라이언트-서버 애플리케이션(마이크에서 직접 스트리밍하는 모바일·웹 앱 등)에서는 임시 토큰을 사용해 클라이언트 코드에 API 키가 노출되는 것을 피하세요.

클라이언트 연결을 시작하기 전에 서버에서 제한된 임시 토큰을 만드세요:

Python

import datetime
from google import genai

client = genai.Client()
expire_time = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(minutes=30)

token = client.auth_tokens.create(
    config={
        "uses": 1,
        "expire_time": expire_time,
        "live_connect_constraints": {
            "model": "gemini-3.5-transcribe-live",
            "config": {
                "response_modalities": ["TEXT"],
                "input_audio_transcription": {
                    "language_codes": [],
                },
            },
        },
    }
)

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});
const expireTime = new Date(Date.now() + 30 * 60 * 1000).toISOString();

const token = await client.authTokens.create({
  config: {
    uses: 1,
    expireTime: expireTime,
    liveConnectConstraints: {
      model: 'gemini-3.5-transcribe-live',
      config: {
        responseModalities: ['TEXT'],
        inputAudioTranscription: {
          languageCodes: [],
        },
      },
    },
  },
});

REST

curl -X POST "https://generativelanguage.googleapis.com/v1beta/auth_tokens" \
  -H "x-goog-api-key: ${GEMI...EY}" \
  -H "Content-Type: application/json" \
  -d '{
    "uses": 1,
    "expireTime": "YYYY-MM-DDTHH:MM:SSZ",
    "liveConnectConstraints": {
      "model": "models/gemini-3.5-transcribe-live",
      "config": {
        "responseModalities": ["TEXT"],
        "inputAudioTranscription": {
          "languageCodes": []
        }
      }
    }
  }'

지원 언어

Gemini 3.5 Transcribe Live에서 지원하는 언어와 BCP-47 언어 코드는 다음과 같아요:

언어 BCP-47 코드 언어 BCP-47 코드
Afrikaans af-ZA Japanese ja-JP
Amharic am-ET Javanese jv-ID
Arabic (Egypt) ar-EG Kabuverdianu kea-CV
Armenian hy-AM Kannada kn-IN
Assamese as-IN Kazakh kk-KZ
Azerbaijani az-AZ Korean ko-KR
Belarusian be-BY Kyrgyz ky-KG
Bengali (Bangladesh) bn-BD Latvian lv-LV
Bengali (India) bn-IN Lingala ln-CD
Bosnian bs-BA Lithuanian lt-LT
Bulgarian bg-BG Macedonian mk-MK
Bulgarian (Aromanian) rup-BG Malay ms-MY
Burmese my-MM Malayalam ml-IN
Cantonese (Traditional) yue-Hant-HK Maltese mt-MT
Catalan ca-ES Mandarin Chinese (Simplified) cmn-Hans-CN
Cebuano ceb Marathi mr-IN
Central Khmer km-KH Mongolian mn-MN
Croatian hr-HR Nepali ne-NP
Czech cs-CZ Norwegian nb-NO
Danish da-DK Oriya or-IN
Dutch nl-NL Polish pl-PL
English (Great Britain) en-GB Portuguese (Brazil) pt-BR
English (India) en-IN Portuguese (Portugal) pt-PT
English (United States) en-US Punjabi pa-IN
Estonian et-EE Punjabi (Gurmukhi script) pa-Guru-IN
Farsi fa-IR Romanian ro-RO
Filipino fil-PH Russian ru-RU
Finnish fi-FI Serbian sr-RS
French fr-FR Sindhi (Arabic script) sd-Arab-IN
Galician gl-ES Slovak sk-SK
Georgian ka-GE Slovenian sl-SI
German de-DE Spanish (Latin America) es-419
Greek el-GR Spanish (United States) es-US
Gujarati gu-IN Swahili (Kenya) sw-KE
Hausa ha-NG Swedish sv-SE
Hebrew he-IL Tajik tg-TJ
Hindi hi-IN Telugu te-IN
Hungarian hu-HU Thai th-TH
Icelandic is-IS Turkish tr-TR
Indian English en-IN Ukrainian uk-UA
Indonesian id-ID Uzbek uz-UZ
Italian it-IT Vietnamese vi-VN

파라미터 레퍼런스

input_audio_transcription과 realtime_input_config의 필드로 라이브 전사를 구성해요:

파라미터 타입 설명
language_codes 문자열 배열 BCP-47 언어 코드 (예: ["en-US"] ). 생략하거나 비우면( [] ) 모델이 언어를 자동 감지하고 다국어 음성을 처리.
custom_vocabulary 문자열 배열 음성 인식을 바이어스할 최대 1,000개 사용자 정의 용어, 약어, 브랜드명, 고유 명사.
mode String 전사 모드: "VERBATIM" (기본) 또는 "SMART" (Smart transcription). "SMART"로 설정하면 필러 단어 제거, 목록 포맷, 불유창성 수정.
automatic_activity_detection.disabled Boolean true로 설정하면 자동 음성 활동 감지를 비활성화하고 activityStart와 activityEnd 신호를 수동으로 보냄.

서버 응답 필드

필드 설명
server_content.interim_input_transcription 사용자가 말하는 동안 지속적으로 내보내지는 저지연·중간 부분 전사 가설.
server_content.input_transcription 음성 턴이 끝나면 내보내지는 확정·권위 입력 전사.

제한 사항

  • 세션 지속 시간: 라이브 전사 세션은 최대 10분 연속 스트리밍을 지원해요.
  • 화자 분리: 라이브 스트리밍 세션에서는 화자 분리(diarization)가 지원되지 않아요. 화자 분리가 필요하면 비스트리밍 오디오 전사 엔드포인트를 사용하세요.
  • 단어 수준 타임스탬프: Live API에서는 단어 수준 타임스탬프가 지원되지 않아요. Live API는 발화 수준 타임스탬프(interim_input_transcription, input_transcription)를 내보내요.
  • 커스텀 어휘: custom_vocabulary에 최대 1,000개 용어를 제공할 수 있지만, 보통 최대 100개 용어에서 최상의 결과를 얻어요.
  • 모드 호환성: Smart transcription("mode": "SMART")은 필러 단어를 제거하고 의도 인식 텍스트를 포맷하지만, 단어 주석과는 결합할 수 없어요.

다음으로

더 알아보기 (Learn more)