Lesson bekommt dieselbe Anhang-Infrastruktur wie Documentation (Material, Arbeitsblätter, Experimentunterlagen), samt Fix einer Sync-Lücke, die Anhang- Dateibytes bisher nur für Documentation statt generisch übertragen hat (IHasAttachments). Sitzplan-Tab bekommt einen "Plätze mischen"-Button für Klausursitzpläne. Neu: mehrschrittiger Gefährdungsbeurteilungs-Assistent mit optionalem KI-Entwurf (ai-backend/gbu.php) und PDF-Export, Format bewusst als JSON-Anhang statt eigener Datenbank-Entität. Details und Architekturentscheidungen in TODO.md (4.2, 7.1.5, 10.1.8). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
KI-Backend (TODO 4.5.9)
Kleines PHP-Zwischenelement, das die KI-gestützte Planungsunterstützung des Desktop-Clients absichert: der LLM-API-Key liegt nur hier auf dem Server, der Desktop-Client bekommt nur ein eigenes Bearer-Token gegen dieses Backend und ein pro Nutzer geführtes Guthaben. Deployment macht der Nutzer selbst — dieses README beschreibt die nötigen Schritte.
Voraussetzungen
- PHP 8.1 oder neuer (nutzt
never-Rückgabetypen,match, First-Class-Callable-Syntax nicht, aber typisierte Properties/Enums nicht zwingend — 8.1 ist die sichere Untergrenze). - PHP-Erweiterungen:
pdo_mysql,curl,json(bei den meisten Hosting-Paketen bereits dabei). - Eine MySQL- oder MariaDB-Datenbank.
- Ein API-Key für Anthropic (
https://console.anthropic.com).
Einrichtung
-
Datenbank anlegen und
schema.sqlimportieren:mysql -u <user> -p <datenbankname> < schema.sql -
config.example.phpnachconfig.phpkopieren und ausfüllen (DB-Zugang, Anthropic-API-Key, ggf. die Preistabelle gegen die aktuelle Anthropic-Preisseite prüfen — Preise ändern sich).config.phpist in.gitignoreund darf nie committet werden. -
Falls du die Web-Variante aus Schritt 5 nutzen willst (kein PHP-CLI/SSH auf dem Hosting):
setup_secretinconfig.phpauf einen langen Zufallswert setzen (z.B. peropenssl rand -hex 32erzeugt, oder ein beliebiger Passwortgenerator). -
Den kompletten
ai-backend/-Ordner auf den PHP-Server hochladen. Empfehlung:config.php,db.php,schema.sql,providers/undscripts/außerhalb des öffentlichen Webroots ablegen, falls das Hosting das erlaubt (Pfade in denrequire-Aufrufen entsprechend anpassen) — robuster als sich allein auf die mitgelieferte.htaccesszu verlassen, die nur bei Apache mit aktiviertemmod_rewrite/erlaubten.htaccess-Overrides greift. -
Ersten Nutzer anlegen (weitere Lehrer später genauso) — zwei Varianten, je nachdem was das Hosting hergibt:
Mit PHP-CLI/SSH-Zugriff (
scripts/create-user.php):php scripts/create-user.php sebastian "einStarkesPasswort" 10.00Der dritte Parameter ist das Startguthaben in USD, optional (Standard 0).
Ohne CLI/SSH (übliches Shared Hosting, nur FTP/Webspace) — der Server läuft nach Schritt 4 bereits, also einmalig von deinem eigenen Rechner aus aufrufen:
curl -X POST https://DEINE-DOMAIN/setup-user.php \ -d '{"secret":"<setup_secret aus config.php>","username":"sebastian","password":"einStarkesPasswort","balanceUsd":10.00}'Direkt danach
setup-user.phpper FTP wieder vom Server löschen — sonst bleibt ein Endpunkt erreichbar, der (mit dem Geheimnis) beliebige Nutzer anlegen kann. Ein erneuter Aufruf mit demselben Benutzernamen aktualisiert Passwort/Guthaben, falls z.B. das Guthaben beim ersten Versuch vergessen wurde — dafür braucht es die Datei aber kein zweites Mal auf dem Server, einfach vor dem Löschen alle nötigen Nutzer in einer Sitzung anlegen. -
In
LehrerApp.Desktop/AppBootstrapper.csdie KonstanteAiBackendUrlauf die tatsächlich deployte Domain setzen und die App neu bauen. -
In der App unter Einstellungen → KI-Unterstützung aktivieren und mit dem angelegten Nutzer anmelden.
Update für bereits deployte Installationen (Prompt-Caching-Nachtrag)
Falls ai-backend/ schon einmal deployt wurde (schema.sql lief bereits, config.php existiert
schon): zwei Schritte nötig, damit Abrechnung und plan.php mit dem neuen Prompt Caching
funktionieren.
- Migration einmalig einspielen (ergänzt zwei neue, nicht-destruktive Spalten):
mysql -u <user> -p <datenbankname> < migrations/2026-08-add-cache-tokens.sql - In der bestehenden
config.phpbei jedem Preistabellen-Eintragcache_write/cache_readergänzen (sieheconfig.example.phpfür aktuelle Richtwerte) — ohne diese Ergänzung wird der Cache-Anteil einfach mit 0 USD abgerechnet (kein Fehler, aber auch keine Kostenersparnis).
Danach alle geänderten Dateien (plan.php, db.php, providers/, .htaccess, falls noch nicht
aktuell) erneut hochladen.
Update für bereits deployte Installationen ("Schattenfeld"-Nachtrag, explain.php)
Kein neues DB-Schema nötig (nutzt dieselben users/tokens/transactions-Tabellen wie plan.php).
Einfach die neue Datei explain.php sowie die aktualisierten db.php und plan.php hochladen
(die Abrechnungslogik wurde aus plan.php in eine gemeinsame Funktion ai_backend_call_and_charge
in db.php verschoben, damit explain.php sie mitverwenden kann, ohne sie zu duplizieren).
Update für bereits deployte Installationen (Gefährdungsbeurteilungs-Entwurf, gbu.php)
Kein neues DB-Schema nötig (nutzt dieselben users/tokens/transactions-Tabellen wie
plan.php/explain.php). Einfach die neue Datei gbu.php hochladen.
Prompt Caching
Der Systemprompt in plan.php ist vollständig statisch (identisch bei jeder Anfrage, jedes
Nutzers). AnthropicProvider.php markiert ihn deshalb als cacheable — wiederholte Anfragen
innerhalb der Anthropic-Cache-TTL (Standard: 5 Minuten) zahlen für diesen Anteil nur den stark
reduzierten "Cache-Read"-Preis statt des vollen Input-Preises; der erste Aufruf nach TTL-Ablauf
zahlt einen kleinen Aufpreis fürs Neuschreiben des Caches. Das passiert automatisch, ohne
Zutun — nichts an der Bedienung der App ändert sich dadurch. Beobachtbar ist der Effekt in der
transactions-Tabelle (cache_creation_input_tokens vs. cache_read_input_tokens).
Smoke-Test ohne echten API-Key
plan.php liest die Umgebungsvariable AI_BACKEND_FAKE_PROVIDER — bei 1 wird statt eines
echten Anthropic-Aufrufs providers/FakeProvider.php verwendet (liefert eine feste,
schema-valide Test-Antwort). Damit lassen sich Login, Guthabenprüfung und die 402-Pfade lokal
prüfen, ohne echte Kosten zu verursachen:
AI_BACKEND_FAKE_PROVIDER=1 php -S localhost:8000 -t .
Dann z.B.:
curl -X POST http://localhost:8000/login.php \
-d '{"username":"sebastian","password":"einStarkesPasswort"}'
curl http://localhost:8000/status.php -H "Authorization: Bearer <token aus login.php>"
Wichtig: AI_BACKEND_FAKE_PROVIDER niemals auf dem produktiven Server setzen — sonst bekommt
die App nur die feste Test-Antwort statt echter KI-Vorschläge.
Fehlerbehebung: "Anmeldung abgelaufen" direkt nach dem Anmelden
Login funktioniert, aber jede Anfrage danach (Guthaben, KI-Anfrage) meldet sofort eine
abgelaufene/ungültige Anmeldung? Typische Ursache: viele Apache/PHP-FPM-Hosting-Setups reichen
den Authorization-Header standardmäßig gar nicht erst an PHP durch — login.php braucht ihn
nicht, aber status.php/plan.php schon, und der kommt dort leer an.
Dagegen sind bereits zwei Absicherungen eingebaut:
.htaccesserzwingt die Weiterleitung des Headers (RewriteRule .* - [E=HTTP_AUTHORIZATION:%1]).db.phpliest zusätzlichREDIRECT_HTTP_AUTHORIZATIONundgetallheaders()als Fallback.
Falls es trotzdem weiter auftritt: .htaccess-Overrides sind auf manchem Hosting serverseitig
deaktiviert (AllowOverride None), oder es läuft nginx statt Apache (dort gilt .htaccess
grundsätzlich nicht) — dann hilft nur eine serverseitige Konfiguration durch den Hoster/Support
(z.B. bei nginx ein fastcgi_param HTTP_AUTHORIZATION $http_authorization;).
Was hiermit NICHT geprüft ist
- Ob Anthropic zuverlässig valides JSON im erwarteten Schema liefert (reine Prompt-Qualitätsfrage, nur mit dem echten API-Key zu beurteilen).
- Ob die berechneten Kosten exakt mit der tatsächlichen Anthropic-Abrechnung übereinstimmen.
- TLS/
.htaccess-Wirksamkeit und PHP-Version/Erweiterungen auf dem tatsächlichen Hosting. - Die komplette Kette Desktop → dieses Backend → Anthropic unter echten Netzwerkbedingungen.
- Ob Prompt Caching tatsächlich greift (
cache_read_input_tokens> 0 bei einer zweiten Anfrage innerhalb von 5 Minuten) — derFakeProvidersimuliert kein Caching, das lässt sich nur gegen die echte Anthropic-API beobachten (z.B. per Blick in dietransactions-Tabelle).
Guthaben aufladen
Für den aktuellen Umfang (ein bis wenige Nutzer) reicht ein manueller SQL-Befehl:
UPDATE users SET balance_usd = balance_usd + 10.00 WHERE username = 'sebastian';
INSERT INTO transactions (user_id, type, cost_usd, balance_after)
SELECT id, 'topup', -10.00, balance_usd FROM users WHERE username = 'sebastian';
(Kein Admin-UI in dieser Ausbaustufe — bei Bedarf später ergänzbar.)