home.social

#kunstlicheintelligenzki — Public Fediverse posts

Live and recent posts from across the Fediverse tagged #kunstlicheintelligenzki, aggregated by home.social.

  1. Wozu lernen wir – und für und mit welcher Technologie?

    Ich habe vor kurzem in meinem Blog aufgeschrieben, dass ich die ‚Nordsterne‘ in pädagogischen Transformationsdebatten oft zu eng gefasst finde. Denn diese betrachten in der Regel die Frage: Wie wollen wir lehren und lernen? Ich habe als Ergänzung formuliert: Wozu lernen wir? Als Antwort habe ich die Perspektive einer l(i)ebenswerten Welt vorgeschlagen, die sich aus dem ‚Woher?‘ ergibt – den in uns angelegten menschlichen Potenzialen, die es in Lernräumen zu entfalten gilt.

    In diesem Blogbeitrag möchte ich gerne noch einen Schritt weiter gehen und diese Perspektive eines doppelt erweiterten Nordsterns mit meinem Fokus auf Bildung und Digitalisierung zusammenbringen.

    Warum ein pädagogischer Nordstern unter Einbezug von Digitalisierung?

    Debatten zu Bildung und Digitalisierung beschränken sich erfreulicherweise schon lange nicht mehr nur um ‚Lehren und Lernen mit digitalen Tools‘. Stattdessen gerät zusätzlich immer mehr auch die Perspektive von ‚Lehren und Lernen in einer digital-geprägten Welt‘, also die Frage der Lernkultur, in den Blick. Relativ unverbunden steht dann aber oft daneben, dass wir Digitalisierung auch als Lerngegenstand betrachten und reflektieren sollten. Dieser Aspekt ist bleibt dann meist nur ein kleines Add-On.

    Aus meiner Sicht gehören diese drei Ansätze zusammen, sollten verbindend reflektiert werden und führen zu der dann um den Fokus der Digitalisierung erweiterten Nordstern-Frage:

    Wozu lernen wir – und für und mit welcher Technologie?

    Diese Erweiterung ist aus meiner Sicht vor allem deshalb relevant, weil wir Bildung ausgehend von unserer zunehmend digital-geprägten Gesellschaft gestalten. Das hat eine doppelte Konsequenz:

    1. Es gilt zu klären, welche Digitalisierung wir uns in und für eine l(i)ebenswerte Welt vorstellen. Wir müssen also mit dem gesamtgesellschaftlichen Nordstern auch einen Technologie-Nordstern entwickeln.
    2. Es gilt zu reflektieren, wie die gegenwärtige Ausgestaltung von Digitalisierung unsere Handlungsmöglichkeiten hin zur Realisierung dieser Nordsterne behindert oder auch befördern kann.

    Welche Digitalisierung wollen wir für eine l(i)ebenswerte Welt?

    Eine l(i)ebenswerte Welt ist eine Welt, in der Leben entfaltet ist und sich weiter entfalten kann. Damit das gelingt, müssen wir uns an ‚organischer Intelligenz‘ orientieren.

    Organische Intelligenz funktioniert nicht über den Versuch der Kontrolle, sondern durch Verbindung. Das Bild ist hier ein Garten, in dem ich etwas pflanzen und das Wachstum der Pflanzen unterstützen kann. Es ist keine Fabrik, in der nach festen Vorgaben etwas durchgeplant entsteht.

    Die vorherrschende Digitalisierung funktioniert dagegen überwiegend konträr zu diesem Leitbild der Lebendigkeit. Sie ist geprägt von Plattformen, die Silos darstellen, deren Infrastruktur auf Verwertbarkeit durch Monopolisierung und Machtkonzentration zielt. Pädagogisch ist hierbei relevant, dass genau diese Grundlage auch einen Einfluss auf unsere Nutzungspraxis hat: Diese Form der Digitalisierung unterstützt und befördert Individualisierung, Konkurrenzdenken und Standardisierung.

    Für eine Digitalisierung, die sich an organischer Intelligenz orientiert und diese befördert, wäre stattdessen Dezentralität der für mich wichtigste Hebel von Veränderung.

    Hintergrund ist hier, dass Dezentralität im Kern bedeutet, nicht auf Kontrolle zu setzen, sondern Verbindung und Offenheit zu ermöglichen. Wenn Digitalisierung deshalb statt auf Monopolisierung auf Dezentralisierung setzen würde, dann würde sie sich sehr grundlegend verändern und ein verändertes, viel menschlicheres Handeln ermöglichen.

    Im Fediverse habe ich das wie folgt beschrieben:

    Dezentralisierung würde in diesem Sinne ermöglichen:

    • Communities statt Plattformen
    • Schnittstellen und Interoperabilität statt abgegrenzten Silos
    • Demokratie, Mündigkeit und Selbstbestimmung statt Machtkonzentration, Abhängigkeit und Manipulation
    • Gemeinschaftlichkeit statt Individualisierung
    • Kooperation statt Konkurrenz
    • Gemeinwohl statt Verwertbarkeit
    • Demut und Bedeutung statt Ego und Lärm
    • Vielfalt statt Standardisierung
    • Sicherheit und Experimentieren statt Angst und Druck

    Ich finde an solchen Gegenüberstellungen mit einer pädagogischen Brille spannend, dass wir auf der Ebene von Haltung und Werten sehr viele Überschneidungen dazu sehen, wie wir uns nicht nur eine bessere Technologie, sondern auch eine bessere Bildung vorstellen. Es steckt also viel Synergiepotential darin, diese wünschenswerten Systemveränderungen verbindend zu betrachten. Und es wird deutlich, dass eine pädagogische Perspektive für die Gestaltung einer veränderten Technologie sehr viel beizutragen hat.

    Wie nutzen und gestalten wir Digitalisierung, um zu einer l(i)ebenswerten Welt zu gelangen?

    Wenn der Nordstern klarer wird, dann schließt sich die Frage an, wie wir ihn erreichen können. Herausfordernd ist dabei in der Bildung vor allem die extreme Ambivalenz, die mit der vorherrschenden, gegenwärtigen Technologie verbunden ist:

    1. Auf der einen Seite steht die vorherrschende Digitalisierung wie oben dargestellt konträr zu Lebendigkeit und damit zu menschlichen Potenzialen, die wir im Interesse einer l(i)ebenswerten Welt eigentlich zur Entfaltung bringen wollen.
    2. Auf der anderen Seite ermöglicht selbst diese Digitalisierung, weil ihr Geschäftsmodell ist, menschliche Bedürfnisse anzusprechen, immer wieder flüchtige Einblicke und auch konkrete Möglichkeiten, sie im Sinne von organischer Intelligenz zu nutzen. Beispielsweise kann ich die Mustererkennung von monopolisierten KI-Modellen als Hilfe für Gruppenprozesse nutzen oder für mich hilfreiche soziale Verbindungen über proprietäre Social Media Plattformen aufbauen.

    In dieser Ambivalenz ist pädagogisch für mich ein Dreiklang nötig:

    1. Akzeptanz: Anerkennen, dass es Technologie in ihrer gegenwärtigen Form gibt und dass sie Teil unserer Lebenswelt – der Lehrenden wie auch der Lernenden – ist.
    2. Reflexion: Am besten ausgehend von konkreter Nutzung Räume schaffen, um zu reflektieren, wo Technologie uns manipuliert und wo wir uns deshalb vor ihr schützen sollten und wie uns trotz alledem in manchen Bereichen ein ‚Hacking‘ gelingen kann.
    3. Alternativen: Perspektiven für eine veränderte Technologie öffnen, die genau mit den Mustern bricht, die wir oben als konträr zum Prinzip der Lebendigkeit identifiziert haben.

    Je verbindender wir diese drei Herausforderungen aufgreifen und je mehr wir sie mit der Nordstern-Orientierung verbinden, umso wirkungsvoller.

    Für den zurzeit viel diskutierten Bereich der künstlichen Intelligenz kann das beispielsweise bedeuten:

    • KI nicht verbieten/aussperren, sondern in der Bildung zu einem Thema machen.
    • Über KI-Nutzung reflektieren, bewusst auch Lernen ohne KI ermöglichen und uns zugleich an einer kontra-intuitiven KI-Nutzung versuchen, die KI nicht als Antwortmaschine, sondern als Resonanzmaschine versteht.
    • Auf die Suche begeben, wo es eine andere Form von KI gibt, was für mich vor allem offene KI-Modelle sind und zugleich auch nicht-generative KI, die in der gegenwärtigen Diskussion fast gar nicht im Blick ist.

    Am Beispiel von Social Media könnte es bedeuten:

    • Social Media als Teil von Lebensrealität anerkennen und mit einem offenen Blick Nutzungspraktiken und Gewohnheiten verstehen und besprechen.
    • Räume schaffen, um zu reflektieren, was Social Media mit uns macht und gemeinsam zu Vereinbarungen zu kommen, wo Abschalten allen Beteiligten besser tut. Und zugleich erkunden, wo Social Media zum Beispiel Verbindung und Zuspruch ermöglichen kann.
    • Alternativen wie das Fediverse stärken und gerade im Bildungskontext erfahrbar machen.

    Fazit: Pädagogische Diskussion erweitern

    Ich freue mich darauf, diese erweiterten Perspektiven, die jede einzelne für sich sicher nicht neu sind, mit solch einem verbindenden Nordstern-Blick im Kontext von Bildung und Digitalisierung aufzugreifen und zu gestalten.

    Durch die Entwicklungen rund um KI sehe ich in diesem Bereich aktuell einen wirkungsvollen Hebel. um Veränderungen anstoßen zu können, weil sich angesichts von immer leistungsfähigeren Maschinen gesamtgesellschaftlich immer drängender die Frage stellt, was uns als Menschen ausmacht – und damit auch, welche Digitalisierung wir eigentlich wollen. Ich denke, dass wir mit einem Blick auf Lernen als Entwicklung, Ermöglichung und Ermächtigung in einer gemeinschaftlichen Perspektive hier vieles beitragen können.

    Wer am 18. April bei der edunautika in Hamburg ist: Sehr gerne möchte ich dazu in einer Session gemeinsam weiter denken!

    #DigitaleMündigkeit #KünstlicheIntelligenzKI
  2. Jugend Medien Bildung. Gutes Lernen in der KI-Ära?

    Heute habe ich einen Online-Impuls für ein Kooperationsnetzwerk der evangelischen Akademie Frankfurt gestaltet. Insgesamt ging es bei der Veranstaltung um die Ende des letzten Jahres veröffentlichte JIM-Studie, die dieses Mal einen Fokus auf KI legte. Im ersten Teil wurden die Ergebnisse vorgestellt. Im zweiten Teil sprach ich zum Thema: Nach dem Hype. KI als neue Lebensrealität.

    Ich habe ohne Folien präsentiert. Das klappt auch im Online-Kontext aus meiner Sicht sehr gut. Hilfreich finde ich einen sehr klaren, prägnanten Aufbau, dem die Teilnehmenden gut folgen können. Ich habe das heute anhand von drei Begriffen realisiert: Radikale Gegenwart, Schieberegler und gemeinwohlorientierte Digitalpädagogik. Diese drei Begriffe habe ich direkt zu Beginn angekündigt und zum aktiven Zuhören (= gerne Mitkritzeln) eingeladen. Danach konnte ich die Begriffe Schritt für Schritt mit Gegenständen in der Kamera und Mini-Interaktionen im Chat und in BreakOuts vertiefen.

    Neben diesem klaren und strukturierten Aufbau kann ich die folgenden Aspekte aus Vorbereitung und Durchführung weiter empfehlen:

    1. Wenn es mit einer Frage in BreakOut Räume geht, dann ist es – gerade wenn man keine Folien hat – hilfreich, diese Frage parallel auch nochmals in den Chat zu packen, so dass alle sie mitbekommen.
    2. Eine schöne Mini-Methode ist, eine ‚Wie können wir …?‘-Frage zu stellen. Wer dann in einen BreakOut Raum mit gerader Zahl per Zufall zugeordnet wird, sucht nach Lösungen. Wer in einen BreakOut Raum mit ungerader Zahl zugeteilt wird, sucht nach Anti-Lösungen (= Was wäre das Dümmste, was wir tun könnten?). Anschließend sammelt man im Chat erst die Anti-Lösungen und dann die Lösungen.
    3. Solch ein Vortrag lässt sich sehr gut in Interaktion mit einem KI-Sprachmodell vorbereiten. Ich bin heute so vorgegangen, dass ich mir meinen Vortrag überlegt habe, ihn dann einmal eingesprochen habe (wobei ich mich noch viel verhaspelt habe und nicht immer einen roten Faden hatte), das Transkript des Geplappers mit einem KI-Sprachmodell geteilt habe – verbunden mit dem Prompt, mir daraus bitte einen kurzen Spickzettel für den Vortrag zu erstellen. Den so generierten Spickzettel konnte ich dann neben mich leben. Dieses Vorgehen ist also doppelt sinnvoll: Erstens übe ich den Vortrag, zweitens erhalte ich einen Spickzettel, der mir dann bei der Durchführung hilft.

    Inhaltlich fand ich es vor allem wichtig, einen Blick auf so genannten KI-SLOP zu werfen. Und sich zu vergegenwärtigen, was KI-SLOP ist (= für mich: Inhalte ohne Interesse für den Inhalt zu teilen) und was nicht (= für mich: wenn Menschen sich mit KI als Werkzeug auszudrücken versuchen und dabei Inhalte empfehlen, denen ich aus meiner Sicht schlechte Qualität zuspreche). In diesem Fall kann AI-SLOP auch etwas anderes sein. Nämlich nicht Sehr Langweiliger, Ordinärer Plödsinn, sondern Spielerische Lust an Offenen Prozessen.

    Beim Vortrag hatte ich dazu ein Mini-Klappbild:

    Mein Fazit: Das war nur ein kurzer Impuls, aber ich hatte den Eindruck, dass er für einige der Zuhörenden eine hilfreiche Perspektiven-Erweiterung war. Persönlich habe ich mich sehr über das Interesse und die vielen Nachfragen an der vorgestellten gemeinwohlorientierten Digitalpädagogik gefreut.

    #KünstlicheIntelligenzKI #OnlineLernen
  3. Dein persönlicher KI-Chatbot für Blogs: Open Source & lokal

    Immer wieder stoße ich im Internet auf das Angebot, mir eine ‚eigene KI‘ zu programmieren. Dahinter verbergen sich allerdings höchst unterschiedliche Dinge. In den meisten Fällen wird nicht tatsächlich ein KI-Modell programmiert. Stattdessen werden bestimmte Einstellungen zum Chatten festgelegt (z.B. ‚Gib möglichst kurze Antworten‘ oder ‚Sprich mich mit Du und Nele an‘) und/oder bestimmte Quellen festgelegt, auf die sich ein genutzter Chatbot in der Antwort beziehen soll.

    Der Fachbegriff für letzteres lautet: Retrieval Augmented Generation, abgekürzt RAG. Solch eine RAG-Herangehensweise ermöglicht es, einem KI-Modell eine bestimmte Datenbasis zur Verfügung zu stellen, auf die sich der Chatbot in seinen Antworten dann vorrangig beziehen soll.

    Ich habe RAG bereits vielfach auf meinem eigenen KI-Server ausprobiert. Meistens habe ich das aber auf einzelne Inhalte (z.B. auf einen bestimmten Blogbeitrag oder ein Transkript) bezogen, nicht auf umfangreichere Quellen wie z.B. alle Beiträge eines bestimmten Blogs. Genau dafür suchte ich jetzt nach einer Lösung. In diesem Zusammenhang wollte ich zugleich eine Lösung entwickeln, die auch andere möglichst ohne Kosten und umfangreiche technische Vorkenntnisse für sich nutzen können und die mit Open Source Modellen sowie unabhängig von proprietären KI-Plattformen funktioniert. Das Vorhaben ist somit – neben dem praktischen Nutzen – vor allem auch ein Projekt für mehr digitale Mündigkeit. Mein Vorgehen habe ich als Schritt-für Schritt Anleitung aufgeschrieben.

    Das klingt für dich interessant? Dann lies weiter und probiere die Anleitung sehr gerne für dich aus. Vielleicht hast du ja auch Lust, sie weiter zu verbessern!

    Grundsätzliche Schritte

    Unser Ziel ist ein RAG-Chatbot für Blog-Websites im Terminal deines Rechners (= dem schwarzen Fenster, in das sich Befehle eintippen lassen). Du kannst den Chatbot auch dann bauen, wenn du bisher noch nicht mit dem Terminal gearbeitet hast.

    Wir gehen in mehreren Schritten vor: Nach ein paar Vorbereitungen nutzen wir zuerst ein Programm, mit dem wir alle Beiträge von den von uns gewünschten Blogs herunterladen. Dabei überprüfen wir natürlich zunächst, ob die Blog-Inhaber*innen solch ein Scraping untersagen. Wenn nein, dann versuchen wir die Beiträge entweder über die REST-API (das funktioniert oft bei WordPress-Blogs; es ist ein maschinenlesbarer Zugang zu den Daten der Website) oder ansonsten über den RSS-Feed oder die Sitemap zu erhalten. Wir müssen dazu lediglich die URL (= die Internetadresse) der gewünschten Blogs eingegeben.

    Aus den eingesammelten Beiträgen gestalten wir dann – mit einem weiteren Open Source Programm – eine Vektor-Datenbank. Das ist eine komprimierte und maschinenlesbare Variante der Inhalte, die als Datenbasis von KI-Sprachmodellen genutzt werden kann. Diese Datenbank verbinden wir mit dem Open Source KI-Modell Mistral.

    Im letzten Schritt nutzen wir dann ein weiteres Programm – den eigentlichen Chatbot – um in die Kommunikation über die Beiträge in den Blogs zu gehen. Das funktioniert dann weiterhin über das Terminal, aber du wirst sehen, dass du im Ergebnis dann einen Bot gestaltet hast, der ähnlich wie andere KI-Chatbots menschliche Sprache auswerten und mit passenden Reaktionen – in unserem Fall mit den Inhalten basierend auf den eingepflegten Blogbeiträgen – antworten kann.

    Die folgende Anleitung stellt dir den Code für das ‚Beitragseinsammel-Programm‘ und das ‚Chatbot-Programm‘ zum direkten Weiternutzen zur Verfügung. Ich empfehle dir, erst einmal damit zu arbeiten. Später kannst du aber natürlich diese Programme auch anpassen und deinen Chatbot so noch sehr viel spezifischer für deine Bedürfnisse gestalten.

    Wozu das Ganze?

    Ich finde dieses Projekt aus mehreren Gründen ziemlich cool:

    1. Es ist ein offenes Projekt, das dir die Potenziale von Open Source Software zeigt und dir eine Alternative zu den proprietären KI-Plattformen ermöglicht.
    2. Es ist ein Projekt für mehr digitale Mündigkeit. Du verstehst und kannst erkunden, wie KI-Chatbots unter der Haube funktionieren.
    3. Du bekommst eine sehr nützliche Anwendung, mit der du Online-Inhalte aus von dir ausgewählten Blogs über eine Chatoberfläche in einem vernetzten Sinne ‚lesen‘ kannst.

    Schritt-für-Schritt Anleitung

    Insgesamt wirst du für die folgenden Schritte wahrscheinlich ungefähr eine Stunde Zeit brauchen. Du kannst jederzeit zwischendrin aufhören und wieder neu einsteigen.

    (Einschränkend möchte ich anmerken, dass ich die Schritte auf meinem Linux-Laptop durchgeführt habe. Ich gebe auch die entsprechenden Befehle für Windows und Mac an, aber habe diese nicht selbst getestet. Entstanden ist die Anleitung basierend auf meiner Ideen dazu, allgemeiner Internet-Recherche und Interaktion mit KI-Chatbots)

    Schritt 1: Vorbereitungen

    Um solch einen RAG-Chatbot für Blog-Websites im Terminal selbst zu basteln, sind zunächst drei Vorbereitungen nötig:

    1. Du solltest grundlegende Befehle im Terminal kennen.
    2. Du solltest dir Python installieren.
    3. Du solltest dir einen Mistral-API-Key besorgen.

    1. Grundlegende Befehle im Terminal kennen

    Zunächst zu den grundlegenden Befehlen im Terminal: Diese unterscheiden sich, je nachdem, ob du Linux, Windows oder einen Mac nutzt. Du findest das Terminal mit diesen Eingaben – oder auch einfach über eine Programmsuche:

    Linux:

    • Tastenkombination Strg + Alt + T oder
    • Suche nach „Terminal/ Konsole“ in deinen Anwendungen

    Windows:

    • Tastenkombination Windows-Taste + R, dann cmd eingeben und Enter drücken oder
    • Suche nach „Eingabeaufforderung“ bzw. „PowerShell“ im Startmenü

    Mac:

    • Tastenkombination Cmd + Leertaste, dann „Terminal“ eingeben oder
    • Gehe zu Programme → Dienstprogramme → Terminal

    Es öffnet sich dann ein kleines Konsolen-Fenster, über das du an deinen Rechner bestimmte Befehle senden kannst.

    (Ich habe die Terminal-Nutzung in den letzten Monaten übrigens sehr viel in Interaktion mit KI-Chatbots gelernt. Vor allem die Anthropic-Modelle funktionieren dazu aus meiner Sicht gut. Du kannst dann einfach im KI-Chat fragen, was der richtige Befehl für das Terminal ist, diesen dann im Zweifel noch einmal im Internet gegenchecken, dann eingeben – und wenn du dann einen irritierenden Output bekommst oder irgend etwas nicht funktioniert, diesen wiederum kopieren, mit dem KI-Chatbot teilen und dir erklären lassen.)

    Hier kommt eine kleine Basis-Übung, um das Prinzip des Terminals zu verstehen.

    Du brauchst dazu diese Befehle:

    Linux/Mac:

    • mkdir ~/Verzeichnis – um ein Verzeichnis zu erstellen
    • cd ~/Verzeichnis – um in das Verzeichnis zu wechseln
    • nano Dateiname – um eine Datei zu erstellen und/oder zu öffnen
    • Nach dem Eintragen dann Strg+O und Enter und Strg+X, um das Programm zu speichern und zu schließen

    Windows (PowerShell):

    • mkdir ~/Verzeichnis – um ein Verzeichnis zu erstellen
    • cd ~/Verzeichnis – um in das Verzeichnis zu wechseln
    • notepad Dateiname.txt – um eine Datei zu erstellen und/oder zu öffnen
    • Nach dem Eintragen über Datei → Speichern und dann Fenster schließen

    Windows (CMD):

    • mkdir %USERPROFILE%\Verzeichnis – um ein Verzeichnis zu erstellen
    • cd %USERPROFILE%\Verzeichnis – um in das Verzeichnis zu wechseln
    • notepad Dateiname.txt – um eine Datei zu erstellen und/oder zu öffnen

    Mit diesen Befehlen kannst du die Terminal-Nutzung testen:

    Für Linux/Mac gib im Terminal ein:

    mkdir ~/mein-testverzeichnis
    mkdir ~/mein-testverzeichnis

    Dann drücke Enter.

    cd ~/mein-testverzeichnis
    cd ~/mein-testverzeichnis

    Dann drücke Enter.

    nano testdatei.txt
    nano testdatei.txt

    Dann drücke Enter.

    Für Windows (PowerShell) gib im Terminal ein:

    mkdir ~/mein-testverzeichnis
    mkdir ~/mein-testverzeichnis

    Dann drücke Enter.

    cd ~/mein-testverzeichnis
    cd ~/mein-testverzeichnis

    Dann drücke Enter.

    notepad testdatei.txt
    notepad testdatei.txt

    Dann drücke Enter.

    Für Windows (CMD) gib im Terminal ein:

    mkdir %USERPROFILE%\mein-testverzeichnis
    mkdir %USERPROFILE%\mein-testverzeichnis

    Dann drücke Enter.

    cd %USERPROFILE%\mein-testverzeichnis
    cd %USERPROFILE%\mein-testverzeichnis

    Dann drücke Enter.

    notepad testdatei.txt
    notepad testdatei.txt

    Dann drücke Enter.

    Es öffnet sich ein Eingabefeld. Bei Linux/Mac ist das direkt im Terminal (nano-Editor), bei Windows öffnet sich der Notepad-Editor. Schreibe irgend etwas rein. Zum Beispiel:

    Hallo, ich erstelle eine Datei mithilfe des Terminals :-)

    Bei Linux/Mac: Dann Strg+O und Enter und Strg+X

    Bei Windows: Speichere über Datei → Speichern und schließe das Notepad-Fenster

    Nach dem Schließen gelangst du wieder zum ursprünglichen Terminal-Fenster zurück. Dass deine Befehle erfolgreich waren, siehst du, wenn du deine Ordnerverwaltung öffnest und darin tatsächlich den erstellten Ordner ‚mein-testverzeichnis‘ mit der Datei testdatei.txt und deinem Inhalt darin findest.

    (Ich fand das ziemlich großartig, als ich das zum ersten Mal gemacht habe. Wenn es auch dein erstes Mal ist, dann kannst du dich erst einmal freuen, dass du jetzt einen ziemlich coolen Weg kennst, um mit deinem Gerät zu kommunizieren! Wir werden das im Folgenden nutzen!)

    2. Python installieren

    Mit diesem Basiswissen können wir uns dann direkt an die Installation von Python machen. Das ist eine Programmiersprache, in der die Programme geschrieben sein werden, mit denen wir arbeiten.

    Ob Python bei dir ohnehin schon installiert ist, kannst du überprüfen mit diesem Befehl im Terminal:

    python --version
    python --version

    oder

    python3 --version
    python3 --version

    Wenn dann eine Versionsnummer zurückkommt (z.B. Python 3.13.11), ist Python installiert.

    Wenn nicht, dann kannst du es folgendermaßen installieren:

    Linux (Ubuntu/Debian):

    sudo apt update
    sudo apt install python3 python3-pip
    sudo apt updatesudo apt install python3 python3-pip

    Mac:

    brew install python3
    brew install python3

    (Falls Homebrew nicht installiert ist, folge dieser Anleitung.)

    Windows:

    1. Gehe auf https://www.python.org/downloads/
    2. Lade die neueste Version herunter
    3. Führe den Installer aus und setze unbedingt den Haken bei „Add Python to PATH“
    4. Klicke auf „Install Now“

    3. Mistral API-Key besorgen

    Als letztes benötigst du noch einen Mistral API Key. Mistral ist ein europäisches Open Source KI-Modell. Wir nutzen im Folgenden Mistral-small, was in einem begrenzten, aber für den persönlichen Gebrauch erfahrungsgemäß mehr als ausreichendem Umfang für Entwickler*innen kostenfrei zur Verfügung steht.

    (Mit dieser sehr umfangreichen, kostenfreien Entwicklungsmöglichkeit mit der Mistral-API verfolgt das Unternehmen übrigens eine aus meiner Sicht sehr stimmige Open Source Strategie: Je mehr Menschen mit der Mistral-API erkunden und ausprobieren, desto mehr Ideen und Produkte werden auf dieser Basis auch entwickelt, von denen einige dann später einen kostenpflichtigen Plan in Anspruch nehmen werden.)

    Um dir einen Mistral-API-Key zu besorgen, öffnest du diese Website, registrierst dich, gehst auf API und erstellst dir einen API-Key und speicherst ihn ab. Du kannst ihn z.B. „Blog-Chatbot“ nennen. Den API Key speicherst du dir ab, so dass du ihn später nutzen kannst.

    Wichtig: Du solltest den API-Key nicht öffentlich teilen!

    Schritt 2: Projekt aufsetzen

    In diesem Schritt sind wir weiter vorbereitend tätig, aber du kannst direkt deine Learnings aus Schritt 1 anwenden. Wir benötigen nämlich eine Projektumgebung (= einen Ordner auf deinem Rechner, in dem du deinen KI-Chatbot und die Inhalte speicherst). In diesem Ordner speicherst du dann deinen Mistral-API-Key und die weiteren, benötigten Programme.

    1. Projektverzeichnis erstellen

    Zunächst erstellen wir die Projektumgebung:

    Linux/Mac:

    mkdir ~/blog-chatbot-projekt
    cd ~/blog-chatbot-projekt
    mkdir ~/blog-chatbot-projektcd ~/blog-chatbot-projekt

    Windows (PowerShell):

    mkdir ~/blog-chatbot-projekt
    cd ~/blog-chatbot-projekt
    mkdir ~/blog-chatbot-projektcd ~/blog-chatbot-projekt

    Windows (CMD):

    mkdir %USERPROFILE%\blog-chatbot-projekt
    cd %USERPROFILE%\blog-chatbot-projekt
    mkdir %USERPROFILE%\blog-chatbot-projektcd %USERPROFILE%\blog-chatbot-projekt

    2. Virtuelle Umgebung erstellen

    Dann erstellen wir eine virtuelle Umgebung. Das ist sinnvoll, weil damit alle installierten Programme nur für dieses Projekt gelten und nicht mit anderen Python-Projekten auf deinem Rechner in Konflikt kommen können.

    Linux/Mac:

    python3 -m venv venv
    source venv/bin/activate
    python3 -m venv venvsource venv/bin/activate

    Windows (PowerShell):

    python -m venv venv
    .\venv\Scripts\Activate.ps1
    python -m venv venv.\venv\Scripts\Activate.ps1

    (Falls du eine Fehlermeldung bekommst, führe einmalig aus: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser)

    Windows (CMD):

    python -m venv venv
    venv\Scripts\activate.bat
    python -m venv venvvenv\Scripts\activate.bat

    Nach der Aktivierung siehst du (venv) am Anfang deiner Kommandozeile – das zeigt, dass die virtuelle Umgebung aktiv ist.

    3. API-Key speichern

    Jetzt speichern wir deinen Mistral-API-Key in einer Datei, die wir so erstellen, wie du es eingangs mit der Testdatei gelernt hast:

    Linux/Mac:

    nano .env
    nano .env

    Windows:

    notepad .env
    notepad .env

    Trage dann folgendes ein (ersetze YOUR_API_KEY durch deinen tatsächlichen Key, den du dir auf der Mistral-Website erstellt hast):

    MISTRAL_API_KEY=dein_api_key_hier
    MISTRAL_API_KEY=dein_api_key_hier

    Bei Linux/Mac: Speichere mit Strg+O, Enter, Strg+X

    Bei Windows: Speichere über Datei → Speichern und schließe Notepad

    4. Benötigte Programme installieren

    Jetzt installieren wir die Programme, die wir für unseren Chatbot brauchen:

    • chromadb – Die Vektor-Datenbank, in der die Blog-Beiträge gespeichert werden
    • sentence-transformers – Für die semantische Suche und Einbettung der Texte
    • requests – Um Webseiten abzurufen
    • mistralai – Das Programm, um mit der Mistral-API zu kommunizieren
    • python-dotenv – Um den API-Key aus der .env-Datei zu laden
    • feedparser – Um RSS-Feeds zu lesen
    • beautifulsoup4 – Um HTML-Inhalte zu verarbeiten

    Bei all diesen Programmen handelt es sich um Open Source Programme. Die Installation funktioniert über eine Befehlseingabe zur Installation (und etwas Zeit).

    Für alle Betriebssysteme (wenn virtuelle Umgebung aktiviert ist) gib diesen Befehl im Terminal ein:

    pip install chromadb sentence-transformers requests mistralai python-dotenv feedparser beautifulsoup4
    pip install chromadb sentence-transformers requests mistralai python-dotenv feedparser beautifulsoup4

    Dieser Installationsvorgang kann einige Minuten dauern. Lass das Terminal einfach arbeiten, bis du wieder die ursprüngliche Eingabeaufforderung siehst.

    Schritt 3: Blogs einsammeln und Vektordatenbank erstellen

    Mit der eingerichteten Basis aus Schritt 2 können wir jetzt das Blog-Einsammel-Programm erstellen und ausführen, so dass damit unsere Vektor-Datenbank erstellt wird. Ich nenne dieses Programm im Folgenden ‚Verwaltungsprogramm‘.

    1. Das Verwaltungsprogramm erstellen

    Du gehst wie folgt vor:

    Linux/Mac:

    nano blog_verwalten.py
    nano blog_verwalten.py

    Windows:

    notepad blog_verwalten.py
    notepad blog_verwalten.py

    Dann trägst du diesen Inhalt ein:

    import requests
    import chromadb
    from chromadb.utils import embedding_functions
    from bs4 import BeautifulSoup
    import feedparser
    from urllib.parse import urlparse
    from urllib.robotparser import RobotFileParser
    
    # Vektordatenbank-Setup
    client = chromadb.PersistentClient(path="./chroma_db")
    embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
        model_name="paraphrase-multilingual-MiniLM-L12-v2"
    )
    collection = client.get_or_create_collection(
        name="blog_posts",
        embedding_function=embedding_fn
    )
    
    def ist_gueltige_wahl(eingabe, optionen):
        """Prüft ob Eingabe eine gültige Menü-Option ist"""
        return eingabe in optionen
    
    def blog_existiert(blog_url):
        """Prüft ob Blog bereits in Datenbank ist"""
        alle_daten = collection.get()
        for meta in alle_daten['metadatas']:
            if meta['blog_url'] == blog_url:
                return True
        return False
    
    def pruefe_robots_txt(blog_url):
        """Prüft, ob Scraping erlaubt ist"""
        print("[1/5] Prüfe robots.txt... ", end="", flush=True)
        try:
            rp = RobotFileParser()
            rp.set_url(f"https://{blog_url}/robots.txt")
            rp.read()
            if rp.can_fetch("*", f"https://{blog_url}/"):
                print("✅ Scraping erlaubt")
                return True
            else:
                print("❌ Blog erlaubt kein automatisches Auslesen")
                print("\n💡 Respektiere die Vorgaben des Blogs!")
                print("   Alternative: XML-Export hochladen (falls du Zugriff hast)\n")
                return False
        except:
            # Keine robots.txt = erlaubt
            print("✅ Keine robots.txt (= erlaubt)")
            return True
    
    def hole_via_wordpress_api(blog_url):
        """Versucht Beiträge über WordPress REST API zu holen"""
        print("[2/5] WordPress REST API... ", end="", flush=True)
        alle_posts = []
        seite = 1
        try:
            while True:
                url = f"https://{blog_url}/wp-json/wp/v2/posts?per_page=100&page={seite}"
                response = requests.get(url, timeout=10)
                if response.status_code != 200 or not response.json():
                    break
                alle_posts.extend(response.json())
                seite += 1
    
            if alle_posts:
                print(f"✅ {len(alle_posts)} Beiträge gefunden")
                return [(p['title']['rendered'],
                        BeautifulSoup(p['content']['rendered'], 'html.parser').get_text(),
                        p['link'])
                       for p in alle_posts]
            else:
                print("❌ nicht verfügbar")
                return []
        except:
            print("❌ nicht verfügbar")
            return []
    
    def hole_via_rss(blog_url):
        """Versucht Beiträge über RSS-Feed zu holen"""
        print("[3/5] RSS-Feed Standard... ", end="", flush=True)
        feed_urls = [
            f"https://{blog_url}/feed/",
            f"https://{blog_url}/rss/",
            f"https://{blog_url}/feed.xml",
            f"https://{blog_url}/rss.xml"
        ]
    
        for feed_url in feed_urls:
            try:
                feed = feedparser.parse(feed_url)
                if feed.entries:
                    print(f"✅ {len(feed.entries)} Beiträge gefunden")
                    return [(e.title,
                            BeautifulSoup(e.get('content', [{}])[0].get('value', e.get('summary', '')), 'html.parser').get_text(),
                            e.link)
                           for e in feed.entries]
            except:
                continue
    
        print("❌ nicht verfügbar")
        return []
    
    def hole_via_rss_pagination(blog_url):
        """Versucht RSS mit Pagination"""
        print("[4/5] RSS mit Pagination... ", end="", flush=True)
        alle_posts = []
        seite = 2  # Seite 1 hatten wir schon
        try:
            while seite <= 50:  # Max 50 Seiten
                feed_url = f"https://{blog_url}/feed/?paged={seite}"
                feed = feedparser.parse(feed_url)
                if not feed.entries:
                    break
                alle_posts.extend(feed.entries)
                seite += 1
    
            if alle_posts:
                print(f"✅ weitere {len(alle_posts)} Beiträge")
                return [(e.title,
                        BeautifulSoup(e.get('content', [{}])[0].get('value', e.get('summary', '')), 'html.parser').get_text(),
                        e.link)
                       for e in alle_posts]
            else:
                print("❌ nicht verfügbar")
                return []
        except:
            print("❌ nicht verfügbar")
            return []
    
    def blog_hinzufuegen(blog_url):
        """Holt alle Beiträge von einem Blog"""
        # Prüfen ob Blog schon existiert
        if blog_existiert(blog_url):
            print(f"\n⚠️  Blog {blog_url} ist bereits gespeichert!")
            print("💡 Tipp: Erst löschen (Option 3), dann neu hinzufügen.\n")
            return
    
        print(f"\n📥 Versuche Beiträge von {blog_url} zu holen...\n")
    
        # 1. robots.txt prüfen
        if not pruefe_robots_txt(blog_url):
            return
    
        # 2-4. Verschiedene Methoden probieren
        alle_beitraege = []
    
        beitraege = hole_via_wordpress_api(blog_url)
        if beitraege:
            alle_beitraege.extend(beitraege)
        else:
            beitraege = hole_via_rss(blog_url)
            if beitraege:
                alle_beitraege.extend(beitraege)
                # Versuche noch mehr zu holen
                mehr = hole_via_rss_pagination(blog_url)
                if mehr:
                    alle_beitraege.extend(mehr)
    
        print(f"[5/5] Zusammenfassung... ", end="", flush=True)
    
        if not alle_beitraege:
            print("❌")
            print("\n❌ Keine Beiträge gefunden!\n")
            print("Mögliche Gründe:")
            print("• API/Feed sind deaktiviert")
            print("• URL ist nicht erreichbar")
            print("• Blog nutzt ungewöhnliche Struktur")
            print("\n💡 Alternative: XML-Export hochladen\n")
            return
    
        print(f"✅ {len(alle_beitraege)} Beiträge insgesamt")
        print("\n💾 Speichere in Datenbank...")
    
        for i, (titel, inhalt, link) in enumerate(alle_beitraege):
            collection.add(
                documents=[inhalt],
                metadatas=[{
                    "title": titel,
                    "link": link,
                    "blog_url": blog_url
                }],
                ids=[f"{blog_url}_{i}"]
            )
            if (i+1) % 50 == 0:
                print(f"  {i+1} Beiträge verarbeitet...")
    
        print(f"✓ Fertig! {len(alle_beitraege)} Beiträge von {blog_url} gespeichert.\n")
    
    def blogs_anzeigen():
        """Zeigt alle gespeicherten Blogs"""
        alle_daten = collection.get()
        if not alle_daten['metadatas']:
            print("\n❌ Noch keine Blogs gespeichert!\n")
            return
    
        blogs = {}
        for meta in alle_daten['metadatas']:
            blog = meta['blog_url']
            blogs[blog] = blogs.get(blog, 0) + 1
    
        print(f"\n📚 Gespeicherte Blogs ({len(blogs)}):")
        for blog, anzahl in sorted(blogs.items()):
            print(f"  • {blog} ({anzahl} Beiträge)")
        print()
    
    def blog_loeschen(blog_url):
        """Löscht alle Beiträge eines Blogs"""
        alle_daten = collection.get()
        ids_to_delete = [
            alle_daten['ids'][i] 
            for i, meta in enumerate(alle_daten['metadatas']) 
            if meta['blog_url'] == blog_url
        ]
    
        if not ids_to_delete:
            print(f"\n❌ Blog {blog_url} nicht gefunden!\n")
            return
    
        collection.delete(ids=ids_to_delete)
        print(f"\n✓ {len(ids_to_delete)} Beiträge von {blog_url} gelöscht.\n")
    
    def menu():
        while True:
            print("=" * 50)
            print("BLOG-CHATBOT VERWALTUNG")
            print("=" * 50)
            print("1 - Blog hinzufügen")
            print("2 - Blogs anzeigen")
            print("3 - Blog löschen")
            print("0 - Beenden")
            print("=" * 50)
    
            wahl = input("\nDeine Wahl: ").strip()
    
            if not ist_gueltige_wahl(wahl, ['0', '1', '2', '3']):
                print("\n❌ Ungültige Eingabe! Bitte wähle eine der Optionen (0-3).\n")
                continue
    
            if wahl == "1":
                url = input("Blog-URL (z.B. ebildungslabor.de): ").strip()
                blog_hinzufuegen(url)
            elif wahl == "2":
                blogs_anzeigen()
            elif wahl == "3":
                url = input("Welchen Blog löschen? ").strip()
                blog_loeschen(url)
            elif wahl == "0":
                print("\nTschüss! 👋\n")
                break
    
    if __name__ == "__main__":
        menu()
    import requestsimport chromadbfrom chromadb.utils import embedding_functionsfrom bs4 import BeautifulSoupimport feedparserfrom urllib.parse import urlparsefrom urllib.robotparser import RobotFileParser# Vektordatenbank-Setupclient = chromadb.PersistentClient(path="./chroma_db")embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(    model_name="paraphrase-multilingual-MiniLM-L12-v2")collection = client.get_or_create_collection(    name="blog_posts",    embedding_function=embedding_fn)def ist_gueltige_wahl(eingabe, optionen):    """Prüft ob Eingabe eine gültige Menü-Option ist"""    return eingabe in optionendef blog_existiert(blog_url):    """Prüft ob Blog bereits in Datenbank ist"""    alle_daten = collection.get()    for meta in alle_daten['metadatas']:        if meta['blog_url'] == blog_url:            return True    return Falsedef pruefe_robots_txt(blog_url):    """Prüft, ob Scraping erlaubt ist"""    print("[1/5] Prüfe robots.txt... ", end="", flush=True)    try:        rp = RobotFileParser()        rp.set_url(f"https://{blog_url}/robots.txt")        rp.read()        if rp.can_fetch("*", f"https://{blog_url}/"):            print("✅ Scraping erlaubt")            return True        else:            print("❌ Blog erlaubt kein automatisches Auslesen")            print("\n💡 Respektiere die Vorgaben des Blogs!")            print("   Alternative: XML-Export hochladen (falls du Zugriff hast)\n")            return False    except:        # Keine robots.txt = erlaubt        print("✅ Keine robots.txt (= erlaubt)")        return Truedef hole_via_wordpress_api(blog_url):    """Versucht Beiträge über WordPress REST API zu holen"""    print("[2/5] WordPress REST API... ", end="", flush=True)    alle_posts = []    seite = 1    try:        while True:            url = f"https://{blog_url}/wp-json/wp/v2/posts?per_page=100&page={seite}"            response = requests.get(url, timeout=10)            if response.status_code != 200 or not response.json():                break            alle_posts.extend(response.json())            seite += 1        if alle_posts:            print(f"✅ {len(alle_posts)} Beiträge gefunden")            return [(p['title']['rendered'],                    BeautifulSoup(p['content']['rendered'], 'html.parser').get_text(),                    p['link'])                   for p in alle_posts]        else:            print("❌ nicht verfügbar")            return []    except:        print("❌ nicht verfügbar")        return []def hole_via_rss(blog_url):    """Versucht Beiträge über RSS-Feed zu holen"""    print("[3/5] RSS-Feed Standard... ", end="", flush=True)    feed_urls = [        f"https://{blog_url}/feed/",        f"https://{blog_url}/rss/",        f"https://{blog_url}/feed.xml",        f"https://{blog_url}/rss.xml"    ]    for feed_url in feed_urls:        try:            feed = feedparser.parse(feed_url)            if feed.entries:                print(f"✅ {len(feed.entries)} Beiträge gefunden")                return [(e.title,                        BeautifulSoup(e.get('content', [{}])[0].get('value', e.get('summary', '')), 'html.parser').get_text(),                        e.link)                       for e in feed.entries]        except:            continue    print("❌ nicht verfügbar")    return []def hole_via_rss_pagination(blog_url):    """Versucht RSS mit Pagination"""    print("[4/5] RSS mit Pagination... ", end="", flush=True)    alle_posts = []    seite = 2  # Seite 1 hatten wir schon    try:        while seite <= 50:  # Max 50 Seiten            feed_url = f"https://{blog_url}/feed/?paged={seite}"            feed = feedparser.parse(feed_url)            if not feed.entries:                break            alle_posts.extend(feed.entries)            seite += 1        if alle_posts:            print(f"✅ weitere {len(alle_posts)} Beiträge")            return [(e.title,                    BeautifulSoup(e.get('content', [{}])[0].get('value', e.get('summary', '')), 'html.parser').get_text(),                    e.link)                   for e in alle_posts]        else:            print("❌ nicht verfügbar")            return []    except:        print("❌ nicht verfügbar")        return []def blog_hinzufuegen(blog_url):    """Holt alle Beiträge von einem Blog"""    # Prüfen ob Blog schon existiert    if blog_existiert(blog_url):        print(f"\n⚠️  Blog {blog_url} ist bereits gespeichert!")        print("💡 Tipp: Erst löschen (Option 3), dann neu hinzufügen.\n")        return    print(f"\n📥 Versuche Beiträge von {blog_url} zu holen...\n")    # 1. robots.txt prüfen    if not pruefe_robots_txt(blog_url):        return    # 2-4. Verschiedene Methoden probieren    alle_beitraege = []    beitraege = hole_via_wordpress_api(blog_url)    if beitraege:        alle_beitraege.extend(beitraege)    else:        beitraege = hole_via_rss(blog_url)        if beitraege:            alle_beitraege.extend(beitraege)            # Versuche noch mehr zu holen            mehr = hole_via_rss_pagination(blog_url)            if mehr:                alle_beitraege.extend(mehr)    print(f"[5/5] Zusammenfassung... ", end="", flush=True)    if not alle_beitraege:        print("")        print("\n❌ Keine Beiträge gefunden!\n")        print("Mögliche Gründe:")        print("• API/Feed sind deaktiviert")        print("• URL ist nicht erreichbar")        print("• Blog nutzt ungewöhnliche Struktur")        print("\n💡 Alternative: XML-Export hochladen\n")        return    print(f"✅ {len(alle_beitraege)} Beiträge insgesamt")    print("\n💾 Speichere in Datenbank...")    for i, (titel, inhalt, link) in enumerate(alle_beitraege):        collection.add(            documents=[inhalt],            metadatas=[{                "title": titel,                "link": link,                "blog_url": blog_url            }],            ids=[f"{blog_url}_{i}"]        )        if (i+1) % 50 == 0:            print(f"  {i+1} Beiträge verarbeitet...")    print(f"✓ Fertig! {len(alle_beitraege)} Beiträge von {blog_url} gespeichert.\n")def blogs_anzeigen():    """Zeigt alle gespeicherten Blogs"""    alle_daten = collection.get()    if not alle_daten['metadatas']:        print("\n❌ Noch keine Blogs gespeichert!\n")        return    blogs = {}    for meta in alle_daten['metadatas']:        blog = meta['blog_url']        blogs[blog] = blogs.get(blog, 0) + 1    print(f"\n📚 Gespeicherte Blogs ({len(blogs)}):")    for blog, anzahl in sorted(blogs.items()):        print(f"  • {blog} ({anzahl} Beiträge)")    print()def blog_loeschen(blog_url):    """Löscht alle Beiträge eines Blogs"""    alle_daten = collection.get()    ids_to_delete = [        alle_daten['ids'][i]         for i, meta in enumerate(alle_daten['metadatas'])         if meta['blog_url'] == blog_url    ]    if not ids_to_delete:        print(f"\n❌ Blog {blog_url} nicht gefunden!\n")        return    collection.delete(ids=ids_to_delete)    print(f"\n{len(ids_to_delete)} Beiträge von {blog_url} gelöscht.\n")def menu():    while True:        print("=" * 50)        print("BLOG-CHATBOT VERWALTUNG")        print("=" * 50)        print("1 - Blog hinzufügen")        print("2 - Blogs anzeigen")        print("3 - Blog löschen")        print("0 - Beenden")        print("=" * 50)        wahl = input("\nDeine Wahl: ").strip()        if not ist_gueltige_wahl(wahl, ['0', '1', '2', '3']):            print("\n❌ Ungültige Eingabe! Bitte wähle eine der Optionen (0-3).\n")            continue        if wahl == "1":            url = input("Blog-URL (z.B. ebildungslabor.de): ").strip()            blog_hinzufuegen(url)        elif wahl == "2":            blogs_anzeigen()        elif wahl == "3":            url = input("Welchen Blog löschen? ").strip()            blog_loeschen(url)        elif wahl == "0":            print("\nTschüss! 👋\n")            breakif __name__ == "__main__":    menu()

    Bei Linux/Mac: Speichere mit Strg+O, Enter, Strg+X

    Bei Windows: Speichere über Datei → Speichern und schließe Notepad

    2. Programm ausführen

    Jetzt kannst du das Programm starten:

    Für alle Betriebssysteme (virtuelle Umgebung muss aktiv sein):

    python blog_verwalten.py
    python blog_verwalten.py

    Mit der Eingabe dieses Befehls braucht es nur ein paar Sekunden bis das Verwaltungsprogramm im Terminal aktiv wird. Du wirst dann gefragt, was du machen willst. Am sinnvollsten ist es, hier jetzt erst einmal Blogs hinzuzufügen (Option 1).

    Wenn du diese Option auswählst, wirst du aufgefordert, die URL des gewünschten Blogs einzugeben, von dem die Inhalte geholt und gespeichert werden sollen.

    Manchmal wirst du die Meldung bekommen, dass Scraping bei diesem Blog nicht erlaubt ist. Das solltest du respektieren.

    Du kannst so viele Blogs einsammeln, wie du möchtest. Ich würde an deiner Stelle erst einmal mit 3-5 Blogs beginnen.

    Insgesamt ist dieser Schritt eher langweilig, weil du nicht siehst, was im Hintergrund passiert. Wenn du neugierig bist, kannst du aber über den Datei-Manager deine angelegte Projektumgebung öffnen. Darin siehst du den Ordner chroma_db mit einer SQLite-Datenbank und einem Ordner mit mehreren Vektoren, was aber für dich alles nicht direkt lesbar und verwendbar ist. Ich fand es trotzdem spannend, mir mal direkt anzuschauen, wie so etwas aussieht.

    Schritt 4: Deinen Chat-Bot gestalten

    Die angelegte Vektor-Datenbank und der eingetragene Mistral-API-Key sind nun die Basis, mit der du deinen Blog-Chatbot erstellst. Grundsätzlich lässt sich dieser sehr unterschiedlich gestalten. Im Bot-Programm, das ich dir im folgenden vorschlage, wirst du beim Start des Chats immer erst gefragt, auf welche Blogs du dich bei deiner Frage beziehen willst. Dann kannst du einen oder mehrere der eingespeicherten Blogs auswählen, dann eine Frage stellen und daraufhin dann eine möglichst hilfreiche Antwort erhalten. Mit dem Befehl ‚quellen‘ erreichst du, dass die Links zu den durchsuchten Beiträgen angezeigt werden. Mit dem Befehl ‚mehr‘ werden nicht nur 3, sondern 10 Beiträge für die Antwort verwendet. (Du kannst diese Zahlen natürlich auch hochsetzen, dann kostet dich das mehr Tokens. Für mich passt es so ganz gut).

    In dem Chatbot-Programm ist auch ein Systemprompt enthalten, den du ebenfalls ändern kannst, wenn du das möchtest. Aber auch hier ist es wahrscheinlich ganz sinnvoll, erst einmal diese Vorlage auszuprobieren. Ich habe eine ganze Weile experimentiert und war dann mit der jetzt entstandenen Version recht zufrieden!

    So gehst du vor, um das Chat-Programm einzutragen:

    Linux/Mac:

    nano chatbot.py
    nano chatbot.py

    Windows:

    notepad chatbot.py
    notepad chatbot.py

    Du trägst diesen Inhalt ein:

    import os
    from dotenv import load_dotenv
    import chromadb
    from chromadb.utils import embedding_functions
    from mistralai import Mistral
    
    # Lade API-Key
    load_dotenv()
    api_key = os.getenv("MISTRAL_API_KEY")
    client_mistral = Mistral(api_key=api_key)
    
    # Lade Vektordatenbank
    client_chroma = chromadb.PersistentClient(path="./chroma_db")
    embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
        model_name="paraphrase-multilingual-MiniLM-L12-v2"
    )
    collection = client_chroma.get_collection(
        name="blog_posts",
        embedding_function=embedding_fn
    )
    
    # Speichere letzte Quellen und Frage
    letzte_quellen = []
    letzte_frage = ""
    
    def hole_verfuegbare_blogs():
        """Zeigt alle gespeicherten Blogs"""
        alle_daten = collection.get()
        if not alle_daten['metadatas']:
            return []
    
        blogs = set(meta['blog_url'] for meta in alle_daten['metadatas'])
        return sorted(list(blogs))
    
    def blog_auswahl():
        """Lässt Nutzer*in Blogs auswählen"""
        blogs = hole_verfuegbare_blogs()
    
        if not blogs:
            print("\n❌ Keine Blogs gespeichert!")
            print("💡 Führe erst 'python blog_verwalten.py' aus.\n")
            return None
    
        print("\n📚 Verfügbare Blogs:")
        print("0 - Alle Blogs durchsuchen")
        for i, blog in enumerate(blogs, 1):
            print(f"{i} - {blog}")
    
        while True:
            wahl = input("\nWelche Blogs durchsuchen? (Zahl oder mehrere mit Komma, z.B. 1,3): ").strip()
    
            if wahl == "0":
                return None  # None = alle Blogs
    
            try:
                nummern = [int(n.strip()) for n in wahl.split(",")]
                ausgewaehlte = [blogs[n-1] for n in nummern if 1 <= n <= len(blogs)]
                if ausgewaehlte:
                    return ausgewaehlte
                else:
                    print("❌ Ungültige Auswahl!")
            except:
                print("❌ Ungültige Eingabe!")
    
    def extrahiere_keywords(frage):
        """Extrahiert wichtige Schlüsselwörter aus der Frage"""
        stopwords = ['was', 'ist', 'sind', 'der', 'die', 'das', 'ein', 'eine', 'mit', 'zu', 'im', 'in',
                     'von', 'für', 'und', 'oder', 'wie', 'wo', 'wann', 'warum', 'welche', 'welcher',
                     'gibt', 'es', 'dazu', 'darüber', 'findest', 'du', 'infos', 'informationen',
                     'schreibt', 'blog', 'beitrag', 'dem', 'den', 'des', 'an', 'auf', 'bei',
                     'nach', 'über', 'aus', 'hier', 'da', 'haben', 'hat', 'wird', 'werden', 'kann',
                     'können', 'soll', 'sollen', 'muss', 'müssen']
    
        woerter = frage.lower().split()
        keywords = [w.strip('?.,!') for w in woerter if w.lower() not in stopwords]
        return keywords
    
    def keyword_suche(keywords, blog_filter=None):
        """Sucht direkt nach Keywords in Titeln und Texten"""
        alle_daten = collection.get()
        treffer = []
    
        for i, meta in enumerate(alle_daten['metadatas']):
            # Blog-Filter anwenden
            if blog_filter and meta['blog_url'] not in blog_filter:
                continue
    
            titel_lower = meta['title'].lower()
            text_lower = alle_daten['documents'][i].lower()
    
            # Zähle Keyword-Matches
            titel_matches = sum(1 for kw in keywords if kw in titel_lower)
            text_matches = sum(1 for kw in keywords if kw in text_lower)
    
            if titel_matches > 0 or text_matches > 1:  # Mind. 1 im Titel ODER 2 im Text
                treffer.append({
                    'id': alle_daten['ids'][i],
                    'doc': alle_daten['documents'][i],
                    'meta': meta,
                    'score': titel_matches * 3 + text_matches
                })
    
        # Nach Score sortieren
        treffer.sort(key=lambda x: x['score'], reverse=True)
        return treffer
    
    def hybrid_suche(frage, blog_filter=None, anzahl_beitraege=3):
        """Kombiniert Keyword-First mit semantischer Suche"""
        # 1. Keywords extrahieren
        keywords = extrahiere_keywords(frage)
    
        # 2. Keyword-Suche
        keyword_treffer = keyword_suche(keywords, blog_filter)
    
        # 3. Semantische Vektorsuche
        if blog_filter:
            vektor_results = collection.query(
                query_texts=[frage],
                n_results=anzahl_beitraege * 2,
                where={"blog_url": {"$in": blog_filter}}
            )
        else:
            vektor_results = collection.query(
                query_texts=[frage],
                n_results=anzahl_beitraege * 2
            )
    
        # 4. Kombiniere: Keyword-Treffer zuerst, dann Vektor-Results
        finale_results = []
        verwendete_ids = set()
    
        # Keyword-Treffer haben Priorität
        for treffer in keyword_treffer[:anzahl_beitraege]:
            finale_results.append({
                'doc': treffer['doc'],
                'meta': treffer['meta'],
                'ist_keyword_match': True
            })
            verwendete_ids.add(treffer['id'])
    
        # Fülle auf mit Vektor-Results
        if vektor_results and vektor_results['documents'][0]:
            for i in range(len(vektor_results['documents'][0])):
                if len(finale_results) >= anzahl_beitraege:
                    break
    
                meta = vektor_results['metadatas'][0][i]
    
                # Prüfe ob schon dabei
                if any(r['meta']['link'] == meta['link'] for r in finale_results):
                    continue
    
                finale_results.append({
                    'doc': vektor_results['documents'][0][i],
                    'meta': meta,
                    'ist_keyword_match': False
                })
    
        if not finale_results:
            return None
    
        return {
            'documents': [[r['doc'] for r in finale_results]],
            'metadatas': [[r['meta'] for r in finale_results]],
            'keyword_flags': [r['ist_keyword_match'] for r in finale_results]
        }
    
    def chat(frage, blog_filter=None, konversation_history=None, anzahl_beitraege=3):
        """Beantwortet Frage basierend auf Blog-Inhalten"""
        global letzte_quellen, letzte_frage
    
        if konversation_history is None:
            konversation_history = []
    
        # Frage speichern
        letzte_frage = frage
    
        # Hybrid-Suche
        results = hybrid_suche(frage, blog_filter, anzahl_beitraege)
    
        if not results or not results['documents'][0]:
            return "❌ Keine passenden Beiträge gefunden!", konversation_history
    
        # Quellen speichern
        letzte_quellen = []
        for i, meta in enumerate(results['metadatas'][0], 1):
            marker = "🎯" if results['keyword_flags'][i-1] else ""
            letzte_quellen.append(f"   {marker}{i}. {meta['title']}\n      {meta['link']}")
    
        # Kontext zusammenbauen
        kontext_teile = []
        for i, doc in enumerate(results['documents'][0], 1):
            meta = results['metadatas'][0][i-1]
            kontext_teile.append(f"[Beitrag {i}: {meta['title']}]\n{doc[:2000]}")
    
        kontext = "\n\n---\n\n".join(kontext_teile)
    
        # System-Prompt
        system_prompt = """Du bist ein hilfreicher Assistent, der auf Basis von Blogbeiträgen antwortet.
    
    REGELN:
    1. Nutze ALLE Informationen aus den gegebenen Texten
    2. Lies die Texte VOLLSTÄNDIG und GENAU
    3. Wenn ein Text die Antwort enthält, NUTZE sie
    4. Antworte natürlich und gesprächig
    5. KEINE Quellenangaben in der Antwort (nur auf explizite Nachfrage)
    6. Wenn du etwas nicht weißt: Sage es ehrlich"""
    
        # Konversationshistorie aufbauen
        messages = [{"role": "system", "content": system_prompt}]
    
        # Bisherige Konversation
        for msg in konversation_history:
            messages.append(msg)
    
        # Aktuelle Frage mit Kontext
        user_message = f"""VERFÜGBARE BLOGBEITRÄGE:
    {kontext}
    
    ---
    
    FRAGE: {frage}"""
    
        messages.append({"role": "user", "content": user_message})
    
        # API-Aufruf
        response = client_mistral.chat.complete(
            model="mistral-small-latest",
            messages=messages,
            temperature=0.3
        )
    
        antwort = response.choices[0].message.content
    
        # Konversation aktualisieren
        konversation_history.append({"role": "user", "content": frage})
        konversation_history.append({"role": "assistant", "content": antwort})
    
        return antwort, konversation_history
    
    def zeige_quellen():
        """Zeigt die durchsuchten Beiträge"""
        global letzte_quellen
    
        if not letzte_quellen:
            print("\n❌ Keine Quellen verfügbar (stelle erst eine Frage).\n")
            return
    
        print("\n📖 Diese Beiträge wurden als relevant erachtet und durchsucht:")
        print("   (🎯 = Keyword-Match im Titel oder Text)\n")
        for quelle in letzte_quellen:
            print(quelle)
        print()
    
    def main():
        global letzte_frage
    
        print("\n" + "=" * 50)
        print("BLOG-CHATBOT")
        print("=" * 50)
    
        # Blog-Auswahl
        blog_filter = blog_auswahl()
    
        if blog_filter is None:
            print("\n🔍 Durchsuche: ALLE Blogs")
        else:
            print(f"\n🔍 Durchsuche: {', '.join(blog_filter)}")
    
        print("\n💬 Stelle deine Fragen!")
        print("\n📋 Befehle:")
        print("   • 'mehr'     → Durchsucht 10 statt 3 Beiträge zur letzten Frage")
        print("   • 'quellen'  → Zeigt die durchsuchten Beiträge")
        print("   • 'wechseln' → Ändert die Blog-Auswahl")
        print("   • 'exit'     → Beendet das Programm\n")
    
        # Konversationshistorie
        konversation = []
    
        # Chat-Loop
        while True:
            eingabe = input("❓ ")
    
            if eingabe.lower() in ['exit', 'quit', 'tschüss', 'bye']:
                print("\nBis bald! 👋\n")
                break
    
            if eingabe.lower() == 'quellen':
                zeige_quellen()
                continue
    
            if eingabe.lower() == 'mehr':
                if not letzte_frage:
                    print("\n❌ Stelle erst eine Frage!\n")
                    continue
                print(f"\n🔎 Durchsuche nun 10 Beiträge zur Frage: '{letzte_frage}'\n")
                antwort, konversation = chat(letzte_frage, blog_filter, konversation, anzahl_beitraege=10)
                print(f"\n💡 {antwort}\n")
                continue
    
            if eingabe.lower() == 'wechseln':
                blog_filter = blog_auswahl()
                konversation = []
                letzte_frage = ""
                if blog_filter is None:
                    print("\n🔍 Durchsuche: ALLE Blogs\n")
                else:
                    print(f"\n🔍 Durchsuche: {', '.join(blog_filter)}\n")
                continue
    
            if not eingabe.strip():
                continue
    
            antwort, konversation = chat(eingabe, blog_filter, konversation, anzahl_beitraege=3)
            print(f"\n💡 {antwort}\n")
    
    if __name__ == "__main__":
        main()
    import osfrom dotenv import load_dotenvimport chromadbfrom chromadb.utils import embedding_functionsfrom mistralai import Mistral# Lade API-Keyload_dotenv()api_key = os.getenv("MISTRAL_API_KEY")client_mistral = Mistral(api_key=api_key)# Lade Vektordatenbankclient_chroma = chromadb.PersistentClient(path="./chroma_db")embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(    model_name="paraphrase-multilingual-MiniLM-L12-v2")collection = client_chroma.get_collection(    name="blog_posts",    embedding_function=embedding_fn)# Speichere letzte Quellen und Frageletzte_quellen = []letzte_frage = ""def hole_verfuegbare_blogs():    """Zeigt alle gespeicherten Blogs"""    alle_daten = collection.get()    if not alle_daten['metadatas']:        return []    blogs = set(meta['blog_url'] for meta in alle_daten['metadatas'])    return sorted(list(blogs))def blog_auswahl():    """Lässt Nutzer*in Blogs auswählen"""    blogs = hole_verfuegbare_blogs()    if not blogs:        print("\n❌ Keine Blogs gespeichert!")        print("💡 Führe erst 'python blog_verwalten.py' aus.\n")        return None    print("\n📚 Verfügbare Blogs:")    print("0 - Alle Blogs durchsuchen")    for i, blog in enumerate(blogs, 1):        print(f"{i} - {blog}")    while True:        wahl = input("\nWelche Blogs durchsuchen? (Zahl oder mehrere mit Komma, z.B. 1,3): ").strip()        if wahl == "0":            return None  # None = alle Blogs        try:            nummern = [int(n.strip()) for n in wahl.split(",")]            ausgewaehlte = [blogs[n-1] for n in nummern if 1 <= n <= len(blogs)]            if ausgewaehlte:                return ausgewaehlte            else:                print("❌ Ungültige Auswahl!")        except:            print("❌ Ungültige Eingabe!")def extrahiere_keywords(frage):    """Extrahiert wichtige Schlüsselwörter aus der Frage"""    stopwords = ['was', 'ist', 'sind', 'der', 'die', 'das', 'ein', 'eine', 'mit', 'zu', 'im', 'in',                 'von', 'für', 'und', 'oder', 'wie', 'wo', 'wann', 'warum', 'welche', 'welcher',                 'gibt', 'es', 'dazu', 'darüber', 'findest', 'du', 'infos', 'informationen',                 'schreibt', 'blog', 'beitrag', 'dem', 'den', 'des', 'an', 'auf', 'bei',                 'nach', 'über', 'aus', 'hier', 'da', 'haben', 'hat', 'wird', 'werden', 'kann',                 'können', 'soll', 'sollen', 'muss', 'müssen']    woerter = frage.lower().split()    keywords = [w.strip('?.,!') for w in woerter if w.lower() not in stopwords]    return keywordsdef keyword_suche(keywords, blog_filter=None):    """Sucht direkt nach Keywords in Titeln und Texten"""    alle_daten = collection.get()    treffer = []    for i, meta in enumerate(alle_daten['metadatas']):        # Blog-Filter anwenden        if blog_filter and meta['blog_url'] not in blog_filter:            continue        titel_lower = meta['title'].lower()        text_lower = alle_daten['documents'][i].lower()        # Zähle Keyword-Matches        titel_matches = sum(1 for kw in keywords if kw in titel_lower)        text_matches = sum(1 for kw in keywords if kw in text_lower)        if titel_matches > 0 or text_matches > 1:  # Mind. 1 im Titel ODER 2 im Text            treffer.append({                'id': alle_daten['ids'][i],                'doc': alle_daten['documents'][i],                'meta': meta,                'score': titel_matches * 3 + text_matches            })    # Nach Score sortieren    treffer.sort(key=lambda x: x['score'], reverse=True)    return trefferdef hybrid_suche(frage, blog_filter=None, anzahl_beitraege=3):    """Kombiniert Keyword-First mit semantischer Suche"""    # 1. Keywords extrahieren    keywords = extrahiere_keywords(frage)    # 2. Keyword-Suche    keyword_treffer = keyword_suche(keywords, blog_filter)    # 3. Semantische Vektorsuche    if blog_filter:        vektor_results = collection.query(            query_texts=[frage],            n_results=anzahl_beitraege * 2,            where={"blog_url": {"$in": blog_filter}}        )    else:        vektor_results = collection.query(            query_texts=[frage],            n_results=anzahl_beitraege * 2        )    # 4. Kombiniere: Keyword-Treffer zuerst, dann Vektor-Results    finale_results = []    verwendete_ids = set()    # Keyword-Treffer haben Priorität    for treffer in keyword_treffer[:anzahl_beitraege]:        finale_results.append({            'doc': treffer['doc'],            'meta': treffer['meta'],            'ist_keyword_match': True        })        verwendete_ids.add(treffer['id'])    # Fülle auf mit Vektor-Results    if vektor_results and vektor_results['documents'][0]:        for i in range(len(vektor_results['documents'][0])):            if len(finale_results) >= anzahl_beitraege:                break            meta = vektor_results['metadatas'][0][i]            # Prüfe ob schon dabei            if any(r['meta']['link'] == meta['link'] for r in finale_results):                continue            finale_results.append({                'doc': vektor_results['documents'][0][i],                'meta': meta,                'ist_keyword_match': False            })    if not finale_results:        return None    return {        'documents': [[r['doc'] for r in finale_results]],        'metadatas': [[r['meta'] for r in finale_results]],        'keyword_flags': [r['ist_keyword_match'] for r in finale_results]    }def chat(frage, blog_filter=None, konversation_history=None, anzahl_beitraege=3):    """Beantwortet Frage basierend auf Blog-Inhalten"""    global letzte_quellen, letzte_frage    if konversation_history is None:        konversation_history = []    # Frage speichern    letzte_frage = frage    # Hybrid-Suche    results = hybrid_suche(frage, blog_filter, anzahl_beitraege)    if not results or not results['documents'][0]:        return "❌ Keine passenden Beiträge gefunden!", konversation_history    # Quellen speichern    letzte_quellen = []    for i, meta in enumerate(results['metadatas'][0], 1):        marker = "🎯" if results['keyword_flags'][i-1] else ""        letzte_quellen.append(f"   {marker}{i}. {meta['title']}\n      {meta['link']}")    # Kontext zusammenbauen    kontext_teile = []    for i, doc in enumerate(results['documents'][0], 1):        meta = results['metadatas'][0][i-1]        kontext_teile.append(f"[Beitrag {i}: {meta['title']}]\n{doc[:2000]}")    kontext = "\n\n---\n\n".join(kontext_teile)    # System-Prompt    system_prompt = """Du bist ein hilfreicher Assistent, der auf Basis von Blogbeiträgen antwortet.REGELN:1. Nutze ALLE Informationen aus den gegebenen Texten2. Lies die Texte VOLLSTÄNDIG und GENAU3. Wenn ein Text die Antwort enthält, NUTZE sie4. Antworte natürlich und gesprächig5. KEINE Quellenangaben in der Antwort (nur auf explizite Nachfrage)6. Wenn du etwas nicht weißt: Sage es ehrlich"""    # Konversationshistorie aufbauen    messages = [{"role": "system", "content": system_prompt}]    # Bisherige Konversation    for msg in konversation_history:        messages.append(msg)    # Aktuelle Frage mit Kontext    user_message = f"""VERFÜGBARE BLOGBEITRÄGE:{kontext}---FRAGE: {frage}"""    messages.append({"role": "user", "content": user_message})    # API-Aufruf    response = client_mistral.chat.complete(        model="mistral-small-latest",        messages=messages,        temperature=0.3    )    antwort = response.choices[0].message.content    # Konversation aktualisieren    konversation_history.append({"role": "user", "content": frage})    konversation_history.append({"role": "assistant", "content": antwort})    return antwort, konversation_historydef zeige_quellen():    """Zeigt die durchsuchten Beiträge"""    global letzte_quellen    if not letzte_quellen:        print("\n❌ Keine Quellen verfügbar (stelle erst eine Frage).\n")        return    print("\n📖 Diese Beiträge wurden als relevant erachtet und durchsucht:")    print("   (🎯 = Keyword-Match im Titel oder Text)\n")    for quelle in letzte_quellen:        print(quelle)    print()def main():    global letzte_frage    print("\n" + "=" * 50)    print("BLOG-CHATBOT")    print("=" * 50)    # Blog-Auswahl    blog_filter = blog_auswahl()    if blog_filter is None:        print("\n🔍 Durchsuche: ALLE Blogs")    else:        print(f"\n🔍 Durchsuche: {', '.join(blog_filter)}")    print("\n💬 Stelle deine Fragen!")    print("\n📋 Befehle:")    print("   • 'mehr'     → Durchsucht 10 statt 3 Beiträge zur letzten Frage")    print("   • 'quellen'  → Zeigt die durchsuchten Beiträge")    print("   • 'wechseln' → Ändert die Blog-Auswahl")    print("   • 'exit'     → Beendet das Programm\n")    # Konversationshistorie    konversation = []    # Chat-Loop    while True:        eingabe = input("")        if eingabe.lower() in ['exit', 'quit', 'tschüss', 'bye']:            print("\nBis bald! 👋\n")            break        if eingabe.lower() == 'quellen':            zeige_quellen()            continue        if eingabe.lower() == 'mehr':            if not letzte_frage:                print("\n❌ Stelle erst eine Frage!\n")                continue            print(f"\n🔎 Durchsuche nun 10 Beiträge zur Frage: '{letzte_frage}'\n")            antwort, konversation = chat(letzte_frage, blog_filter, konversation, anzahl_beitraege=10)            print(f"\n💡 {antwort}\n")            continue        if eingabe.lower() == 'wechseln':            blog_filter = blog_auswahl()            konversation = []            letzte_frage = ""            if blog_filter is None:                print("\n🔍 Durchsuche: ALLE Blogs\n")            else:                print(f"\n🔍 Durchsuche: {', '.join(blog_filter)}\n")            continue        if not eingabe.strip():            continue        antwort, konversation = chat(eingabe, blog_filter, konversation, anzahl_beitraege=3)        print(f"\n💡 {antwort}\n")if __name__ == "__main__":    main()

    Bei Linux/Mac: Speichere mit Strg+O, Enter, Strg+X

    Bei Windows: Speichere über Datei → Speichern und schließe Notepad

    Damit hast du es geschafft und kannst deinen Chatbot jetzt nutzen.

    Schritt 5: Deinen Chatbot nutzen!

    Mit diesem Befehl startest du deinen Chatbot:

    Für alle Betriebssysteme (virtuelle Umgebung muss aktiv sein):

    python chatbot.py
    python chatbot.py

    Es braucht wahrscheinlich ein paar Sekunden. Wie oben beschrieben stellt der Chatbot dir dann in der oben geteilten Version des Bot-Programms zunächst die Frage, welche eingespeicherten Blogs du als Basis nutzen willst. Dann kannst du deine Frage eingeben. Wenn du noch tiefer recherchieren willst, dann gib mehr ein. Wenn du andere Blogs als Grundlage nehmen willst, dann nutze den Befehl wechseln. Mit quellen siehst du, welche Beiträge der Bot durchsucht hat.

    Hier ist ein exemplarischer Chatverlauf, bei dem ich den Bot auf die eBildungslabor-Beiträge als RAG fokussiert habe:

    ❓ Wie definiert Nele Kreativität?

    💡 Nele definiert Kreativität im pädagogischen Kontext als die Fähigkeit, Neues denken zu können. Dabei geht es nicht primär um künstlerisches Tätigsein, sondern darum, über das Bestehende hinauszudenken und innovative Lösungen zu finden. Diese Definition steht im Rahmen der 4K-Kompetenzen, die auch Kommunikation, Kollaboration und kritisches Denken umfassen. Kreativität wird hier also als eine Schlüsselkompetenz für den digitalen Wandel und die aktive Gestaltung unserer Gesellschaft verstanden.

    ❓ Was schreibt Nele zu H5P?

    💡 Nele schreibt über H5P, dass es ein großartiges und offenes Tool zur Gestaltung von interaktiven Online-Materialien zum Lernen und Lehren ist. Sie schätzt besonders, dass es immer wieder neue Inhaltstypen gibt, die viel Raum für eine gezielte Nutzung im jeweiligen pädagogischen Setting bieten. Nele erwähnt, dass viele ihrer Lieblings-Inhaltstypen von Oliver, einem freiberuflichen Programmierer für H5P, entwickelt wurden.

    Sie stellt verschiedene Anwendungen und Möglichkeiten von H5P vor, wie den Phrase-Randomizer, der für kreative Ideenentwicklung genutzt werden kann, oder die Game Map, mit der interaktive Karten gestaltet werden können. Nele beschreibt auch, wie sie H5P genutzt hat, um aus Fotokollen interaktive Lerninhalte zu erstellen, indem sie den Inhaltstyp „Image Hotspots“ verwendet hat. Dabei werden Hintergrundbilder mit zusätzlichen Informationen und Erläuterungen versehen, um so einen vertiefenden Lerninhalt zu schaffen.

    Insgesamt zeigt Nele auf, wie vielseitig und nützlich H5P für die Gestaltung interaktiver und kreativer Bildungsmaterialien sein kann.

    ❓ quellen

    📖 Diese Beiträge wurden als relevant erachtet und durchsucht:
    (🎯 = Keyword-Match im Titel oder Text)

    🎯1. Praxis-Tipp: Inspirationsduschen mit H5P
    ebildungslabor.de/blog/praxis-
    🎯2. H5P-Hacking: Mit der Game Map interaktive Online-Karten gestalten
    ebildungslabor.de/blog/h5p-hac
    🎯3. Schnelle Idee für H5P: Image Hotspots statt ‚Fotokoll‘
    ebildungslabor.de/blog/schnell

    Ich finde: Ziemlich gut und nützlich!

    Mit exit kommst du aus dem Bot wieder raus und kannst dich dann daran machen, z.B. die Bot-Datei zu überarbeiten. (Du weißt ja jetzt, wie es geht: nano chatbot.py (Linux/Mac) bzw. notepad chatbot.py (Windows) eingeben und Änderungen vornehmen.)

    Wenn du das Terminal geschlossen hast und irgendwann später den Bot wieder öffnen willst, dann öffnest du wie in Schritt 2 beschrieben mit dem cd Befehl wieder das Verzeichnis, aktivierst anschließend die virtuelle Umgebung und startest dann das Botprogramm (mit dem Befehl: python chatbot.py)

    Fazit

    Ich habe mir erste Blogs eingespeichert und mit dem entwickelten Bot eine Weile gechattet. Komplexere Anfragen (Wie definiert Melanie im Vergleich zu Nele den Begriff Kreativität?) sind mit diesem Basis-Setup nicht möglich. Um sich aber einen Überblick über Blog-Inhalte zu verschaffen und zusammenfassende Verbindungen zu nutzen sowie Hinweise auf weiterführende Quellen zu erhalten, finde ich diese selbst gebastelte Anwendung richtig hilfreich. Ich kann dir das Nachbasteln somit sehr empfehlen.

    Viel Freude beim Erkunden und Ausprobieren!

    Bonus: Einen RAG-Chatbot online bringen!

    So wie du einen RAG-Chatbot in deinem Terminal gestalten kannst, kannst du solch eine Anwendung natürlich auch auf einen Online-Server übertragen und dann anderen öffentlich zur Verfügung stellen. Das wird im Internet ja zuhauf angeboten. Man kann es sich eben aber auch selbst gestalten.

    Ich habe das mit einer leicht angepassten Version des obigen Bot-Programms ausprobiert, in dem ich darin nur die Inhalte von meiner eigenen Website eingespeist habe und dazu einen Bot erstellt habe. Der Server kostet ca. 4 Euro / Monat. Ich lasse ihn auf eine Subdomain meiner Website verweisen. Ich habe außerdem (um meine Mistral-Tokens nicht alle direkt aufzubrauchen) eine Begrenzung auf 25 Fragen bei der öffentlichen Nutzung eingestellt. Als Interface habe ich die Open Source Software Streamlit genutzt (anstelle des Terminals auf dem jeweils eigenen Rechner). Das Ergebnis kannst du auf bot.ebildungslabor.de ausprobieren.

    (Wenn wir mit einer lokalen und damit dezentralen Nutzung vorankommen, so wie ich es in meinem obigen Blogbeitrag beschrieben habe, dann sind solche Website-Bots bald überflüssig. Schließlich ist es ja viel sinnvoller, sich selbst genau die Anwendung mit den Inhalten, Einstellungen und Systemprompts zu bauen, die man gerade braucht!)

    #DigitaleMündigkeit #KünstlicheIntelligenzKI
  4. WirtschaftsWoche: KI: Nutzen Mitarbeiter ChatGPT unkontrolliert, kann es gefährlich werden

    Schnell noch die E-Mail von ChatGPT formulieren lassen oder eine Übersetzung durch die KI anfordern – Mitarbeiter ahnen nicht, dass sie damit unter Umständen Recht brechen. Doch das kann teuer werden 
    KI: Nutzen Mitarbeiter ChatGPT unkontrolliert, kann es gefährlich werden

  5. Experten des Weltwirtschaftsforums schlagen Alarm: Desinformation durch digitale Medien und künstliche Intelligenz seien die größten Gefahren für die Menschheit. Der Risikoforscher Gerd Gigerenzer hält gerade die Deutschen für blauäugig was TikTok und Co. angeht.#Grabar-Edda #Lossau-Norbert #Desinformation #Fakes #Bildung #Erziehung #KünstlicheIntelligenzKI #SozialeMedien #GerdGigerenzer #TikTok #UniversitätPotsdam #Salzburg #Chicago #DonaldTrump #Brexit #Risikokompetenz #Karriere
    Desinformation: „Realität und empfundene Wirklichkeit treiben zunehmend auseinander“
  6. Welt: BigFM: Der Radiosender aus der KI-Retorte

    Radiomoderatoren kann man lieben oder hassen – aber könnte ihren Job auch eine Künstliche Intelligenz übernehmen? Und im Plauderton Meldungen aus der Region vorlesen und Musiktitel ansagen? Ein deutscher Radiosender probiert es aus.
    BigFM: Der Radiosender aus der KI-Retorte