# 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 1. Datenbank anlegen und `schema.sql` importieren: ```bash mysql -u -p < schema.sql ``` 2. `config.example.php` nach `config.php` kopieren und ausfüllen (DB-Zugang, Anthropic-API-Key, ggf. die Preistabelle gegen die aktuelle Anthropic-Preisseite prüfen — Preise ändern sich). `config.php` ist in `.gitignore` und darf nie committet werden. 3. Falls du die Web-Variante aus Schritt 5 nutzen willst (kein PHP-CLI/SSH auf dem Hosting): `setup_secret` in `config.php` auf einen langen Zufallswert setzen (z.B. per `openssl rand -hex 32` erzeugt, oder ein beliebiger Passwortgenerator). 4. Den kompletten `ai-backend/`-Ordner auf den PHP-Server hochladen. **Empfehlung:** `config.php`, `db.php`, `schema.sql`, `providers/` und `scripts/` außerhalb des öffentlichen Webroots ablegen, falls das Hosting das erlaubt (Pfade in den `require`-Aufrufen entsprechend anpassen) — robuster als sich allein auf die mitgelieferte `.htaccess` zu verlassen, die nur bei Apache mit aktiviertem `mod_rewrite`/erlaubten `.htaccess`-Overrides greift. 5. 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`): ```bash php scripts/create-user.php sebastian "einStarkesPasswort" 10.00 ``` Der 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: ```bash curl -X POST https://DEINE-DOMAIN/setup-user.php \ -d '{"secret":"","username":"sebastian","password":"einStarkesPasswort","balanceUsd":10.00}' ``` **Direkt danach `setup-user.php` per 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. 6. In `LehrerApp.Desktop/AppBootstrapper.cs` die Konstante `AiBackendUrl` auf die tatsächlich deployte Domain setzen und die App neu bauen. 7. 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. 1. Migration einmalig einspielen (ergänzt zwei neue, nicht-destruktive Spalten): ```bash mysql -u -p < migrations/2026-08-add-cache-tokens.sql ``` 2. In der bestehenden `config.php` bei jedem Preistabellen-Eintrag `cache_write`/`cache_read` ergänzen (siehe `config.example.php` fü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: ```bash AI_BACKEND_FAKE_PROVIDER=1 php -S localhost:8000 -t . ``` Dann z.B.: ```bash curl -X POST http://localhost:8000/login.php \ -d '{"username":"sebastian","password":"einStarkesPasswort"}' curl http://localhost:8000/status.php -H "Authorization: Bearer " ``` **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: - `.htaccess` erzwingt die Weiterleitung des Headers (`RewriteRule .* - [E=HTTP_AUTHORIZATION:%1]`). - `db.php` liest zusätzlich `REDIRECT_HTTP_AUTHORIZATION` und `getallheaders()` 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) — der `FakeProvider` simuliert kein Caching, das lässt sich nur gegen die echte Anthropic-API beobachten (z.B. per Blick in die `transactions`-Tabelle). ## Guthaben aufladen Für den aktuellen Umfang (ein bis wenige Nutzer) reicht ein manueller SQL-Befehl: ```sql 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.)