Quản lý phiên cho Live API

Gemini Live API xử lý các luồng âm thanh hoặc văn bản liên tục được gọi là phiên. Bạn có thể quản lý vòng đời của phiên, từ lần bắt tay ban đầu đến việc chấm dứt một cách suôn sẻ.

Giới hạn cho phiên

Đối với Live API, một phiên đề cập đến một kết nối liên tục, trong đó dữ liệu đầu vào và đầu ra được truyền trực tuyến liên tục qua một kết nối.

Nếu phiên vượt quá bất kỳ giới hạn nào sau đây, kết nối sẽ bị chấm dứt. Tuy nhiên, hãy lưu ý rằng Live API cung cấp một số lựa chọn (xem bên dưới) để xử lý các giới hạn liên quan đến phiên này.

  • Cửa sổ ngữ cảnh của phiên bị giới hạn ở mức 128.000 token.

    Do giới hạn này về cửa sổ ngữ cảnh, sau đây là thời lượng phiên tối đa gần đúng dựa trên phương thức nhập:

    • Phiên chỉ nhập âm thanh bị giới hạn ở mức 15 phút.
    • Phiên nhập video + âm thanh bị giới hạn ở mức 2 phút.
  • Thời lượng kết nối bị giới hạn ở mức khoảng 10 phút.

    Bạn sẽ nhận được thông báo sắp kết thúc khoảng 60 giây trước khi kết nối kết thúc.

Dưới đây là một số lựa chọn để xử lý các giới hạn liên quan đến phiên:

  • Nén cửa sổ ngữ cảnh của phiên để máy chủ tự động duy trì kích thước ngữ cảnh trong giới hạn.

  • Tiếp tục phiên để tránh mất ngữ cảnh trò chuyện trong thời gian ngắn khi mạng bị ngắt kết nối hoặc sau khi nhận được thông báo sắp kết thúc.

Bắt đầu phiên

Hãy xem hướng dẫn bắt đầu sử dụng Live API để xem đoạn mã đầy đủ cho biết cách bắt đầu phiên.

Cập nhật giữa phiên

Các mô hình Live API hỗ trợ các tính năng nâng cao sau đây cho các bản cập nhật giữa phiên:

Thêm các bản cập nhật nội dung tăng dần

Bạn có thể thêm các bản cập nhật tăng dần trong phiên đang hoạt động. Hãy dùng tính năng này để gửi dữ liệu đầu vào dạng văn bản, thiết lập ngữ cảnh của phiên hoặc khôi phục ngữ cảnh của phiên.

  • Đối với ngữ cảnh dài hơn, bạn nên cung cấp bản tóm tắt một tin nhắn để giải phóng cửa sổ ngữ cảnh cho các lượt tương tác tiếp theo.

  • Đối với ngữ cảnh ngắn, bạn có thể gửi các lượt tương tác theo từng lượt để biểu thị chính xác trình tự các sự kiện, chẳng hạn như đoạn mã bên dưới.

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
);

Cập nhật hướng dẫn hệ thống giữa phiên

Chỉ dùng được khi bạn sử dụng Vertex AI Gemini API làm nhà cung cấp API.

Bạn có thể cập nhật hướng dẫn hệ thống trong phiên đang hoạt động. Hãy dùng tính năng này để điều chỉnh phản hồi của mô hình, chẳng hạn như thay đổi ngôn ngữ phản hồi hoặc sửa đổi giọng điệu.

Để cập nhật hướng dẫn hệ thống giữa phiên, bạn có thể gửi nội dung văn bản có vai trò system. Hướng dẫn hệ thống đã cập nhật sẽ vẫn có hiệu lực trong thời gian còn lại của phiên.

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}");
}

Nén cửa sổ ngữ cảnh

Nhấp vào nhà cung định Gemini API để xem nội dung dành riêng cho nhà cung cấp và mã trên trang này.

Live API Cửa sổ ngữ cảnh của phiên Live API lưu trữ dữ liệu được truyền trực tuyến theo thời gian thực (25 token mỗi giây (TPS) cho âm thanh và 258 TPS cho video) cũng như nội dung khác, bao gồm cả dữ liệu đầu vào dạng văn bản và dữ liệu đầu ra của mô hình. Tất cả các mô hình Live API đều có giới hạn về cửa sổ ngữ cảnh của phiên là 128.000 token.

Theo mặc định, do giới hạn này về cửa sổ ngữ cảnh, sau đây là thời lượng phiên tối đa gần đúng dựa trên phương thức nhập:

  • Phiên chỉ nhập âm thanh bị giới hạn ở mức 15 phút.
  • Phiên nhập video + âm thanh bị giới hạn ở mức 2 phút.

Trong các phiên chạy dài, khi cuộc trò chuyện diễn ra, nhật ký token âm thanh và/hoặc video sẽ tích luỹ. Nếu nhật ký này vượt quá giới hạn của mô hình, thì mô hình có thể bị ảo giác, chậm lại hoặc phiên có thể bị chấm dứt bắt buộc.

Để bật các phiên dài hơn, bạn có thể bật tính năng nén cửa sổ ngữ cảnh bằng cách đặt trường contextWindowCompression trong LiveGenerationConfig. Khi được bật, máy chủ sẽ sử dụng cơ chế cửa sổ trượt để tự động loại bỏ các lượt cũ nhất hoặc tóm tắt các lượt đó để duy trì kích thước ngữ cảnh trong giới hạn mặc định hoặc giới hạn đã chỉ định. Hướng dẫn hệ thống không bị loại bỏ và sẽ luôn ở đầu cửa sổ ngữ cảnh.

Theo quan điểm của người dùng, điều này cho phép thời lượng phiên về mặt lý thuyết là vô hạn vì "bộ nhớ" được quản lý liên tục.

Bạn có thể định cấu hình cơ chế cửa sổ trượt cũng như tuỳ ý số lượng token kích hoạt tính năng nén: (xem các chế độ cài đặt và giá trị hiện có bên dưới). Dưới đây là một số điểm cần cân nhắc ở cấp cao về việc sử dụng các chế độ cài đặt này:

  • Việc đặt targetTokens rất thấp sẽ giải phóng thêm không gian ngữ cảnh cho các luồng liên tục, nhưng mô hình sẽ nhanh chóng "quên" các lượt cũ của cuộc trò chuyện.

  • Việc đặt targetTokens gần với triggerTokens sẽ giữ lại nhiều bộ nhớ hơn nhưng sẽ kích hoạt các quy trình nén thường xuyên hơn.

Cài đặt Giá trị mặc định cho cửa sổ trượt nếu không được đặt trong cấu hình Giá trị nhỏ nhất Giá trị lớn nhất
triggerTokens
độ dài ngữ cảnh trước khi kích hoạt tính năng nén
80% giới hạn cửa sổ ngữ cảnh của mô hình 5.000 128.000
targetTokens
số lượng token mục tiêu cần giữ lại
50% giá trị triggerTokens
  • Nếu triggerTokens không được đặt rõ ràng, thì targetTokens sẽ mặc định là 50% giá trị mặc định triggerTokens.
  • Giá trị targetTokens phải nhỏ hơn giá trị triggerTokens
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)
        )
    )
);

Phát hiện thời điểm phiên sắp kết thúc

Thời lượng tối đa của một kết nối WebSocket liên tục là khoảng 10 phút. Thông báo sắp kết thúc sẽ được gửi đến máy khách 60 giây trước khi kết nối kết thúc. Điều này có thể giúp bạn thực hiện các hành động khác (ví dụ: tiếp tục phiên).

Ví dụ sau đây cho biết cách phát hiện việc chấm dứt kết nối sắp xảy ra bằng cách theo dõi thông báo sắp kết thúc:

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}");
    }
}

Tiếp tục phiên

Live API hỗ trợ tiếp tục phiên để tránh mất ngữ cảnh trò chuyện. Mỗi phiên đều có một mã nhận dạng và bạn có thể sử dụng mã này theo những cách sau:

  • Duy trì phiên trước khi đạt đến giới hạn thời gian kết nối

    Thời lượng tối đa của một kết nối WebSocket liên tục là khoảng 10 phút. Bạn có thể phát hiện thời điểm kết nối sắp kết thúc bằng cách theo dõi thông báo sắp kết thúc, và sau đó kéo dài phiên bằng cách thiết lập kết nối mới bằng mã nhận dạng phiên.

  • Tiếp tục phiên ngay sau khi kết nối bị gián đoạn

    Nếu kết nối chấm dứt hoặc bị gián đoạn trước giới hạn thời gian kết nối tối đa (ví dụ: chuyển từ WiFi sang 5G), máy chủ sẽ giữ trạng thái phiên trong khoảng 10 phút. Trong khoảng thời gian này, bạn có thể tiếp tục phiên bằng cách thiết lập kết nối mới bằng mã nhận dạng phiên.

  • Tiếp tục phiên sau một khoảng thời gian dài

    Sau khi kết nối kết thúc, máy chủ sẽ giữ trạng thái phiên trong vài giờ. Trong khoảng thời gian này, bạn có thể tiếp tục phiên bằng cách thiết lập kết nối mới bằng mã nhận dạng phiên. Xin lưu ý rằng khoảng thời gian này khác nhau đối với hai nhà cung cấp Gemini API: Gemini Developer API2 giờ | Vertex AI Gemini API24 giờ.

Theo mặc định, tính năng tiếp tục phiên bị tắt. Để bật tính năng tiếp tục phiên, hãy chuyển cấu hình tiếp tục trống khi thiết lập kết nối mới. Khi được bật, máy chủ sẽ định kỳ gửi các bản cập nhật chứa mã nhận dạng tiếp tục phiên. Nếu phiên bị ngắt kết nối, bạn có thể kết nối lại và chuyển mã nhận dạng này để tiếp tục phiên với ngữ cảnh còn nguyên vẹn.

Các ví dụ sau đây cho thấy 2 lựa chọn để tiếp tục phiên:

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)
  );
}