Gemini Live API は、 セッションと呼ばれる音声またはテキストの連続ストリームを処理します。最初のハンドシェイクから正常な終了まで、セッションのライフサイクルを管理できます。
セッションの上限
Live API の場合、 セッション とは、入力 と出力が接続を介して継続的にストリーミングされる永続的な接続を指します。
セッションが次の上限の いずれか を超えると、接続は終了します。ただし、Live API には、セッション関連の上限を処理するためのオプションがいくつか用意されています(下記を参照) 。
セッション コンテキスト ウィンドウ は 128,000 トークンに制限されています。
このコンテキスト ウィンドウの上限により、入力モードに基づくセッションの最大長は次のようになります。
- 音声のみの入力セッションは
15 分 に制限されます。 - 動画と音声の入力は
2 分 に制限されます。
- 音声のみの入力セッションは
接続時間 は約
10 分 に制限されます。接続が終了する約
60 秒 前に、 going away通知が届きます。
セッション関連の上限を処理するオプションは次のとおりです。
セッション コンテキスト ウィンドウを圧縮して 、サーバーがコンテキスト サイズを上限内に自動的に維持するようにします。
セッションを再開 して、短いネットワーク切断中や going away通知を受信した後に、会話のコンテキストが失われるのを防ぎます。
セッションを開始する
セッションを開始する方法を示す完全なスニペットについては、 スタートガイドLive API をご覧ください。
セッション中に更新する
Live API モデルは、 セッション中の更新に関して次の高度な機能をサポートしています。
システム指示を更新する (Vertex AI Gemini API のみ)
コンテンツの増分更新を追加する
アクティブなセッション中に増分更新を追加できます。これを使用して、テキスト入力の送信、セッション コンテキストの確立、セッション コンテキストの復元を行います。
コンテキストが長い場合は、1 つのメッセージの概要を提供して、後続のインタラクション用にコンテキスト ウィンドウを空けておくことをおすすめします。
コンテキストが短い場合は、ターンバイターンのインタラクションを送信して、イベントの正確なシーケンスを表すことができます(次のスニペットなど)。
Swift
// Define initial turns (history/context).
let turns: [ModelContent] = [
ModelContent(role: "user", parts: [TextPart("What is the capital of France?")]),
ModelContent(role: "model", parts: [TextPart("Paris")]),
]
// Send history, keeping the conversational turn OPEN (false).
await session.sendContent(turns, turnComplete: false)
// Define the new user query.
let newTurn: [ModelContent] = [
ModelContent(role: "user", parts: [TextPart("What is the capital of Germany?")]),
]
// Send the final query, CLOSING the turn (true) to trigger the model response.
await session.sendContent(newTurn, turnComplete: true)
Kotlin
Not yet supported for Android apps - check back soon!
Java
Not yet supported for Android apps - check back soon!
Web
const turns = [{ text: "Hello from the user!" }];
await session.send(
turns,
false // turnComplete: false
);
console.log("Sent history. Waiting for next input...");
// Define the new user query.
const newTurn [{ text: "And what is the capital of Germany?" }];
// Send the final query, CLOSING the turn (true) to trigger the model response.
await session.send(
newTurn,
true // turnComplete: true
);
console.log("Sent final query. Model response expected now.");
Dart
// Define initial turns (history/context).
final List turns = [
Content(
"user",
[Part.text("What is the capital of France?")],
),
Content(
"model",
[Part.text("Paris")],
),
];
// Send history, keeping the conversational turn OPEN (false).
await session.send(
input: turns,
turnComplete: false,
);
// Define the new user query.
final List newTurn = [
Content(
"user",
[Part.text("What is the capital of Germany?")],
),
];
// Send the final query, CLOSING the turn (true) to trigger the model response.
await session.send(
input: newTurn,
turnComplete: true,
);
Unity
// Define initial turns (history/context).
List turns = new List {
new ModelContent("user", new ModelContent.TextPart("What is the capital of France?") ),
new ModelContent("model", new ModelContent.TextPart("Paris") ),
};
// Send history, keeping the conversational turn OPEN (false).
foreach (ModelContent turn in turns)
{
await session.SendAsync(
content: turn,
turnComplete: false
);
}
// Define the new user query.
ModelContent newTurn = ModelContent.Text("What is the capital of Germany?");
// Send the final query, CLOSING the turn (true) to trigger the model response.
await session.SendAsync(
content: newTurn,
turnComplete: true
);
セッション中にシステム指示を更新する
| API プロバイダとして Vertex AI Gemini API を使用する場合にのみ使用できます。 |
アクティブなセッション中にシステム指示を更新できます。これを使用して、モデルのレスポンスを調整します(レスポンス言語の変更やトーンの変更など)。
セッション中にシステム指示を更新するには、system ロールでテキスト コンテンツを送信します。更新されたシステム指示は、セッションの残りの期間有効になります。
Swift
await session.sendContent(
[ModelContent(
role: "system",
parts: [TextPart("new system instruction")]
)],
turnComplete: false
)
Kotlin
Not yet supported for Android apps - check back soon!
Java
Not yet supported for Android apps - check back soon!
Web
Not yet supported for Web apps - check back soon!
Dart
try {
await _session.send(
input: Content(
'system',
[Part.text('new system instruction')],
),
turnComplete: false,
);
} catch (e) {
print('Failed to update system instructions: $e');
}
Unity
try
{
await session.SendAsync(
content: new ModelContent(
"system",
new ModelContent.TextPart("new system instruction")
),
turnComplete: false
);
}
catch (Exception e)
{
Debug.LogError($"Failed to update system instructions: {e.Message}");
}
コンテキスト ウィンドウを圧縮する
|
Gemini API プロバイダをクリックして、このページでプロバイダ固有のコンテンツとコードを表示します。 |
Live API セッション コンテキスト ウィンドウには、リアルタイムでストリーミングされたデータ (音声の場合は 1 秒あたり 25 トークン(TPS)、動画の場合は 258 TPS)や、テキスト入力やモデル出力などの他の コンテンツが保存されます。すべての Live API モデルの セッション コンテキスト ウィンドウの上限は 128,000 トークンです。
デフォルトでは、このコンテキスト ウィンドウの上限により、入力モードに基づくセッションの最大長は次のようになります。
- 音声のみの入力セッションは
15 分 に制限されます。 - 動画と音声の入力は
2 分 に制限されます。
長時間実行されるセッションでは、会話が進むにつれて、音声トークンや動画トークンの履歴が蓄積されます。この履歴がモデルの上限を超えると、モデルがハルシネーションを起こしたり、処理速度が低下したり、セッションが強制的に終了したりする可能性があります。
セッションを長くするには、LiveGenerationConfig の一部として contextWindowCompression フィールドを設定して、 コンテキスト ウィンドウの圧縮 を有効にします。有効にすると、サーバーは スライディング ウィンドウ メカニズムを使用して、最も古いターンを自動的に破棄するか、要約して、コンテキスト サイズをデフォルトまたは指定された上限内に維持します。システム指示は破棄されず、常にコンテキスト ウィンドウの先頭に保持されます。
ユーザーの観点からすると、「メモリ」が常に管理されるため、セッションの継続時間は理論上無限になります。
スライディング ウィンドウ メカニズムと、圧縮をトリガーするトークンの数を 必要に応じて 構成できます(使用可能な設定と値については、下記をご覧ください)。これらの設定を使用する際の考慮事項は次のとおりです。
targetTokensを非常に低い値に設定すると、連続ストリーム用のコンテキスト領域が解放されますが、モデルは会話の古いターンをすぐに「忘れて」しまいます。targetTokensをtriggerTokensに近い値に設定すると、より多くのメモリが保持されますが、圧縮ルーチンが頻繁にトリガーされます。
| 設定 | 構成で設定されていない場合のスライディング ウィンドウのデフォルト | 最小値 | 最大値 |
|---|---|---|---|
triggerTokens圧縮がトリガーされる前のコンテキストの長さ |
モデルのコンテキスト ウィンドウの上限の 80% | 5,000 | 128,000 |
targetTokens保持するトークンの目標数 |
triggerTokens 値の 50%
|
0 | 128,000 |
Swift
// Initialize the Gemini Developer API backend service
let liveModel = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
modelName: "gemini-2.5-flash-native-audio-preview-12-2025",
// Enable context window compression.
// (Optional) Configure the number of tokens in the context window that triggers the compression.
generationConfig: LiveGenerationConfig(
responseModalities: [.audio],
contextWindowCompression: ContextWindowCompressionConfig(
triggerTokens: 10000,
slidingWindow: SlidingWindow(
targetTokens: 2000,
)
)
)
)
Kotlin
// Initialize the Gemini Developer API backend service
val liveModel = Firebase.ai(backend = GenerativeBackend.googleAI()).liveModel(
modelName = "gemini-2.5-flash-native-audio-preview-12-2025",
// Enable context window compression.
// (Optional) Configure the number of tokens in the context window that triggers the compression.
generationConfig = liveGenerationConfig {
responseModality = ResponseModality.AUDIO,
contextWindowCompression = ContextWindowCompressionConfig(
triggerTokens = 10000,
slidingWindow = SlidingWindow(targetTokens = 2000)
)
}
)
Java
// Initialize the Gemini Developer API backend service
LiveGenerativeModel lm = FirebaseAI.getInstance(GenerativeBackend.googleAI()).liveModel(
"gemini-2.5-flash-native-audio-preview-12-2025",
// Enable context window compression.
// (Optional) Configure the number of tokens in the context window that triggers the compression.
new LiveGenerationConfig.Builder()
.setResponseModality(ResponseModality.AUDIO)
.setContextWindowCompression(
new ContextWindowCompressionConfig(10000, new SlidingWindow(2000))
)
.build()
);
Web
const ai = getAI(firebaseApp, { backend: new GoogleAIBackend() });
const liveModel = getLiveGenerativeModel(ai, {
model: "gemini-2.5-flash-native-audio-preview-12-2025",
// Enable context window compression.
// (Optional) Configure the number of tokens in the context window that triggers the compression.
generationConfig: {
responseModalities: [ResponseModality.AUDIO],
contextWindowCompression: {
triggerTokens: 10000,
slidingWindow: {
targetTokens: 2000,
},
},
},
});
Dart
final _liveModel = FirebaseAI.googleAI().liveGenerativeModel(
model: 'gemini-2.5-flash-native-audio-preview-12-2025',
// Enable context window compression.
// (Optional) Configure the number of tokens in the context window that triggers the compression.
liveGenerationConfig: LiveGenerationConfig(
responseModalities: [ResponseModalities.audio],
contextWindowCompression: ContextWindowCompressionConfig(
triggerTokens: 10000,
slidingWindow: SlidingWindow(targetTokens: 2000),
),
),
);
Unity
var liveModel = FirebaseAI.GetInstance(FirebaseAI.Backend.GoogleAI()).GetLiveModel(
modelName: "gemini-2.5-flash-native-audio-preview-12-2025",
// Enable context window compression.
// (Optional) Configure the number of tokens in the context window that triggers the compression.
liveGenerationConfig: new LiveGenerationConfig(
responseModalities: new[] { ResponseModality.Audio },
contextWindowCompression: new ContextWindowCompressionConfig(
triggerTokens: 10000,
slidingWindow: new SlidingWindow(targetTokens: 2000)
)
)
);
セッションが終了するタイミングを検出する
単一の連続した WebSocket 接続の最大継続時間は約
次の例は、 going away 通知をリッスンして、接続の終了を検出する方法を示しています。
Swift
for try await response in session.responses {
switch response.payload {
case .goingAwayNotice(let goingAwayNotice):
// Prepare for the session to close soon
if let timeLeft = goingAwayNotice.timeLeft {
print("Server going away in \(timeLeft) seconds")
}
}
}
Kotlin
for (response in session.responses) {
when (val message = response.payload) {
is LiveServerGoAway -> {
// Prepare for the session to close soon
val remaining = message.timeLeft
logger.info("Server going away in $remaining")
}
}
}
Java
session.getResponses().forEach(response -> {
if (response.getPayload() instanceof LiveServerResponse.GoingAwayNotice) {
LiveServerResponse.GoingAwayNotice notice = (LiveServerResponse.GoingAwayNotice) response.getPayload();
// Prepare for the session to close soon
Duration timeLeft = notice.getTimeLeft();
}
});
Web
for await (const message of session.receive()) {
switch (message.type) {
...
case "goingAwayNotice":
console.log("Server going away. Time left:", message.timeLeft);
break;
}
}
Dart
Future _handleLiveServerMessage(LiveServerResponse response) async {
final message = response.message;
if (message is GoingAwayNotice) {
// Prepare for the session to close soon
developer.log('Server going away. Time left: ${message.timeLeft}');
}
}
Unity
foreach (var response in session.Responses) {
if (response.Payload is LiveSessionGoingAway notice) {
// Prepare for the session to close soon
TimeSpan timeLeft = notice.TimeLeft;
Debug.Log($"Server going away notice received. Remaining: {timeLeft}");
}
}
セッションを再開する
Live API は、会話のコンテキストが失われるのを防ぐために、セッションの再開をサポートしています。すべてのセッションにハンドルがあり、次の方法で使用できます。
接続時間の上限に達する前にセッションを維持する
単一の連続した WebSocket 接続の最大継続時間は約
10 分 です。 going away通知をリッスンして接続が終了するタイミングを検出し、そして セッション ハンドルを使用して新しい接続を確立することでセッションを延長できます。接続が切断された直後にセッションを再開する
接続が最大接続時間の上限に達する前に終了または切断された場合 (Wi-Fi から 5G に切り替えるなど)、サーバーはセッション状態を 約
10 分間 保持します。この期間中に、セッション ハンドルを使用して新しい接続を確立することで、セッションを再開できます。長時間経過した後にセッションを再開する
接続が終了すると、サーバーはセッション状態を数時間保持します。 この期間中に、セッション ハンドルを使用して新しい接続を確立することで、セッションを再開できます。この期間は、 2 つの Gemini APIプロバイダによって異なります: Gemini Developer API は
2 時間 、 Vertex AI Gemini API は24 時間 です。
デフォルトでは、セッションの再開は無効になっています。セッションの再開を有効にするには、新しい接続を確立するときに空の再開構成を渡します。有効にすると、サーバーはセッション再開ハンドルを含む更新を定期的に送信します。セッションが切断された場合は、再接続してこのハンドルを渡すことで、コンテキストを維持したままセッションを再開できます。
次の例は、セッションを再開する 2 つの方法を示しています。
Swift
// Local variable to save the active session handle
var activeSessionHandle: String?
// Initialize the session. Passing an empty config requests the server to send SessionResumptionUpdate
var session = try await liveModel.connect(
sessionResumption: SessionResumptionConfig()
)
// Start receiving responses
for try await message in session.responses {
// Check for new session handles inside your message handling loop
switch message.payload {
case let .sessionResumptionUpdate(updateMessage):
guard let newHandle = updateMessage.newHandle, updateMessage.resumable else {
continue
}
activeSessionHandle = newHandle
print("SessionResumptionUpdate: handle \(newHandle)")
// ... handle other LiveServerMessage types ...
default:
break
}
}
// The following are alternative options to resume a session. Choose only one.
// Option 1: Create and connect a session to resume with the saved handle
if let handle = activeSessionHandle {
session = try await liveModel.connect(
sessionResumption: SessionResumptionConfig(handle: handle)
)
}
// Option 2: Resume the session directly on an existing session object
if let handle = activeSessionHandle {
try await session.resumeSession(
sessionResumption: SessionResumptionConfig(handle: handle)
)
}
Kotlin
// Local variable to save the active session handle
var activeSessionHandle: String? = null
// Initialize the session. Passing an empty config requests the server to send SessionResumptionUpdate
var session = liveModel.connect(
sessionResumption = SessionResumptionConfig()
)
// Start receiving responses
session.receive().collect { message ->
// Process other received response types...
// Check for new session handles inside your message handling loop
if (message is LiveSessionResumptionUpdate) {
if (message.resumable == true && message.newHandle != null) {
activeSessionHandle = message.newHandle
Log.d("TAG", "SessionResumptionUpdate: handle ${message.newHandle}")
}
}
}
// The following are alternative options to resume a session. Choose only one.
// Option 1: Create and connect a session to resume with the saved handle
activeSessionHandle?.let { handle ->
session = liveModel.connect(
sessionResumption = SessionResumptionConfig(handle = handle)
)
}
// Option 2: Resume the session directly on an existing session object
activeSessionHandle?.let { handle ->
session.resumeSession(
sessionResumption = SessionResumptionConfig(handle = handle)
)
}
Java
For Java, session resumption is not yet supported. Check back soon!
Web
// Local variable to save the active session handle
let activeSessionHandle = null;
// Initialize the session. Passing an empty object requests the server to send SessionResumptionUpdate
let session = await liveModel.connect({});
// Start receiving responses
for await (const message of session.receive()) {
// Process other received response types...
// Check for new session handles inside your message handling loop
if (message.type === 'sessionResumptionUpdate') {
if (message.resumable && message.newHandle) {
activeSessionHandle = message.newHandle;
console.log(`SessionResumptionUpdate: handle ${activeSessionHandle}`);
}
}
}
// The following are alternative options to resume a session. Choose only one.
// Option 1: Create and connect a session to resume with the saved handle
if (activeSessionHandle) {
session = await liveModel.connect({
handle: activeSessionHandle
});
}
// Option 2: Resume the session directly on an existing session object
if (activeSessionHandle) {
await session.resumeSession({
handle: activeSessionHandle
});
}
Dart
// Local variable to save the active session handle
String? _activeSessionHandle;
// Initialize the session. Passing an empty config requests the server to send SessionResumptionUpdate
var _session = await _liveModel.connect(
sessionResumption: SessionResumptionConfig(),
);
// Start receiving responses
await for (final message in _session.receive()) {
// Process other received response types...
// Check for new session handles inside your message handling loop
if (message is SessionResumptionUpdate &&
message.resumable != null &&
message.resumable!) {
_activeSessionHandle = message.newHandle;
log('SessionResumptionUpdate: handle ${message.newHandle}');
}
}
// The following are alternative options to resume a session. Choose only one.
// Option 1: Create and connect a session to resume with the saved handle
if (_activeSessionHandle != null) {
_session = await _liveModel.connect(
sessionResumption: SessionResumptionConfig.resume(_activeSessionHandle!),
);
}
// Option 2: Alternatively, resume the session directly on an existing session object
if (_activeSessionHandle != null) {
await _session.resumeSession(
sessionResumption: SessionResumptionConfig.resume(_activeSessionHandle!),
);
}
Unity
// Local variable to save the active session handle
string activeSessionHandle = null;
// Initialize the session. Passing an empty config requests the server to send SessionResumptionUpdate
var session = await liveModel.ConnectAsync(
sessionResumption: new SessionResumptionConfig()
);
// Start receiving responses
await foreach (var response in session.ReceiveAsync())
{
// Process other received response types...
// Check for new session handles inside your message handling loop
if (response.Message is LiveSessionResumptionUpdate updateMessage)
{
if (updateMessage.Resumable == true && !string.IsNullOrEmpty(updateMessage.NewHandle))
{
activeSessionHandle = updateMessage.NewHandle;
Debug.Log($"SessionResumptionUpdate: handle {activeSessionHandle}");
}
}
}
// The following are alternative options to resume a session. Choose only one.
// Option 1: Create and connect a session to resume with the saved handle
if (!string.IsNullOrEmpty(activeSessionHandle)) {
session = await liveModel.ConnectAsync(
sessionResumption: new SessionResumptionConfig(activeSessionHandle)
);
}
// Option 2: Resume the session directly on an existing session object
if (!string.IsNullOrEmpty(activeSessionHandle)) {
await session.ResumeSessionAsync(
sessionResumption: new SessionResumptionConfig(activeSessionHandle)
);
}