Container-Richtlinien
Capability-Container laufen im Selu-Docker-Netzwerk zusammen mit dem Orchestrator. Um Sicherheit und Performance zu gewährleisten, folge diesen Richtlinien beim Erstellen deines Container-Images.
Netzwerk
Abschnitt betitelt „Netzwerk“- Dein gRPC-Server muss auf Port 50051 hören. Der Orchestrator erwartet dies und mappt ihn automatisch.
- Capabilities können ausgehende HTTP/HTTPS-Anfragen an externe APIs stellen. Du kannst das über den
network-Abschnitt in deinermanifest.yamlsteuern. - Capabilities können nicht direkt mit anderen Capability-Containern kommunizieren. Alle Inter-Capability-Koordination läuft über den Orchestrator via
delegate_to_agent. - Der Orchestrator verbindet sich mit deinem Container über das interne Docker-Bridge-Netzwerk. Binde nicht an
127.0.0.1— binde an0.0.0.0oder[::]. - Container werden in separate Docker-Bridge-Netzwerke nach Klasse eingeteilt:
selu-tools-netfür Tool-Klasse-Capabilities undselu-envs-netfür Environment-Klasse-Capabilities.
Netzwerk-Richtlinien
Abschnitt betitelt „Netzwerk-Richtlinien“
Steuere ausgehenden Netzwerkzugriff in deiner manifest.yaml:
network: mode: allowlist hosts: - "api.openweathermap.org:443" - "*.icloud.com:443" # Wildcard für Subdomains - "cdn.example.com" # Beliebiger PortNetzwerk-Modi:
none(Standard) — Kein externer Netzwerkzugriffallowlist— Nur die aufgelisteten Hosts sind erreichbarany— Uneingeschränkter Zugriff (mit Vorsicht verwenden)
Wildcard-Muster unterstützen Subdomain-Matching:
*.example.com:443trifft aufapi.example.com:443zu, aber nicht aufexample.com:443*.example.comtrifft auf Subdomains an beliebigen Ports zu
Der Orchestrator betreibt einen Egress-Proxy, der alle ausgehenden Anfragen für Sicherheitsüberprüfungen protokolliert. Nutzer können diese Protokolle auf der Detail-Seite deines Agenten einsehen. Jeder Container erhält ein eindeutiges Egress-Proxy-Auth-Token zur Anfragenzuordnung.
Ressourcenlimits
Abschnitt betitelt „Ressourcenlimits“Der Orchestrator setzt Ressourcenlimits durch, die in deiner manifest.yaml deklariert sind. Wenn du keine Limits deklarierst, gelten vernünftige Standards:
| Ressource | Standard | Empfohlenes Maximum |
|---|---|---|
| Speicher | 128 MB | 1 GB |
| CPU | 0,5 Kerne | 2 Kerne |
| Timeout pro Aufruf | 30 s | 120 s |
| PIDs | 64 | 1024 |
| Offene Dateien (ulimit nofile) | 256 | 256 |
Bildverarbeitung für multimodale Capabilities
Abschnitt betitelt „Bildverarbeitung für multimodale Capabilities“Wenn deine Capability Bilder als Teil von Tool-Ergebnissen an das LLM sendet, wendet Selu vor dem Versand an den Provider einen gemeinsamen Normalisierungsschritt an.
So funktioniert es
Abschnitt betitelt „So funktioniert es“Bevor eine multimodale Nachricht an einen LLM-Provider gesendet wird, prüft Selu jedes eingebettete Bild gegen das Größenlimit des Providers:
- Wenn das Bild passt — wird es unverändert gesendet.
- Wenn das Bild zu groß ist — versucht Selu, es zu verkleinern und als JPEG neu zu komprimieren, um es unter das Limit zu bringen. Es werden mehrere Skalierungsfaktoren (bis zu 20 % der Originalgröße) und Qualitätsstufen (bis zu JPEG-Qualität 35) ausprobiert.
- Wenn es immer noch nicht passt — wird das Bild vollständig weggelassen und durch einen kurzen Texthinweis ersetzt, damit der Turn fortgesetzt werden kann, statt fehlzuschlagen.
Das Größenlimit hängt vom Provider ab:
- Amazon Bedrock: 5 MB pro Bild
- Andere Provider: 5 MB (gleicher Standardwert, aus Sicherheitsgründen angewendet)
Was das für deine Capability bedeutet
Abschnitt betitelt „Was das für deine Capability bedeutet“Du musst nichts Besonderes tun — die Normalisierung erfolgt automatisch. Beachte jedoch folgende Punkte:
- Bevorzuge angemessen große Bilder. Normalisierung kann die Qualität verschlechtern. Wenn dein Tool Bilder erzeugt, halte sie möglichst unter dem Limit.
- Verlasse dich nicht auf exakte Pixelabmessungen. Verkleinerte Bilder können vom Original abweichen. Wenn exakte Pixeldaten für nachgelagerte Tools wichtig sind, verwende stattdessen Artefakte.
- Weggelassene Bilder erzeugen einen Text-Platzhalter. Das LLM sieht dann einen Hinweis wie:
„Bild weggelassen: Datei ist zu groß für den Modell-Eingang (Limit 5 MB). Bitte den Nutzer, ein kleineres Bild hochzuladen."Deine Capability sollte den Fall berücksichtigen, dass ein von ihr bereitgestelltes Bild für das Modell nicht sichtbar ist.
Session-Isolation
Abschnitt betitelt „Session-Isolation“Selu unterstützt zwei Session-Modelle, die beeinflussen, wie deine Capability-Container verwaltet werden:
Geteilte Sessions (Standard)
Abschnitt betitelt „Geteilte Sessions (Standard)“- Mehrere Gesprächs-Threads teilen sich dieselben Capability-Container
- Container-Zustand und Arbeitsbereich-Daten bleiben threadübergreifend erhalten
- Ressourceneffizienter
- Gut für zustandslose Capabilities
Per-Thread-Isolation
Abschnitt betitelt „Per-Thread-Isolation“- Jeder Gesprächs-Thread erhält dedizierte Capability-Container
- Vollständige Arbeitsbereich-Isolation zwischen Threads
- Höhere Ressourcennutzung, verhindert aber threadübergreifende Interferenzen
- Unverzichtbar für zustandsbehaftete Capabilities wie Dateimanager oder Entwicklungsumgebungen
Agenten können Per-Thread-Isolation wählen, indem sie session.isolation: per_thread in ihrer agent.yaml setzen. Wenn dies aktiviert ist:
- Deine Capability erhält einen einzigartigen Container für jeden Gesprächs-Thread
- Arbeitsbereich-Daten werden nicht zwischen verschiedenen Nutzer-Gesprächen geteilt
- Der Container-Lebenszyklus ist an den spezifischen Thread gebunden, nicht an die Nutzer-Session
Image-Best-Practices
Abschnitt betitelt „Image-Best-Practices“- Verwende minimale Base-Images —
python:3.12-slim,golang:1.22-alpineoderdebian:bookworm-slim. Kleinere Images bedeuten schnellere Installationen für Nutzer. - Multi-Stage-Builds — Kompiliere in einem Build-Stage, kopiere nur das Binary/Runtime in das finale Stage.
- Pinne Versionen — Pinne immer Base-Image-Tags und Dependency-Versionen für Reproduzierbarkeit.
- Non-Root-Nutzer — Führe deinen Prozess als Non-Root-Nutzer aus. Selu wird Images, die als Root laufen, während der Marketplace-Überprüfung markieren.
Beispiel für ein Multi-Stage-Dockerfile:
FROM python:3.12-slim AS builderWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slimWORKDIR /appCOPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packagesCOPY . .USER 1000EXPOSE 50051CMD ["python", "server.py"]Sicherheit
Abschnitt betitelt „Sicherheit“- Keine Host-Mounts — Capabilities können keine Host-Verzeichnisse mounten. Aller Dateizugriff ist auf das ephemerale Dateisystem des Containers beschränkt.
- Kein privilegierter Modus — Container laufen unprivilegiert. Capabilities, die spezielle Linux-Capabilities benötigen, werden vom Marketplace abgelehnt.
- Zugangsdaten via Umgebungsvariablen — Verwende den
credentials-Abschnitt inmanifest.yaml, um erforderliche Geheimnisse zu deklarieren. Der Orchestrator injiziert sie beim Start aus dem verschlüsselten Credential-Store des Nutzers. Kodiere niemals Geheimnisse fest. - Orchestrator-injizierte Umgebungsvariablen — Zusätzlich zu Zugangsdaten erhält jeder Container die Umgebungsvariablen
SELU_SESSION_IDundSELU_CAPABILITY_ID, die die aktuelle Session und Capability-Instanz identifizieren. - Keine eingehenden Ports — Nur Port 50051 ist exponiert, und nur für das interne Docker-Netzwerk. Capabilities sind von außerhalb des Selu-Stacks nicht zugänglich.
Überlegungen zur Arbeitsbereich-Isolation
Abschnitt betitelt „Überlegungen zur Arbeitsbereich-Isolation“Beim Entwerfen von Capabilities für Agenten, die Per-Thread-Isolation verwenden, berücksichtige:
Dateisystem-Zustand
Abschnitt betitelt „Dateisystem-Zustand“- Verwende
/workspacefür thread-spezifische Dateien und Daten - Alle Container erhalten ein tmpfs-Mount bei
/tmp(64 MB,noexec) unabhängig von der Dateisystem-Richtlinie — nutze es nur für temporäre Scratch-Daten - Erwarte ein sauberes Dateisystem für jeden neuen Gesprächs-Thread
- Verlasse dich nicht darauf, dass Dateien über verschiedene Gespräche hinweg bestehen
Externer Zustand
Abschnitt betitelt „Externer Zustand“- Verwende externe APIs oder Datenbanken für Zustände, die threadübergreifend bestehen sollen
- Erwäge die Verwendung der eingebauten Speicher-Tools des Agenten für einfache Schlüssel-Wert-Persistierung
- Dokumentiere alle externen Abhängigkeiten klar in deinem Manifest
Ressourcen-Bereinigung
Abschnitt betitelt „Ressourcen-Bereinigung“- Implementiere ordnungsgemäße Bereinigung in deinen Capability-Shutdown-Handlern
- Lasse keine Hintergrundprozesse nach Tool-Aufrufen laufen
- Verwende angemessene Timeouts für langfristige Operationen
- Veraltete Container aus früheren Orchestrator-Läufen werden beim Start automatisch bereinigt — verlasse dich nicht darauf, dass dein Container einen Orchestrator-Neustart überlebt
Credential-Deklarationen
Abschnitt betitelt „Credential-Deklarationen“Deklariere alle Geheimnisse, die deine Capability benötigt, im credentials-Abschnitt von manifest.yaml. Nutzer werden während des Agenten-Setups aufgefordert, diese einzugeben:
credentials: - name: OPENWEATHER_API_KEY scope: system required: true description: > API-Schlüssel von OpenWeatherMap (https://openweathermap.org/api). Erstelle ein kostenloses Konto, um deinen Schlüssel zu erhalten. - name: CACHE_REDIS_URL scope: system required: false description: > Optionale Redis-URL zum Cachen von Wetterdaten. Falls nicht angegeben, werden Antworten jedes Mal frisch abgerufen.Der Orchestrator injiziert diese als Umgebungsvariablen beim Starten deines Containers. Füge immer hilfreiche Beschreibungen hinzu — sie werden Nutzern angezeigt, wenn sie den Agenten einrichten.
Credential-Scopes
Abschnitt betitelt „Credential-Scopes“system— Geteilt über alle Nutzer. Wird einmalig von einem Admin gesetzt.user— Persönlich für jeden Nutzer. Jede Person gibt ihre eigenen API-Schlüssel an.
Wähle system für organisationsweite API-Schlüssel und user für persönliche Konten.
Health Checks
Abschnitt betitelt „Health Checks“Implementiere das Healthcheck RPC aus capability.proto. Der Orchestrator ruft diese gRPC-Methode während des Container-Starts auf, um die Bereitschaft zu prüfen. Sobald deine Capability mit ready: true antwortet, gilt der Container als aktiv und der Orchestrator beginnt, Aufrufe an ihn weiterzuleiten.
Nach dem Start gibt es keine periodische Überwachung. Wenn eine Capability während eines Aufrufs fehlschlägt, wird der Fehler an den Agenten zurückgemeldet — der Orchestrator startet den Container nicht automatisch neu.
Dynamische vs. statische Tools
Abschnitt betitelt „Dynamische vs. statische Tools“Für Capabilities mit dynamischer Tool-Discovery hat dein Discovery-Tool Zugriff auf konfigurierte Zugangsdaten und kann je nach Verfügbarkeit unterschiedliche Tool-Sets zurückgeben:
def handle_discovery(self, credentials): tools = [ # Immer verfügbar { "name": "basic_search", "description": "Grundlegende Suchfunktionalität", "input_schema": {"type": "object", "properties": {"query": {"type": "string"}}}, "recommended_policy": "allow" } ]
# Zusätzliche Tools basierend auf Zugangsdaten if "PREMIUM_API_KEY" in credentials: tools.append({ "name": "premium_search", "description": "Erweiterte Suche mit Premium-Funktionen", "input_schema": {"type": "object", "properties": {"query": {"type": "string"}}}, "recommended_policy": "ask" })
if "ADMIN_TOKEN" in credentials: tools.append({ "name": "admin_functions", "description": "Administrative Operationen", "input_schema": {"type": "object", "properties": {"action": {"type": "string"}}}, "recommended_policy": "block" })
return toolsSo kann deine Capability ihre Funktionalität je nach konfigurierten Zugangsdaten anpassen.
Artefakt-Anhänge
Abschnitt betitelt „Artefakt-Anhänge“Capabilities können Dateien über das Artefakt-System mit Agenten austauschen. Das ermöglicht Tools, Dokumente zu verarbeiten, Dateien zu erzeugen und komplexe Daten zwischen Capability-Aufrufen innerhalb derselben Session weiterzugeben.
Artefakte empfangen
Abschnitt betitelt „Artefakte empfangen“Wenn ein Agent dein Tool mit Dateianhängen aufruft, lädt der Orchestrator sie automatisch in deinen Container, bevor dein Tool aufgerufen wird:
def handle_tool_call(self, tool_name, args, session_id): if tool_name == "process_document": attachments = args.get("attachments", [])
for attachment in attachments: filename = attachment["filename"] mime_type = attachment["mime_type"] size_bytes = attachment["size_bytes"] capability_artifact_id = attachment["capability_artifact_id"]
# Nutze die gRPC DownloadInputArtifact-Methode, um die Dateidaten zu holen file_data = self.download_artifact(capability_artifact_id)
result = process_file(file_data, mime_type)Artefakte erzeugen
Abschnitt betitelt „Artefakte erzeugen“Deine Tools können Dateien erzeugen und als Artefakte zurückgeben, damit andere Tools sie verwenden können:
def handle_tool_call(self, tool_name, args, session_id): if tool_name == "generate_report": pdf_data = create_pdf_report(args["report_type"])
artifact_id = self.upload_artifact("report.pdf", "application/pdf", pdf_data)
return { "message": "Bericht erfolgreich erstellt", "artifact": { "filename": "report.pdf", "mime_type": "application/pdf", "capability_artifact_id": artifact_id } }Artefakt-Lebenszyklus
Abschnitt betitelt „Artefakt-Lebenszyklus“- Artefakte sind auf die Nutzer-Session beschränkt und verfallen automatisch nach 6 Stunden
- Der Orchestrator übernimmt das gesamte Artefakt-ID-Management und die Übersetzung zwischen Capabilities
- Die Dateigröße ist auf 5 MB pro Artefakt begrenzt
- Gängige MIME-Typen werden unterstützt (Dokumente, Bilder, Textdateien usw.)
Nächste Schritte
Abschnitt betitelt „Nächste Schritte“Siehe die gRPC-Schnittstelle für den vollständigen Proto-Vertrag oder das manifest.yaml-Referenz für vollständige Schema-Details.