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는 서버 측 자동 음성 시작 감지와 클라이언트 측 음성 끝 감지를 결합해 지연 없는 턴 확정을 제공해요:
- 서버 측 자동 VAD는 계속 활성화되어 프리픽스 오디오 패딩으로 음성 시작을 정확히 감지하고 앞 단어 잘림을 방지해요.
- 클라이언트 측 VAD가 침묵을 감지: 로컬 온디바이스 VAD가 화자가 말을 멈췄다고 감지하면 클라이언트가 즉시
audio_stream_end신호를 보내요. - 빠른 확정: 서버가
audio_stream_end를 즉시 턴 확정 프롬프트로 처리해, 기본 서버 측 침묵 대기 시간을 건너뛰고 최소 지연으로 확정 전사를 반환해요. - 폴백: 클라이언트 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")은 필러 단어를 제거하고 의도 인식 텍스트를 포맷하지만, 단어 주석과는 결합할 수 없어요.
다음으로
- 비스트리밍 오디오 파일용 Gemini Transcribe 문서 읽기.
- 대화형 음성 에이전트용 Live API 개요 읽기.
- 실시간 음성-음성 번역용 Live translation 가이드 읽기.
- Live API 스트리밍 가격은 가격 페이지 확인.
- Live API 기능 가이드 살펴보기.