Funktionswerkzeuge

Erweitern Sie die Fähigkeiten von Agents mit benutzerdefinierten Rust-Funktionen.


Was sind Funktionswerkzeuge?

Mit Funktionswerkzeugen können Sie Agents Fähigkeiten über die Unterhaltung hinaus verleihen – etwa APIs aufrufen, Berechnungen durchführen, auf Datenbanken zugreifen oder beliebige benutzerdefinierte Logik ausführen. LLM entscheidet anhand der Anfrage des Benutzers, wann ein Werkzeug verwendet werden soll.

Wichtige Highlights:

  • 🚀 #[tool]-Makro – Werkzeugregistrierung ohne Boilerplate (empfohlen)
  • 🔧 FunctionTool::new() – beliebige asynchrone Funktionen manuell einbinden
  • 📝 JSON-Parameter – flexible Ein- und Ausgaben
  • 🎯 Typsichere Schemata – automatisches JSON-Schema aus Typen über schemars
  • 🔗 Kontextzugriff – Sitzungsstatus, Artefakte, Speicher

Pipeline der Werkzeugausführung

Rendering architecture…

Der schnellste Weg, Werkzeuge zu erstellen. Das Makro verwendet Ihren Dokumentationskommentar als Beschreibung und leitet das JSON-Schema aus dem Typ Ihrer Argumente ab:

use adk_tool::tool;
use adk_core::AdkError;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::{json, Value};

#[derive(Deserialize, JsonSchema)]
struct WeatherArgs {
    /// The city to look up
    city: String,
    /// Temperature unit (celsius or fahrenheit)
    unit: Option<String>,
}

/// Get the current weather for a city.
#[tool]
async fn get_weather(args: WeatherArgs) -> Result<Value, AdkError> {
    Ok(json!({ "temp": 22, "city": args.city }))
}

// Generated: pub struct GetWeather; — implements adk_core::Tool
// Use it: agent_builder.tool(Arc::new(GetWeather))

Wenn Ihr Werkzeug Sitzungskontext benötigt, fügen Sie Arc<dyn ToolContext> als ersten Parameter hinzu:

use adk_core::ToolContext;
use std::sync::Arc;

/// Search the user's saved documents.
#[tool]
async fn search_docs(
    ctx: Arc<dyn ToolContext>,
    args: SearchArgs,
) -> Result<Value, AdkError> {
    let user_id = ctx.user_id();
    // ... use context for scoped access
}

Attribute für Werkzeugmetadaten

Kennzeichnen Sie Werkzeuge direkt im Makro als schreibgeschützt, nebenläufigkeitssicher oder lang laufend:

/// Look up cached data — no side effects, safe for parallel dispatch.
#[tool(read_only, concurrency_safe)]
async fn cache_lookup(args: LookupArgs) -> Result<Value, AdkError> {
    Ok(json!({"result": "cached"}))
}

/// Start a long-running background report.
#[tool(long_running)]
async fn generate_report(args: ReportArgs) -> Result<Value, AdkError> {
    Ok(json!({"task_id": "abc123", "status": "processing"}))
}

Verfügbare Attribute (alle optional, frei kombinierbar):

AttributWirkung
read_onlyis_read_only() → true — eines von zwei Signalen, die für den nebenläufigen Auto-Versand erforderlich sind
concurrency_safeis_concurrency_safe() → true — eines von zwei Signalen, die für den nebenläufigen Auto-Versand erforderlich sind
long_runningis_long_running() → true — verhindert, dass LLM ein ausstehendes Tool erneut aufruft

Plain #[tool] ohne Attribute behält die Standardwerte (alle false) bei, sodass vorhandener Code nicht beeinträchtigt wird.


Alternative: FunctionTool::new()

Für dynamische Tools oder wenn Sie eine explizite Registrierung bevorzugen:

Erstellen Sie ein Tool mit FunctionTool::new() und fügen Sie immer ein Schema hinzu, damit LLM weiß, welche Parameter übergeben werden sollen:

use adk_rust::prelude::*;
use adk_rust::Launcher;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::Arc;

#[derive(JsonSchema, Serialize, Deserialize)]
struct WeatherParams {
    /// The city or location to get weather for
    location: String,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = GeminiModel::new(&api_key, "gemini-3.7-flash")?;

    // Weather tool with proper schema
    let weather_tool = FunctionTool::new(
        "get_weather",
        "Get current weather for a location",
        |_ctx, args| async move {
            let location = args.get("location")
                .and_then(|v| v.as_str())
                .unwrap_or("unknown");
            Ok(json!({
                "location": location,
                "temperature": "22°C",
                "conditions": "sunny"
            }))
        },
    )
    .with_parameters_schema::<WeatherParams>(); // Required for LLM to call correctly!

    let agent = LlmAgentBuilder::new("weather_agent")
        .instruction("You help users check the weather. Always use the get_weather tool.")
        .model(Arc::new(model))
        .tool(Arc::new(weather_tool))
        .build()?;

    Launcher::new(Arc::new(agent)).run().await?;
    Ok(())
}

⚠️ Wichtig: Verwenden Sie immer .with_parameters_schema<T>() – andernfalls weiß LLM nicht, welche Parameter übergeben werden sollen, und ruft das Tool möglicherweise nicht auf.

So funktioniert es:

  1. Der Benutzer fragt: „Wie ist das Wetter in Tokio?“
  2. LLM entscheidet, get_weather mit {"location": "Tokyo"} aufzurufen
  3. Das Tool gibt {"location": "Tokyo", "temperature": "22°C", "conditions": "sunny"} zurück
  4. LLM formatiert die Antwort: „Das Wetter in Tokio ist sonnig bei 22 °C.“

Schritt 2: Parameterverarbeitung

Extrahieren Sie Parameter aus dem JSON args:

let order_tool = FunctionTool::new(
    "process_order",
    "Process an order. Parameters: product_id (required), quantity (required), priority (optional)",
    |_ctx, args| async move {
        // Required parameters - return error if missing
        let product_id = args.get("product_id")
            .and_then(|v| v.as_str())
            .ok_or_else(|| adk_core::AdkError::tool("product_id is required"))?;
        
        let quantity = args.get("quantity")
            .and_then(|v| v.as_i64())
            .ok_or_else(|| adk_core::AdkError::tool("quantity is required"))?;
        
        // Optional parameter with default
        let priority = args.get("priority")
            .and_then(|v| v.as_str())
            .unwrap_or("normal");
        
        Ok(json!({
            "order_id": "ORD-12345",
            "product_id": product_id,
            "quantity": quantity,
            "priority": priority,
            "status": "confirmed"
        }))
    },
);

Schritt 3: Typisierte Parameter mit Schema

Verwenden Sie für komplexe Tools typisierte Strukturen mit JSON Schema:

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(JsonSchema, Serialize, Deserialize)]
struct CalculatorParams {
    /// The arithmetic operation to perform
    operation: Operation,
    /// First operand
    a: f64,
    /// Second operand
    b: f64,
}

#[derive(JsonSchema, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum Operation {
    Add,
    Subtract,
    Multiply,
    Divide,
}

let calculator = FunctionTool::new(
    "calculator",
    "Perform arithmetic operations",
    |_ctx, args| async move {
        let params: CalculatorParams = serde_json::from_value(args)?;
        let result = match params.operation {
            Operation::Add => params.a + params.b,
            Operation::Subtract => params.a - params.b,
            Operation::Multiply => params.a * params.b,
            Operation::Divide if params.b != 0.0 => params.a / params.b,
            Operation::Divide => return Err(adk_core::AdkError::tool("Cannot divide by zero")),
        };
        Ok(json!({ "result": result }))
    },
)
.with_parameters_schema::<CalculatorParams>();

Das Schema wird mithilfe von schemars automatisch aus den Rust-Typen generiert.


Schritt 4: Agent mit mehreren Tools

Fügen Sie einem Agenten mehrere Tools hinzu:

let agent = LlmAgentBuilder::new("assistant")
    .instruction("Help with calculations, conversions, and weather.")
    .model(Arc::new(model))
    .tool(Arc::new(calc_tool))
    .tool(Arc::new(convert_tool))
    .tool(Arc::new(weather_tool))
    .build()?;

LLM wählt automatisch das passende Tool anhand der Anfrage des Benutzers aus.


Fehlerbehandlung

Geben Sie Fehler mit der Komponente Tool für tool-spezifische Fehler zurück:

use adk_core::{AdkError, ErrorComponent, ErrorCategory};

let divide_tool = FunctionTool::new(
    "divide",
    "Divide two numbers",
    |_ctx, args| async move {
        let a = args.get("a").and_then(|v| v.as_f64())
            .ok_or_else(|| AdkError::new(
                ErrorComponent::Tool,
                ErrorCategory::InvalidInput,
                "tool.divide.missing_param",
                "Parameter 'a' is required",
            ))?;
        let b = args.get("b").and_then(|v| v.as_f64())
            .ok_or_else(|| AdkError::new(
                ErrorComponent::Tool,
                ErrorCategory::InvalidInput,
                "tool.divide.missing_param",
                "Parameter 'b' is required",
            ))?;
        
        if b == 0.0 {
            return Err(AdkError::new(
                ErrorComponent::Tool,
                ErrorCategory::InvalidInput,
                "tool.divide.division_by_zero",
                "Cannot divide by zero",
            ));
        }
        
        Ok(json!({ "result": a / b }))
    },
);

Für eine schnelle Migration funktioniert auch die abwärtskompatible Kurzform:

Err(AdkError::tool("Parameter 'a' is required"))

Fehlermeldungen werden an LLM übergeben, das es erneut versuchen oder nach anderen Eingaben fragen kann.


Tool-Kontext

Greifen Sie über ToolContext auf Sitzungsinformationen zu:

#[derive(JsonSchema, Serialize, Deserialize)]
struct GreetParams {
    #[serde(default)]
    message: Option<String>,
}

let greet_tool = FunctionTool::new(
    "greet",
    "Greet the user with session info",
    |ctx, _args| async move {
        let user_id = ctx.user_id();
        let session_id = ctx.session_id();
        let agent_name = ctx.agent_name();
        Ok(json!({
            "greeting": format!("Hello, user {}!", user_id),
            "session": session_id,
            "served_by": agent_name
        }))
    },
)
.with_parameters_schema::<GreetParams>();

Verfügbarer Kontext:

  • ctx.user_id() – Aktuelle Benutzer-ID
  • ctx.session_id() – Aktuelle Sitzungs-ID
  • ctx.agent_name() – Name des Agenten
  • ctx.artifacts() – Zugriff auf den Artefaktspeicher
  • ctx.search_memory(query) – Dienst zur Speichersuche

Lang laufende Tools

Für Vorgänge, die erheblich Zeit in Anspruch nehmen (Datenverarbeitung, externe APIs), verwenden Sie das nicht blockierende Muster:

  1. Start-Tool kehrt sofort mit einem task_id zurück
  2. Hintergrundarbeit läuft asynchron
  3. Status-Tool ermöglicht es Benutzern, den Fortschritt zu überprüfen
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

#[derive(JsonSchema, Serialize, Deserialize)]
struct ReportParams {
    topic: String,
}

#[derive(JsonSchema, Serialize, Deserialize)]
struct StatusParams {
    task_id: String,
}

// Shared task store
let tasks: Arc<RwLock<HashMap<String, TaskState>>> = Arc::new(RwLock::new(HashMap::new()));
let tasks1 = tasks.clone();
let tasks2 = tasks.clone();

// Tool 1: Start (returns immediately)
let start_tool = FunctionTool::new(
    "generate_report",
    "Start generating a report. Returns task_id immediately.",
    move |_ctx, args| {
        let tasks = tasks1.clone();
        async move {
            let topic = args.get("topic").and_then(|v| v.as_str()).unwrap_or("general").to_string();
            let task_id = format!("task_{}", rand::random::<u32>());
            
            // Store initial state
            tasks.write().await.insert(task_id.clone(), TaskState {
                status: "processing".to_string(),
                progress: 0,
                result: None,
            });

            // Spawn background work (non-blocking!)
            let tasks_bg = tasks.clone();
            let tid = task_id.clone();
            tokio::spawn(async move {
                // Simulate work...
                tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
                if let Some(t) = tasks_bg.write().await.get_mut(&tid) {
                    t.status = "completed".to_string();
                    t.result = Some("Report complete".to_string());
                }
            });

            // Return immediately with task_id
            Ok(json!({"task_id": task_id, "status": "processing"}))
        }
    },
)
.with_parameters_schema::<ReportParams>()
.with_long_running(true);  // Mark as long-running

// Tool 2: Check status
let status_tool = FunctionTool::new(
    "check_report_status",
    "Check report generation status",
    move |_ctx, args| {
        let tasks = tasks2.clone();
        async move {
            let task_id = args.get("task_id").and_then(|v| v.as_str()).unwrap_or("");
            if let Some(t) = tasks.read().await.get(task_id) {
                Ok(json!({"status": t.status, "result": t.result}))
            } else {
                Ok(json!({"error": "Task not found"}))
            }
        }
    },
)
.with_parameters_schema::<StatusParams>();

Wichtige Punkte:

  • .with_long_running(true) teilt dem Agenten mit, dass dieses Tool einen ausstehenden Status zurückgibt
  • Das Tool startet die Arbeit mit tokio::spawn() und kehrt sofort zurück
  • Stellen Sie ein Tool zur Statusprüfung bereit, damit Benutzer den Fortschritt regelmäßig abfragen können

Dadurch wird ein Hinweis hinzugefügt, der verhindert, dass der LLM das Tool wiederholt aufruft.


Streaming-Fortschritt eines Tools

Lang laufende Tools können Zwischenausgaben an die Benutzeroberfläche senden, während sie noch ausgeführt werden, sodass der Benutzer die Standardausgabe eines Shell-Befehls, die Protokolle eines Builds oder die Bytes eines Downloads live sieht, anstatt auf das Endergebnis zu warten. Rufen Sie ToolContext::emit_progress auf, sobald die Ausgabe eintrifft:

use adk_core::{Result, Tool, ToolContext};
use std::sync::Arc;

#[async_trait::async_trait]
impl Tool for BuildTool {
    // ... name(), description(), parameters_schema() ...

    async fn execute(&self, ctx: Arc<dyn ToolContext>, args: serde_json::Value) -> Result<serde_json::Value> {
        // Emit chunks as they arrive — each becomes a partial Event on the
        // agent's EventStream, the SAME stream the model's reply travels on.
        ctx.emit_progress("stdout", "Compiling project...\n").await;
        ctx.emit_progress("stdout", "Build finished in 4.2s\n").await;
        ctx.emit_progress("stderr", "warning: unused variable `x`\n").await;

        // The final return value is still the complete result the model consumes.
        Ok(serde_json::json!({ "status": "ok", "warnings": 1 }))
    }
}

Die Signatur:

async fn emit_progress(&self, stream: &str, chunk: &str)
  • stream — eine Bezeichnung für den Datenblock: "stdout", "stderr" oder ein beliebiger benutzerdefinierter Kanal.
  • chunk — der auszugebende Text (für eine terminalartige Ausgabe pro Zeile ausgeben).

So erreicht es die Benutzeroberfläche. Das Framework leitet jeden Datenblock als partielles Event im EventStream des Agenten weiter. Ein Verbraucher erkennt ihn mit event.tool_progress_stream() und rendert ihn live. Es gibt keinen zweiten Kanal und kein Auslesen von Protokollen — Fortschritt, Modelltext und das endgültige Tool-Ergebnis treffen alle in einem einzigen geordneten Stream ein.

Abwärtskompatibel. Das standardmäßige emit_progress führt keine Aktion aus, sodass vorhandene Tools und Runner, die kein Streaming verwenden, nicht beeinträchtigt werden. Nur Tools, die dies ausdrücklich aktivieren, geben Fortschritt aus, und nur Verbraucher, die tool_progress_stream() prüfen, bekommen ihn zu sehen.

Der Fortschritt ist begrenzt und verlustbehaftet. Ein Tool kann Ausgaben schneller erzeugen, als ein Client sie verarbeitet – etwa ein Compilerprotokoll, ein Shell-Befehl oder eine außer Kontrolle geratene Schleife –, daher begrenzt das Framework die Datenmenge, die es vorhält und weiterleitet, anstatt sie unbegrenzt anwachsen zu lassen:

LimitWertBei Überschreitung
Warteschlangentiefe pro Tool-Batch256 EreignisseDas Tool wartet bis zu 100 ms auf freien Speicherplatz, danach wird der Chunk verworfen
Bytes pro Chunk8 KiBDer Chunk wird an einer Zeichengrenze abgeschnitten
Bytes pro Tool-Aufruf1 MiBVerbleibender Fortschritt wird nicht weitergeleitet

Wenn die Ausgabe aus einem dieser Gründe verworfen wird, wird für diesen Aufruf genau ein Fortschrittsereignis mit dem Text [adk: tool progress truncated] ausgegeben, sodass eine Lücke immer sichtbar statt still bleibt. Ein langsamer Consumer verlangsamt das Tool daher kurzzeitig, kann es aber niemals unbegrenzt blockieren oder den Speicher erschöpfen.

Diese Limits gelten nur für den Fortschritt. Das endgültige Ergebnis eines Tools ist davon nicht betroffen. Kürze daher große Ergebnisse innerhalb des Tools, falls dies für dich relevant ist.

Siehe das Beispiel streaming_bash für eine vollständige Web-UI, die live bash-Ausgaben und einmalige Tool-Ergebnisse (read_file, grep, glob) aus einem einzigen Ereignis-Feed rendert. Das Streaming-Tool bash selbst befindet sich in adk-devtools.


Beispiele ausführen

cargo adk new tool_agent --template tools
cd tool_agent
cargo run

Best Practices

  1. Klare Beschreibungen – Hilf dem LLM zu verstehen, wann das Tool verwendet werden soll
  2. Eingaben validieren – Gib hilfreiche Fehlermeldungen für fehlende Parameter zurück
  3. Strukturierte JSON zurückgeben – Verwende klare Feldnamen
  4. Tools fokussiert halten – Jedes Tool sollte eine Sache gut erledigen
  5. Schemas verwenden – Definiere für komplexe Tools Parameterschemas
  6. Sichere schreibgeschützte Tools markieren – Setze sowohl .with_read_only(true) als auch .with_concurrency_safe(true), damit der Auto-Dispatcher sie in seine nebenläufige Teilmenge aufnehmen kann

Tool-Metadaten: Schreibgeschützt und nebenläufig

Markiere Tools als schreibgeschützt oder nebenläufigkeitssicher, um eine intelligentere Verteilung zu ermöglichen:

// A lookup tool that performs no side effects
let lookup = FunctionTool::new("lookup", "Look up data", |_ctx, args| async move {
    Ok(json!({"result": "cached data"}))
})
.with_read_only(true)
.with_concurrency_safe(true); // Auto mode requires both signals

// A mutation tool (defaults: read_only=false, concurrency_safe=false)
let update = FunctionTool::new("update", "Update record", |_ctx, args| async move {
    Ok(json!({"updated": true}))
});

Wenn ToolExecutionStrategy::Auto aktiv ist, führt die Dispatch-Schleife zunächst Aufrufe nebenläufig aus, wenn die ausgewählten Tools true sowohl aus is_read_only() als auch aus is_concurrency_safe() zurückgeben. Anschließend führt sie alle verbleibenden Aufrufe sequenziell aus. ToolExecutionStrategy::Parallel ist eine explizite Überschreibung, die diese Signale umgeht; daher ist der Aufrufer für die Sicherheit der Nebenläufigkeit verantwortlich.


SimpleToolContext: Tools außerhalb der Agentenschleife verwenden

Wenn Sie ein Tool außerhalb der Agentenschleife aufrufen müssen (zum Testen, im MCP-Servermodus oder zur Delegation an einen Sub-Agenten), verwenden Sie SimpleToolContext, anstatt die vollständige ToolContext-Trait-Hierarchie zu implementieren:

use adk_tool::SimpleToolContext;
use adk_core::ToolContext;
use std::sync::Arc;

// Construct with just a caller name — all other fields get sensible defaults
let ctx = SimpleToolContext::new("my-test-harness");

// Optionally override the function call ID
let ctx = SimpleToolContext::new("my-mcp-server")
    .with_function_call_id("custom-call-id");

// Bind a session ID so session-aware tools (and MCP servers that key state by
// session) see a stable identifier instead of the empty default.
let ctx = SimpleToolContext::new("my-mcp-server")
    .with_session_id("session-42");

// Use it to execute any tool
let tool_ctx: Arc<dyn ToolContext> = Arc::new(ctx);
let result = my_tool.execute(tool_ctx, json!({"key": "value"})).await?;

Standardwerte: user_id() → "anonymous", session_id() / branch() → "", artifacts() → None, search_memory() → leeres Vec. Sowohl invocation_id als auch function_call_id sind automatisch generierte UUIDs. Setzen Sie mit with_session_id(...) eine echte Sitzung, wenn ein Tool den Sitzungsstatus weiterleitet oder pro Sitzung speichert.


StatefulTool: Gemeinsamer Status über Aufrufe hinweg

Verwenden Sie für Tools, die ihren Status zwischen Aufrufen beibehalten müssen (Zähler, Caches, Verbindungspools), StatefulTool<S>:

use adk_tool::StatefulTool;
use adk_core::ToolContext;
use std::sync::Arc;
use tokio::sync::RwLock;

struct AppCache {
    entries: RwLock<HashMap<String, String>>,
}

let cache = Arc::new(AppCache {
    entries: RwLock::new(HashMap::new()),
});

let cache_tool = StatefulTool::new(
    "cache_lookup",
    "Look up a value in the application cache",
    cache.clone(),
    |state, _ctx, args| async move {
        let key = args["key"].as_str().unwrap_or("");
        let entries = state.entries.read().await;
        let value = entries.get(key).cloned().unwrap_or_default();
        Ok(json!({"key": key, "value": value}))
    },
)
.with_read_only(true)
.with_concurrency_safe(true);

StatefulTool klont bei jedem Aufruf den Arc<S> (eine kostengünstige Erhöhung der Referenzanzahl), sodass alle Ausführungen denselben zugrunde liegenden Status gemeinsam verwenden. Es unterstützt dieselben Builder-Methoden wie FunctionTool: with_long_running, with_parameters_schema, with_response_schema, with_scopes, with_read_only und with_concurrency_safe.



Multimodale Function Responses

Gemini-3-Modelle unterstützen den Empfang von Bildern, Audio, PDFs und Dateiverweisen in Function Responses – nicht nur von JSON. Tools können multimodale Daten zurückgeben, indem sie inline_data- und/oder file_data-Arrays in ihren Rückgabewert JSON aufnehmen:

/// Tool that returns a chart image alongside JSON metadata.
async fn generate_chart(
    _ctx: Arc<dyn ToolContext>,
    args: serde_json::Value,
) -> Result<serde_json::Value> {
    let png_bytes: Vec<u8> = render_chart(&args);

    // Include inline_data in the return value — the framework extracts it automatically
    Ok(json!({
        "response": {
            "title": "Q4 Sales",
            "chart_type": "bar"
        },
        "inline_data": [{
            "mime_type": "image/png",
            "data": png_bytes
        }]
    }))
}

Das Framework:

  1. Erkennt inline_data/file_data über FunctionResponseData::from_tool_result()
  2. Kodiert eingebettete Binärdaten in Base64
  3. Verschachtelt die Bestandteile im functionResponse-Drahtobjekt (entsprechend dem API-Format von Gemini 3)

Dateiverweise

Verwenden Sie für große, extern gespeicherte Dateien file_data mit einem URI, anstatt Bytes einzubetten:

Ok(json!({
    "response": { "document_id": "report-2024", "pages": 12 },
    "file_data": [{
        "mime_type": "application/pdf",
        "file_uri": "gs://my-bucket/reports/report-2024.pdf"
    }]
}))

Direkte Konstruktion

Für Code auf Framework-Ebene (benutzerdefinierte Agenten, Konvertierungsebenen) erstellen Sie FunctionResponseData direkt:

use adk_core::{FunctionResponseData, InlineDataPart, FileDataPart};

// JSON + inline image
let frd = FunctionResponseData::with_inline_data(
    "chart_tool",
    json!({"title": "Q4 Chart"}),
    vec![InlineDataPart { mime_type: "image/png".into(), data: png_bytes }],
);

// JSON + file reference
let frd = FunctionResponseData::with_file_data(
    "doc_tool",
    json!({"status": "ok"}),
    vec![FileDataPart { mime_type: "application/pdf".into(), file_uri: "gs://bucket/file.pdf".into() }],
);

// JSON + both
let frd = FunctionResponseData::with_multimodal("tool", json, inline_parts, file_parts);

Hinweis: Multimodale Funktionsantworten erfordern Modelle der Gemini-3-Serie wie gemini-3.7-flash. Frühere Modelle geben einen 400-Fehler zurück.

Unter examples/multimodal_function_response/ finden Sie ein vollständiges, funktionierendes Beispiel.


Zurück: ← mistral.rs | Weiter: Integrierte Tools →