Kleine, spezialisierte Werkzeuge als ideale Erweiterung für KI-Agenten.
1. Das Problem: Der Medienbruch im Agent-Workflow
KI-Agenten wie Claude Code arbeiten primär in einer textbasierten Umgebung (Terminal, Editor). Die Notwendigkeit einer visuellen Ausgabe führt in der Praxis oft zu einem signifikanten Medienbruch:
- Kontextwechsel: Der Entwickler muss den Editor verlassen und den Browser öffnen.
- Manueller Prozess: Einloggen bei Portalen wie Midjourney oder DALL-E.
- Integration: Das Herunterladen und Einordnen der generierten Bilder in das Projektverzeichnis erfolgt händisch.
Dieser Bruch verhindert die vollständige Automatisierung von Workflows, bei denen Bildmaterial (z. B. Icons für Dokumentationen oder UI-Elemente) direkt durch den Agenten erzeugt werden soll.
"A tool should be transparent. It should not be something you think about. It should be something you think with." — Bjarne Stroustrup: The C++ Programming Language
Für einen Agenten ist ein Tool dann "transparent", wenn es über eine standardisierte Schnittstelle (CLI) aufrufbar ist und deterministische Ergebnisse liefert.
2. Die Lösung: Bildgenerierung per CLI
beuys ist ein minimalistisches Werkzeug, das die Bildgenerierung in den Terminal-Workflow integriert. Es reduziert die Interaktion auf einen einzigen Befehl:
beuys "Ein minimalistisches Icon für ein Terminal-Tool" --size 1024x1024
Die Vorteile für den Agenten und den Entwickler: - Kontext-Erhalt: Der Aufruf erfolgt direkt aus dem Arbeitsverzeichnis. - Automatisierbarkeit: Der Agent kann das Tool selbstständig aufrufen, den Pfad des generierten Bildes erfassen und es weiterverarbeiten. - Minimalismus: Fokus auf eine einzige Aufgabe gemäß dem Unix-Prinzip.
→ GitHub: github.com/mhennemeyer/beuys
Installation
# Option A: Binary kopieren
cp scripts/dist/beuys /usr/local/bin/
# Option B: Direkt als Script ausführen
./scripts/beuys "Eine Blume im Sonnenlicht"
3. Implementierung: Zero-Dependency-Ansatz
Moderne Software-Stacks leiden häufig unter "Dependency Bloat". Ein CLI-Tool für Agenten sollte jedoch leichtgewichtig und auf jedem System ohne komplexe Installation (pip install ...) lauffähig sein.
❌ Impure/Heavy: OpenAI Client-Bibliothek
Die Nutzung des offiziellen Clients bringt transitive Abhängigkeiten mit sich und erfordert eine Pflege der Versionen.
from openai import OpenAI
client = OpenAI()
response = client.images.generate(
model="dall-e-3",
prompt="...",
n=1,
)
image_url = response.data[0].url
✅ Pure/Light: Direkter API-Aufruf via urllib
beuys nutzt ausschließlich die Python-Standardbibliothek. Das macht das Tool immun gegen Änderungen in Drittanbieter-Bibliotheken und sorgt für sofortige Einsatzbereitschaft.
import json
import urllib.request
def call_openai(prompt, api_key):
payload = json.dumps({
"model": "dall-e-3",
"prompt": prompt,
"size": "1024x1024",
"n": 1,
}).encode("utf-8")
req = urllib.request.Request(
"https://api.openai.com/v1/images/generations",
data=payload,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
},
method="POST",
)
with urllib.request.urlopen(req) as resp:
body = json.loads(resp.read().decode("utf-8"))
return body["data"][0]["url"]
4. Architektur: Spezialisierung vs. Integration
Die Entscheidung gegen eine direkte Integration der Bildgenerierung als Plugin folgt architektonischen Überlegungen. Die Bereitstellung als eigenständiges, kleines Tool bietet mehrere Vorteile:
| Merkmal | Spezialisiertes Tool (Beuys) | Integrierte Funktion |
|---|---|---|
| Testbarkeit | Isoliert im Terminal testbar | Erfordert komplettes Agenten-Setup |
| Wartbarkeit | ~180 Zeilen Code, vollständig erfassbar | Teil einer komplexen Codebase |
| Portabilität | Als Binary überall einsetzbar | Gebunden an den Agenten |
| Unix-Prinzip | "Do one thing and do it well" | "Swiss Army Knife" (Komplexität) |
Diese Spezialisierung erlaubt es, die Bildgenerierung unabhängig vom Rest des Systems zu versionieren. Wenn sich die API von OpenAI ändert, muss nur dieses eine winzige Tool angepasst werden.
Selbst ausprobieren
Der zentrale Baustein des beuys-CLIs ist eine reine Funktion, die Eingabeparameter in ein API-konformes JSON-Payload überführt – ohne Netzwerk, ohne Seiteneffekt. Genau das macht das Tool testbar und API-Updates trivial: Was an OpenAI geschickt wird, lässt sich isoliert prüfen.
import json
def build_payload(prompt: str, size: str = "1024x1024", n: int = 1) -> dict:
"""Reiner Payload-Builder für die OpenAI Image-API."""
if not prompt.strip():
raise ValueError("prompt darf nicht leer sein")
if size not in {"256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"}:
raise ValueError(f"Ungültige size: {size}")
return {
"model": "dall-e-3",
"prompt": prompt.strip(),
"size": size,
"n": n,
}
def encode_payload(payload: dict) -> bytes:
"""Serialisierung getrennt vom Bauen – Single Responsibility."""
return json.dumps(payload).encode("utf-8")
examples = [
("Ein minimalistisches Icon für ein Terminal-Tool", "1024x1024"),
("Eine Blume im Sonnenlicht", "512x512"),
]
for prompt, size in examples:
payload = build_payload(prompt, size=size)
body = encode_payload(payload)
print(f"prompt={prompt!r:50} size={size:9} bytes={len(body)}")
print(f" payload = {payload}")
Erweiterungs-Idee: Ergänze einen reinen validate_prompt-Helper (z. B. Mindestlänge, verbotene Begriffe) und kombiniere ihn mit build_payload zu einer kleinen Pipeline – die Funktion bleibt rein und ohne Netzwerk testbar.
Genau diese Erweiterung machen wir jetzt: Ein reiner validate_prompt-Helper plus eine kleine prepare-Pipeline, die Validierung und Payload-Bau verkettet. Das Ergebnis ist ein Tupel ("ok", payload) oder ("err", reason) – ohne Exception, ohne Seiteneffekt, problemlos in Tests einsetzbar.
FORBIDDEN = {"nsfw", "violence", "blood"}
def validate_prompt(prompt: str, min_len: int = 8):
"""Reiner Validator: (ok, prompt) oder (err, reason)."""
p = prompt.strip()
if len(p) < min_len:
return ("err", f"prompt zu kurz (<{min_len} Zeichen)")
lowered = p.lower()
hit = next((w for w in FORBIDDEN if w in lowered), None)
if hit:
return ("err", f"verbotener Begriff: {hit!r}")
return ("ok", p)
def build_payload(prompt: str, size: str = "1024x1024", n: int = 1) -> dict:
return {"model": "dall-e-3", "prompt": prompt, "size": size, "n": n}
def prepare(prompt: str, size: str = "1024x1024"):
"""Pipeline: validate → build_payload. Bleibt rein."""
status, value = validate_prompt(prompt)
if status == "err":
return ("err", value)
return ("ok", build_payload(value, size=size))
cases = [
"Ein minimalistisches Icon für ein Terminal-Tool",
"kurz", # zu kurz
"Bild mit NSFW-Inhalt bitte", # verbotener Begriff
"Eine Blume im Sonnenlicht",
]
for c in cases:
print(f"{c!r:48} → {prepare(c)}")
5. Fazit
beuys zeigt, dass funktionale Erweiterungen für KI-Agenten keine komplexen Frameworks benötigen. Ein tiefes Verständnis der Standard-Protokolle (HTTP/JSON) und der Verzicht auf unnötige Abhängigkeiten führen zu robusteren und wartbareren Systemen.
Quellen & Ressourcen
- GitHub Repository: github.com/mhennemeyer/beuys – MIT-Lizenz, plattformübergreifend (macOS, Windows, Linux).
- Eric S. Raymond: The Art of Unix Programming – Fokus auf kleine, kombinierbare Tools.
- Python Standard Library Documentation:
urllib.request.