تعالج عمليات Gemini Live API تدفقات مستمرة من الصوت أو النص تُعرف باسم الجلسات. يمكنك إدارة مراحل الجلسة، بدءًا من عملية المصافحة الأولية وصولاً إلى الإنهاء السلس.
الحدود القصوى للجلسات
بالنسبة إلى Live API، تشير الجلسة إلى اتصال دائم يتم فيه بث البيانات المدخلة والمخرجة باستمرار عبر اتصال.
إذا تجاوزت الجلسة أيًا من الحدود التالية، سيتم إنهاء الاتصال. يُرجى العِلم أنّ Live API يوفّر بعض الخيارات (انظر أدناه) للتعامل مع هذه الحدود المرتبطة بالجلسة.
يقتصر عدد الرموز المميزة المستندة إلى السياق في الجلسة على 128 ألف رمز مميز.
نتيجةً لهذا الحدّ الأقصى لقدرة الاستيعاب، إليك الحدّ الأقصى التقريبي لطول الجلسة استنادًا إلى طرق الإدخال:
- تقتصر جلسات الإدخال الصوتي فقط على
15 دقيقة . - يجب ألا تتجاوز مدة الفيديو والصوت المُدخَلَين
دقيقتَين .
- تقتصر جلسات الإدخال الصوتي فقط على
مدة الاتصال محدودة بحوالي
10 دقائق .ستتلقّى إشعارًابالانتقال إلى وضع عدم الاتصال قبل
60 ثانية تقريبًا من انتهاء الاتصال.
في ما يلي بعض الخيارات للتعامل مع الحدود المتعلّقة بالجلسات:
ضغط قدرة استيعاب الجلسة ليحافظ الخادم تلقائيًا على حجم السياق ضمن الحدّ المسموح به.
استئناف جلسة لمنع فقدان سياق المحادثة أثناء انقطاع الاتصال بالشبكة لفترة وجيزة أو بعد تلقّي إشعار going away.
بدء جلسة
يمكنك الانتقال إلى دليل البدء في Live API للاطّلاع على مقتطف كامل يوضّح كيفية بدء جلسة.
التحديث في منتصف الجلسة
تتيح نماذج Live API الميزات المتقدّمة التالية للتحديثات في منتصف الجلسة:
تعديل تعليمات النظام (في Agent Platform Gemini API (formerly Vertex AI) فقط)
إضافة تعديلات المحتوى المتزايدة
يمكنك إضافة تحديثات تزايدية أثناء جلسة نشطة. يمكنك استخدام هذه الميزة لإرسال نص أو إنشاء سياق جلسة أو استعادته.
بالنسبة إلى السياقات الأطول، ننصحك بتقديم ملخّص واحد للرسالة لإتاحة مساحة في نافذة السياق للتفاعلات اللاحقة.
بالنسبة إلى السياقات القصيرة، يمكنك إرسال تفاعلات اتّجاهات مفصّلة لتمثيل التسلسل الدقيق للأحداث، مثل المقتطف أدناه.
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
// Define initial turns (history/context).
val turns = listOf(
content("user") {
text("What is the capital of France?")
},
content("model") {
text("Paris")
}
)
// Send history, keeping the conversational turn OPEN (false).
turns.forEach {
session.send(
content = it,
turnComplete = false
)
}
// Define the new user query.
val newTurn = content("user") {
text("What is the capital of Germany?")
}
// Send the final query, CLOSING the turn (true) to trigger the model response.
session.send(
content = newTurn,
turnComplete = true
)
Java
// Define initial turns (history/context).
List turns =
Arrays.asList(
new Content.Builder().setRole("user").addText("What is the capital of France?").build(),
new Content.Builder().setRole("model").addText("Paris").build());
for (Content turn : turns) {
session.send(
turn,
false // turnComplete: false
);
}
// Define the new user query.
Content newTurn = new Content.Builder().addText("What is the capital of Germany?").build();
// Send the final query, CLOSING the turn (true) to trigger the model response.
session.send(
newTurn,
true // isTurnComplete: true
);
Web
const turns = [{ type: "text", 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 = [{ type: "text", 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
);
تعديل تعليمات النظام في منتصف الجلسة
| لا تتوفّر هذه الميزة إلا عند استخدام Agent Platform Gemini API (formerly Vertex AI) كموفّر لواجهة برمجة التطبيقات. |
يمكنك تعديل تعليمات النظام أثناء جلسة نشطة. يمكنك استخدام هذه التعليمات لتعديل ردود النموذج، على سبيل المثال لتغيير لغة الردّ أو تعديل أسلوبه.
لتعديل تعليمات النظام في منتصف الجلسة، يمكنك إرسال محتوى نصي باستخدام الدور system. ستبقى تعليمات النظام المعدَّلة سارية طوال الفترة المتبقية من الجلسة.
Swift
await session.sendContent(
[ModelContent(
role: "system",
parts: [TextPart("new system instruction")]
)],
turnComplete: false
)
Kotlin
// In a coroutine scope
session.send(
content = content("system") {
text("new system instruction")
},
turnComplete = false
)
Java
session.send(
new Content.Builder()
.setRole("system")
.addText("new system instruction")
.build(),
/* turnComplete: */ false
);
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
|
0 | 128,000 |
Swift
// ...
let liveModel = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
modelName: "GEMINI_LIVE_API_MODEL_NAME",
// 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
// ...
val liveModel = Firebase.ai(backend = GenerativeBackend.googleAI()).liveModel(
modelName = "GEMINI_LIVE_API_MODEL_NAME",
// 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
// ...
LiveGenerativeModel lm = FirebaseAI.getInstance(GenerativeBackend.googleAI()).liveModel(
"GEMINI_LIVE_API_MODEL_NAME",
// 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_LIVE_API_MODEL_NAME",
// 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_LIVE_API_MODEL_NAME',
// 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_LIVE_API_MODEL_NAME",
// 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 دقائق تقريبًا. يمكنك رصد اقتراب انتهاء الاتصال من خلال الاستماع إلى إشعار المغادرة، ثم تمديد الجلسة من خلال إنشاء اتصال جديد باستخدام معرّف الجلسة.استئناف جلسة بعد انقطاع الاتصال مباشرةً
إذا تم إنهاء الاتصال أو انقطاعه قبل الحد الأقصى لمدة الاتصال (على سبيل المثال، التبديل من شبكة Wi-Fi إلى شبكة الجيل الخامس)، يحتفظ الخادم بحالة الجلسة لمدة
10 دقائق تقريبًا. خلال هذه الفترة، يمكنك استئناف الجلسة من خلال إنشاء اتصال جديد باستخدام معرّف الجلسة.استئناف جلسة بعد فترة زمنية طويلة
بعد انتهاء عملية الربط، يحتفظ الخادم بحالة الجلسة لبضع ساعات. وخلال هذه الفترة، يمكنك استئناف الجلسة من خلال إنشاء اتصال جديد باستخدام معرّف الجلسة. يُرجى العِلم أنّ هذه الفترة الزمنية تختلف حسب مقدّم خدمة Gemini API: Gemini Developer API:
ساعتان Agent Platform Gemini API (formerly Vertex AI):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)
);
}