Alain Lilic

LiveStatische Seite mit KI-ChatDüsseldorf

Seniorenbetreuung Lala: Landingpage mit KI-Chat

Startseite von Seniorenbetreuung Lala in Düsseldorf am Desktop: Überschrift 'Zeit schenken. Herzen berühren.', zwei Schaltflächen, darunter eine für ein kostenloses Kennenlerngespräch, rechts das Herz-Logo, unten rechts die geschlossene Chat-Schaltfläche.
Startseite von Seniorenbetreuung Lala auf dem Smartphone: Logo, Überschrift, zwei Schaltflächen und die geschlossene Chat-Schaltfläche unten rechts.

Problem

Seniorenbetreuung Lala ist eine Alltagsbegleitung für Seniorinnen und Senioren in Düsseldorf. Die Inhaberin arbeitet allein, viele Anfragen kommen von Angehörigen. Gebraucht wurde eine ruhige, gut lesbare Seite und ein Chat, der Fragen zur Betreuung, zum Einzugsgebiet und zu den Kosten rund um die Uhr beantwortet. Er darf nur geprüfte Angaben nennen, keine Preise erfinden, keinen medizinischen Rat geben und muss klar als KI erkennbar sein.

Meine Rolle

Ich habe die Seite konzipiert, den Agenten konfiguriert und Domain, Hosting und Sicherheits-Header eingerichtet. Die Chat-Plattform dahinter ist meine eigene Sprach- und Chat-Agenten-Plattform. Texte und Angaben kamen von der Inhaberin. Ich habe spezifiziert, jede Änderung geprüft, mit Lighthouse und einem Chat-Testlauf vor dem Livegang getestet und entschieden, was live geht.

Vorgehen

Die Seite ist statisches HTML ohne Build-Schritt, mit selbst gehosteten, auf die genutzten Zeichen reduzierten Schriften und GSAP für die Animationen. Ohne JavaScript oder bei reduzierter Bewegung ist alles sofort sichtbar. Vercel liefert sie mit strengen Headern aus: die Content Security Policy erlaubt Scripts und Verbindungen nur von der eigenen Domain, die zwei Inline-Scripts laufen über SHA-256-Hashes. Das Chat-Widget lädt erst im Leerlauf nach dem Seitenaufbau, ein Platzhalter merkt sich einen frühen Klick auf einen Chat-Knopf.

Der Chat läuft auf meiner Sprach- und Chat-Agenten-Plattform. Ein Rewrite in vercel.json reicht /api/chat/* serverseitig weiter, der Browser spricht nur mit der Domain der Seite. Der Agent ist reine Konfiguration: eine Persona mit Leitplanken (Angaben zu Betreuung und Kosten nur aus der Wissensbasis, keine erfundenen Zusagen, kein medizinischer Rat, im Notfall die 112), eine Wissenssuche über eine Markdown-Datei, Prompt-Regeln gegen Termin- und Rückrufzusagen und ein Ausgabe-Filter, der trotzdem erzeugte Buchungszusagen ersetzt, weil der Chat nichts weiterleitet. Die erste Antwort trägt den KI-Hinweis. Seit 26.09.2026 nutzt der Chat ein kostenloses Modell über OpenRouter mit einer Liste von Ausweichmodellen gegen Überlastung (HTTP 429), danach Groq als zweiten Anbieter.

Architektur

  1. Besucherin auf seniorenbetreuung-lala.de

    Statische Seite auf Vercel mit GSAP. chat.js (9 KB) lädt erst im Leerlauf.

  2. Vercel-Rewrite /api/chat/*

    Gleiche Domain, CSP mit connect-src 'self'. Die Plattform ist serverseitig angebunden.

  3. Web-Chat-API (FastAPI)

    Origin-Allowlist, 20 Nachrichten pro Minute und IP, 500 pro Tag und Agent, höchstens 400 Tokens pro Antwort.

  4. Prompt-Aufbau

    Persona mit Leitplanken, Chat-Regeln und Treffer aus der Wissensbasis (LanceDB, top_k 4).

  5. Sprachmodell

    OpenRouter mit einer Liste kostenloser Modelle gegen HTTP 429, danach Groq als anderer Anbieter.

  6. Ausgabe

    Filter gegen Buchungszusagen, KI-Hinweis vor der ersten Antwort, Chatverläufe werden nach 30 Tagen gelöscht.

Entscheidung

Gleiche Domain per Rewrite statt fremdem Script

Ich hätte das Widget direkt von der Plattform-Domain laden können. Stattdessen liegt eine eingefrorene, neutrale Kopie (9 KB) im Repo der Seite, und vercel.json reicht /api/chat/* serverseitig weiter. Dadurch reicht 'self' in script-src und connect-src, es gibt kein CORS, und im Quelltext und im Netzwerk-Tab steht nur die eigene Domain der Seite. Der Preis: Änderungen am Plattform-Widget wandern nicht mehr automatisch mit. Und hinter dem Rewrite kommen GET-Anfragen ohne Origin-Header an. Deshalb lässt der Server einen fehlenden Origin nur bei den lesenden GET-Routen durch, nie bei Chat-Nachrichten, die ein Sprachmodell aufrufen.

Code-Auszug

agents/seniorenbetreuung-lala/agent.yaml (gekürzt)Seitlich scrollenyaml
telefon: false
persona: |
  [...]
  Fragen zu [...] Einzugsgebiet und Kosten beantwortest du
  ausschließlich mit der Wissenssuche; du erfindest niemals Preise oder
  Zusagen. [...]
  Ganz wichtig: Seniorenbetreuung Lala leistet KEINE medizinische oder
  pflegerische Versorgung und du gibst niemals medizinischen Rat. Wird
  akute Hilfe benötigt, verweise ruhig und deutlich auf den Notruf 112.
llm:
  provider: openrouter
  # Gratis-Modelle sind oft kurz überlastet (HTTP 429): 'models' lässt
  # OpenRouter der Reihe nach auf die nächsten freien Modelle ausweichen
  model: nvidia/nemotron-3-ultra-550b-a55b:free
  options:
    extra:
      extra_body:
        models:
          - nvidia/nemotron-3-ultra-550b-a55b:free
          - google/gemma-4-31b-it:free
          - qwen/qwen3.8-27b:free
llm_fallback:  # anderer Dienst als der Primary
  provider: groq
  model: openai/gpt-oss-120b
Der Agent ist Konfiguration statt Code: Leitplanken in der Persona, kostenlose Modelle mit Ausweichliste, Groq als zweiter Anbieter.

Ergebnisse

  • 94Lighthouse Performance mobil, lokal gemessen (Desktop 100), 22.08.2026
  • 100Lighthouse Accessibility und Best Practices gegen die Live-URL, 22.08.2026
  • 57 KBSchriften nach dem Subsetting, vorher 126 KB
  • 9 KBChat-Widget, lädt erst im Leerlauf nach dem Seitenaufbau
0
CSP-Verstöße beim Durchklicken inklusive Chat (lokaler Nachbau der Auslieferung)
30
Tage Löschfrist für Chatverläufe, auf dem Server aktiv seit 22.08.2026

Was ich daraus gelernt habe

Seitentext und Wissensbasis des Chats pflege ich getrennt, jede Änderung an der Seite muss ich in der Wissensbasis von Hand nachziehen. Heute würde ich beide aus einer Quelle speisen oder die Seite selbst indexieren. Ein Satz auf der Seite versprach außerdem eine persönliche Antwort, obwohl eine KI antwortet. Das habe ich am 26.09.2026 korrigiert, seitdem prüfe ich Seitentexte gegen das, was das System wirklich tut. Die CSP-Hashes ziehe ich noch von Hand nach, wenn sich ein Inline-Script ändert. Heute würde ich sie in einem kleinen Build-Schritt erzeugen.