Neuer Endpunkt ai-backend/explain.php mit eigenem statischen Systemprompt (Begründung des Phasenaufbaus, mögliche Stolpersteine, Differenzierungsideen). Bewusst als separater Endpunkt statt Zusatzfeld in jeder plan.php-Antwort, damit die Erklärung nur bei tatsächlicher Nutzung abgerechnet wird statt bei jeder Planungsanfrage mitgeneriert zu werden. Die Guthaben-Abrechnung (SELECT-FOR-UPDATE, Transaktions-Insert) wurde aus plan.php nach ai_backend_call_and_charge in db.php ausgelagert, damit sie nicht an zwei Stellen gepflegt werden muss. Kein neues DB-Schema nötig. Im AiAssistDialog erscheint je Stunde ein Button "Didaktischen Hintergrund erklären", der nach dem Laden durch den Text ersetzt wird. 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).
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.)