Willkommen zur Plugin-Entwicklungsanleitung für den Gemini Desktop Agent! Plugins sind der Schlüssel, um die Fähigkeiten des Agenten zu erweitern, indem sie ihm Zugriff auf externe Tools, APIs und benutzerdefinierte Logik ermöglichen. Diese Anleitung führt dich durch den Prozess der Plugin-Erstellung, mit besonderem Fokus auf die Integration mit der nativen Function Calling Fähigkeit der Gemini API.
Das Plugin-System des Gemini Desktop Agenten ermöglicht es dem KI-Modell (Gemini), intelligent zu entscheiden, wann eine externe Funktion (dein Plugin) aufgerufen werden soll, um eine Benutzeranfrage besser zu beantworten oder eine Aktion auszuführen.
Der Ablauf ist typischerweise wie folgt:
- Der Nutzer stellt eine Anfrage.
- Die
MainAppsendet diese Anfrage zusammen mit den Deklarationen aller verfügbaren Plugins an die Gemini API. - Gemini analysiert die Anfrage und die Plugin-Deklarationen.
- Wenn Gemini entscheidet, ein Plugin zu nutzen, antwortet es nicht mit direktem Text, sondern mit einem strukturierten
FunctionCall-Objekt. Dieses Objekt spezifiziert, welches deiner Plugins aufgerufen werden soll und mit welchen Argumenten. - Die
MainAppempfängt diesenFunctionCall, identifiziert das entsprechende Plugin und führt dessen Python-Code mit den von Gemini gelieferten Argumenten aus. - Das Ergebnis der Plugin-Ausführung wird von der
MainAppzurück an die Gemini API gesendet. - Gemini verwendet dieses Ergebnis, um eine endgültige, menschenlesbare Antwort für den Nutzer zu generieren.
Jedes Plugin residiert in einem eigenen Unterordner innerhalb des Hauptverzeichnisses plugins/.
Beispiel-Ordnerstruktur:
gemini-desktop-agent/
│
├── plugins/
│ │
│ └── mein_beispiel_plugin/
│ │
│ ├── __init__.py # Macht den Ordner zu einem Python-Paket
│ ├── manifest.json # Metadaten und Funktionsdeklaration für Gemini
│ └── plugin_code.py # Der eigentliche Python-Code des Plugins
│
├── main.py
├── quick_ask_overlay.py
└── ... (andere Kerndateien)
Diese Datei kann leer sein. Ihre Existenz signalisiert Python, dass der Ordner (mein_beispiel_plugin) als Python-Paket behandelt werden soll, was für korrekte Importe notwendig ist.
Diese JSON-Datei ist entscheidend. Sie enthält alle Metadaten, die der PluginManager und die Gemini API benötigen, um dein Plugin zu verstehen und zu nutzen.
Felder im Detail:
-
name(String):- Ein für Menschen lesbarer Name deines Plugins, wie er z.B. in einer Plugin-Verwaltungs-UI angezeigt werden könnte.
- Beispiel:
"Wettervorhersage"
-
version(String):- Die Version deines Plugins, vorzugsweise im Semantic Versioning Format (z.B.
"1.0.2"). - Beispiel:
"0.1.0"
- Die Version deines Plugins, vorzugsweise im Semantic Versioning Format (z.B.
-
description(String):- Eine kurze, allgemeine Beschreibung, was das Plugin tut. Diese ist primär für menschliche Nutzer oder eine Plugin-Verwaltungs-UI gedacht.
- Beispiel:
"Ruft die aktuelle Wettervorhersage für einen bestimmten Ort ab."
-
module_name(String):- Der Dateiname deines Python-Moduls, das die Plugin-Logik enthält (ohne die
.py-Endung). - Beispiel:
"wetter_api_handler"(entsprichtwetter_api_handler.py)
- Der Dateiname deines Python-Moduls, das die Plugin-Logik enthält (ohne die
-
function_name(String):- Der Name der spezifischen Python-Funktion innerhalb deines Moduls (
module_name.py), die als Haupteinstiegspunkt für das Plugin dient und von derMainAppaufgerufen wird. - Beispiel:
"get_weather_for_location"
- Der Name der spezifischen Python-Funktion innerhalb deines Moduls (
-
invocation_commands(Array von Strings, veraltet):- Dieses Feld wurde vom älteren, Regex-basierten Plugin-System verwendet. Es ist für neue Plugins, die auf Gemini Function Calling setzen, nicht mehr primär relevant, kann aber für Abwärtskompatibilität oder als Fallback noch vorhanden sein.
- Beispiel:
["wetter", "vorhersage"]
-
parameters(Array von Objekten, veraltet):- Ebenfalls Teil des alten Systems, beschreibt Parameter für die Regex-basierte Auslösung. Für neue Plugins, die
function_declarationnutzen, ist dies weniger wichtig. - Beispiel:
[ { "name": "location", "type": "string", "description": "Der Ort für die Wettervorhersage." } ]
- Ebenfalls Teil des alten Systems, beschreibt Parameter für die Regex-basierte Auslösung. Für neue Plugins, die
-
function_declaration(Objekt, ESSENZIELL für Gemini Function Calling):- Dieses Objekt definiert, wie dein Plugin der Gemini API für native Funktionsaufrufe präsentiert wird. Es folgt einer Teilmenge des OpenAPI-Schemas.
name(String):- Der Name, den Gemini intern verwenden wird, um diese Funktion/dieses Tool zu identifizieren und anzufordern.
- Wichtig: Muss den Namenskonventionen für Funktionen folgen (alphanumerisch, Unterstriche erlaubt, z.B.
get_weather_forecast,scheduleMeeting). Keine Leerzeichen oder Sonderzeichen. - Dieser Name wird von
MainAppverwendet, um die korrekte Python-Funktion deines Plugins zuzuordnen. Es ist oft sinnvoll, diesen Namen identisch oder sehr ähnlich zum Python-Funktionsnamen (function_name) zu halten. - Beispiel:
"get_weather_forecast"
description(String):- Extrem wichtig! Dies ist die Hauptinformation, die Gemini verwendet, um zu verstehen, was deine Funktion tut und wann sie aufgerufen werden sollte.
- Sei präzise, detailliert und gib klare Anwendungsfälle oder Beispiele an. Erkläre den Zweck und die Fähigkeiten der Funktion.
- Beispiel:
"Ruft die aktuelle Wettervorhersage (Temperatur, Zustand, Wind) für einen gegebenen Ort ab. Nützlich, wenn der Nutzer nach dem Wetter an einem spezifischen Ort fragt."
parameters(Objekt):- Definiert die Eingabeparameter, die deine Funktion von Gemini erwartet.
type(String): Muss immerOBJECTsein (Großbuchstaben!).properties(Objekt): Ein Objekt, bei dem jeder Schlüssel der Name eines Parameters ist, den deine Python-Funktion als Argument erwartet.- Jedes Parameter-Objekt innerhalb von
propertieshat:type(String): Der Datentyp des Parameters. Gemini unterstütztSTRING,INTEGER,NUMBER(für Fließkommazahlen),BOOLEAN,ARRAY. (Immer Großbuchstaben!).description(String): Eine klare Beschreibung dieses spezifischen Parameters, seines Zwecks, seines erwarteten Formats und ggf. Beispiele. Dies hilft Gemini, die richtigen Werte aus der Nutzeranfrage zu extrahieren.- Beispiel für einen
location-Parameter:"Der Stadt- und optional Ländername für die Wetterabfrage, z.B. 'Berlin, DE' oder 'London'."
- Beispiel für einen
enum(Array von Strings, optional): Wenn ein Parameter nur eine feste Auswahl von Werten annehmen darf, liste diese hier auf. Dies erhöht die Genauigkeit.- Beispiel für einen
unit-Parameter:{"type": "STRING", "description": "Temperatureinheit.", "enum": ["celsius", "fahrenheit"]}
- Beispiel für einen
- Jedes Parameter-Objekt innerhalb von
required(Array von Strings): Eine Liste der Parameternamen ausproperties, die zwingend von Gemini bereitgestellt werden müssen, damit deine Funktion korrekt ausgeführt werden kann.- Beispiel:
["location"](wennlocationimmer benötigt wird)
- Beispiel:
Beispiel manifest.json für ein Wetter-Plugin:
{
"name": "Wettervorhersage",
"version": "1.1.0",
"description": "Ruft die aktuelle Wettervorhersage ab.",
"module_name": "wetter_plugin_code",
"function_name": "hole_wetterdaten",
"function_declaration": {
"name": "get_weather_forecast",
"description": "Ruft die aktuelle Wettervorhersage (Temperatur in Celsius und Wetterbeschreibung) für einen bestimmten Ort ab. Zu verwenden, wenn der Nutzer explizit nach dem Wetter fragt.",
"parameters": {
"type": "OBJECT",
"properties": {
"location": {
"type": "STRING",
"description": "Der Name der Stadt (und optional des Landes, z.B. 'Berlin,DE') für die die Wettervorhersage abgerufen werden soll."
},
"unit": {
"type": "STRING",
"description": "Die gewünschte Temperatureinheit.",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
}Diese Datei enthält die eigentliche Python-Logik deines Plugins.
Beispiel plugins/mein_wetter_plugin/wetter_plugin_code.py:
import random # Für ein Dummy-Beispiel
# Der Name dieser Funktion muss mit "function_name" im Manifest übereinstimmen.
# Die Parameter sollten mit den in "function_declaration.parameters.properties"
# definierten Namen und erwarteten Typen übereinstimmen.
def hole_wetterdaten(location: str, unit: str = "celsius") -> dict:
"""
Simuliert das Abrufen von Wetterdaten.
In einer echten Anwendung würde hier ein API-Aufruf zu einem Wetterdienst erfolgen.
"""
print(f"[WetterPlugin] Wetter für '{location}' in '{unit}' angefragt.")
# Dummy-Daten für das Beispiel
if "berlin" in location.lower():
temp_c = random.randint(5, 25)
condition = random.choice(["Sonnig", "Wolkig", "Leichter Regen"])
elif "london" in location.lower():
temp_c = random.randint(0, 20)
condition = random.choice(["Bedeckt", "Nieselregen", "Windig"])
else:
return {"error": f"Wetterdaten für {location} nicht verfügbar."}
if unit.lower() == "fahrenheit" and temp_c is not None:
temp_f = (temp_c * 9/5) + 32
return {
"location": location,
"temperature": round(temp_f, 1),
"unit": "fahrenheit",
"condition": condition
}
elif temp_c is not None:
return {
"location": location,
"temperature": temp_c,
"unit": "celsius",
"condition": condition
}
return {"error": "Unbekannter Fehler beim Abrufen der Wetterdaten."}
# Optionaler Testblock
if __name__ == '__main__':
print(hole_wetterdaten(location="Berlin"))
print(hole_wetterdaten(location="London", unit="fahrenheit"))
print(hole_wetterdaten(location="Mond"))Wichtige Aspekte deiner Python-Funktion:
- Parameter: Die Funktionssignatur sollte die in
function_declaration.parameters.propertiesdefinierten Parameter als Argumente haben. Du kannst Standardwerte für optionale Parameter definieren. - Typ-Annotationen: Die Verwendung von Typ-Annotationen (z.B.
location: str) ist guter Stil und hilft bei der Entwicklung. - Logik: Hier implementierst du, was dein Plugin tun soll (API-Aufrufe, Berechnungen, Dateizugriffe etc.).
- Rückgabewert:
- Deine Funktion muss ein Ergebnis zurückgeben.
- Dieses Ergebnis wird von
MainAppan Gemini gesendet, damit Gemini eine finale Antwort formulieren kann. - Der Rückgabewert sollte für Gemini leicht verständlich sein. Oft ist ein Dictionary (das dann als JSON-Struktur an Gemini geht) eine gute Wahl, besonders wenn mehrere Datenpunkte zurückgegeben werden. Einfache Strings oder Zahlen sind auch möglich.
- Im Falle eines Fehlers innerhalb deines Plugins, gib eine klare Fehlermeldung zurück, idealerweise als Teil eines Dictionaries mit einem
"error"-Schlüssel (z.B.return {"error": "API-Limit erreicht"}).MainAppwird dies dann als Fehler an Gemini melden.
- Keine direkten UI-Interaktionen: Dein Plugin-Code sollte idealerweise keine direkten UI-Operationen (wie das Anzeigen von
QMessageBox) durchführen. Solche Interaktionen sollten vonMainAppbasierend auf den Ergebnissen des Plugins oder den Antworten von Gemini gesteuert werden. Logge Fehler oder Informationen mitprint().
- Platzierung: Lege deinen Plugin-Ordner (z.B.
mein_beispiel_plugin/) in dasplugins/Verzeichnis der Hauptanwendung. - Neustart: Starte die
main.pydes Gemini Desktop Agenten neu. - Discovery: Der
PluginManagerwird dein Plugin beim Start automatisch entdecken, dasmanifest.jsonlesen und diefunction_declarationregistrieren. Du solltest eine Log-Ausgabe in der Konsole sehen, die dein Plugin erwähnt. - Interaktion: Stelle im Haupt-Chatfenster Anfragen an Gemini, die die Funktionalität deines Plugins erfordern könnten. Wenn deine
function_declaration(insbesondere diedescriptions) gut formuliert ist, wird Gemini versuchen, dein Plugin über einenFunctionCallaufzurufen.
- Konsolenausgaben: Achte auf
print()-Ausgaben vonPluginManager(beim Entdecken von Plugins),MainApp(wenn einFunctionCallempfangen und verarbeitet wird) und deinem eigenen Plugin-Code. - Manifest-Validierung: Stelle sicher, dass dein
manifest.jsonvalides JSON ist und alle benötigten Felder (besonders innerhalb derfunction_declaration) korrekt formatiert sind. Die Typen (OBJECT,STRING,INTEGERetc.) in der Deklaration müssen großgeschrieben sein. - Parameter-Übereinstimmung: Die Namen und erwarteten Typen der Parameter in deiner Python-Funktion müssen mit den
propertiesin derfunction_declarationübereinstimmen. - Gemini's Antwort: Wenn Gemini dein Plugin nicht wie erwartet aufruft, experimentiere mit der
descriptiondeiner Funktion und ihrer Parameter in derfunction_declaration. Mache sie so klar und unmissverständlich wie möglich. - Testen des Plugins isoliert: Nutze den
if __name__ == '__main__':-Block in deiner Plugin-Python-Datei, um die Funktion unabhängig von der Hauptanwendung zu testen.
Viel Erfolg bei der Entwicklung deiner Plugins!