إدارة الجلسات في Live API

يعالج Gemini Live API تدفقات مستمرة من الصوت أو النص تُعرف باسم الجلسات. يمكنك إدارة دورة حياة الجلسة، بدءًا من المصافحة الأولية وصولاً إلى الإنهاء السلس.

حدود الجلسات

في Live API، تشير الجلسة إلى اتصال مستمر يتم فيه بث الإدخال والإخراج بشكل مستمر عبر اتصال.

إذا تجاوزت الجلسة أيًا من الحدود التالية، يتم إنهاء الاتصال. يُرجى العِلم أنّ Live API يوفّر بعض الخيارات (الموضّحة أدناه) للتعامل مع هذه الحدود المرتبطة بالجلسة.

  • تقتصر قدرة استيعاب الجلسة على 128 ألف رمز مميّز.

    بسبب هذا الحدّ المفروض على قدرة الاستيعاب، إليك الحد الأقصى التقريبي لأطوال الجلسات استنادًا إلى طرق الإدخال:

    • تقتصر الجلسات التي تستخدم إدخال الصوت فقط على 15 دقيقة.
    • تقتصر الجلسات التي تستخدم إدخال الفيديو والصوت على دقيقتَين.
  • يقتصرطول الاتصال على 10 دقائق تقريبًا.

    ستتلقّى إشعارًا _بانتهاء الاتصال_ قبل 60 ثانية تقريبًا من انتهاء الاتصال.

في ما يلي بعض الخيارات للتعامل مع الحدود المرتبطة بالجلسة:

بدء جلسة

يُرجى الانتقال إلى دليل البدء في Live API للاطّلاع على مقتطف كامل يوضّح كيفية بدء جلسة.

تعديل الجلسة أثناء انعقادها

تتوافق نماذج Live API مع الإمكانات المتقدّمة التالية لـ تعديل الجلسة أثناء انعقادها:

إضافة تعديلات تدريجية على المحتوى

يمكنك إضافة تعديلات تدريجية أثناء انعقاد جلسة نشطة. استخدِم هذه الميزة لإرسال إدخال نصي أو إنشاء سياق الجلسة أو استعادة سياق الجلسة.

  • بالنسبة إلى السياقات الأطول، ننصحك بتقديم ملخّص لرسالة واحدة لتحرير قدرة الاستيعاب للتفاعلات اللاحقة.

  • بالنسبة إلى السياقات القصيرة، يمكنك إرسال تفاعلات متتالية لتمثيل التسلسل الدقيق للأحداث، مثل المقتطف أدناه.

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

تعديل تعليمات النظام أثناء انعقاد الجلسة

لا يتوفّر هذا الخيار إلا عند استخدام 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 قدرة استيعاب الجلسة في Live API البيانات التي يتم بثها في الوقت الفعلي (25 رمزًا مميّزًا في الثانية للبيانات الصوتية و258 رمزًا مميّزًا في الثانية للفيديو) بالإضافة إلى المحتوى الآخر، بما في ذلك الإدخالات النصية ونتائج النموذج. تحتوي جميع نماذج Live API على حدّ أقصى لقدرة استيعاب الجلسة يبلغ 128 ألف رمز مميّز.

بسبب هذا الحدّ المفروض على قدرة الاستيعاب، إليك الحد الأقصى التقريبي لأطوال الجلسات استنادًا إلى طرق الإدخال:

  • تقتصر الجلسات التي تستخدم إدخال الصوت فقط على 15 دقيقة.
  • تقتصر الجلسات التي تستخدم إدخال الفيديو والصوت على دقيقتَين.

في الجلسات الطويلة، يتراكم سجلّ الرموز المميّزة الصوتية و/أو المرئية مع تقدّم المحادثة. إذا تجاوز هذا السجلّ الحدّ الأقصى للنموذج، قد يقدّم النموذج معلومات غير صحيحة أو يصبح بطيئًا أو قد يتم إنهاء الجلسة قسرًا.

لإتاحة جلسات أطول، يمكنك تفعيل ضغط قدرة الاستيعاب من خلال ضبط الحقل contextWindowCompression كجزء من LiveGenerationConfig. عند تفعيل هذه الميزة، يستخدم الخادم آلية نافذة منزلقة لتجاهل أقدم الأدوار تلقائيًا أو تلخيصها للحفاظ على حجم السياق ضمن الحدود التلقائية أو المحدّدة. لا يتم تجاهل تعليمات النظام وستظل دائمًا في بداية قدرة الاستيعاب.

من منظور المستخدم، يتيح ذلك مدة جلسة غير محدودة نظريًا لأنّه تتم إدارة "الذاكرة" باستمرار.

يمكنك ضبط آلية النافذة المنزلقة بالإضافة إلى عدد الرموز المميّزة التي تؤدي إلى الضغط اختياريًا (راجِع الإعدادات والقيم المتاحة أدناه). في ما يلي بعض الاعتبارات العامة حول استخدام هذه الإعدادات:

  • سيؤدي ضبط targetTokens على قيمة منخفضة جدًا إلى تحرير مساحة أكبر في السياق للتدفقات المستمرة، ولكنّ النموذج "سينسى" بسرعة الأدوار القديمة من المحادثة.

  • سيؤدي ضبط targetTokens على قيمة أقرب إلى triggerTokens إلى الاحتفاظ بمزيد من الذاكرة، ولكن سيؤدي ذلك إلى تشغيل إجراءات الضغط بشكل متكرر أكثر بكثير.

الإعداد القيمة التلقائية للنافذة المنزلقة إذا لم يتم ضبطها في الإعداد القيمة الصغرى القيمة القصوى
triggerTokens
طول السياق قبل تشغيل الضغط
‫80% من الحدّ الأقصى لقدرة استيعاب النموذج 5000 128000
targetTokens
العدد المستهدَف من الرموز المميّزة التي يجب الاحتفاظ بها
‫50% من قيمة triggerTokens
  • إذا لم يتم ضبط triggerTokens بشكل صريح ، ستكون القيمة التلقائية لـ targetTokens هي 50% من القيمة التلقائية لـ triggerTokens.
  • يجب أن تكون قيمة targetTokens أقل من قيمة triggerTokens
0 128000

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 واحد ومستمر هو تقريبًا 10 دقائق. يتم إرسال إشعار بانتهاء الاتصال إلى العميل قبل 60 ثانية من انتهاء الاتصال، ما يساعدك في اتّخاذ إجراءات إضافية (مثلاً، من خلال استئناف الجلسة).

يوضّح المثال التالي كيفية رصد إنهاء وشيك للاتصال من خلال الاستماع إلى إشعار بانتهاء الاتصال:

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 دقائق. يمكنك رصد وقت انتهاء الاتصال من خلال الاستماع إلى إشعار بانتهاء الاتصال، و ثم تمديد الجلسة من خلال إنشاء اتصال جديد باستخدام معرّف الجلسة.

  • استئناف جلسة بعد انقطاع الاتصال مباشرةً

    إذا انتهى الاتصال أو انقطع قبل الحدّ الأقصى لوقت الاتصال (مثلاً، الانتقال من شبكة Wi-Fi إلى شبكة 5G)، يحتفظ الخادم بحالة الجلسة لمدة 10 دقائق تقريبًا. خلال هذه الفترة، يمكنك استئناف الجلسة من خلال إنشاء اتصال جديد باستخدام معرّف الجلسة.

  • استئناف جلسة بعد فترة زمنية طويلة

    بعد انتهاء الاتصال، يحتفظ الخادم بحالة الجلسة لبضع ساعات. خلال هذه الفترة، يمكنك استئناف الجلسة من خلال إنشاء اتصال جديد باستخدام معرّف الجلسة. يُرجى العِلم أنّ هذه الفترة تختلف بالنسبة إلى مزوّدَي Gemini API : Gemini Developer API هي ساعتان | Vertex AI Gemini API هي 24 ساعة.

يتم إيقاف ميزة استئناف الجلسة تلقائيًا. لتفعيل ميزة استئناف الجلسة، مرِّر إعدادات استئناف فارغة عند إنشاء اتصال جديد. عند تفعيل هذه الميزة، يرسل الخادم بشكل دوري تعديلات تحتوي على معرّف استئناف الجلسة. إذا تم قطع اتصال الجلسة، يمكنك إعادة الاتصال وتمرير هذا المعرّف لاستئناف الجلسة مع الحفاظ على سياقها.

يوضّح المثالان التاليان خيارَين لاستئناف الجلسة:

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