Build integrated IS/BS/CF financial workbooks in Excel.
日本語の概要は準備中です。原文の説明を表示しています。
Vollständige Dokumentation des OpenAmer Plugin-API-Systems. Hooks, Events, Lifecycle, manifest.json Format, Config-Integration und Plugin-Manager.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
Dokumentation für Plugin-Autoren — Architektur, Hooks, Lifecycle und Best Practices für das OpenAmer-Plugin-System.
Das Plugin-System von OpenAmer erlaubt es, das Agent-Verhalten ohne
Kernänderungen zu erweitern. Plugins sind selbstständige Python-Pakete,
die in einem eigenen Verzeichnis unter plugins/ oder desktop-plugins/
leben. Jedes Plugin:
plugin.yamlregister(ctx)-Funktion in
__init__.pyopenamer-repo/
├── plugins/ # Built-in Plugins (bundled)
│ ├── browser/
│ │ ├── browser_use/
│ │ │ ├── __init__.py
│ │ │ ├── plugin.yaml
│ │ │ └── provider.py
│ │ └── ...
│ ├── image_gen/
│ ├── memory/
│ └── ...
├── desktop-plugins/ # Desktop/User-Plugins
│ └── examples/
│ ├── hello-world/
│ └── slash-command/
└── scripts/
└── plugin-manager.py # CLI-Verwaltung
| Prinzip | Beschreibung |
|---|---|
| Narrow Waist | Das Plugin-API ist schmal — wenige Hooks, klare Verträge |
| Selbstständig | Jedes Plugin ist ein eigenständiges Python-Paket |
| Keine Kern-Importe | Plugins importieren NIEMALS Core-Module direkt — nur die Context API |
| Config-gesteuert | Plugins werden per config.yaml ein-/ausgeschaltet |
| Thread-sicher | Alle Hooks können in Multi-Thread-Umgebungen laufen |
Jedes Plugin durchläuft einen definierten Lifecycle:
┌──────────────────────────────────────────────────┐
│ DISCOVERY │
│ Plugin wird beim Start im Plugin-Pfad gefunden │
│ → plugin.yaml wird gelesen │
│ → Manifest wird validiert │
└────────────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ LOADING │
│ __init__.py wird importiert │
│ register(ctx) wird aufgerufen │
│ → Hooks werden registriert │
│ → Initialisierungslogik läuft │
└────────────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ ENABLED │
│ Plugin ist aktiv (sofern enabled: true) │
│ → onReady feuert │
│ → Hooks reagieren auf Events │
└────────────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ DISABLED │
│ Plugin wird per Config ausgeschaltet │
│ → Hook-Aufrufe werden ignoriert │
│ → Ressourcen bleiben geladen (kein Teardown) │
└────────────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ TEARDOWN │
│ (Nicht implementiert — cleanup per Cron/Manuell) │
└──────────────────────────────────────────────────┘
| Phase | Beschreibung |
|---|---|
| Discovery | Der Plugin-Loader durchsucht plugins/ und desktop-plugins/ nach plugin.yaml. Wird das Plugin per Config deaktiviert (plugin.<name>.enabled: false), wird es übersprungen. |
| Loading | Das __init__.py des Plugins wird importiert und register(ctx) aufgerufen. Das Plugin registriert hier seine Hooks. |
| Enabled | Sobald der Agent bereit ist, feuert onReady. Danach reagiert das Plugin auf Events gemäß seiner registrierten Hooks. |
| Disabled | Das Plugin bleibt im Speicher, aber Hook-Callbacks werden nicht aufgerufen. Ein Re-Enable zur Laufzeit ist möglich. |
| Teardown | Geplant für zukünftige Versionen — ermöglicht Ressourcen-Freigabe bei Plugin-Deaktivierung oder Agent-Shutdown. |
Hooks sind die primäre Möglichkeit, in das Agent-Verhalten einzugreifen. Jeder Hook hat einen definierten Payload und Rückgabetyp.
onReadyWird einmal aufgerufen, nachdem der Agent vollständig gestartet ist und bevor die erste User-Nachricht verarbeitet wird.
def on_hook_ready(ctx) -> None:
"""Wird nach dem Agent-Start aufgerufen."""
logger.info("Plugin bereit — Agent ist gestartet")
ctx.register_tool("my_tool", my_tool_handler)
Use Cases:
onMessageWird bei jeder eingehenden User-Nachricht aufgerufen — vor der Agent-Verarbeitung.
def on_hook_message(ctx, message: str) -> str | None:
"""Wird bei jeder User-Nachricht aufgerufen.
Args:
message: Die rohe User-Nachricht.
Returns:
Optional modifizierte Nachricht (wird an Agent weitergegeben)
oder None (keine Änderung).
"""
if "!wetter" in message:
return ctx.format_weather(get_weather())
return None # Keine Änderung — an Agent weiterleiten
Use Cases:
onCommandWird bei registrierten Slash-Commands aufgerufen (/cmd).
def on_hook_command(ctx, command: str, args: str) -> str | None:
"""Wird bei einem registrierten Slash-Command aufgerufen.
Args:
command: Der Command-Name ohne Slash (z. B. "translate" für "/translate")
args: Die restliche Argument-Zeile
Returns:
Antwort-String oder None (wenn Command nicht behandelt)
"""
if command == "translate":
target = args.split() if args else "en"
text = " ".join(args.split()[1:]) if len(args.split()) > 1 else ""
return f"Übersetzung ({target}): {do_translate(text, target)}"
return None
Use Cases:
/translate, /remind, /calc)/deploy, /build)onCronRunWird bei Cron-Job-Ausführung aufgerufen.
def on_hook_cron(ctx, cron_id: str) -> None:
"""Wird bei Cron-Job-Execution aufgerufen.
Args:
cron_id: Die ID des Cron-Jobs (aus der Cron-Konfiguration).
"""
if cron_id == "daily_summary":
summary = generate_daily_summary()
ctx.send_notification("daily-summary", summary)
Use Cases:
onToolCallWird vor und nach jedem Tool-Call des Agenten aufgerufen.
def on_hook_tool_call(ctx, tool_name: str, arguments: dict,
phase: str, result: Any = None) -> dict | None:
"""Wird vor/nach Tool-Calls aufgerufen.
Args:
tool_name: Name des aufgerufenen Tools
arguments: Tool-Argumente
phase: "before" (vor Ausführung) oder "after" (nach Ausführung)
result: Tool-Ergebnis (nur bei phase="after")
Returns:
Bei phase="before": Optional modifizierte Argumente
Bei phase="after": Optional modifiziertes Ergebnis
"""
if phase == "before" and tool_name == "web_search":
# Query-Logging
ctx.log_debug(f"Web-Suche: {arguments.get('query', '')}")
return None # Keine Änderung an Argumenten
if phase == "after" and tool_name == "terminal":
# Nur erste 500 Zeichen des Outputs weitergeben
result["output"] = result.get("output", "")[:500]
return result
return None
Use Cases:
plugin.yaml — Manifest-FormatJedes Plugin benötigt eine plugin.yaml im Plugin-Stammverzeichnis.
name: my-plugin
version: 1.0.0
description: "Kurze Beschreibung des Plugins"
author: "Dein Name"
# -- Pflichtfelder -----------------------------------------------------------
name: my-plugin # Eindeutiger Plugin-Name (lowercase, hyphens)
version: 1.0.0 # Semver
description: "Kurze Beschreibung" # Einzeilige Beschreibung
author: "Dein Name" # Autor/Organisation
# -- Optionale Felder --------------------------------------------------------
kind: backend # plugin-Typ: backend | frontend | service | tool
license: "MIT" # SPDX-Lizenz-ID
# -- Hooks (vom Plugin genutzt, deklarativ für den Loader) -------------------
hooks:
- onReady
- onMessage
- onCommand
- onCronRun
- onToolCall
# -- Bereitgestellte Provider (optional) -------------------------------------
provides:
browser_providers:
- my-browser
tools:
- my-custom-tool
commands:
- translate
# -- Abhängigkeiten (optional) -----------------------------------------------
requires:
openamer: ">=0.4.0" # Mindest-OpenAmer-Version
python: ">=3.11" # Python-Version
plugins: # Benötigte andere Plugins
- memory
# -- Konfigurations-Schema (optional) ----------------------------------------
config:
schema:
api_key:
type: string
description: "API-Key für externen Dienst"
secret: true # Wert wird in .env erwartet
timeout:
type: integer
default: 30
description: "Timeout in Sekunden"
endpoint:
type: string
default: "https://api.example.com"
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
name | string | Ja | Eindeutiger Identifier. Wird in Config-Pfaden verwendet: plugin.<name>.enabled |
version | string | Ja | Semver-konforme Version |
description | string | Ja | Kurzbeschreibung (max. 120 Zeichen) |
author | string | Ja | Autor oder Organisation |
kind | string | Nein | Typ: backend, frontend, service, tool |
license | string | Nein | SPDX-Lizenz-ID (z. B. MIT, Apache-2.0) |
hooks | list | Nein | Deklarative Hook-Liste (vom Loader geprüft) |
provides | dict | Nein | Vom Plugin bereitgestellte Provider/Tools/Commands |
requires | dict | Nein | Abhängigkeiten zu OpenAmer-Version, Python, anderen Plugins |
config.schema | dict | Nein | JSON-Schema-artige Konfigurationsdeklaration |
register(ctx) — EinsprungspunktJedes Plugin muss eine register(ctx)-Funktion in __init__.py exportieren.
Sie ist der einzige Einsprungspunkt und wird vom Plugin-Loader aufgerufen.
from typing import Any, Optional
def register(ctx: "PluginContext") -> None:
"""Plugin-Einsprungspunkt.
Wird vom Plugin-Loader nach dem Import aufgerufen.
Registriere hier deine Hooks.
Args:
ctx: PluginContext — die Context-API für das Plugin
"""
pass
register()def register(ctx) -> None:
"""Registriere alle Hooks des Plugins."""
# onReady: einmalige Initialisierung nach Agent-Start
@ctx.on_ready
def on_ready():
logger.info("Plugin ist bereit")
# onMessage: verarbeite eingehende Nachrichten
@ctx.on_message
def on_message(message: str) -> Optional[str]:
if "!ping" in message:
return "pong!"
return None
# onCommand: Slash-Commands
@ctx.on_command
def on_command(command: str, args: str) -> Optional[str]:
if command == "hello":
return f"Hallo! Args: {args}"
return None
def register(ctx) -> None:
ctx.on_ready(my_ready_handler)
ctx.on_message(my_message_handler, priority=10)
ctx.on_command("greet", my_greet_handler)
ctx.on_cron("daily_report", my_cron_handler)
ctx.on_tool_call(my_tool_call_handler, tools=["web_search", "terminal"])
Der ctx-Parameter in register() bietet die vollständige Plugin-Schnittstelle.
| Methode | Beschreibung |
|---|---|
ctx.on_ready(handler) | Registriert onReady-Handler |
ctx.on_message(handler, priority=0) | Registriert onMessage-Handler (höhere Priority = früherer Aufruf) |
ctx.on_command(command, handler) | Registriert onCommand-Handler für /command |
ctx.on_cron(cron_id, handler) | Registriert onCronRun-Handler für Cron-Job cron_id |
ctx.on_tool_call(handler, tools=None) | Registriert onToolCall-Handler. tools=None = alle Tools |
ctx.register_tool(name, handler) | Stellt ein neues Tool für den Agenten bereit |
ctx.register_browser_provider(provider) | Registriert einen Browser-Provider |
ctx.get_config(key, default=None) | Liest Plugin-Konfiguration aus config.yaml |
ctx.get_secret(key) | Liest Secret aus .env (Feld mit secret: true in plugin.yaml) |
ctx.send_notification(channel, message) | Sendet eine Notification (z. B. in die Chat-Oberfläche) |
ctx.log_info(msg) | Loggt auf INFO-Ebene |
ctx.log_debug(msg) | Loggt auf DEBUG-Ebene |
ctx.log_error(msg) | Loggt auf ERROR-Ebene |
ctx.store.set(key, value) | Persistente Schlüssel-Wert-Speicher (Plugin-scoped) |
ctx.store.get(key, default=None) | Liest aus dem Plugin-Speicher |
ctx.get_plugin_dir() | Gibt den absoluten Pfad zum Plugin-Verzeichnis zurück |
onMessagedef register(ctx) -> None:
# Security-Filter läuft zuerst (priority=100)
@ctx.on_message(priority=100)
def security_filter(message):
if contains_bad_words(message):
return "[Nachricht blockiert durch Security-Plugin]"
return None
# Übersetzungs-Plugin läuft später (priority=0)
@ctx.on_message(priority=0)
def translator(message):
if message.startswith("!translate"):
return translate(message)
return None
Plugins werden über die OpenAmer-config.yaml gesteuert.
# config.yaml
plugin:
hello-world:
enabled: true # Plugin ist aktiv
slash-command:
enabled: false # Plugin ist deaktiviert (wird geladen, aber Hooks feuern nicht)
plugin:
translate:
enabled: true
api_key: "" # Direkter Key (nicht empfohlen — lieber .env)
timeout: 30
endpoint: "https://libretranslate.com/translate"
weather:
enabled: true
units: metric # metric | imperial
default_city: "Berlin"
.envFür sensitive Werte (API-Keys, Tokens) sollte das Plugin das secret: true-
Feld in der plugin.yaml nutzen. Der User setzt den Wert dann in .env:
# .env
MY_PLUGIN_API_KEY=sk-...
ANOTHER_SECRET=abc123
Das Plugin liest Secrets per ctx.get_secret("api_key").
Plugins können per CLI ein-/ausgeschaltet werden:
# Plugin aktivieren
openamer config set plugin.hello-world.enabled true
# Plugin deaktivieren
openamer config set plugin.hello-world.enabled false
# Plugin-Konfiguration setzen
openamer config set plugin.weather.units metric
# Config anzeigen
openamer config get plugin.hello-world.enabled
Die Config-Integration folgt OpenAmers einheitlichem Konfigurationssystem:
config.yaml
├── plugin
│ ├── <plugin-name>
│ │ ├── enabled: true/false
│ │ ├── ... (plugin-spezifische Felder aus config.schema)
│ │ └── ... (vom Plugin definierte Konfiguration)
│ └── ...
├── model
├── memory
└── ...
Der Plugin-Manager (scripts/plugin-manager.py) ist ein CLI-Tool zur
Verwaltung aller Plugins.
# Alle Plugins auflisten
python scripts/plugin-manager.py list
# Plugin installieren (aus Pfad)
python scripts/plugin-manager.py install ./desktop-plugins/examples/hello-world
# Plugin deaktivieren
python scripts/plugin-manager.py disable my-plugin
# Plugin aktivieren
python scripts/plugin-manager.py enable my-plugin
# Plugin-Health-Check
python scripts/plugin-manager.py check my-plugin
# Alle Plugins prüfen
python scripts/plugin-manager.py check --all
plugin-manager.py listOpenAmer Plugin-Manager
═══════════════════════════════════════════════════════
Built-in Plugins (plugins/):
✓ browser-firecrawl v1.0.0 enabled Firecrawl cloud browser
✓ browser-browser-use v1.0.0 enabled Browser Use cloud browser
✓ memory v0.1.0 enabled Persistent memory system
✓ image-gen v0.1.0 enabled Image generation
User Plugins (desktop-plugins/):
✗ hello-world v1.0.0 disabled Mein erstes Plugin
✓ slash-command v1.0.0 enabled Custom Slash-Commands
═══════════════════════════════════════════════════════
6 Plugins insgesamt | 5 enabled | 1 disabled
from openamer_state import ...). Nutze nur die Context API.onReady initialisieren, nicht beim Import.
Nutze plugin_utils.lazy_singleton für Thread-Safety.ctx.get_config() steuerbar machen.plugin.yaml unter hooks, damit der Loader vorab prüfen kann.| Problem | Lösung |
|---|---|
| Plugin blockiert Agent-Start | onReady niemals blockierend machen; Hintergrund-Thread für langsame Init nutzen |
| Hook feuert nicht | Prüfen ob plugin.<name>.enabled: true in config.yaml |
| Config-Wert wird nicht gefunden | Plugin-Name in plugin.yaml mit Config-Pfad vergleichen (beide lowercase, hyphens) |
| Thread-Race beim Singleton | plugin_utils.lazy_singleton oder SingletonSlot verwenden |
| Plugin-Import-Fehler | __init__.py muss register(ctx) exportieren; alle Importe in register() (lazy) |
| Config-Änderung wirkt nicht | Config wird nur beim Agent-Neustart neu geladen (Runtime-Reload geplant) |
onMessage überschreibt andere Plugins | return None wenn keine Änderung; return str nur wenn du die Nachricht ersetzen willst |
| Plugin | Beschreibung | Ordner |
|---|---|---|
| hello-world | Minimal-Plugin: onReady → loggt "Hello World!", onMessage → !ping/pong | desktop-plugins/examples/hello-world/ |
| slash-command | Slash-Commands: /hello, /weather | desktop-plugins/examples/slash-command/ |
Beide sind vollständig implementiert unter desktop-plugins/examples/.
plugin.yaml erstellt mit allen Pflichtfeldern__init__.py mit register(ctx)-Funktionplugin.yaml deklariertsecret: true + .env gelöst (falls API-Keys nötig)plugin.<name>.enabled: true in config.yaml gesetztlazy_singleton für Shared Resources)plugin-manager.py check validiertまだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Build integrated IS/BS/CF financial workbooks in Excel.
日本語の概要は準備中です。原文の説明を表示しています。
Run and grow the OpenAmer Agent-to-Agent swarm: identity, trust, node-to-node ask, signed skill/insight sharing, and the autonomous self-learning loop.
日本語の概要は準備中です。原文の説明を表示しています。
Use for A/B experiments on OpenAmer configs and skills.
日本語の概要は準備中です。原文の説明を表示しています。
Roleplay a hostile user to find and triage UX pain points.
日本語の概要は準備中です。原文の説明を表示しています。
Agent mesh: master/worker nodes, HTTP delegation, heartbeat.
日本語の概要は準備中です。原文の説明を表示しています。
Use for agentic commerce payments tasks. Candidate synthesized from trend radar; awaiting trial promotion.
日本語の概要は準備中です。原文の説明を表示しています。