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 cho đến khi kết thúc một cách suôn sẻ.

Giới hạn cho phiên

Đối với Live API, phiên đề cập đến một kết nối liên tục, trong đó đầ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, thì 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 ở 128.000 token.

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

    • Các phiên nhập chỉ bằng âm thanh được giới hạn trong 15 phút.
    • Đầu vào video và âm thanh bị giới hạn ở 2 phút.
  • Thời lượng kết nối bị giới hạn trong 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.

Sau đâ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 một phiên để tránh mất ngữ cảnh cuộc trò chuyện trong trường hợp mạng bị ngắt kết nối trong thời gian ngắn hoặc sau khi nhận được thông báo sắp rời khỏi.

Bắt đầu một phiên

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

Cập nhật trong phiên

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

Thêm nội dung cập nhật gia tăng

Bạn có thể thêm các bản cập nhật gia tăng trong phiên hoạt động. Sử dụng phương thức này để gửi dữ liệu nhập văn bản, thiết lập bối cảnh phiên hoặc khôi phục bối cảnh phiên.

  • Đối với những bối cảnh dài hơn, bạn nên cung cấp một bản tóm tắt tin nhắn duy nhất để 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 các ngữ cảnh ngắn, bạn có thể gửi các lượt tương tác từng bước để biểu thị chính xác trình tự của 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 trong phiên

Chỉ có khi bạn dùng Vertex AI Gemini API làm trình cung cấp API.

Bạn có thể cập nhật chỉ dẫn hệ thống trong một phiên hoạt động. Sử dụng tham số này để điều chỉnh câu trả lời của mô hình, ví dụ: thay đổi ngôn ngữ của câu trả lời hoặc sửa đổi giọng điệu.

Để cập nhật hướng dẫn cho hệ thống trong phiên, bạn có thể gửi nội dung văn bản có vai trò system. Hướng dẫn mới cập nhật cho hệ thống 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 cấp Gemini API để xem nội dung và mã dành riêng cho nhà cung cấp trên trang này.

Live API Cửa sổ ngữ cảnh của phiên lưu trữ dữ liệu được truyền trực tiếp theo thời gian thực (25 mã thông báo 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 là 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 cửa sổ ngữ cảnh phiên là 128.000 token.

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

  • Các phiên nhập chỉ bằng âm thanh được giới hạn trong 15 phút.
  • Đầu vào video và âm thanh bị giới hạn ở 2 phút.

Trong các phiên kéo dài, khi cuộc trò chuyện diễn ra, nhật ký mã thông báo â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ể đưa ra thông tin sai lệch, hoạt động chậm 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 làm một phần của 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 gần đây 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 được chỉ định. Các chỉ dẫn hệ thống sẽ không bị loại bỏ và luôn nằm ở đầ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ề 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ỳ chọn số lượng mã thông báo kích hoạt tính năng nén: (xem các chế độ cài đặt và giá trị có sẵn bên dưới). Sau đây là một số điểm cần cân nhắc chung về việc sử dụng các chế độ cài đặt này:

  • Việc đặt targetTokens ở mức 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 trò chuyện cũ hơn.

  • Việc đặt targetTokens gần với triggerTokens sẽ giúp duy trì 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 nhiều.

Cài đặt 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 quá trình nén được kích hoạt
80% giới hạn cửa sổ ngữ cảnh của mô hình 5.000 128.000
targetTokens
số lượng mã thông báo mục tiêu cần giữ lại
50% giá trị triggerTokens
  • Nếu bạn không đặt triggerTokens một cách rõ ràng, thì targetTokens sẽ mặc định là 50% giá trị triggerTokens mặc định.
  • 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 một 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 ứng dụng khách 60 giây trước khi kết nối kết thúc. Thông báo này có thể giúp bạn thực hiện các hành động khác (ví dụ: bằng cách tiếp tục một phiên).

Ví dụ sau đây minh hoạ cách phát hiện một kết nối sắp bị chấm dứt bằng cách theo dõi thông báo 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}");
    }
}

Tiếp tục một 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 giá trị nhận dạng và bạn có thể sử dụng giá trị 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 nghe thông báo sắp kết thúc, sau đó kéo dài phiên bằng cách thiết lập một kết nối mới bằng cách sử dụng mã nhận dạng phiên.

  • Tiếp tục phiên ngay sau khi mất kết nối

    Nếu một kết nối kết thúc hoặc bị ngắt trước khi hết thời gian kết nối tối đa (ví dụ: chuyển từ Wi-Fi sang 5G), thì 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 một kết nối mới bằng cách sử dụ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 một kết nối mới bằng cách sử dụ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 2 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 truyền một cấu hình tiếp tục trống khi thiết lập một 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ột mã xử lý 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à truyền mã nhận dạng này để tiếp tục phiên mà không bị mất ngữ cảnh.

Các ví dụ sau đây minh hoạ 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)
  );
}