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

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

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

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

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

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

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

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

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

الإعداد القيمة التلقائية للنافذة المنزلقة في حال عدم ضبطها في الإعداد القيمة الصغرى القيمة القصوى
triggerTokens
طول السياق قبل بدء عملية الضغط
‫80% من الحد الأقصى لقدرة استيعاب النموذج 5000 128,000
targetTokens
عدد الرموز المميزة المستهدَف المطلوب الاحتفاظ بها
‫50% من قيمة triggerTokens
  • إذا لم يتم ضبط triggerTokens بشكل صريح، ستكون القيمة التلقائية لـ targetTokens هي% 50 من القيمة التلقائية لـ triggerTokens.
  • يجب أن تكون قيمة targetTokens أقل من قيمة 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)
        )
    )
);

رصد وقت انتهاء الجلسة

الحد الأقصى لمدة اتصال WebSocket واحد ومتواصل هو حوالي 10 دقائق. يتم إرسال إشعار بالإيقاف إلى العميل قبل 60 ثانية من انتهاء الاتصال، ما يساعدك في اتّخاذ إجراءات إضافية (على سبيل المثال، من خلال استئناف جلسة).

يوضّح المثال التالي كيفية رصد إنهاء وشيك للاتصال من خلال الاستماع إلى إشعار 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 إلى شبكة الجيل الخامس)، يحتفظ الخادم بحالة الجلسة لمدة 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)
  );
}