diff --git a/.gitignore b/.gitignore index 7c6c43f..5812bd3 100644 --- a/.gitignore +++ b/.gitignore @@ -97,7 +97,7 @@ ipython_config.py # Docu Coderr_Checklist.md -Coderr_CLAUDE.md +CLAUDE.md # pyenv # For a library or package, you might want to ignore these files since the code is diff --git a/assistant_app/knowledge/infrastruktur.md b/assistant_app/knowledge/infrastruktur.md index 49a8499..aa12a11 100644 --- a/assistant_app/knowledge/infrastruktur.md +++ b/assistant_app/knowledge/infrastruktur.md @@ -52,7 +52,7 @@ die noch nie zurückgespielt wurde, ist keine Sicherung, sondern eine Annahme. ## Wie neue Versionen live gehen -Das Ausrollen passiert nicht von Hand, sondern über eine Pipeline in GitHub +Benjamin deployt nicht von Hand, sondern über eine CI/CD-Pipeline in GitHub Actions. Bei jeder Änderung laufen zuerst die Prüfungen: Codeformatierung, statische Analyse, automatische Tests, ein vollständiger Build. Erst wenn alles davon durchläuft und die Änderung im Hauptzweig landet, wird ausgerollt. diff --git a/assistant_app/knowledge/projekt-bestellapp.md b/assistant_app/knowledge/projekt-bestellapp.md new file mode 100644 index 0000000..480616f --- /dev/null +++ b/assistant_app/knowledge/projekt-bestellapp.md @@ -0,0 +1,38 @@ +--- +titel: BestellApp +quelle: Projekt BestellApp +--- + +## Was die BestellApp ist + +Die BestellApp ist eine Bestelloberfläche nach dem Vorbild von Lieferdiensten +wie Lieferando, gebaut in reinem JavaScript mit HTML und CSS, ohne Framework. +Die Speisekarte einer Pizzeria mit Pizzen, Desserts und Getränken wird aus +einem JavaScript-Datenobjekt erzeugt. + +Gerichte lassen sich in den Warenkorb legen, in der Menge ändern und wieder +entfernen. Zwischensumme, Lieferkosten und Gesamtsumme werden bei jeder +Änderung neu berechnet. Auf großen Bildschirmen steht der Warenkorb neben der +Speisekarte, auf dem Smartphone öffnet er sich als eigenes Dialogfenster. Nach +dem Bestellen erscheint eine Bestätigung, und der Warenkorb wird geleert. +Bestellt wird dabei nichts, es gibt kein Backend. + +Die BestellApp ist eines der ersten Projekte, die Benjamin an der Developer +Akademie gebaut hat. Sie ist unter benjaminblarr.de/bestellapp erreichbar, der +Quelltext liegt öffentlich auf GitHub. + +## Was Benjamin an der BestellApp über Frameworks gelernt hat + +Der Zustand der BestellApp liegt in einfachen Variablen und Objekten. Jede +Änderung muss von Hand ins DOM zurückgeschrieben werden, in der richtigen +Reihenfolge und an jeder Stelle, an der derselbe Wert auftaucht. + +Genau das ist hier die Schwierigkeit: Den Warenkorb gibt es zweimal, einmal +neben der Speisekarte für große Bildschirme und einmal im Dialog für das +Smartphone. Jeder Zähler, jeder Einzelpreis und jede Summe muss an beiden +Stellen nachgeführt werden. Wird eine Stelle vergessen, zeigen die beiden +Warenkörbe unterschiedliche Beträge. + +Diesen Teil nimmt einem ein Framework wie Angular ab: Man ändert die Daten, und +die Oberfläche folgt von selbst. Weil Benjamin es vorher von Hand gemacht hat, +weiß er, welches Problem Datenbindung eigentlich löst. diff --git a/assistant_app/knowledge/projekt-book-store.md b/assistant_app/knowledge/projekt-book-store.md new file mode 100644 index 0000000..b8b7620 --- /dev/null +++ b/assistant_app/knowledge/projekt-book-store.md @@ -0,0 +1,36 @@ +--- +titel: Book-Store +quelle: Projekt Book-Store +--- + +## Was der Book-Store ist + +Der Book-Store ist ein Buchkatalog mit Likes und Kommentaren, gebaut in reinem +JavaScript mit HTML und CSS, ohne Framework. Er zeigt neun Bücher mit Titel, +Autor, Erscheinungsjahr, Genre und Preis. Jedes Buch lässt sich liken und +kommentieren. + +Likes und Kommentare bleiben nach dem Neuladen erhalten, weil sie im +LocalStorage des Browsers gespeichert werden. Einen Server gibt es nicht. Jeder +Besucher sieht deshalb nur seine eigenen Likes und Kommentare. + +Der Book-Store ist das erste Projekt, das Benjamin an der Developer Akademie +gebaut hat. Er ist unter benjaminblarr.de/bookstore erreichbar, der Quelltext +liegt öffentlich auf GitHub. + +## Wie der Book-Store Daten im Browser speichert + +Der LocalStorage speichert nur Text. Der Book-Store wandelt deshalb nach jedem +Like und jedem Kommentar die gesamte Bücherliste in JSON um und legt sie unter +einem festen Schlüssel ab. Beim Laden der Seite wird sie wieder eingelesen und +ersetzt die Daten aus dem Quelltext. Ein Kommentar mit leerem Namen oder leerem +Text wird gar nicht erst gespeichert. + +Die Daten liegen damit an zwei Orten: im Quelltext und im Browser. Das +funktioniert, solange sich die Bücherliste im Quelltext nicht ändert. Käme ein +neues Buch dazu, sähe ein Besucher mit gespeichertem Stand es nicht, weil sein +alter Stand den neuen überschreibt. + +An diesem kleinen Projekt hat Benjamin gelernt, was es heißt, Zustand dauerhaft +zu speichern. Es ist dieselbe Frage, die in größeren Anwendungen eine Datenbank +beantwortet: Welcher Stand gilt, wenn es zwei gibt? diff --git a/assistant_app/knowledge/projekt-portfolio.md b/assistant_app/knowledge/projekt-portfolio.md new file mode 100644 index 0000000..de0a3a3 --- /dev/null +++ b/assistant_app/knowledge/projekt-portfolio.md @@ -0,0 +1,88 @@ +--- +titel: Portfolio +quelle: Projekt Portfolio +--- + +## Was das Portfolio ist + +Das Portfolio unter benjaminblarr.de ist Benjamins persönliche Website, eine +Single-Page-Anwendung mit Angular und TypeScript. Sie stellt ihn vor, zeigt +seine Technologien, eine Auswahl seiner Projekte mit Links zu den laufenden +Anwendungen, Stimmen von Teampartnern und ein Kontaktformular. + +Die Seite ist zweisprachig, Deutsch und Englisch, und passt sich von großen +4K-Bildschirmen bis hinunter zu 320 Pixel breiten Smartphones an. + +Das Design stammt von der Developer Akademie. Benjamins Arbeit ist die +Umsetzung: der Aufbau der Komponenten, das Layout auf allen Breiten, die +Übersetzung, das Formular mit dem Backend dahinter und die Auslieferung auf +seinen eigenen Server. Der Quelltext liegt öffentlich auf GitHub. + +## Wie das Portfolio technisch aufgebaut ist + +Das Portfolio nutzt Angular 21 mit Standalone-Komponenten, also ohne +NgModules, und SCSS ohne UI-Framework. Jeder Bereich der Seite ist eine eigene +Komponente, wiederverwendbare Bausteine wie der Knopf, das Kontaktformular und +das Projekt-Overlay liegen getrennt davon. + +Die Texte der Seite stehen nicht in den Templates, sondern in je einer +Sprachdatei für Deutsch und Englisch. + +Für den Knopf, das Kontaktformular und das Projekt-Overlay gibt es Unit-Tests +mit Vitest. Beim Formular wird dabei auch die Anfrage an den Server geprüft, +ohne dass ein echter Server laufen muss. + +## Wie die Zweisprachigkeit des Portfolios funktioniert + +Das Portfolio ist zweisprachig, Deutsch und Englisch. Die Internationalisierung, +kurz i18n, läuft über die Bibliothek ngx-translate. + +Alle sichtbaren Texte stehen in zwei JSON-Dateien, eine pro Sprache, mit +denselben 143 Schlüsseln. Die Templates enthalten nur die Schlüssel, nicht die +Texte. Fest im Template stehen nur der Name, die Berufsbezeichnung und die +E-Mail-Adresse. + +Die Sprachdateien werden zur Laufzeit geladen. Dadurch wechselt die Sprache +ohne Neuladen, und der Besucher bleibt an der Stelle der Seite, an der er +gerade war. Die gewählte Sprache merkt sich der Browser für den nächsten +Besuch. Fehlt in einer Sprache ein Text, erscheint die englische Fassung +statt eines leeren Feldes. + +Beim Wechsel wird auch das Sprachattribut der Seite umgestellt. So liest ein +Screenreader englische Texte mit englischer Aussprache vor und nicht mit +deutscher. + +Angular bringt mit @angular/localize eine eigene Lösung mit, die aber für jede +Sprache eine eigene Fassung der Seite baut. Für einen Sprachwechsel ohne +Neuladen passt ngx-translate besser. + +## Wie das Portfolio deployt wird + +Das Portfolio wird automatisch über GitHub Actions deployt. Bei jedem Pull +Request und jedem Push auf den Hauptzweig laufen Linting mit ESLint, die +Formatprüfung mit Prettier, die Tests und ein vollständiger Build. Alle +Prüfungen laufen auch dann weiter, wenn eine davon scheitert. So meldet ein +Durchlauf jedes Problem und nicht nur das erste. + +Ausgerollt wird nur, was im Hauptzweig landet. Die Pipeline baut die Seite dann +neu und überträgt sie per rsync auf den Server. Dabei werden alte Dateien im +selben Durchgang entfernt und die Dateirechte direkt beim Schreiben gesetzt. + +Nach dem Deployment prüft die Pipeline, ob die Seite erreichbar ist und ob die +Sprachdateien wirklich als JSON ankommen. + +## Was Benjamin am Kontaktformular des Portfolios gelernt hat + +Das Kontaktformular prüft die Eingaben zweimal: im Browser, damit der Besucher +sofort sieht, was fehlt, und auf dem Server, weil man sich auf den Browser nie +verlassen darf. + +Einmal passten die beiden Prüfungen nicht zusammen. Der Server verlangte +mindestens zehn Zeichen in der Nachricht, das Formular im Browser nicht. Kurze +Nachrichten wurden also abgeschickt, vom Server abgelehnt, und der Besucher sah +nur eine allgemeine Fehlermeldung. Seitdem legt Benjamin bei jeder Änderung +beide Seiten nebeneinander und vergleicht sie Feld für Feld. + +Die Fehlermeldungen des Servers zeigt das Formular bewusst nicht an. Sie gibt +es nur auf Deutsch, das Portfolio ist aber zweisprachig. Die Meldungen im +Formular kommen deshalb aus den Sprachdateien. diff --git a/assistant_app/knowledge/projekt-quizly.md b/assistant_app/knowledge/projekt-quizly.md new file mode 100644 index 0000000..223a918 --- /dev/null +++ b/assistant_app/knowledge/projekt-quizly.md @@ -0,0 +1,42 @@ +--- +titel: Quizly +quelle: Projekt Quizly +--- + +## Was Quizly ist + +Quizly ist das Projekt, in dem Benjamin KI-Modelle in ein eigenes Backend +eingebunden hat. Das Backend mit Django und dem Django REST Framework macht aus +einem YouTube-Video ein Quiz. Der Nutzer gibt einen Link ein, das Backend lädt +die Tonspur mit yt-dlp herunter, wandelt sie mit dem Spracherkennungsmodell +Whisper in Text um und lässt Gemini über dessen API zehn Fragen mit je vier Antworten +schreiben. + +Die Umwandlung in Text läuft lokal im Backend, der Ton geht also an keinen +fremden Dienst. Nur das fertige Transkript geht an Gemini. Welche Modelle +verwendet werden, lässt sich über die Konfiguration ändern, ohne den Code +anzufassen. + +Die Anmeldung läuft über JWT in HttpOnly-Cookies. Beim Abmelden kommt der +Refresh-Token auf eine Sperrliste und kann nicht wiederverwendet werden. + +Quizly ist ein Ausbildungsprojekt der Developer Akademie. Das Frontend war +vorgegeben, Benjamins Arbeit ist das Backend. Der Quelltext liegt öffentlich +auf GitHub. + +## Wie Quizly die Antwort der KI absichert + +Eine KI liefert nicht zuverlässig das Format, das man sich wünscht. Quizly +verlässt sich deshalb nicht auf den Text der Anfrage, sondern gibt Gemini ein +festes Antwortschema vor. Die Antwort kommt als JSON in genau der Struktur, die +das Backend erwartet. + +Vor dem Speichern prüft Quizly zusätzlich, ob die richtige Antwort jeder Frage +wörtlich unter den vier Antwortmöglichkeiten steht. Ist das nicht der Fall, +bricht die Anfrage mit einem Fehler ab. Ohne diese Prüfung könnte ein Quiz +gespeichert werden, in dem eine Frage keine richtige Antwort hat, und das +würde erst ein Nutzer beim Spielen bemerken. + +Die Tests rufen weder YouTube noch Whisper noch Gemini auf. Die ganze Kette ist +in den Tests ersetzt, damit sie schnell laufen, nichts kosten und jedes Mal +gleich ausgehen. diff --git a/assistant_app/knowledge/projekte-ueberblick.md b/assistant_app/knowledge/projekte-ueberblick.md new file mode 100644 index 0000000..90d6f28 --- /dev/null +++ b/assistant_app/knowledge/projekte-ueberblick.md @@ -0,0 +1,72 @@ +--- +titel: Projekte im Überblick +quelle: Projektübersicht +--- + +## Welche Projekte Benjamin gebaut hat und mit welchem Stack + +Benjamins Projekte in der Reihenfolge, in der sie entstanden sind, jeweils mit +den wichtigsten Technologien: + +- **Book-Store**: JavaScript, HTML, CSS, Speicherung im LocalStorage +- **BestellApp**: JavaScript, HTML, CSS +- **Pokédex**: JavaScript, Daten von der PokéAPI +- **El Pollo Loco**: JavaScript, objektorientiert, Zeichnen auf Canvas +- **Join**: Angular, TypeScript, SCSS, Supabase, im Team zu viert +- **Portfolio**: Angular, TypeScript, SCSS, zweisprachig +- **Cardelia**: Python, Django, Celery, Redis, Supabase +- **Coderr**: Python, Django REST Framework, PostgreSQL +- **Quizly**: Python, Django REST Framework, Whisper, Gemini +- **Videoflix**: Python, Django REST Framework, Redis, RQ, FFmpeg, Docker + +Dazu kommt der Assistent auf dieser Seite: Django, PostgreSQL mit pgvector und +ein eigener Embedding-Dienst mit FastAPI. + +Die Liste zeigt auch den Weg: Angefangen hat Benjamin mit reinem JavaScript +ohne Framework, dann kamen Angular und TypeScript, danach das Backend mit +Python und Django und zuletzt der Betrieb auf einem eigenen Server. + +## Welche Projekte Benjamin mit Django und Python gebaut hat + +Im Backend arbeitet Benjamin mit Python, Django und dem Django REST Framework. +Damit sind fünf Projekte entstanden: + +- **Coderr** ist eine REST-API für eine Plattform, auf der Dienstleistungen + angeboten, bestellt und bewertet werden. Sie läuft auf seinem eigenen Server. +- **Quizly** macht aus einem YouTube-Video ein Quiz: Der Ton wird lokal mit + Whisper in Text umgewandelt, Gemini schreibt daraus zehn Fragen. +- **Videoflix** ist eine Videoplattform, die hochgeladene Videos im Hintergrund + in mehrere Auflösungen umwandelt. Das Projekt ist noch in Arbeit. +- **Cardelia** ist ein Kartenkatalog mit Sammlungsverwaltung, sein größtes und + einziges ohne Vorgaben entstandenes Projekt. +- **Der Assistent auf dieser Seite** sucht Antworten in einer Wissensbasis mit + PostgreSQL und pgvector. + +Die ersten drei sind Ausbildungsprojekte der Developer Akademie mit +vorgegebenem Frontend. Benjamins Arbeit ist dort das Backend dahinter. + +## Welche Frontend-Projekte Benjamin gebaut hat + +Im Frontend hat Benjamin zuerst ohne Framework gearbeitet, in reinem +JavaScript mit HTML und CSS. So sind der Book-Store, die BestellApp, der +Pokédex und das Spiel El Pollo Loco entstanden. Das war die Vorgabe der +Developer Akademie und hat einen Sinn: Wer einmal jede Änderung von Hand ins +DOM geschrieben hat, versteht, was ein Framework später abnimmt. + +Danach kamen Angular und TypeScript. Damit sind Join, ein Task-Manager im Team +zu viert, und sein Portfolio gebaut, beide mit SCSS und ohne UI-Framework wie +Bootstrap oder Angular Material. + +Alle Frontend-Projekte sind öffentlich erreichbar und laufen auf Benjamins +eigenem Server. + +## Warum im Portfolio nur vier Projekte stehen + +Im Portfolio stehen vier Projekte: Join, El Pollo Loco, Cardelia und Coderr. +Der Pokédex, die BestellApp und der Book-Store laufen weiterhin und sind +erreichbar, werden dort aber nicht mehr gezeigt. + +Das ist eine bewusste Entscheidung. Vier Projekte lesen sich als Auswahl, sechs +oder mehr als Sammlung. Kommt ein neues Projekt dazu, wird deshalb getauscht +und nicht angehängt. Die Auswahl soll zeigen, was Benjamin heute kann, und +nicht alles, was er je gebaut hat. diff --git a/assistant_app/retrieval_questions.json b/assistant_app/retrieval_questions.json index 9a2fb6f..49e8c63 100644 --- a/assistant_app/retrieval_questions.json +++ b/assistant_app/retrieval_questions.json @@ -1,7 +1,7 @@ [ - {"question": "Welche Projekte hat Benjamin mit Django gebaut?", "expected": ["Was Coderr ist", "Wie Coderr aufgebaut ist", "Die Architektur von Cardelia", "Videoflix, eine Streaming-Plattform mit Hintergrundverarbeitung"]}, - {"question": "Hat er schon mal selbst einen Server deployt?", "expected": ["Benjamin betreibt seine Projekte auf einem eigenen Server", "Wie neue Versionen live gehen"]}, - {"question": "Wie deployt Benjamin seine Projekte?", "expected": ["Wie neue Versionen live gehen"]}, + {"question": "Welche Projekte hat Benjamin mit Django gebaut?", "expected": ["Was Coderr ist", "Wie Coderr aufgebaut ist", "Die Architektur von Cardelia", "Videoflix, eine Streaming-Plattform mit Hintergrundverarbeitung", "Welche Projekte Benjamin mit Django und Python gebaut hat"]}, + {"question": "Hat er schon mal selbst einen Server deployt?", "expected": ["Benjamin betreibt seine Projekte auf einem eigenen Server", "Wie neue Versionen live gehen", "Wie das Portfolio deployt wird"]}, + {"question": "Wie deployt Benjamin seine Projekte?", "expected": ["Wie neue Versionen live gehen", "Wie das Portfolio deployt wird"]}, {"question": "Wie sichert er seinen Server ab?", "expected": ["Absicherung des Servers"]}, {"question": "Macht er Backups?", "expected": ["Datensicherung"]}, {"question": "Warum Nginx und Gunicorn?", "expected": ["Wie die Anwendungen ausgeliefert werden"]}, @@ -21,18 +21,33 @@ {"question": "Wie schützt das Kontaktformular vor Spam?", "expected": ["Das Kontaktformular des Portfolios läuft über Coderr"]}, {"question": "Wie arbeitest du mit KI?", "expected": ["Wie Benjamin mit KI arbeitet"]}, {"question": "Wie ist dieses System gebaut?", "expected": ["Der Assistent auf dieser Seite"]}, - {"question": "Was für Projekte hast du bisher gebaut?", "expected": ["Welche Projekte nach Vorgabe entstanden sind und welche frei"]}, + {"question": "Was für Projekte hast du bisher gebaut?", "expected": ["Welche Projekte nach Vorgabe entstanden sind und welche frei", "Welche Projekte Benjamin gebaut hat und mit welchem Stack"]}, {"question": "Was hast du bei Join gemacht?", "expected": ["Join war Teamarbeit zu viert", "Die schwierigste technische Stelle in Join", "Was Join ist"]}, - {"question": "Wie deployst du deine Projekte?", "expected": ["Wie neue Versionen live gehen"]}, + {"question": "Wie deployst du deine Projekte?", "expected": ["Wie neue Versionen live gehen", "Wie das Portfolio deployt wird"]}, {"question": "Wo wohnst du?", "expected": ["Wo Benjamin lebt und wie er arbeiten möchte"]}, {"question": "Arbeitest du mit Git und Pull Requests?", "expected": ["Wie Benjamin mit Git arbeitet"]}, - {"question": "How does Benjamin deploy his projects?", "expected": ["Wie neue Versionen live gehen"]}, + {"question": "How does Benjamin deploy his projects?", "expected": ["Wie neue Versionen live gehen", "Wie das Portfolio deployt wird"]}, {"question": "Does he run his own server?", "expected": ["Benjamin betreibt seine Projekte auf einem eigenen Server"]}, {"question": "Where does Benjamin live and is he open to remote work?", "expected": ["Wo Benjamin lebt und wie er arbeiten möchte"]}, {"question": "Why did Videoflix use JWT in cookies?", "expected": ["Warum Videoflix JWT verwendet und nicht Token-Authentifizierung"]}, {"question": "What was the hardest part of the Pokédex?", "expected": ["Wo die eigentliche Schwierigkeit im Pokédex lag"]}, {"question": "How does this assistant work?", "expected": ["Der Assistent auf dieser Seite"]}, - {"question": "What did you build with Django?", "expected": ["Was Coderr ist", "Wie Coderr aufgebaut ist", "Die Architektur von Cardelia", "Videoflix, eine Streaming-Plattform mit Hintergrundverarbeitung"]}, + {"question": "What did you build with Django?", "expected": ["Was Coderr ist", "Wie Coderr aufgebaut ist", "Die Architektur von Cardelia", "Videoflix, eine Streaming-Plattform mit Hintergrundverarbeitung", "Welche Projekte Benjamin mit Django und Python gebaut hat"]}, + {"question": "Welche Projekte hat Benjamin gebaut und mit welchem Stack?", "expected": ["Welche Projekte Benjamin gebaut hat und mit welchem Stack"]}, + {"question": "Which technologies has he worked with in his projects?", "expected": ["Welche Projekte Benjamin gebaut hat und mit welchem Stack", "Welche Projekte Benjamin mit Django und Python gebaut hat", "Welche Frontend-Projekte Benjamin gebaut hat"]}, + {"question": "Was hat er mit Angular gebaut?", "expected": ["Welche Frontend-Projekte Benjamin gebaut hat", "Was Join ist", "Was das Portfolio ist", "Wie das Portfolio technisch aufgebaut ist"]}, + {"question": "Warum stehen nicht alle Projekte im Portfolio?", "expected": ["Warum im Portfolio nur vier Projekte stehen"]}, + {"question": "Wie ist dein Portfolio gebaut?", "expected": ["Was das Portfolio ist", "Wie das Portfolio technisch aufgebaut ist"]}, + {"question": "Is the portfolio available in English?", "expected": ["Was das Portfolio ist", "Wie das Portfolio technisch aufgebaut ist", "Wie die Zweisprachigkeit des Portfolios funktioniert"]}, + {"question": "Wie hat er die Mehrsprachigkeit im Portfolio umgesetzt?", "expected": ["Wie die Zweisprachigkeit des Portfolios funktioniert"]}, + {"question": "How does the portfolio handle i18n?", "expected": ["Wie die Zweisprachigkeit des Portfolios funktioniert"]}, + {"question": "Was ist die BestellApp?", "expected": ["Was die BestellApp ist"]}, + {"question": "Warum hat er zuerst ohne Framework programmiert?", "expected": ["Was Benjamin an der BestellApp über Frameworks gelernt hat", "Welche Frontend-Projekte Benjamin gebaut hat"]}, + {"question": "Was ist der Book-Store?", "expected": ["Was der Book-Store ist"]}, + {"question": "How does the Book-Store keep likes after a reload?", "expected": ["Wie der Book-Store Daten im Browser speichert", "Was der Book-Store ist"]}, + {"question": "Was ist Quizly?", "expected": ["Was Quizly ist"]}, + {"question": "Hat Benjamin schon mal eine KI-API in ein Backend eingebaut?", "expected": ["Was Quizly ist", "Wie Quizly die Antwort der KI absichert"]}, + {"question": "How does Quizly make sure the AI output is valid?", "expected": ["Wie Quizly die Antwort der KI absichert"]}, {"question": "Schreib mir ein Rezept für Lasagne.", "expected": []}, {"question": "Wie wird das Wetter morgen in Berlin?", "expected": []}, {"question": "Wer hat die Fußball-WM 2014 gewonnen?", "expected": []},