تفاعلات Gemini API (تجريبي)

يوفّر ADK-Rust عميلاً مخصصًا لـ تفاعلات API من Google — وهو الاتجاه الجديد من Google لـ Gemini API. ويستبدل بنية الطلب/الاستجابة الخاصة بـ generateContent بمورد Interaction ذي حالة، مبني حول مخطط زمني للخطوات مكتوب الأنواع، وسجل محفوظ على الخادم، وسير عمل أصيل قائم على الوكلاء.

إن Interactions API في مرحلة تجريبية. توصي Google باستخدام generateContent لأحمال العمل الإنتاجية المستقرة، وقد تُجري تغييرات غير متوافقة على مخطط Interactions. يثبّت ADK-Rust عقد Api-Revision: 2026-05-20 (مخطط الخطوات).

نظرة عامة

┌─────────────────────────────────────────────────────────────────────┐
│                  Gemini Interactions API Client                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   Endpoint:  POST /v1beta/interactions                              │
│   Builder:   Gemini::create_interaction()                           │
│   Feature:   interactions (adk-gemini)                              │
│             gemini-interactions (adk-model / adk-rust)              │
│                                                                     │
│   Capabilities:                                                     │
│   • Single-turn and streaming (step.delta events)                   │
│   • Server-side history via previous_interaction_id                 │
│   • Typed step timeline (thought, function_call, model_output, …)   │
│   • Multimodal input (text, image, audio, document, video)          │
│   • Structured output (response_format JSON schema)                 │
│   • Client-side function calling + built-in server tools            │
│   • Background / long-running tasks (background = true)              │
│   • Lifecycle: get / delete / cancel a stored interaction           │
│                                                                     │
│   vs generateContent (GeminiModel):                                 │
│   • Stateful conversations (server stores history)                  │
│   • Observable execution steps for agentic UIs                      │
│   • New models & tools launch here first                            │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

متى تستخدم أيًّا من API

الجانبgenerateContent (GeminiModel)التفاعلات API (create_interaction)
نقطة النهايةPOST /v1beta/models/{model}:generateContentPOST /v1beta/interactions
الاستقرارمستقر، موصى به للاستخدام في الإنتاجتجريبي، قد يتغير المخطط
السجليعيد العميل إرسال النص الكامل للمحادثةمن جانب الخادم عبر previous_interaction_id
شكل الاستجابةcandidates + partsمخطط زمني لـ steps
وقت تشغيل الوكيل (سمة Llm)✅ نقل افتراضي✅ تفعيل اختياري عبر use_interactions_api
النماذج / الأدوات الجديدةالإطلاق هنا أولًا

يستخدم وقت تشغيل الوكيل ADK (السمة Llm، وحلقة الأدوات، وRunner) ‏generateContent افتراضيًا. يمكنك أيضًا تشغيل التفاعلات API من خلال وقت التشغيل نفسه عبر تفعيل use_interactions_api(true) على GeminiModel — راجع التفاعلات كوسيلة نقل لوقت التشغيل أدناه. يظل العميل المباشر (الموثق أولًا) متاحًا للجهات المستدعية التي تريد سجلًا من جهة الخادم، أو خطوات قابلة للمراقبة، أو نماذج حصرية للإصدار التجريبي دون إشراك وكيل.

التفعيل

# Direct client (adk-gemini)
adk-gemini = { version = "2.1.0", features = ["interactions"] }

# Through the model facade / umbrella
adk-model = { version = "2.1.0", features = ["gemini-interactions"] }
adk-rust  = { version = "2.1.0", features = ["gemini-interactions"] }

لا تضيف الميزة أي تبعيات جديدة، وهي إضافة كاملة إلى generateContent API الحالي.

البدء السريع

use adk_gemini::{Gemini, Model, ThinkingLevel};

let gemini = Gemini::new(std::env::var("GEMINI_API_KEY")?)?;

let interaction = gemini
    .create_interaction()
    .model(Model::Gemini35Flash)
    .system_instruction("You are concise.")
    .input_text("What is the capital of France?")
    .thinking_level(ThinkingLevel::Low)
    .send()
    .await?;

println!("{}", interaction.output_text().unwrap_or_default());

البث

عند البث، يصدر API نموذج أحداث SSE موجّهًا بالخطوات. والمسار الأكثر شيوعًا هو تجميع أجزاء النص من أحداث step.delta:

use futures::StreamExt;

let mut stream = gemini
    .create_interaction()
    .model(Model::Gemini35Flash)
    .input_text("Write a haiku about Rust.")
    .stream()
    .await?;

while let Some(event) = stream.next().await {
    if let Some(fragment) = event?.text_delta() {
        print!("{fragment}");
    }
}

أنواع الأحداث: interaction.created، وstep.start، وstep.delta، وstep.stop، وinteraction.status_update، وinteraction.completed، وerror. وتتم إزالة تسلسل الأحداث المستقبلية غير المعروفة إلى InteractionSseEvent::Other بدلًا من فشل البث.

التفاعلات متعددة الأدوار من جهة الخادم

مرّر id الخاص بتفاعل سابق لمتابعة المحادثة دون إعادة إرسال السجل. لاحظ أن tools وsystem_instruction وgeneration_config مرتبطة بنطاق التفاعل، ويجب تحديدها مجددًا في كل دور:

let first = gemini.create_interaction()
    .model(Model::Gemini35Flash)
    .input_text("My favorite color is teal.")
    .send().await?;

let second = gemini.create_interaction()
    .model(Model::Gemini35Flash)
    .previous_interaction_id(&first.id)
    .input_text("What is my favorite color?")
    .send().await?;

استدعاء الدوال

يعرض API الخاص بالتفاعلات استدعاءات الأدوات من جانب العميل باعتبارها خطوات function_call ذات حالة requires_action. قدّم النتائج في دور متابعة:

use serde_json::json;

let interaction = gemini.create_interaction()
    .model(Model::Gemini35Flash)
    .function("get_weather", "Get the weather",
        json!({"type": "object", "properties": {"location": {"type": "string"}}}))
    .input_text("Weather in Boston?")
    .send().await?;

if interaction.status.requires_action() {
    let follow_up = gemini.create_interaction()
        .model(Model::Gemini35Flash)
        .previous_interaction_id(&interaction.id);

    let mut follow_up = follow_up;
    for (call_id, name, _args) in interaction.pending_function_calls() {
        follow_up = follow_up.function_result(call_id, name, json!({"temperature": "72F"}));
    }
    let final_interaction = follow_up.send().await?;
    println!("{}", final_interaction.output_text().unwrap_or_default());
}

المخرجات المهيكلة

use serde_json::json;

let interaction = gemini.create_interaction()
    .model(Model::Gemini35Flash)
    .input_text("Summarize this article: ...")
    .json_schema(json!({
        "type": "object",
        "properties": { "summary": { "type": "string" } },
        "required": ["summary"]
    }))
    .send().await?;

دورة الحياة

يمكن استرداد التفاعلات المخزنة (وهو الإعداد الافتراضي من جهة الخادم)، أو حذفها، أو إلغاؤها:

let fetched = gemini.get_interaction(&interaction.id, /* include_input */ true).await?;
gemini.cancel_interaction(&interaction.id).await?; // background tasks only
gemini.delete_interaction(&interaction.id).await?;

قيم الحالة

يعكس InteractionStatus دورة حياة API: ‏InProgress، وRequiresAction، وCompleted، وFailed، وCancelled، وIncomplete، وBudgetExceeded. استخدم is_terminal() وrequires_action() للتحكم في التدفق.

القيود

لا يدعم Interactions API حتى الآن Batch API أو التخزين المؤقت الصريح (يتوفر التخزين المؤقت الضمني من جانب الخادم عبر previous_interaction_id). يستخدم وقت تشغيل العامل ADK ‏generateContent افتراضيًا؛ ويتوفر Interactions API بوصفه عميلًا مستقلًا موثقًا أعلاه، وكذلك بوصفه وسيلة نقل اختيارية لوقت التشغيل (انظر أدناه).


Interactions بوصفه وسيلة نقل لوقت التشغيل (العوامل + المشغّل)

يوثّق كل ما سبق عميل الاتصال المباشر (adk_gemini::interactions) — وهو قدرة مستقلة تستدعيها يدويًا. يوثّق هذا القسم وسيلة نقل وقت التشغيل المبنية عليه: مفتاح تبديل في GeminiModel يتيح لـ LlmAgent وRunner عاديين، وحلقة الأدوات، والجلسات قيادة Interactions API مع إجراء تغييرات صفرية على كود العامل.

يحاكي هذا ADK-Python، حيث يحافظ Gemini(model=..., use_interactions_api=True) على Agent والمشغّل والأدوات نفسها. العامل مستقل عن وسيلة النقل: يجب ألا يتطلب تغيير طريقة تواصل النموذج مع الواجهة الخلفية نوعًا جديدًا من العوامل.

لا يزال generateContent هو الخيار الافتراضي

يظل generateContent وسيلة النقل الافتراضية والموصى بها لأعباء العمل الإنتاجية المستقرة. إن Interactions API تجريبية وقد يتغير مخططها. فعّل وسيلة النقل عمدًا، لكل نموذج. عندما لا تستدعي use_interactions_api(true)، يعمل GeminiModel تمامًا كما كان من قبل — ولا يحدث أي تغيير سلوكي في مسار generateContent.

تفعيل وسيلة النقل

تخضع وسيلة النقل لميزة gemini-interactions (المُمرَّرة من adk-rustadk-modeladk-gemini/interactions):

adk-model = { version = "2.1.0", features = ["gemini-interactions"] }
adk-rust  = { version = "2.1.0", features = ["gemini-interactions"] }

فعّل المفتاح في النموذج، ثم غلّفه داخل LlmAgent وRunner عاديين — ولا يتغير أي شيء آخر في إعداد العامل:

use adk_agent::LlmAgentBuilder;
use adk_core::{Content, Part, SessionId, UserId};
use adk_model::GeminiModel;
use adk_runner::Runner;
use adk_session::{CreateRequest, InMemorySessionService, SessionService};
use futures::StreamExt;
use std::collections::HashMap;
use std::sync::Arc;

// 1. Build a Gemini model and toggle the Interactions transport.
//    `use_interactions_api` validates the model id against the allowlist and
//    returns `Result<Self>`, so it is fallible (`?`).
let model = GeminiModel::new(std::env::var("GEMINI_API_KEY")?, "gemini-3.7-flash")?
    .use_interactions_api(true)?;

// 2. Wrap it in a normal LlmAgent — unchanged agent API.
let agent = Arc::new(
    LlmAgentBuilder::new("assistant")
        .instruction("You are concise.")
        .model(Arc::new(model))
        .build()?,
);

// 3. Drive it through the standard Runner.
let sessions: Arc<dyn SessionService> = Arc::new(InMemorySessionService::new());
sessions
    .create(CreateRequest {
        app_name: "assistant".into(),
        user_id: "user".into(),
        session_id: Some("session-1".into()),
        state: HashMap::new(),
    })
    .await?;
let runner = Runner::builder()
    .app_name("assistant")
    .agent(agent)
    .session_service(sessions)
    .build()?;

let mut stream = runner
    .run(
        UserId::new("user")?,
        SessionId::new("session-1")?,
        Content::new("user").with_text("What is the capital of France?"),
    )
    .await?;

while let Some(event) = stream.next().await {
    let event = event?;
    // The server-assigned interaction id is a first-class field on every event.
    if let Some(id) = event.interaction_id() {
        println!("interaction_id = {id}");
    }
    if let Some(content) = &event.llm_response.content {
        for part in &content.parts {
            if let Part::Text { text } = part {
                print!("{text}");
            }
        }
    }
}

الإعدادات الافتراضية المطابقة للمواصفات

يعتمد النقل افتراضيًا على الوضع المقصود لـ Interactions API، والمُكوَّن عبر InteractionOptions (المُعاد تصديره من adk_model::gemini):

الخيارالافتراضيالمعنى
storetrueتُخزَّن التفاعلات على جانب الخادم، لذا يعمل الاستمرار المعتمد على الحالة وقابلية الرصد مباشرةً.
statefultrueتستمر المحادثات متعددة الأدوار عبر previous_interaction_id؛ ولا تُرسَل عند التسلسل سوى محتويات الدور الحالي.
backgroundBackgroundMode::AgentTargetsOnlybackground=true لأهداف الوكلاء (البحث المتعمق، التشغيل طويل الأمد)؛ وfalse لأهداف النماذج للحفاظ على انخفاض زمن استجابة جولات المحادثة.
poll_interval1sمدى تكرار استطلاع تفاعل في الخلفية حتى وصوله إلى حالة نهائية.

تجاوز أيٍّ من هذه باستخدام interaction_options:

use adk_model::gemini::{BackgroundMode, InteractionOptions};
use std::time::Duration;

let model = GeminiModel::new(api_key, "gemini-3.7-flash")?
    .use_interactions_api(true)?
    .interaction_options(InteractionOptions {
        store: true,
        stateful: true,
        background: BackgroundMode::AgentTargetsOnly,
        poll_interval: Duration::from_millis(500),
    });

يحتوي BackgroundMode على ثلاثة متغيرات: AgentTargetsOnly (الافتراضي)، وAlways، و Never.

عندما تكون قيمة store هي false، تعمل قواعد عدم توافق API على تعطيل الاستمرار ذي الحالة والتنفيذ في الخلفية؛ ثم تنقل طبقة النقل إدخال السجل، تمامًا مثل generateContent.

الأهداف المدعومة (قائمة السماح)

يدعم API الخاص بـ Interactions مجموعة ثابتة من الأهداف. يتحقق use_interactions_api(true) من معرّف النموذج وقت التهيئة، ويعيد AdkError بالفئة InvalidInput (مع تسمية الأهداف المدعومة) عندما لا يكون المعرّف موجودًا في قائمة السماح، بدلًا من تأجيل الأمر إلى رفض غير واضح من الخادم.

يعيّن هدف النموذج حقل الطلب model؛ بينما يعيّن هدف الوكيل الحقل agent.

أهداف النماذج:

  • gemini-3.7-flash
  • gemini-3.6-flash
  • gemini-3.5-flash
  • gemini-3.5-flash-lite
  • gemini-3.1-flash-lite
  • gemini-3.1-pro-preview
  • gemini-3-flash-preview
  • gemini-2.5-pro
  • gemini-2.5-flash
  • gemini-2.5-flash-lite
  • lyria-3-clip-preview
  • lyria-3-pro-preview

هذه قائمة السماح الخاصة بتوافق طبقة النقل، وليست قائمة توصيات. ينبغي أن تبدأ التطبيقات الجديدة باستخدام gemini-3.7-flash؛ وتظل المعرّفات الأقدم والتجريبية مدرجة لأن نقطة نهاية Interactions لا تزال تقبلها.

أهداف الوكلاء:

  • deep-research-pro-preview-12-2025
  • deep-research-preview-04-2026
  • deep-research-max-preview-04-2026
// Unsupported targets fail fast at configuration time:
let result = GeminiModel::new(api_key, "gpt-4")?.use_interactions_api(true);
assert!(result.is_err()); // AdkError { category: InvalidInput, .. }

يمثل تعداد InteractionTarget (المُعاد تصديره أيضًا من adk_model::gemini) وجهةً تم التحقق منها إذا كنت بحاجة إلى فحص التصنيف مباشرةً.

مزج الأدوات المضمّنة والمخصصة (bypass_multi_tools_limit)

يمنع API الخاص بـ Interactions مزج الأدوات المضمّنة (من جانب الخادم) مع أدوات الدوال المخصصة في طلب واحد. لاستخدام بحث Google إلى جانب أداة الدالة الخاصة بك، مثلًا، حوّل الأداة المضمّنة إلى أداة لاستدعاء الدوال حتى تكون مجموعة الأدوات بأكملها موحّدة. وهذا يحاكي ADK-Python's bypass_multi_tools_limit=True.

توجد عملية التحويل في سمة BypassMultiToolsLimit، التي تنفذها أغلفة الأدوات المضمّنة (GoogleSearchTool، UrlContextTool، GeminiFileSearchTool). تأخذ with_bypass_multi_tools_limit(agent) وكيلًا داخليًا للبحث المؤسَّس أحادي الدور — وهو LlmAgent عادي مُهيَّأ باستخدام الأداة المضمّنة ونموذج Gemini — وتُرجع Arc<dyn Tool> تُبلغ عن is_builtin() == false وتُشغّل السلوك المضمّن داخليًا، مع إرجاع استجابة دالة عادية.

use adk_agent::LlmAgentBuilder;
use adk_tool::{BypassMultiToolsLimit, FunctionTool, GoogleSearchTool};
use adk_model::GeminiModel;
use std::sync::Arc;

// The grounded-search agent the bypass tool delegates to: a normal LlmAgent
// with the built-in GoogleSearchTool + a Gemini model.
let search_agent = Arc::new(
    LlmAgentBuilder::new("grounded-search")
        .instruction("Answer the query using Google Search. Be factual and concise.")
        .model(Arc::new(GeminiModel::new(&api_key, "gemini-3.7-flash")?))
        .tool(Arc::new(GoogleSearchTool::new()))
        .build()?,
);

// Convert the built-in search tool into a function tool (is_builtin() == false).
let search_tool = GoogleSearchTool::new().with_bypass_multi_tools_limit(search_agent);

// A custom function tool to mix alongside it.
let weather_tool: Arc<dyn adk_core::Tool> = Arc::new(/* your FunctionTool */);

// Now the tool set is uniform (all function tools) and the Interactions
// transport accepts it.
let model = GeminiModel::new(&api_key, "gemini-3.7-flash")?.use_interactions_api(true)?;
let agent = Arc::new(
    LlmAgentBuilder::new("assistant")
        .model(Arc::new(model))
        .tool(search_tool)
        .tool(weather_tool)
        .build()?,
);

إذا تركت أداة مضمّنة غير متجاوزة أثناء مزجها مع أدوات الدوال باستخدام نقل Interactions، تُرجع عملية بناء الطلب AdkError بالفئة InvalidInput، وتوجّهك إلى with_bypass_multi_tools_limit. يمر معرّف استدعاء الدالة ذهابًا وإيابًا دون تغيير عبر حلقة الأداة، تمامًا كما يحدث مع generateContent.

الاستمرارية ذات الحالة والبديل الاحتياطي للاحتفاظ

interaction_id هو حقل من الدرجة الأولى، وليس قناة جانبية. كل LlmResponse يحمل interaction_id: Option<String> (يملؤه نقل Interactions، أو None بخلاف ذلك)، ويعرضه Event عبر موصّل الوصول event.interaction_id() — محاكيًا ADK-Python's event.interaction_id.

الاستمرارية محايدة تجاه المزوّد. يحمل LlmRequest حقلًا إضافيًا هو previous_response_id: Option<String>، ويملؤه LlmAgent من interaction_id للحدث الأحدث. يعيّن نقل Interactions هذا الحقل إلى previous_interaction_id في الطلب ويرسل محتويات الدور الحالي فقط (بدلًا من السجل الكامل). لا توجد تعليمات ربط خاصة بـ Gemini في adk-agent؛ ويكون الحقل غير مستخدم (أي لا تأثير له) مع generateContent والمزوّدين الآخرين.

Turn 1:  request (transcript)        → interaction v1_abc   → event.interaction_id() == "v1_abc"
Turn 2:  request previous_response_id = "v1_abc"
         → previous_interaction_id = "v1_abc", sends only the new turn
         → interaction v1_def        → event.interaction_id() == "v1_def"

البديل عند انتهاء نافذة الاحتفاظ. تنتهي صلاحية التفاعلات المخزّنة. إذا كانت قيمة previous_interaction_id المقدَّمة قديمة أو منتهية الصلاحية، يعيد الخادم NotFound. يتعامل النقل مع ذلك بشفافية: إذ يعود إلى إرسال النص الكامل ويبدأ تفاعلًا جديدًا — ولا يظهر أي خطأ للوكيل أو المُشغِّل. تستمر المحادثات متعددة الأدوار في العمل عبر حدّ الاحتفاظ دون معالجة خاصة في التعليمات البرمجية الخاصة بك.

الأنواع المُعاد تصديرها

عند تفعيل الميزة gemini-interactions، تتوفر العناصر التالية من adk_model::gemini:

  • GeminiTransportGenerateContent (الافتراضي) أو Interactions.
  • InteractionOptionsstore وstateful وbackground وpoll_interval.
  • BackgroundModeAgentTargetsOnly (الافتراضي) وAlways وNever.
  • InteractionTarget — وجهة نموذج/وكيل تم التحقق منها.

توجد واجهة التجاوز في adk-tool (يمكن الوصول إليها عبر adk_tool أو الحزمة الشاملة):

  • سمة BypassMultiToolsLimit مع with_bypass_multi_tools_limit(agent).
  • تُنفَّذ بواسطة GoogleSearchTool وUrlContextTool وGeminiFileSearchTool.

إن حقلي النواة الإضافيين LlmResponse.interaction_id وLlmRequest.previous_response_id موجودان دائمًا (ولا يخضعان لشرط الميزة)، ولذلك فإن موصّل الوصول event.interaction_id() يُترجم بصرف النظر عن موفّري الخدمة المفعّلين.