0Pricing
AI Agents · Lektion

Plugins entdecken und registrieren

Tool-Register, Manifestdateien und dynamisches Laden von Tools zur Laufzeit.

Plugins entdecken und registrieren ist eine kostenlose AI Agents-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des AI Agents-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der AI Agents-Kurs umfasst insgesamt 4 Lektionen.

Plugin-Systeme für Agenten

Ein Plugin-System ermöglicht es Agenten, neue Tools zur Laufzeit zu erkennen und zu laden, ohne den Kerncode des Agenten zu ändern. Der Agent liest ein Plugin-Verzeichnis, lädt das Manifest jedes Plugins, validiert es und fügt dessen Tools der aktiven Tool-Registry hinzu.

Dadurch werden modulare, erweiterbare Agent-Architekturen möglich.

Format des Plugin-Manifests

Jedes Plugin wird mit einer plugin.json-Manifestdatei ausgeliefert. Das ist die Identitätskarte des Plugins: was es ist, welche Version es hat, welche Tools es bereitstellt und welche Anforderungen es stellt (Python-Pakete, Umgebungsvariablen).

# plugin.json — stored in the plugin's root directory
EXAMPLE_MANIFEST = {
    'name': 'weather-tools',
    'display_name': 'Weather Tools',
    'version': '2.1.0',
    'description': 'Real-time weather and forecast tools',
    'author': 'Jane Developer <jane@example.com>',
    'license': 'MIT',
    'entry_point': 'weather_tools.plugin',  # Python module path
    'tool_definitions': [
        'get_current_weather',
        'get_5day_forecast',
        'get_weather_alerts'
    ],
    'requires': {
        'python_packages': ['requests>=2.28'],
        'env_vars': ['WEATHER_API_KEY']
    },
    'tags': ['weather', 'forecast', 'iot'],
    'min_framework_version': '1.0.0'
}

import json
print(json.dumps(EXAMPLE_MANIFEST, indent=2)[:300])

Loader für Plugin-Verzeichnisse

Durchsuchen Sie ein Verzeichnis nach Plugin-Unterverzeichnissen. Jedes Unterverzeichnis muss ein plugin.json-Manifest enthalten. Der Loader liest jedes Manifest, validiert es und registriert das Plugin in der Plugin-Registry.

import os
import json

PLUGIN_DIR = 'plugins'

def discover_plugins(plugin_dir: str = PLUGIN_DIR) -> list:
    discovered = []
    if not os.path.isdir(plugin_dir):
        print(f'Plugin directory not found: {plugin_dir}')
        return []

    for entry in os.scandir(plugin_dir):
        if not entry.is_dir():
            continue
        manifest_path = os.path.join(entry.path, 'plugin.json')
        if not os.path.exists(manifest_path):
            print(f'Skipping {entry.name}: no plugin.json')
            continue
        try:
            with open(manifest_path) as f:
                manifest = json.load(f)
            manifest['_path'] = entry.path
            manifest['_name'] = entry.name
            discovered.append(manifest)
            print(f'Discovered: {manifest["name"]} v{manifest["version"]}')
        except (json.JSONDecodeError, KeyError) as e:
            print(f'Invalid manifest in {entry.name}: {e}')

    return discovered

plugins = discover_plugins()
print(f'Total plugins discovered: {len(plugins)}')

Manifestvalidierung

Validieren Sie das Manifest vor dem Laden eines Plugins anhand eines Schemas, um fehlende Pflichtfelder, ungültige Versionen oder fehlende Umgebungsvariablen zu erkennen. Lehnen Sie ungültige Plugins ab und protokollieren Sie den Grund.

import os

REQUIRED_MANIFEST_FIELDS = {'name', 'version', 'entry_point', 'tool_definitions'}

def validate_manifest(manifest: dict) -> tuple:
    """
    Returns (is_valid: bool, errors: list)
    """
    errors = []

    # Required fields
    missing = REQUIRED_MANIFEST_FIELDS - set(manifest.keys())
    if missing:
        errors.append(f'Missing required fields: {missing}')

    # Version format
    version = manifest.get('version', '')
    parts = version.split('.')
    if len(parts) != 3 or not all(p.isdigit() for p in parts):
        errors.append(f'Invalid version format: {version}')

    # Check required env vars exist
    env_vars = manifest.get('requires', {}).get('env_vars', [])
    for var in env_vars:
        if not os.environ.get(var):
            errors.append(f'Missing required env var: {var}')

    # Tool definitions must be a non-empty list
    tools = manifest.get('tool_definitions', [])
    if not isinstance(tools, list) or len(tools) == 0:
        errors.append('tool_definitions must be a non-empty list')

    return len(errors) == 0, errors

if __name__ == '__main__':
    good_manifest = {
        'name': 'weather-tools', 'version': '1.2.0',
        'entry_point': 'weather_tools.plugin', 'tool_definitions': [{'name': 'get_weather'}]
    }
    bad_manifest = {'name': 'broken-plugin', 'version': 'v1'}
    print('Good manifest:', validate_manifest(good_manifest))
    print('Bad manifest: ', validate_manifest(bad_manifest))

Dynamisches Laden von Tools

Sobald das Manifest eines Plugins validiert wurde, importieren Sie das Python-Modul des Plugins dynamisch und rufen Sie get_tools() auf, um die Tool-Definitionen und Ausführungsfunktionen abzurufen. Pythons importlib macht dies unkompliziert.

import importlib
import importlib.util
import sys

def load_plugin_module(manifest: dict):
    """
    Import the plugin's Python module and return it.
    Adds the plugin directory to sys.path if needed.
    """
    plugin_path = manifest['_path']
    entry_point = manifest['entry_point']  # e.g. 'weather_tools.plugin'

    # Add plugin directory to path so relative imports work
    if plugin_path not in sys.path:
        sys.path.insert(0, plugin_path)

    try:
        module = importlib.import_module(entry_point)
        return module
    except ImportError as e:
        print(f'Failed to import {entry_point}: {e}')
        return None

def extract_tools_from_module(module, tool_names: list) -> dict:
    """
    Returns {tool_name: {'schema': dict, 'execute': callable}}
    """
    tools = {}
    for name in tool_names:
        schema_fn = getattr(module, f'get_{name}_schema', None)
        execute_fn = getattr(module, f'execute_{name}', None)
        if schema_fn and execute_fn:
            tools[name] = {'schema': schema_fn(), 'execute': execute_fn}
    return tools

if __name__ == '__main__':
    class FakeModule:
        def get_weather_schema(self):
            return {'name': 'get_weather'}
        def execute_weather(self, params):
            return {'temp': 72}

    tools = extract_tools_from_module(FakeModule(), ['weather'])
    print('Extracted tools:', list(tools.keys()))
    print('Weather schema:', tools['weather']['schema'])

Tool-Registry

Die Tool-Registry ist die einzige maßgebliche Quelle für alle verfügbaren Tools. Sie ordnet Tool-Namen ihren Schemas und Ausführungsfunktionen zu. Der Agent fragt die Registry ab, wenn er seine Tool-Liste für jeden LLM-Aufruf erstellt.

class ToolRegistry:
    def __init__(self):
        self._tools: dict = {}  # name -> {'schema', 'execute', 'plugin'}
        self._plugins: dict = {}  # plugin_name -> manifest

    def register_tool(
        self, name: str, schema: dict,
        execute_fn, plugin_name: str
    ):
        if name in self._tools:
            print(f'WARNING: Tool {name} already registered, overwriting')
        self._tools[name] = {
            'schema': schema,
            'execute': execute_fn,
            'plugin': plugin_name
        }

    def register_plugin(self, manifest: dict, tools: dict):
        self._plugins[manifest['name']] = manifest
        for name, tool in tools.items():
            self.register_tool(name, tool['schema'],
                               tool['execute'], manifest['name'])
        print(f'Registered plugin: {manifest["name"]} '
              f'({len(tools)} tools)')

    def get_all_schemas(self) -> list:
        return [t['schema'] for t in self._tools.values()]

    def execute(self, tool_name: str, params: dict):
        if tool_name not in self._tools:
            raise KeyError(f'Unknown tool: {tool_name}')
        return self._tools[tool_name]['execute'](params)

registry = ToolRegistry()

if __name__ == '__main__':
    def get_weather(params):
        return {'temp': 72, 'city': params.get('city')}

    manifest = {'name': 'weather-tools'}
    tools = {'get_weather': {'schema': {'name': 'get_weather'}, 'execute': get_weather}}
    registry.register_plugin(manifest, tools)
    print('Registered schemas:', registry.get_all_schemas())
    print('Execution result:', registry.execute('get_weather', {'city': 'Paris'}))

Suche nach Fähigkeiten

Wenn ein Agent viele Plugins hat, sollte er die Registry nach einem Fähigkeits-Tag durchsuchen können, statt alle Tools in jeden LLM-Aufruf zu laden. Zu viele Tools im Kontext verschlechtern die Genauigkeit der Tool-Auswahl durch das LLM.

class SearchableToolRegistry(ToolRegistry):
    def search(self, query: str) -> list:
        """
        Search tools by name, description, or tags.
        Returns list of matching tool schemas.
        """
        query_lower = query.lower()
        matches = []
        for name, tool in self._tools.items():
            schema = tool['schema']
            plugin = self._plugins.get(tool['plugin'], {})
            plugin_tags = plugin.get('tags', [])

            if (
                query_lower in name.lower() or
                query_lower in schema.get('description', '').lower() or
                any(query_lower in tag.lower() for tag in plugin_tags)
            ):
                matches.append(schema)
        return matches

# Usage:
registry = SearchableToolRegistry()
# After loading plugins...
weather_tools = registry.search('weather')
print(f'Weather tools: {[t["name"] for t in weather_tools]}')

Hot-Reload bei Verzeichnisänderungen

In der Entwicklung ist es nützlich, Plugins bei Dateiänderungen neu zu laden, ohne den Agenten neu zu starten. Verwenden Sie die Bibliothek watchdog, um das Plugin-Verzeichnis zu überwachen und bei Änderungen an plugin.json-Dateien ein Neuladen auszulösen.

# pip install watchdog
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class PluginReloadHandler(FileSystemEventHandler):
    def __init__(self, registry, plugin_loader_fn):
        self.registry = registry
        self.loader = plugin_loader_fn

    def on_modified(self, event):
        if 'plugin.json' in event.src_path:
            print(f'Plugin manifest changed: {event.src_path}')
            self.reload_plugin(event.src_path)

    def reload_plugin(self, manifest_path: str):
        import json, os
        plugin_dir = os.path.dirname(manifest_path)
        try:
            with open(manifest_path) as f:
                manifest = json.load(f)
            manifest['_path'] = plugin_dir
            self.loader(manifest, self.registry)
            print(f'Reloaded: {manifest["name"]}')
        except Exception as e:
            print(f'Reload failed: {e}')

def start_plugin_watcher(plugin_dir: str, registry):
    handler = PluginReloadHandler(registry, lambda m, r: None)
    observer = Observer()
    observer.schedule(handler, plugin_dir, recursive=True)
    observer.start()
    return observer

Abhängigkeiten prüfen und automatisch installieren

Prüfen Sie beim Laden eines Plugins, ob die erforderlichen Python-Pakete installiert sind. Installieren Sie fehlende Pakete optional automatisch mit pip. Protokollieren Sie einen eindeutigen Fehler, wenn eine Abhängigkeit nicht erfüllt werden kann.

import subprocess
import sys
import importlib

def check_and_install_deps(manifest: dict, auto_install: bool = False) -> bool:
    packages = manifest.get('requires', {}).get('python_packages', [])
    missing = []

    for pkg_spec in packages:
        pkg_name = pkg_spec.split('>=')[0].split('==')[0].strip()
        try:
            importlib.import_module(pkg_name.replace('-', '_'))
        except ImportError:
            missing.append(pkg_spec)

    if not missing:
        return True

    print(f'Missing packages for {manifest["name"]}: {missing}')

    if auto_install:
        for pkg in missing:
            print(f'Installing {pkg}...')
            result = subprocess.run(
                [sys.executable, '-m', 'pip', 'install', pkg],
                capture_output=True, text=True
            )
            if result.returncode != 0:
                print(f'Install failed: {result.stderr[:200]}')
                return False
        return True

    return False

if __name__ == '__main__':
    manifest = {'name': 'demo-plugin', 'requires': {'python_packages': ['totally_fake_package_xyz']}}
    ok = check_and_install_deps(manifest, auto_install=False)
    print('All dependencies satisfied:', ok)

Vollständiger Plugin-Bootstrap

Fassen Sie Erkennung, Validierung, Prüfung der Abhängigkeiten, Laden des Moduls und Registrierung in der Registry in einer einzigen Bootstrap-Funktion zusammen, die beim Start des Agenten aufgerufen wird.

def bootstrap_plugins(
    plugin_dir: str,
    registry: ToolRegistry,
    auto_install_deps: bool = False
) -> dict:
    results = {'loaded': [], 'failed': []}
    manifests = discover_plugins(plugin_dir)

    for manifest in manifests:
        name = manifest.get('name', '?')
        is_valid, errors = validate_manifest(manifest)
        if not is_valid:
            print(f'INVALID {name}: {errors}')
            results['failed'].append({'name': name, 'reason': errors})
            continue

        deps_ok = check_and_install_deps(manifest, auto_install_deps)
        if not deps_ok:
            results['failed'].append({'name': name, 'reason': 'missing_deps'})
            continue

        module = load_plugin_module(manifest)
        if not module:
            results['failed'].append({'name': name, 'reason': 'import_error'})
            continue

        tools = extract_tools_from_module(module, manifest['tool_definitions'])
        registry.register_plugin(manifest, tools)
        results['loaded'].append(name)

    print(f'Plugins loaded: {results["loaded"]}')
    print(f'Plugins failed: {results["failed"]}')
    return results

Ereignisse im Plugin-Lebenszyklus protokollieren

Protokollieren Sie jedes wichtige Ereignis im Plugin-Lebenszyklus: Erkennung, Validierungsfehler, erfolgreiches Laden, Hot-Reload und Entladen. Diese Protokolle sind für das Debugging und die Auditierung des Plugin-Systems in der Produktion unerlässlich.

import logging
from datetime import datetime

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
plugin_logger = logging.getLogger('plugin_system')

def log_plugin_event(event: str, plugin_name: str, details: dict = None):
    entry = {
        'event': event,
        'plugin': plugin_name,
        'timestamp': datetime.utcnow().isoformat(),
        'details': details or {}
    }
    if event in ('VALIDATION_FAILED', 'LOAD_FAILED', 'SIGNATURE_INVALID'):
        plugin_logger.warning('Plugin event: %s', entry)
    else:
        plugin_logger.info('Plugin event: %s', entry)

# Usage:
log_plugin_event('DISCOVERED', 'weather-tools', {'path': 'plugins/weather-tools'})
log_plugin_event('LOADED', 'weather-tools', {'tools': ['get_current_weather']})
log_plugin_event('VALIDATION_FAILED', 'bad-plugin', {'errors': ['Missing entry_point']})

if __name__ == '__main__':
    import sys
    plugin_logger.addHandler(logging.StreamHandler(sys.stdout))
    log_plugin_event('LOADED', 'demo-plugin', {'tools': ['get_weather']})

Wissenscheck

Welchen Zweck hat das Feld entry_point in einem Plugin-Manifest?

Zusammenfassung: Plugin-Erkennung und -Registrierung

Ausgezeichnet! Das haben Sie gelernt:

  • Plugin-Manifest: plugin.json mit name, version, entry_point, tool_definitions, requires
  • Erkennung: Verzeichnis nach Unterverzeichnissen mit plugin.json durchsuchen
  • Validierung: Pflichtfelder, Versionsformat, Umgebungsvariablen und nicht leere Tools prüfen
  • Dynamisches Laden: importlib.import_module() über den entry_point-Pfad
  • Tool-Registry: zentrale Zuordnung von Tool-Namen zu Schema und execute-Funktion
  • Hot-Reload: watchdog überwacht das Plugin-Verzeichnis und lädt bei Manifeständerungen neu

Nächstes Thema: Tool-Versionierung und Kompatibilitätsverwaltung.

Häufig gestellte Fragen

Ist die Lektion „Plugins entdecken und registrieren“ kostenlos?

Ja — der vollständige Text von „Plugins entdecken und registrieren“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des AI Agents-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der AI Agents-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Plugins entdecken und registrieren“?

Tool-Register, Manifestdateien und dynamisches Laden von Tools zur Laufzeit. Du übst AI Agents mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um AI Agents zu starten?

Keine Vorkenntnisse erforderlich. AI Agents auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.

Wie lange dauert die Lektion „Plugins entdecken und registrieren“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser AI Agents-Lektion Code schreiben und ausführen?

Ja. Jede AI Agents-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Teilbare Agent-Tools entwerfen
  2. Plugins entdecken und registrieren
  3. Tool-Versionierung und Kompatibilität
  4. Einen Agent-Plugin-Marktplatz erstellen
← Zurück zu AI Agents