OctopOS — Entwickler-Guide
Wie man OctopOS erweitert: neue Tools, Skills, Console-Seiten, Installer-Module.
1. Entwicklungsumgebung
Voraussetzungen
- Python 3.12+
- Node.js 22+
- Laufende OctopOS-VM für Integrationstests
Console — lokale Entwicklung
cd console
npm install
npm run dev # Vite Dev-Server auf Port 5173
API-Calls gehen an /api/.... Dafür wird entweder ein lokaler Core (Port 8765) oder ein Proxy in der Vite-Konfiguration benötigt:
// vite.config.ts
export default defineConfig({
plugins: [react()],
server: { proxy: { "/api": "http://192.168.178.181" } }
})
Core — lokale Entwicklung
cd core
python3 -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
uvicorn octopos_core.main:app --reload --port 8765
Mit --reload startet der Server bei Dateiänderungen automatisch neu.
2. Eigenes Tool schreiben
Tools sind Python-Klassen die von BaseTool erben. Sie werden im tool_registry registriert und können dann in agent.yaml aktiviert werden.
Minimales Beispiel
# core/src/octopos_core/tools/my_tool.py
from ..tool_registry import BaseTool
class MyTool(BaseTool):
@property
def id(self) -> str: return "my_tool"
@property
def description(self) -> str: return "Beschreibung für das LLM"
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"input": {"type": "string", "description": "Die Eingabe"}
},
"required": ["input"]
}
async def execute(self, agent_id, project_id, input, **kwargs) -> str:
return f"Verarbeitet: {input}"
Filesystem-Tool mit Pfad-Sicherheit
Für Tools die auf das Projektverzeichnis zugreifen, muss assert_path_within_project verwendet werden — andernfalls können Agenten das Dateisystem verlassen.
from ..tool_registry import BaseTool, assert_path_within_project
class MyFileTool(BaseTool):
async def execute(self, agent_id, project_id, path, **kwargs) -> str:
safe_path = assert_path_within_project(path, project_id)
if not safe_path.exists():
return f"Datei nicht gefunden: {path}"
return safe_path.read_text(encoding="utf-8")
Tool registrieren
In tool_registry.py am Ende der Registry-Initialisierung eintragen:
registry.register(MyTool())
Tool im Agenten aktivieren
In agent.yaml des gewünschten Agenten:
tools:
- my_tool
3. Eigene Skills schreiben
Skills sind Markdown-Dateien mit YAML-Frontmatter. Sie liegen unter /agents/<id>/skills/ und werden automatisch in den System-Prompt des Agenten geladen.
Skill-Datei
---
skill: Steuerrecht Grundlagen
version: "1.0"
scope: on-demand
triggers:
- steuer
- finanzamt
- umsatzsteuer
priority: 10
---
## Steuerrecht Grundlagen
Umsatzsteuer (USt) beträgt in Deutschland standardmäßig 19%.
Ermäßigter Satz: 7% für Lebensmittel, Bücher, ÖPNV.
Scope-Optionen
| Scope | Verhalten |
|---|---|
always | Immer in jedem Request geladen, unabhängig vom Inhalt der Nachricht |
on-demand | Nur wenn ein Trigger-Keyword im User-Text vorkommt |
Skill über die Konsole anlegen
Agenten → Agent auswählen → Buch-Icon → Neuer Skill
4. Core-Endpoint hinzufügen
Alle Endpoints sind in main.py definiert. Bei wachsender Größe (über ~1500 Zeilen) sollte in separate Router-Module aufgeteilt werden.
Beispiel-Endpoint
from pydantic import BaseModel
from .auth import require_auth
from .audit import audit_log
class MyRequest(BaseModel):
name: str
value: str
@app.post("/my-endpoint")
async def my_endpoint(
req: MyRequest,
current_user: str = Depends(require_auth)
):
# ... Logik ...
await audit_log(
user=current_user,
action="my_resource.create",
target=req.name
)
return {"created": True, "name": req.name}
audit_log-Signatur
await audit_log(
user=current_user, # Benutzername
action="...", # z.B. "agent.create"
target="...", # Betroffenes Objekt (optional)
project_id="...", # Projekt-ID (optional)
details={} # Zusätzliche Infos (optional)
)
Depends(require_auth) für geschützte Endpoints. Für öffentliche Endpoints (z.B. /health, /hooks/*) kein Depends angeben.5. Console-Seite hinzufügen
Neue Seiten bestehen aus 4 Schritten: Komponente, API-Methode, Route und Navigation.
-
1
Page-Komponente erstellen unter
console/src/pages/MyPage.tsximport { useEffect, useState } from "react" import { api } from "@/lib/api" export default function MyPage() { const [data, setData] = useState(null) useEffect(() => { api.getMyData().then(setData) }, []) return ( <div className="p-6"> <h1 className="text-xl font-semibold mb-4">Meine Seite</h1> {/* Inhalt */} </div> ) } -
2
API-Methode hinzufügen in
console/src/lib/api.tsexport interface MyData { id: string name: string } // Im api-Objekt: getMyData: (): Promise<MyData[]> => fetchJSON("/api/my-endpoint"), -
3
Route registrieren in
console/src/App.tsximport MyPage from "./pages/MyPage" // In der Router-Konfiguration: <Route path="/my-page" element={<MyPage />} /> -
4
Nav-Eintrag hinzufügen in
console/src/layouts/AdminLayout.tsximport { MyIcon } from "lucide-react" // In der navItems-Liste: { href: "/my-page", label: "Meine Seite", icon: MyIcon },
Styling-Konventionen
- Tailwind + shadcn-Farben — kein direktes CSS
- Container:
bg-card border rounded-lg - Standard-Text:
text-sm - Sekundärtext:
text-muted-foreground - Fehler:
text-destructive - Erfolg:
text-green-500 - Icons ausschließlich aus
lucide-react
6. Installer-Modul hinzufügen
Installer-Module sind eigenständige Bash-Skripte die von install.sh per source eingebunden werden. Jedes Modul muss idempotent sein.
Modul-Template
#!/usr/bin/env bash
# installer/modules/my_module.sh
install_my_component() {
info "Prüfe my-component..."
if command -v my-tool >/dev/null 2>&1; then
info "my-component bereits installiert — übersprungen"
return 0
fi
info "Installiere my-component..."
apt-get install -y my-package || {
error "Installation von my-component fehlgeschlagen"
return 1
}
success "my-component installiert"
}
install_my_component
In install.sh einbinden
source "$SCRIPT_DIR/modules/my_module.sh"
Regeln für Module
- Idempotent: Mehrfaches Ausführen muss sicher sein
- Ausgabe:
info/success/warn/errorverwenden — kein direktesecho - Ausführbar:
chmod +x installer/modules/my_module.shnicht vergessen - Fehlerbehandlung: Bei kritischen Fehlern
return 1oderexit 1
7. Deploy-Workflow
Core — schnelles Deployment (einzelne Datei)
# Einzelne Datei auf die VM kopieren und Service neu starten
scp core/src/octopos_core/main.py octopos@192.168.178.181:/tmp/main.py
ssh octopos@192.168.178.181 "sudo cp /tmp/main.py /opt/octopos/core/src/octopos_core/main.py && sudo systemctl restart octopos-core"
Core — vollständiges Verzeichnis
scp -r core/src/octopos_core octopos@192.168.178.181:/tmp/octopos_core
ssh octopos@192.168.178.181 "sudo cp -r /tmp/octopos_core /opt/octopos/core/src/ && sudo systemctl restart octopos-core"
Console — Deploy
# Build erstellen
cd console && npm run build
# Dist auf die VM kopieren
scp -r dist/ octopos@192.168.178.181:/tmp/console_dist/
# Auf der VM installieren
ssh octopos@192.168.178.181 "sudo cp -r /tmp/console_dist/. /var/www/octopos/"
Status prüfen
ssh octopos@192.168.178.181 \
"systemctl is-active octopos-core && journalctl -u octopos-core -n 20 --no-pager"
Git-Workflow
git pull --rebase vor einem Push. Commits sollten Feature + Console + Core zusammen bündeln wenn möglich.git pull --rebase origin main
git add core/... console/...
git commit -m "feat: mein neues Feature"
git push
8. Coding-Konventionen
Python (Core)
- Keine neuen Abhängigkeiten ohne Diskussion — Standardbibliothek bevorzugen
- Pydantic für alle Konfigurationen und Request-Bodies
async/awaitfür alle I/O-Operationenloggingstattprint— niemalsprint()in Produktionscode- Exceptions sauber fangen und mit sinnvoller HTTP-Fehlermeldung antworten
- Pfad-Sicherheit: Immer
assert_path_within_projectfür Filesystem-Zugriffe - Audit-Log: Alle schreibenden Operationen mit
audit_logprotokollieren
TypeScript (Console)
- Kein
any— außer in Legacy-Code mit explizitem Kommentar - Interfaces für alle API-Responses in
api.tsdefinieren - Kein direktes
fetch()— immerapi.*-Methoden verwenden useEffect-Cleanups für Intervalle und Subscriptions nicht vergessen- Stabile Keys in Listen — niemals Array-Index als Key
Allgemein
- Installer-Module müssen idempotent sein
- Keine hardcodierten Pfade — Konfiguration oder Konstanten verwenden
- Commits bündeln Feature + Console + Core wenn möglich
Dateigrößen
routers/agents.py, routers/projects.py etc.) um die Wartbarkeit zu erhalten.