[
  {
    "objectID": "Writing/documentation.html",
    "href": "Writing/documentation.html",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "",
    "text": "Die Dokumentation von Forschungssoftware ist entscheidend, um wissenschaftliche Ergebnisse nachvollziehbar und Software für andere nutzbar zu machen. Insbesondere in den Digital Humanities (etwa in der Geschichtswissenschaft) entwickeln Forschende neben Forschung und Lehre oft eigene Software – meist unter hohem Zeitdruck und ohne formale Ausbildung in Softwareentwicklung. Häufig bleibt die Dokumentation deshalb minimal oder unvollständig, was dazu führt, dass andere (und sogar die Autor*innen selbst) viel Zeit aufwenden müssen, um den Code zu verstehen und anzuwenden. Dabei gilt gute Dokumentation als zentrale Voraussetzung, um Forschungssoftware auffindbar, nachvollziehbar und wiederverwendbar zu machen.\nAlle Empfehlungen stützen sich auf Literatur und etablierte Richtlinien (Prlić und Procter 2012; Wilson u. a. 2017; Katz, Niemeyer, und Smith 2021; Endings Project 2020).\nDieser Anforderungskatalog richtet sich an Forschende, die keine Vollzeit-Programmierer sind, und soll wissenschaftlich fundierte Richtlinien für die Dokumentation von Forschungssoftware liefern. Die Empfehlungen berücksichtigen Best Practices des Research Software Engineering (RSE) und insbesondere die Prinzipien des Endings-Projekts für digitale Langlebigkeit (Endings Project 2020). Ziel ist es, ein praxistaugliches Gerüst bereitzustellen, das – trotz Zeitknappheit – die wesentlichen Dokumentationsaspekte abdeckt, um sowohl die Nachvollziehbarkeit der Ergebnisse als auch eine Weiterverwendung der Software zu ermöglichen. Im Folgenden werden die Anforderungen an Inhalt, Format und Umfang der Dokumentation definiert, geeignete (teil-)automatisierte Dokumentationswerkzeuge diskutiert und Best Practices in Form von Vorlagen und Checklisten vorgestellt.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#einleitung",
    "href": "Writing/documentation.html#einleitung",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "",
    "text": "Die Dokumentation von Forschungssoftware ist entscheidend, um wissenschaftliche Ergebnisse nachvollziehbar und Software für andere nutzbar zu machen. Insbesondere in den Digital Humanities (etwa in der Geschichtswissenschaft) entwickeln Forschende neben Forschung und Lehre oft eigene Software – meist unter hohem Zeitdruck und ohne formale Ausbildung in Softwareentwicklung. Häufig bleibt die Dokumentation deshalb minimal oder unvollständig, was dazu führt, dass andere (und sogar die Autor*innen selbst) viel Zeit aufwenden müssen, um den Code zu verstehen und anzuwenden. Dabei gilt gute Dokumentation als zentrale Voraussetzung, um Forschungssoftware auffindbar, nachvollziehbar und wiederverwendbar zu machen.\nAlle Empfehlungen stützen sich auf Literatur und etablierte Richtlinien (Prlić und Procter 2012; Wilson u. a. 2017; Katz, Niemeyer, und Smith 2021; Endings Project 2020).\nDieser Anforderungskatalog richtet sich an Forschende, die keine Vollzeit-Programmierer sind, und soll wissenschaftlich fundierte Richtlinien für die Dokumentation von Forschungssoftware liefern. Die Empfehlungen berücksichtigen Best Practices des Research Software Engineering (RSE) und insbesondere die Prinzipien des Endings-Projekts für digitale Langlebigkeit (Endings Project 2020). Ziel ist es, ein praxistaugliches Gerüst bereitzustellen, das – trotz Zeitknappheit – die wesentlichen Dokumentationsaspekte abdeckt, um sowohl die Nachvollziehbarkeit der Ergebnisse als auch eine Weiterverwendung der Software zu ermöglichen. Im Folgenden werden die Anforderungen an Inhalt, Format und Umfang der Dokumentation definiert, geeignete (teil-)automatisierte Dokumentationswerkzeuge diskutiert und Best Practices in Form von Vorlagen und Checklisten vorgestellt.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#inhaltliche-anforderungen-an-die-dokumentation",
    "href": "Writing/documentation.html#inhaltliche-anforderungen-an-die-dokumentation",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "Inhaltliche Anforderungen an die Dokumentation",
    "text": "Inhaltliche Anforderungen an die Dokumentation\nEin zentrales Problem in der Dokumentation wissenschaftlicher Software ist oft das fehlende Big Picture, also eine klare Darstellung des Was und Warum. Die Dokumentation sollte daher alle Informationen abdecken, die zum Verstehen, Nutzen und Weiterentwickeln der Software nötig sind. Insbesondere sind folgende Inhalte essenziell:\n\nZiel und Zweck der Software (Statement of Need)\nBeschreiben Sie was die Software tut und warum sie entwickelt wurde. Nennen Sie den wissenschaftlichen Zweck, das Forschungsproblem oder die Fragestellung, die mit der Software adressiert wird, sowie die Zielgruppe (wer soll sie nutzen?). Dieser Kontext hilft anderen, den Nutzen der Software einzuschätzen. Beispiel: “Dieses Tool extrahiert Personen-Netzwerke aus historischen Briefkorpora, um sozialwissenschaftliche Analysen zu ermöglichen.” Eine klare Problem- und Zielbeschreibung richtet sich auch nach dem Umfeld ähnlicher Lösungen – falls es bereits etablierte Tools gibt, sollte die Dokumentation die eigene Herangehensweise einordnen (z. B. was die Software anders oder besser macht).\n\n\nInput-/Output-Spezifikation und Datenbeschreibung\nDokumentieren Sie alle Eingabeformate, Ausgabedaten und verwendeten Datensätze. Nutzer*innen müssen wissen, welche Daten die Software erwartet (Dateiformate, Schnittstellen, Parameter) und welche Ergebnisse sie produziert. Idealerweise werden Beispiele angegeben: z. B. Beispiel-Dateien oder -Parameter und die korrespondierende Ausgabe. Falls die Software mit bestimmten Forschungsdaten arbeitet, beschreiben Sie diese Daten und ihre Struktur. Dies umfasst die Datenmodelle (etwa wichtige Felder, deren Bedeutung und kontrollierte Vokabulare) und Annahmen über die Daten. Gemäß den ENDINGS-Prinzipien sollte die Datenstruktur in einem statischen Dokument festgehalten und der Software beigelegt sein – so bleibt nachvollziehbar, wie die Software die Daten interpretiert. Eine Tabelle oder Auflistung der Eingabefelder und Ausgabegrößen mit kurzen Beschreibungen erhöht die Klarheit. Beispiel: “Eingabedatei: CSV mit Spalten Autor, Empfänger, …; Ausgabe: JSON-Datei mit Netzwerk-Metriken pro Briefwechsel.”\n\n\nCode-Abhängigkeiten und technische Voraussetzungen\nListen Sie alle Abhängigkeiten (Dependencies) der Software auf. Dazu gehören verwendete Programmiersprachen/Versionen, erforderliche Bibliotheken oder Frameworks, und sonstige Systemvoraussetzungen (z. B. Betriebssystem, Mindesthardware, Datenbank-Versionen). Wichtig ist, wie diese Abhängigkeiten installiert werden können. Optimal ist eine automatisierte Installationsroutine (z. B. ein requirements.txt für Python oder ein Paketmanager-Befehl). In jedem Fall sollte die Dokumentation mindestens Schritt-für-Schritt-Installationsanleitungen enthalten (inklusive evtl. benötigter Vorkenntnisse, z. B. “Python 3 erforderlich”). Beispiel: “Benötigt Python 3.9 und die Bibliotheken Pandas und NetworkX. Installation: pip install -r requirements.txt.” Falls spezielle technische Voraussetzungen bestehen – etwa Zugriff auf bestimmte Hardware, ein Hochleistungsrechner oder große Speicherkapazitäten – sind diese zu nennen.\n\nTypische Nutzungsszenarien und Workflows: Zeigen Sie anhand von Beispielen, wie die Software benutzt wird. Ein Quickstart-Beispiel senkt die Einstiegshürde enorm. Dies kann z. B. eine Anleitung sein, wie man mit wenigen Schritten von einer Eingabedatei zum gewünschten Ergebnis kommt (“Getting Started”-Abschnitt). Beschreiben Sie typische Workflows in nachvollziehbaren Schritten: Eingabe vorbereiten, Software-Befehl/GUI-Aktion ausführen, Ausgabe interpretieren. Ggf. können mehrere Anwendungsfälle skizziert werden (z. B. “Analyse eines einzelnen Briefes” vs. “Batch-Verarbeitung eines gesamten Korpus”). Diese Beispiele sollten realistisch und möglichst repräsentativ für wissenschaftliche Anwendungen sein. Nutzen Sie gerne kleine Datensamples oder Defaults, damit Nutzer die Beispielschritte direkt ausprobieren können. Idealerweise werden Code-Beispiele mit ausgegebenen Resultaten gezeigt (z. B. in Form von Ausschnitten oder, bei Kommandozeilentools, via --help dokumentiert). \n\nFaustregel: Zeigen statt nur beschreiben – konkrete Anwendungsfälle in der Doku verankern.\n\nWissenschaftlicher Hintergrund und theoretischer Kontext\nDa es sich um Forschungssoftware handelt, sollten Sie den wissenschaftlichen Kontext offenlegen. Das heißt, erklären Sie die grundlegenden Methoden, Algorithmen oder Modelle, die in der Software umgesetzt sind, zumindest in Überblicksform. Verweisen Sie auf relevante Publikationen oder Theorien, damit andere die wissenschaftliche Grundlage nachvollziehen können. Beispielsweise: “Die Implementierung folgt dem Algorithmus von Müller et al. (2019) zur Netzwerkanalyse historischer Korrespondenz.” Halten Sie diesen Abschnitt aber prägnant – Details gehören in die Forschungsarbeit selbst. Wichtig ist, dass die Dokumentation den Brückenschlag zwischen Code und Forschung herstellt. Da viele Wissenschaftler*innen zentrale Aspekte lieber in ihren Artikeln dokumentieren, sollte in der Software-Dokumentation zumindest eine Zusammenfassung mit Querverweis erfolgen. So wissen Nutzer*innen, unter welchen Annahmen oder Theorien das Tool funktioniert. Dieser Hintergrundteil unterscheidet Forschungssoftware-Dokumentation von rein kommerzieller Dokumentation: Es geht nicht nur um wie man das Tool benutzt, sondern auch warum es so funktioniert (Stichwort Nachvollziehbarkeit).\n\n\nBekannte Limitationen, Annahmen und Fehlermeldungen\nGeben Sie ehrlich Auskunft über die Grenzen der Software. Welche Fälle werden nicht abgedeckt? Welche Annahmen über die Daten oder Anwendungsszenarien werden getroffen? Dokumentieren Sie bekannte Probleme oder Einschränkungen (z. B. “funktioniert nur für Deutschsprachige Texte”, “maximale Datenmenge 1 Mio. Datensätze, da Speicherbegrenzung”). Solche Hinweise verhindern Fehlanwendungen und sparen Nutzern Zeit. Falls es bekannte Bugs oder Workarounds gibt, sollten diese ebenfalls (etwa in einer FAQ oder einem Abschnitt “Bekannte Probleme”) erwähnt werden. Eine transparente Auflistung von Limitationen erhöht die Vertrauenswürdigkeit und hilft anderen, die Ergebnisse richtig einzuordnen. Auch aussagekräftige Fehlermeldungen im Programm selbst sind eine Form von Dokumentation: Sie sollten nicht nur kryptisch abbrechen, sondern dem/der Anwender*in idealerweise mitteilen, was schiefging und wie es behoben werden kann (z. B. “Fehler: Ungültiges Datum im Feld XY – bitte Format TT/MM/JJJJ verwenden.”). Solche in den Code integrierten Hinweise ergänzen die schriftliche Dokumentation und tragen zur besseren Nutzbarkeit bei.\n\n\nWeiterentwicklung und Beitragsmöglichkeiten\nObwohl viele Digital-Humanities-Tools primär von Einzelpersonen genutzt werden, sollte dennoch angegeben werden, wie andere ggf. zur Software beitragen oder Support erhalten können. Ein kurzer Hinweis auf den Issue-Tracker (z. B. “Fehler bitte über GitHub-Issues melden”) oder auf die Kontaktmöglichkeit zum Autor (E-Mail) gehört dazu. Ebenso können Community Guidelines skizziert werden: etwa Codierstandards oder ein Verhaltenskodex, falls Beiträge erwartet werden. Für kleinere Projekte reicht oft ein Satz wie “Beiträge durch Pull Requests sind willkommen; bei Fragen wenden Sie sich an…”. Dieser Aspekt muss nicht umfangreich sein, zeigt aber Offenheit und sorgt dafür, dass im Falle von Rückfragen die Hürde für Kontaktaufnahme niedrig ist.\n\n\nProjekt-Metadaten (Lizenz, Zitation, Version)\nTeil der Dokumentation sind auch formale Informationen, die im Repository leicht zugänglich sein sollten. Lizenzinformationen klären die rechtlichen Bedingungen der Nutzung und Weiterverbreitung. Es ist Best Practice, eine LICENSE-Datei beizulegen, aber auch in der README kurz zu erwähnen, unter welcher Lizenz die Software steht. Für Forschungssoftware empfiehlt sich eine offene Lizenz (z. B. MIT, BSD oder Apache 2.0 für Code, CC-BY für Daten), um Nachnutzung nicht zu behindern. Zudem sollte angegeben werden, wie die Software zitiert werden kann (z. B. DOI, Paper-Referenz). Ein eigener Abschnitt “Zitation” oder eine CITATION-Datei beschreibt, welche Publikation oder welcher DOI bei Verwendung der Software in wissenschaftlichen Arbeiten anzugeben ist. Dies erhöht die akademische Sichtbarkeit und stellt sicher, dass Autor*innen Credits für ihre Software bekommen(Smith u. a. 2016). Schließlich ist es sinnvoll, eine Versionsnummer der Software zu nennen (idealerweise in README und im Tool selbst), damit Nutzer wissen, auf welche Ausgabe sich die Dokumentation bezieht – insbesondere, wenn es im Laufe der Zeit Aktualisierungen gibt. Diese Praxis entspricht auch den ENDINGS-Prinzipien, die verlangen, dass jede veröffentlichte Version eindeutig erkennbar ist und zitiert werden kann.\n\n\nZusammenfassung der inhaltlichen Anforderungen\nZusammengefasst sollte die Dokumentation alle W-Fragen beantworten: Was tut die Software, warum wurde sie geschrieben (wissenschaftlicher Zweck), wer soll sie nutzen, wie wird sie benutzt (Inputs, Outputs, Abläufe), womit läuft sie (Umgebung/Abhängigkeiten), unter welchen Bedingungen (Annahmen/Limitationen) und wohin können sich Nutzer wenden (Support/Zitation). All diese Punkte sorgen für Nachvollziehbarkeit (im Sinne von Reproduzierbarkeit der Ergebnisse) und Weiterverwendbarkeit (im Sinne von Adaptierbarkeit der Software für neue Kontexte).",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#format-und-struktur-der-dokumentation",
    "href": "Writing/documentation.html#format-und-struktur-der-dokumentation",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "Format und Struktur der Dokumentation",
    "text": "Format und Struktur der Dokumentation\nFür Forschende ohne viel Ressourcen muss die Dokumentation einfach zugänglich, leicht pflegbar und ohne Spezialsoftware erstellbar sein. Daher empfiehlt es sich, auf leichte Formate und eine klare Struktur zu setzen:\n\nREADME.md als zentrales Dokument\nDie Hauptdokumentation sollte als README in Markdown-Format im Hauptverzeichnis des Code-Repositoriums liegen. Dieses README fungiert als “Startseite” des Projekts und enthält idealerweise eine komprimierte Übersicht aller wichtigen Punkte: Zweck der Software, Kurzbeschreibung, Installation, kurzer Nutzungsbeispiel, Kontakt/Lizenz. Auf Plattformen wie GitHub, GitLab etc. wird die README automatisch angezeigt, was die Sichtbarkeit erhöht. Die Vorteile von Markdown sind die einfache Lesbarkeit in Rohform, die breite Unterstützung (auch in Renderern wie GitHub-Webansicht) und die Eignung für Versionierung (Textdatei im Git). So bleibt die Dokumentation eng mit dem Code verzahnt und unter Versionskontrolle – ein Prinzip, das auch von ENDINGS propagiert wird (Dokumentation soll statisch und zusammen mit den Daten/Code abgelegt werden).\n\n\nStrukturierte Unterteilung in weitere Dateien/Abschnitte\n\n\nexample-project/\n├── README.md\n├── CONTRIBUTING.md (optional)\n├── CHANGELOG.md    (optional)\n├── CITATION.md     (oder CITATION.cff)\n├── LICENSE\n├── data/           (optional)\n│   └── sample_data.csv\n├── docs/           (optional)\n│   ├── INSTALL.md\n│   └── USAGE.md\n├── examples/       (optional)\n│   └── example_workflow.ipynb\n└── src/\n    ├── script.py\n    └── module/\n        └── helper.py\nBeispielhafter Struktur eines Code-Repositories\nSollte die Dokumentation umfangreicher sein, ist es sinnvoll, sie in logisch getrennte Abschnitte aufzuteilen. Dies kann innerhalb der README durch Überschriften geschehen oder durch zusätzliche Markdown-Dateien im Repository (z. B. eine INSTALL.md für ausführliche Installationshinweise, eine USAGE.md oder TUTORIAL.md für detaillierte Benutzeranleitungen, eine CHANGELOG.md für Changelog etc.). Eine gängige Struktur ist z. B.:\n\nREADME.md – Überblick (Ziel, Installation, kurzes Beispiel, Lizenz/Zitation)\ndocs/ Verzeichnis mit weiteren .md-Dateien für tiefergehende Dokumentation (optional)\nCONTRIBUTING.md – Hinweise für Beiträger (falls relevant)\nLICENSE – Lizenztext\nCITATION.cff oder CITATION.md – wie zu zitieren.\n\nDiese Dateien sollten konsistent formatiert und benannt sein, damit sie leicht auffindbar sind. Sie kommen ohne spezielle Tools aus – ein einfacher Texteditor genügt zum Bearbeiten. Auch Wiki-Seiten (etwa in GitHub) können genutzt werden, sind aber weniger dauerhaft versioniert im Vergleich zu Dateien im Code-Repository selbst. Die Dokumentation sollte möglichst im Repository selbst liegen, um sicherzustellen, dass sie gemeinsam mit dem Code versioniert, verteilt und archiviert wird. Externe Dokumentationswebsites sind für kleine Projekte oft Overkill und können im schlimmsten Fall verwaisen.\n\n\nKeine proprietären Formate oder Abhängigkeit von Werkzeugen\nUm Hürden für die Erstellung und Nutzung der Dokumentation gering zu halten, sollte auf gängige, offene Formate gesetzt werden (Plaintext, Markdown, reStructuredText). Vermeiden Sie nach Möglichkeit Formate wie Word-Dokumente oder PDF als primäre Dokumentationsquelle – solche Formate sind nicht diff-freundlich, erschweren Zusammenarbeits-Workflows und sind meist nicht Teil des Versionskontrollsystems. Ein Markdown-Dokument hingegen kann gemeinsam mit dem Code gepflegt werden, und Änderungen sind transparent nachvollziehbar. Zudem erlauben offene Formate eine leichtere Langzeitarchivierung: Gemäß Endings-Prinzip sollten Informationsressourcen in langfristig lesbaren Formaten vorliegen. Markdown/Plaintext erfüllt diese Bedingung (im Gegensatz etwa zu einer Datenbank-gestützten Wissensbasis oder einem proprietären Wiki, das in 10 Jahren evtl. nicht mehr läuft). Im Sinne der Digital Longevity ist eine statische HTML- oder PDF-Version der Dokumentation (automatisch generiert aus Markdown) als Teil der Release-Artefakte sinnvoll – so kann z. B. in jeder veröffentlichten Version ein PDF-Handbuch beigelegt werden, das später zitiert oder referenziert werden kann. Wichtig ist aber, dass die Quelle der Wahrheit immer die im Repository gepflegte Doku bleibt.\n\n\nÜbersichtlichkeit und Navigierbarkeit\nStrukturieren Sie die Dokumentation mit klaren Überschriften und Listen, damit Leser schnell die gesuchten Informationen finden. Eine logische Gliederung (wie in diesem Katalog: Einführung, Anforderungen, Installation, Nutzung, Hintergrund, etc.) hilft unterschiedlichen Nutzergruppen gezielt das Relevante zu finden. Für längere Dokumente kann ein Inhaltsverzeichnis oder eine Abschnittsübersicht am Anfang nützlich sein. Markdown bietet z. B. automatische Toc-Generierung auf manchen Plattformen. Achten Sie darauf, pro Abschnitt nur zusammenhängende Informationen zu behandeln (z. B. alles zu Installation an einem Ort). Wiederholungen sollten vermieden werden: lieber an einer Stelle ausführlich dokumentieren und sonst darauf verweisen, um Konsistenzprobleme zu vermeiden (“Don’t Repeat Yourself” gilt auch für Dokumentation). Bei ähnlichen Projekten können Sie sich an bestehenden Dokumentationsvorlagen orientieren: Viele erfolgreiche Open-Source-Projekte haben auf GitHub eine ähnliche README-Struktur, die als informelles Template dienen kann.\n\n\nBeispiele, Codeblöcke und ggf. Abbildungen einbinden\nNutzen Sie die Möglichkeiten von Markdown, um die Dokumentation lebendig zu gestalten. Zeigen Sie Code-Beispiele als formatierte Codeblöcke, fügen Sie Links zu weiterführenden Ressourcen ein, oder binden Sie bei Bedarf Abbildungen ein (etwa ein Diagramm der Datenpipeline, ein Screenshot der Benutzeroberfläche, etc.). Achten Sie dabei auf Dateigrößen und Formate (Bilder als PNG/JPG, Diagramme wenn möglich als SVG für Langlebigkeit). Falls Diagramme der Architektur oder Workflow-Abbildungen hilfreich sind, können diese mit simplen Mitteln erstellt werden (zur Not handgezeichnet und abfotografiert, besser jedoch mit Tools wie mermaid.js Diagrammen in Markdown oder Graphviz). Diese Visualisierungen sind jedoch nur dann einzusetzen, wenn sie echten Mehrwert bieten und ohne komplexe Build-Prozesse eingebunden werden können. Im Zweifel hat textuelle Beschreibung Vorrang, um nicht vom prinzip “keep it simple” abzuweichen.\n\n\nFazit Format und Struktur\nInsgesamt gilt: Die Dokumentation sollte im gleichen Repository leben wie der Code, klar strukturiert und in einem einfach handhabbaren Format vorliegen. Sie soll ohne spezielle Umgebung lesbar sein – ein Nutzer, der das Repository klont oder herunterlädt, muss sofort Zugang zur Dokumentation haben. Dieses Prinzip entspricht auch den FAIR- und RSE-Richtlinien, die fordern, Software (und deren Doku) auffindbar und zugänglich zu machen, ohne Hürden. Eine gut gepflegte README in Markdown erfüllt diese Anforderungen in den meisten Fällen optimal.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#umfang-und-fokus-der-dokumentation",
    "href": "Writing/documentation.html#umfang-und-fokus-der-dokumentation",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "Umfang und Fokus der Dokumentation",
    "text": "Umfang und Fokus der Dokumentation\nGerade weil Forschende wenig Zeit haben, muss die Dokumentation effizient gestaltet sein – sie soll alle wichtigen Informationen enthalten, aber auch nicht unnötig ausschweifen. Für typische Forschungssoftware-Projekte in den Geisteswissenschaften wird ein Umfang von maximal ca. 10 Seiten (bei Bedarf verteilt auf mehrere Dateien) als ausreichend erachtet. Dieser Richtwert verhindert, dass die Doku zu einer unüberschaubaren Abhandlung wird, und zwingt zur Fokussierung auf das Wesentliche. Wichtig ist der Inhalt, nicht die Länge: eine kürzere, aber inhaltsreiche Dokumentation ist besser als eine lange, die nichts aussagt.\nEin effizienter Umfang lässt sich erreichen, indem man sich auf die oben genannten Kernpunkte konzentriert und Ablenkendes weglässt. Dokumentieren Sie alles, was für Nachvollziehbarkeit und Wiederverwendung nötig ist, und skippen Sie alles andere. Zum Beispiel muss nicht jeder interne Programmiertrick erläutert werden – Quellcode-Kommentare richten sich an Entwickler, während die Nutzerdokumentation sich auf Nutzung und Kontext beschränkt. Verzichten Sie auf seitenlange Theorieableitungen (verweisen Sie stattdessen auf Papers) und auf generische Erklärungen bekannter Technologien (man muss Git oder Python nicht in der Doku erklären, sondern kann referenzieren). Halten Sie auch die Sprache prägnant: kurze Absätze, Listen und einfache Sätze erhöhen die Lesbarkeit. Fachtermini aus dem jeweiligen wissenschaftlichen Bereich dürfen verwendet werden, aber erklären Sie sie, falls die Zielnutzer sie evtl. nicht kennen.\nPriorisierung: Beginnen Sie mit einer Minimaldokumentation, die alle Schlüsselaspekte abdeckt (“keine Dokumentation” ist keine Option). Good Enough Practices empfehlen, als ersten Schritt zumindest einen kurzen erklärenden Kommentar am Anfang jedes Scripts oder eine README mit ein paar Sätzen zu erstellen. Diese Hürde ist niedrig und bringt bereits Nutzen – selbst wenn (noch) keine ausführliche Handbuch-Doku existiert. Später kann die Dokumentation erweitert werden, insbesondere wenn die Software in Kooperation entsteht oder mehr Nutzer gewinnt. Es hat sich gezeigt, dass ausführliche Dokumentation oft erst entsteht, wenn ein echter Bedarf (z. B. durch externe Nutzer) vorhanden ist. Daher: zögern Sie nicht, zunächst klein anzufangen, aber stellen Sie sicher, dass zumindest die kritischen Informationen sofort verfügbar sind (lieber ein 2-seitiges README heute, als das perfekte 30-seitige Handbuch in zwei Jahren, das evtl. nie geschrieben wird).\nDie Obergrenze von ~10 Seiten ist ein Richtwert. Umfangreiche Projekte könnten etwas mehr benötigen, sehr kleine Tools kommen mit einer Seite aus. Das Ziel ist, dass ein interessierter Nutzer die Dokumentation in überschaubarer Zeit durchsehen kann. Ein guter Test ist: Kann eine neue Person in &lt; 1 Stunde mit Hilfe der Doku das Tool zum Laufen bringen und ein einfaches Beispiel ausführen? Wenn ja, ist der Detailgrad angemessen. Wenn die Person hingegen nach 10 Seiten immer noch nicht weiß, wie sie loslegen soll, muss die Doku fokussierter werden. Fügen Sie zur Not eine kurze Übersicht/Zusammenfassung am Anfang ein, die das Wichtigste in Kürze nennt – viele Leser entscheiden in wenigen Minuten, ob sie eine Software weiter betrachten oder nicht, und hier zählt der erste Eindruck.\nEin weiterer Tipp zur Effizienz: Nutzen Sie Verweise und vorhandene Ressourcen. Wenn z. B. Ihr Tool auf einem komplizierten Setup (Datenbank, Webserver) aufbaut, brauchen Sie nicht jede Installationsoption im Detail in Ihrer Doku zu reproduzieren – verlinken Sie auf offizielle Installationsanleitungen dieser Abhängigkeiten, und nennen Sie nur Ihre spezifischen Konfigurationen. Ebenso können Tutorials oder Papers, die schon existieren, als weiterführende Links angegeben werden, anstatt Inhalte redundant zu erklären. Das entlastet Ihre Dokumentation und hält sie schlank.\nZum Fokus gehört auch, zwischen Nutzerdokumentation und Entwicklerdokumentation zu unterscheiden. Dieser Katalog adressiert primär die Nutzerdokumentation (für Endnutzer und für die Autoren selbst, wenn sie das Tool später wieder anfassen). Entwicklerdokumentation (z. B. detaillierte API-Dokumente, Code-Kommentare, technische Architektur) kann separat gehalten werden, sofern nötig, um den Hauptnutzerfluss nicht zu überfrachten. Für viele kleine Forschungssoftware-Projekte sind ausführliche Entwicklerdokus nicht nötig – hier reicht es, den Code gut zu kommentieren und eventuell eine grobe Architekturübersicht bereitzustellen. Konzentrieren Sie die Hauptdokumentation darauf, das Nutzen und Verstehen der Software von außen zu ermöglichen.\nAbschließend sei betont: Ein kompakter, zielgerichteter Dokumentsatz, der genau die relevanten Infos liefert, erhöht die Wahrscheinlichkeit, dass er aktualisiert und genutzt wird. Umfangmonster schrecken ab und veralten schneller. Halten Sie die Dokumentation deshalb so knapp wie möglich, aber so ausführlich wie nötig – ganz im Sinne von Einsteins Prinzip, Dinge so einfach wie möglich zu machen, aber nicht einfacher.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#teil-automatisierte-dokumentationswerkzeuge",
    "href": "Writing/documentation.html#teil-automatisierte-dokumentationswerkzeuge",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "(Teil-)automatisierte Dokumentationswerkzeuge",
    "text": "(Teil-)automatisierte Dokumentationswerkzeuge\nDie Dokumentationslast lässt sich durch den Einsatz geeigneter Werkzeuge erheblich senken. Gerade Forschende, die alleine programmieren, können von (teil-)automatisierter Dokumentation profitieren, um konsistente und aktuelle Unterlagen zu erhalten, ohne alles von Hand schreiben zu müssen. Im Folgenden werden einige Tools und Möglichkeiten vorgestellt – samt Empfehlungen, wann ihr Einsatz sinnvoll oder notwendig ist:\n\nDocstrings und API-Dokumentationsgeneratoren\nNutzen Sie die Möglichkeit, Dokumentation direkt im Quellcode unterzubringen, z. B. in Form von Docstrings (mehrzeilige Strings in Funktionen/Klassen bei Python, Roxygen-Kommentare in R, Javadoc-Kommentare in Java, etc.). Diese dienen doppelt: Zum einen erleichtern sie es Ihnen und Kollegen, den Code beim Lesen zu verstehen, zum anderen können sie von Tools ausgelesen und zu hübschen API-Dokumentationen verarbeitet werden. Idealerweise dokumentieren Sie jede wichtige Funktion, Klasse oder Modul mit einem kurzen Docstring, der Zweck, Parameter, Rückgaben und ggf. Beispiele enthält. Für kleine Scripte genügen ggf. Modul- oder Abschnittskommentare. Wichtig ist Konsistenz im Stil – halten Sie sich an Konventionen Ihres Ökosystems (z. B. Google Style Guide für Python Docstrings oder entsprechende Formatvorgaben für andere Sprachen). Mit Tools wie Sphinx (für Python, aber grundsätzlich sprachunabhängig) können aus Docstrings automatisiert Webseiten oder PDF-Handbücher generiert werden. Sphinx liest z. B. die Python-Docstrings und erzeugt daraus strukturiert eine Dokumentation; Erweiterungen wie napoleon erlauben es, Google- oder Numpy-Style-Dokumentation direkt zu verarbeiten.\n\n\nÄhnliche Generatoren gibt es für nahezu alle Sprachen: Javadoc für Java, Doxygen für C/C++ (und viele andere Sprachen), MkDocs oder pdoc für Python, etc.\nDer Einsatz solcher Tools ist besonders dann sinnvoll, wenn Ihre Forschungssoftware über eine Programmierschnittstelle (API) verfügt, die von anderen genutzt werden soll, oder wenn das Projekt größer wird und die interne Struktur komplexer ist. In solchen Fällen kann eine API-Referenz (automatisch aus dem Code erzeugt) eine erhebliche Hilfe sein. Verpflichtend wird dieser Ansatz etwa, wenn Sie ein Bibliothekspaket veröffentlichen (z. B. ein R-Package in CRAN oder Python-Package auf PyPI) – dort sind Docstrings und generierte Dokumentation quasi Standard. Für ein einmaliges Analyse-Skript in den Digital Humanities ist eine voll ausgebaute API-Doku vielleicht nicht nötig; hier reicht möglicherweise ein inline kommentierter Code. Doch sobald Funktionen von anderen aufgerufen oder das Projekt von mehreren entwickelt wird, sollte ein Dokumentationstool in Betracht gezogen werden, um den Aufwand gering zu halten und Einheitlichkeit zu gewährleisten.\n\n\nJupyter Notebooks und literate programming\nEin mächtiges Werkzeug – gerade in datengetriebenen Geisteswissenschaften – sind Jupyter Notebooks bzw. R Markdown Notebooks (Kluyver u. a. 2016). Diese erlauben es, ausführbaren Code mit erklärendem Text und Visualisierungen in einem Dokument zu vereinen. Für Dokumentationszwecke können Notebooks zweierlei leisten: (1) als Tutorials/Beispiel-Workflows, die Nutzer interaktiv nachvollziehen können, und (2) als Reproduzierbarkeits-Dokumentation für analytische Prozesse. Wenn Ihre Forschungssoftware z. B. eine Bibliothek ist, könnten Sie ein Notebook bereitstellen, das einen typischen Anwendungsfall durchspielt (inklusive Daten-Loading, Aufruf der Funktionen, Darstellung der Ergebnisse).\nNotebooks senken die Hürde, weil Nutzer direkt experimentieren können, und fördern transparente Forschung, da Code, Ergebnisse und Beschreibung zusammenfließen. Sie sind daher sinnvoll, wenn der Hauptanwendungsfall die Durchführung von Analysen oder Datenverarbeitungen ist, die man Schritt für Schritt demonstrieren kann.\n\n\n\n\n\n\nWarnung\n\n\n\nNotebooks erfordern allerdings eine lauffähige Umgebung – das heißt, Sie müssen darauf achten, dass alle Abhängigkeiten im Notebook deklariert sind und die Daten zugänglich sind. Es hat sich gezeigt, dass Notebooks aus Publikationen oft nicht ohne Weiteres laufen, weil Pfade, Datenquellen oder spezielle Umgebungen fehlen. Deshalb: Wenn Sie Notebooks als Doku nutzen, stellen Sie sicher, dass sie leicht ausführbar sind (z. B. durch Bereitstellen von Umgebungsdateien wie environment.yml oder Dockerfiles, kleinen Beispieldatensätzen und klaren Anweisungen im Notebook). Ggf. kann man Notebooks auch in reine Markdown/HTML exportieren und dem Repo beilegen, damit zumindest statisch die Inhalte einsehbar sind.\n\n\nWann sind Notebooks verpflichtend? – Nie im strengen Sinne, aber sie sind quasi Goldstandard, um wissenschaftliche Analysen nachvollziehbar zu machen. In Projekten, wo es um Data Science Workflows oder interaktive Exploration geht, sollten Notebooks stark erwogen werden, während für ein reines Tool/Script eine gut geschriebene README mit Beispielausgabe ausreichend sein kann.\n\n\nSphinx/MkDocs/Doxygen (statische Dokumentationswebseiten)\nFür umfangreichere Projekte oder solche mit eigener Website kann es sinnvoll sein, eine Dokumentationswebsite zu generieren. Tools wie Sphinx (zusammen mit ReadTheDocs für Hosting) oder MkDocs erlauben es, aus Markdown/reStructuredText-Dateien einen ansprechend formatierten HTML-Dokumentationssatz zu bauen. Der Vorteil ist, dass man eine durchsuchbare, verlinkte Doku bekommt, oft mit schönem Layout und zusätzlicher Navigation. Mit Continuous Integration lassen sich diese Seiten bei jedem Git-Push automatisch aktualisieren. Für die Nachhaltigkeit (ENDINGS-Prinzip) ist wichtig, dass diese Webseiten statisch sind – d.h. sie funktionieren ohne Server-Backends und bleiben auch offline nutzbar. Sphinx erfüllt dies, indem es reine HTML-Seiten erzeugt. Solche Tools sind sinnvoll, wenn die Dokumentation sehr groß oder öffentlich weit verbreitet ist – z. B. wenn Ihre Software von vielen genutzt wird und Sie ein professionelles Auftreten wünschen, oder wenn Sie die Doku als PDF veröffentlichen möchten. In kleinen DH-Projekten ist es oft nicht nötig, extra eine Webseite zu hosten; dennoch kann Sphinx auch lokal HTML/PDF erzeugen, was man dem Repo beilegen kann.\nVerpflichtend ist so ein Tool selten, höchstens wenn Förderprogramme oder Journals ein dokumentationsseitiges HTML-Manual verlangen. Wenn Sie jedoch planen, Ihre Software z. B. über Jahre zu pflegen und ggf. einem Journal wie JOSS vorzustellen, dann erwartet die Community meist, dass zumindest eine Sphinx/Doxygen-Doku für die API existiert. Als Daumenregel: ab einer Codebasis &gt; einige tausend Zeilen oder &gt; 5 Module lohnt es sich, eine generierte Dokumentation bereitzustellen, um den Überblick zu behalten.\n\n\nIn-Code Hilfefunktionen und CL-Interface Doku\nFalls Ihre Software ein Command-Line Interface (CLI) hat, stellen Sie sicher, dass eine eingebaute Hilfe vorhanden ist (z. B. Ausgabe bei --help). Viele Nutzer greifen zunächst darauf zurück. Dieses Hilfemenü sollte kurz erläutern, welche Subkommandos oder Optionen existieren. Moderne CLI-Frameworks generieren solche Hilfen oft automatisch aus Ihrem Code (z. B. Click oder argparse in Python erzeugen --help-Texte). Nutzen Sie das, um konsistente Infos zu garantieren.\nFür GUI-Anwendungen sollten Tooltips, Hilfetexte in der Oberfläche oder zumindest ein kleiner Help-Abschnitt im Handbuch vorhanden sein. Diese eingebetteten Hilfen ersetzen keine ausführliche Dokumentation, aber sie senken die Schwelle für alltägliche Fragen.\n\n\nVersionskontrolle und kontinuierliche Dokumentationspflege\nEine Form der Teil-Automatisierung ist es, die Dokumentation an den Entwicklungs-Workflow zu koppeln. So sollte die Dokumentation im selben Versionskontrollsystem (Git) liegen wie der Code, damit Änderungen synchron nachverfolgt werden. Es empfiehlt sich, bei jedem größeren Code-Update zu prüfen, ob die Doku noch stimmt (das kann man sich z. B. als Punkt in Pull-Request-Reviews notieren oder per Issue-Template abfragen). Für Projekte mit Continuous Integration (CI) kann man sogar automatisierte Checks einrichten, die z. B. prüfen, ob die Doku gebaut werden kann oder ob Docstrings fehlen. Einige CI-Skripte generieren bei jedem Commit eine frische Doku (z. B. mittels Sphinx) und veröffentlichen sie – so ist garantiert, dass die aktuelle Codeversion immer eine aktuelle Doku hat. Dieses Level an Automation ist für kleine Projekte evtl. zu viel, aber das Prinzip “Dokumentation versionieren” ist allgemeingültig, um die Entwicklungshistorie konsistent zu halten.\n\n\nSpezialfälle\nIn bestimmten Fällen gibt es weitere Werkzeuge: z. B. Doxygen für automatisierte Code-Diagramme und Querverweise (gerne in C++-Projekten genutzt), oder Swagger/OpenAPI für automatische Dokumentation von Web-APIs. Wenn Ihre Forschungssoftware z. B. einen Webservice anbietet, kann Swagger eine interaktive API-Doku erzeugen. Ebenso können Literatur-Manager wie Manubot oder RMarkdown Bücher helfen, Code und Text zu integrieren (aber das geht über das hinaus, was die meisten DH-Projekte benötigen). Erwähnenswert ist noch Jupyter Book oder R Bookdown, womit man umfangreiche narrative Dokumentationen (inkl. Code) als Website/Book erstellen kann – nützlich, falls Ihre Dokumentation eher ein ausführlicher Lehrtext werden soll (z. B. wenn die Software einen ganzen methodischen Ansatz dokumentiert). Für den hier anvisierten Zweck (knackiger Doku-Katalog) sind solche Tools meist zu schwergewichtig.\n\n\nWann ist was verpflichtend\nEs gibt kein universelles Muss, außer: Irgendeine Form der Doku ist Pflicht. Ob Sie nun per Hand Markdown schreiben oder Sphinx einsetzen, hängt von Kontext und Projektgröße ab. Allgemein gilt: Verwenden Sie Automatisierung wo immer möglich, um sich zu entlasten, aber vermeiden Sie Overhead durch Tools, die Sie nicht brauchen. Ein einzelnes historisches Analyse-Skript braucht kein Doxygen; ein komplexes DH-Toolkit mit API sollte hingegen Doxygen oder Sphinx nutzen, damit die Nutzer nicht den Code lesen müssen, um Funktionen zu verstehen. Denken Sie daran: “Die beste Dokumentation ist die, die sich selbst schreibt.” – dieses Motto aus der Literatur spielt darauf an, dass wir Tools nutzen sollen, die uns Schreibarbeit abnehmen. Perfekt autonom schreibt sich die Dokumentation zwar nie, aber moderne Werkzeuge können Routineaufgaben (z. B. Inhaltsverzeichnisse, Funktionsreferenzen, Formatierung) automatisieren. Dadurch bleibt Ihnen mehr Zeit für das inhaltliche Fine-Tuning der Texte.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#best-practices-vorlagen-und-checklisten",
    "href": "Writing/documentation.html#best-practices-vorlagen-und-checklisten",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "Best Practices, Vorlagen und Checklisten",
    "text": "Best Practices, Vorlagen und Checklisten\nUm zu entscheiden, was dokumentiert wird (und was nicht), helfen etablierte Best Practices sowie Vorlagen aus der Community. Im Folgenden sind einige bewährte Richtlinien zusammengefasst, untermauert von Quellen, die bei der Priorisierung der Dokumentationsinhalte helfen:\n\nOrientierung an Nutzerbedürfnissen\nStellen Sie sich beim Schreiben der Doku die verschiedenen Nutzerrollen vor: “Zukünftiges Ich”, Kolleg*innen, Fachforscher anderer Disziplin und ggf. Software-Entwickler, die den Code erweitern. Jede dieser Gruppen möchte bestimmte Dinge wissen. Forscher*innen fragen: Was kann das Tool? Wie benutze ich es? In welchem Kontext steht es?. Entwickler*innen fragen: Wie kann ich beitragen? Wie funktioniert es unter der Haube?. Priorisieren Sie zunächst die erstgenannten (Anwender) – deshalb Fokus auf Zweck, Nutzung und Ergebnisse in der Hauptdoku. Detailinfos für Entwickler (z. B. Code-Struktur, To-do-Liste) können separat oder später ergänzt werden. Halten Sie sich stets vor Augen: Dokumentation ist primär für Menschen (nicht für Maschinen), daher schreiben Sie klar und vermeiden Sie unnötigen Jargon. Selbst wenn der Code “für sich spricht”, denken Sie daran, dass klare Erläuterungen später viel Zeit sparen.\n\n\nCheckliste für die Mindest-Dokumentation\nDie folgenden Punkte fassen zusammen, was eine gute Dokumentation mindestens enthalten sollte. Sie können auch als Qualitäts-Checkliste dienen, um Ihre Dokumentation zu überprüfen:\n\nZielklärung: Ist der Zweck der Software klar benannt und der wissenschaftliche Need begründet? (Falls nein, ergänzen: Warum existiert dieses Tool?)\nInstallation & Voraussetzungen: Sind alle Schritte, um die Software lauffähig zu machen, dokumentiert (inkl. Dependencies, evtl. mit Installationsbefehlen)? Ist ersichtlich, welche Umgebung nötig ist (OS, Hardware)?\nGrundlegende Nutzung: Gibt es eine Anleitung oder Beispiele, wie man die Software verwendet (Eingabe -&gt; Ausgaben)? Ist mindestens ein typischer Workflow beschrieben, idealerweise mit Beispielinput und -output?\nOptionen & Schnittstellen: Falls relevant – sind alle wichtigen Funktionen, Befehlsoptionen oder API-Methoden dokumentiert? (Nicht unbedingt jede intern, aber alles, was ein Nutzer aufrufen könnte). Für APIs: Sind Parameter und Rückgaben erläutert?\nValidierung & Einschränkungen: Werden Annahmen und Grenzen der Software genannt? Weiß eine Nutzerin, welche Fälle nicht abgedeckt sind oder worauf zu achten ist (z. B. Datenqualität, maximale Größen)? Transparenz hier verhindert Frustration.\nHintergrund & Referenzen: Sind die wichtigsten konzeptionellen Hintergründe oder Referenzen angegeben? (Z. B. theoretische Grundlagen, Algorithmen, Literaturverweise). Das muss kein Essay sein, aber ein paar Sätze + Referenzen schaffen Vertrauen in die wissenschaftliche Fundierung.\nKontakt & Weiterführung: Ist angegeben, wie man Hilfe bekommt oder Fehler melden kann (Issue-Tracker, E-Mail)? Gibt es Hinweise für Beiträge (falls erwünscht) oder zumindest die Information, wer die Autor*innen sind?\nRechtliches & Zitation: Liegt die Lizenz bei und wird sie genannt? Sind Infos zum Zitieren der Software vorhanden (z. B. “Bitte zitieren Sie DOI XYZ”)? Das stellt sicher, dass die Software nachnutzbar und akademisch kreditiert wird.\nAktualität & Version: Entspricht die Dokumentation der aktuellen Softwareversion? (Check: Versionsnummern, Datumsangaben). Veraltete Doku kann schlimmer sein als keine – planen Sie also ein, die Doku mit jedem Release kurz zu überprüfen.\nKonsistenz & Stil: Wird ein einheitlicher Ton und Stil durchgehalten? (z. B. durchgehende Verwendung gleicher Begriffe für Konzepte, Sprache entweder Deutsch oder Englisch einheitlich je nach Zielgruppe). Kleinliche Fehler (Tippfehler, kaputte Links) sind auszumerzen, da sie Nutzer abschrecken.\n\nDiese Checkliste kann vor einem “Release” der Software durchgegangen werden, ähnlich einem Review-Prozess (vgl. JOSS Review-Kriterien, die viele dieser Punkte abdecken). Sie hilft zu entscheiden, was noch dokumentiert werden muss und was eventuell weggelassen werden kann. Alles, was für die obigen Punkte nicht relevant ist, kann man tendenziell aus der Hauptdokumentation herauslassen. Beispielsweise interne Code-Refaktorierungsdetails oder historische Anekdoten zur Entwicklung gehören eher ins interne Changelog oder in Blog-Posts, nicht in die Nutzerdokumentation.\n\n\nPositiv- und Negativbeispiele studieren\nEin guter Weg, die eigene Dokumentation zu verbessern, ist ein Blick auf Projekte mit exzellenter Doku. In der Journal of Open Source Software (JOSS) oder Journal of Open Research Software (JORS) werden oft Softwareartikel veröffentlicht, bei denen die zugehörigen Repositorien vorbildliche READMEs und Wikis haben. Diese können als Vorlage dienen. Achten Sie darauf, wie diese Projekte ihre README strukturieren, welche Abschnitte vorhanden sind und welche nicht. Viele erfolgreiche Projekte haben z. B. eine ähnliche Reihenfolge: Introduction, Installation, Usage, Contributing, License, Citation – ein Muster, das sich bewährt hat. Ebenso gibt es von Initiativen wie der Software Sustainability Institute Blogposts mit Best Practices und sogar Vorlagen (Templates) für Dokumentation. Nutzen Sie solche Ressourcen; sie ersparen einem das Rad neu zu erfinden. Allerdings: Adaptieren Sie sie auf Ihre Bedürfnisse – nicht jede Vorlage passt 1:1.\n\n\nPrinzipien: FAIR und ENDINGS\nBeachten Sie, dass dieser Anforderungskatalog in Einklang mit den Prinzipien des Research Software Engineering und den ENDINGS-Prinzipien steht. Gutes Research Software Engineering fördert u.a. Nachhaltigkeit, Offenheit und Reproduzierbarkeit in der Softwareentwicklung. Dementsprechend legt unsere Dokumentations-Checkliste Wert auf Reproduzierbarkeit (Installation, Daten, Beispiele), Offenheit (Lizenz, offene Formate) und Nachhaltigkeit (Versionierung, Langlebigkeit der Doku). Die ENDINGS-Prinzipien für digitale Projekte betonen insbesondere die Bedeutung von Dokumentation für Datenstrukturen, offenen Lizenzen, statischen Outputs und Zitierbarkeit. Unsere Empfehlungen, etwa ein statisches Markdown-README beizulegen, die Datenmodell-Doku nicht auszulagern oder Zitationsangaben zu machen, setzen genau diese Vorgaben um. Indem Sie also diesem Anforderungskatalog folgen, berücksichtigen Sie automatisch wichtige anerkannte Prinzipien für gute wissenschaftliche Softwarepraxis.\n\n\nKontinuierliche Verbesserung und Feedback\nDokumentation ist kein einmaliges Ereignis, sondern ein fortlaufender Prozess. Best Practice ist, früh Feedback von Testnutzer*innen oder Kolleg*innen einzuholen: Lassen Sie jemanden die Anleitung befolgen und hören Sie auf Stolpersteine. Oft zeigen sich Lücken erst im Praxistest (“Ich wusste nicht, was ich nach Schritt X tun soll” etc.). Planen Sie Zeiten ein, die Dokumentation nachzuführen, insbesondere wenn sich die Software ändert. Ein lebendiges Projekt wird vielleicht Release für Release die Dokumentation erweitern (evtl. neue Tutorials, neue Module dokumentieren). Nutzen Sie auch Issues für Dokumentation: Wenn Nutzer Fragen stellen, überlegen Sie, ob die Antwort in die offizielle Doku übernommen werden sollte. So wächst die Dokumentation organisch entlang der tatsächlichen Bedürfnisse.\n\n\nZusammenfassung Best Practices\nZusammenfassend helfen die genannten Best Practices dabei, die Dokumentation zielgerichtet zu gestalten: Dokumentiert wird, was dem Verständnis und der Nutzung dient; weggelassen wird, was überflüssig oder selbstverständlich ist. Eine gute Dokumentation erzählt eine klare Geschichte über die Software, anstatt den Leser mit irrelevanten Details zu verlieren. Mit den richtigen Werkzeugen und Prinzipien an der Hand kann selbst unter Zeitdruck eine qualitativ hochwertige Dokumentation entstehen – zur Freude aller, die mit der Forschungssoftware arbeiten möchten.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#fazit",
    "href": "Writing/documentation.html#fazit",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "Fazit",
    "text": "Fazit\nDie hier präsentierten Anforderungen und Empfehlungen bieten einen Leitfaden für die Dokumentation von Forschungssoftware in den Digital Humanities. Sie sind darauf ausgerichtet, mit überschaubarem Aufwand maximale Nachvollziehbarkeit, Langlebigkeit und Wiederverwendbarkeit zu erreichen. Indem zentrale Inhalte (Ziele, Inputs/Outputs, Hintergrund, etc.) klar dokumentiert, ein nutzerfreundliches Format (README im Repo) gewählt, der Umfang fokussiert gehalten und hilfreiche Tools eingesetzt werden, kann die Dokumentation zur Stärke eines Projekts werden statt einem lästigen Anhängsel.\nWissenschaftlich fundierte Best Practices – von Ten Simple Rules for Documenting Scientific Software bis zu den ENDINGS-Principles – untermauern diesen Katalog. Die Umsetzung dieser Richtlinien wird dazu beitragen, dass Forschungssoftware aus den Geisteswissenschaften nicht nur kurzfristig von ihren Autor*innen genutzt wird, sondern langfristig von Dritten verstanden, validiert und weiterentwickelt werden kann. So schließt sich der Kreis zwischen guter Softwareentwicklung und guter Wissenschaft: Dokumentation ist das Bindeglied, das Code und Erkenntnis transparent verbindet. In der Praxis bedeutet dies zwar zusätzliche Arbeitsschritte, doch wie die Erfahrung zeigt, zahlen sich diese in Form von Zeiteinsparung bei Nutzern, höherer Zitierbarkeit und größerer Wirkung der Software aus. Mit diesem Anforderungskatalog sind Forschende gut gerüstet, um ihre Softwareprojekte dokumentationstechnisch auf ein solides Fundament zu stellen – trotz knapper Zeit und ohne Informatikabschluss. Denn am Ende gilt: Gut dokumentierte Forschungscode ist nachhaltige Forschung.\n\nTabellarische Übersicht der Dokumentations-Bestandteile\n\n\nEmpfohlene Dokumentationselemente, Inhalte und Umfang. Diese Übersicht kann als Vorlage dienen, welche Komponenten ein Dokumentationspaket enthalten sollte. Je nach Projekt können einige Elemente wegfallen oder kombiniert werden – entscheidend ist, dass die Kerninformationen (siehe oben) nicht fehlen.\n\n\n\n\n\n\n\n\nDokuelement\nInhalt/Purpose\nFormat/Ort\nUmfang\n\n\n\n\nREADME (Hauptdoku)\nZweck der Software; Kurzbeschreibung; Installationsanleitung; einfaches Nutzungsbeispiel; Lizenz- und Kontaktinfo\nMarkdown im Root des Repos (statisch versioniert)\n1–2 Seiten\n\n\nEingabe/Ausgabe-Guide\nBeschreibung der erwarteten Inputs (Datenformat, Parameter) und generierten Outputs (Dateien, Berichte) inkl. Beispielen\nTeil der README oder separate Datei (z.B. USAGE.md)\n1 Seite (mit Beispielen)\n\n\nWissenschaftlicher Hintergrund\nErläuterung der Methode, Theorie, Algorithmen; Verweise auf Literatur\nREADME-Abschnitt “Hintergrund” oder separate Doku (BACKGROUND.md)\n0.5–1 Seite (plus Referenzen)\n\n\nBekannte Limitationen\nAuflistung von Einschränkungen, Annahmen, bekannten Problemen; ggf. Workarounds\nREADME-Abschnitt “Limitations” oder FAQ.md\n0.5 Seite\n\n\nBeispiel-Workflow (Tutorial)\nSchritt-für-Schritt Anleitung mit einem realistischen Anwendungsfall (ggf. mit Code und Screenshot)\nJupyter Notebook (.ipynb) im Repo examples/ Ordner oder Markdown in docs/\n1–3 Seiten / entsprechend Zellen\n\n\nAPI-Referenz\nTechnische Dokumentation von Funktionen/Klassen für Entwickler*innen\nAutomatisch generiert aus Docstrings (z.B. Sphinx in docs/ Ordner, HTML/PDF Ausgabe)\nJe nach Codegröße (ggf. umfangreich)\n\n\nCONTRIBUTING\nAnleitung für Beitragswillige: Code Style, Workflow, Tests, Kontakt\nCONTRIBUTING.md im Repo\n0.5–1 Seite\n\n\nLICENSE / CITATION\nRechtliche Infos (Lizenztext); Zitationsleitfaden (Bevorzugte Zitierweise, DOI)\nJeweils eigene Datei im Repo (Plain Text/Markdown)\nKurz (Standardtext bzw. Referenz)\n\n\nRelease-Information\nVersionshinweise, Änderungsprotokoll (Changelog)\nCHANGELOG.md oder Releases auf GitHub\nfortlaufend pro Version (Stichpunkte)\n\n\n\n\n\n\nSchlusswort\nMit einer solchen Struktur und Herangehensweise lässt sich auch in einem kleinen Forschungsteam eine professionelle Dokumentation erzielen, die den Prinzipien von Open Science und nachhaltiger Softwareentwicklung gerecht wird. Die investierte Mühe wird durch Zeitgewinn bei Wiederverwendung und Erweiterung der Software mehr als aufgewogen. So wird die Forschungssoftware nicht zum einmaligen “Nebenprodukt”, sondern zu einem robusten, teilbaren Ergebnis wissenschaftlicher Arbeit.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/documentation.html#methodik-llms-als-autoren",
    "href": "Writing/documentation.html#methodik-llms-als-autoren",
    "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
    "section": "Methodik / LLMs als ‘Autoren’",
    "text": "Methodik / LLMs als ‘Autoren’\n\nErstellt wurde der initial draft mittels Websuche und “Deep-Research” von gpt-4.5 (preview). Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting durch Nicole Dresselhaus.\nKeine Angst davor, dass dies irgendein LLM-BS ist, um auf der Seite mehr Views zu bekommen. Ich habe alles mehrfach gelesen und würde es genauso schreiben - nur in schlechteren Worten.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html",
    "href": "Writing/ner4all-case-study.html",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "",
    "text": "The referenced study introduced a prompt-driven approach to NER, reframing it “from a purely linguistic task into a humanities-focused task”. Instead of training a specialized NER model for each corpus, the method leverages the fact that large pretrained LLMs already contain vast world knowledge and language understanding. The key idea is to provide the model with contextual definitions and instructions so it can recognize entities in context. Notably, the authors found that with proper prompts, a commercial LLM (ChatGPT-4) could achieve precision and recall on par with or better than state-of-the-art NER tools on a 1921 historical travel guide. This was achieved zero-shot, i.e. without any fine-tuning or additional training data beyond the prompt itself.\nPrompt Strategy: The success of this approach hinges on careful prompt engineering. The final prompt used in the paper had multiple components:\n\nPersona & Context: A brief introduction framing the LLM as an expert reading a historical text, possibly including domain context (e.g. “This text is an early 20th-century travel guide; language is old-fashioned”). This primes the model with relevant background.\nTask Instructions: A clear description of the NER task, including the list of entity categories and how to mark them in text. For example: “Identify all Person (PER), Location (LOC), and Organization (ORG) names in the text and mark each by enclosing it in tags.”\nOptional Examples: A few examples of sentences with correct tagged output (few-shot learning) to guide the model. Interestingly, the study found that zero-shot prompting often outperformed few-shot until ~16 examples were provided. Given the cost of preparing examples and limited prompt length, our implementation will focus on zero-shot usage for simplicity.\nReiteration & Emphasis: The prompt repeated key instructions in different words and emphasized compliance (e.g. “Make sure you follow the tagging format exactly for every example.”). This redundancy helps the model adhere to instructions.\nPrompt Engineering Tricks: They included creative cues to improve accuracy, such as offering a “monetary reward for each correct classification” and the phrase “Take a deep breath and think step by step.”. These tricks, drawn from prior work, encouraged the model to be thorough and careful.\nOutput Format: Crucially, the model was asked to repeat the original text exactly but insert tags around entity mentions. The authors settled on a format like &lt;&lt;PER ... /PER&gt;&gt; to tag people, &lt;&lt;LOC ... /LOC&gt;&gt; for locations, etc., covering each full entity span. This inline tagging format leveraged the model’s familiarity with XML/HTML syntax (from its training data) and largely eliminated problems like unclosed tags or extra spaces. By instructing the model not to alter any other text, they ensured the output could be easily compared to the input and parsed for entities.\n\nWhy Local LLMs? The original experiments used a proprietary API (ChatGPT-4). To make the method accessible to all (and avoid data governance issues of cloud APIs), we implement it with open-source LLMs running locally. Recent openly licensed models are rapidly improving and can handle such extraction tasks given the right prompt. Running everything locally also aligns with the paper’s goal of “democratizing access” to NER for diverse, low-resource texts – there are no API costs or internet needed, and data stays on local hardware for privacy.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#background-llm-based-ner-method-overview",
    "href": "Writing/ner4all-case-study.html#background-llm-based-ner-method-overview",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "",
    "text": "The referenced study introduced a prompt-driven approach to NER, reframing it “from a purely linguistic task into a humanities-focused task”. Instead of training a specialized NER model for each corpus, the method leverages the fact that large pretrained LLMs already contain vast world knowledge and language understanding. The key idea is to provide the model with contextual definitions and instructions so it can recognize entities in context. Notably, the authors found that with proper prompts, a commercial LLM (ChatGPT-4) could achieve precision and recall on par with or better than state-of-the-art NER tools on a 1921 historical travel guide. This was achieved zero-shot, i.e. without any fine-tuning or additional training data beyond the prompt itself.\nPrompt Strategy: The success of this approach hinges on careful prompt engineering. The final prompt used in the paper had multiple components:\n\nPersona & Context: A brief introduction framing the LLM as an expert reading a historical text, possibly including domain context (e.g. “This text is an early 20th-century travel guide; language is old-fashioned”). This primes the model with relevant background.\nTask Instructions: A clear description of the NER task, including the list of entity categories and how to mark them in text. For example: “Identify all Person (PER), Location (LOC), and Organization (ORG) names in the text and mark each by enclosing it in tags.”\nOptional Examples: A few examples of sentences with correct tagged output (few-shot learning) to guide the model. Interestingly, the study found that zero-shot prompting often outperformed few-shot until ~16 examples were provided. Given the cost of preparing examples and limited prompt length, our implementation will focus on zero-shot usage for simplicity.\nReiteration & Emphasis: The prompt repeated key instructions in different words and emphasized compliance (e.g. “Make sure you follow the tagging format exactly for every example.”). This redundancy helps the model adhere to instructions.\nPrompt Engineering Tricks: They included creative cues to improve accuracy, such as offering a “monetary reward for each correct classification” and the phrase “Take a deep breath and think step by step.”. These tricks, drawn from prior work, encouraged the model to be thorough and careful.\nOutput Format: Crucially, the model was asked to repeat the original text exactly but insert tags around entity mentions. The authors settled on a format like &lt;&lt;PER ... /PER&gt;&gt; to tag people, &lt;&lt;LOC ... /LOC&gt;&gt; for locations, etc., covering each full entity span. This inline tagging format leveraged the model’s familiarity with XML/HTML syntax (from its training data) and largely eliminated problems like unclosed tags or extra spaces. By instructing the model not to alter any other text, they ensured the output could be easily compared to the input and parsed for entities.\n\nWhy Local LLMs? The original experiments used a proprietary API (ChatGPT-4). To make the method accessible to all (and avoid data governance issues of cloud APIs), we implement it with open-source LLMs running locally. Recent openly licensed models are rapidly improving and can handle such extraction tasks given the right prompt. Running everything locally also aligns with the paper’s goal of “democratizing access” to NER for diverse, low-resource texts – there are no API costs or internet needed, and data stays on local hardware for privacy.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#solution-architecture",
    "href": "Writing/ner4all-case-study.html#solution-architecture",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "Solution Architecture",
    "text": "Solution Architecture\nOur solution consists of a workflow in n8n that orchestrates the NER process, and a local Ollama server that hosts the LLM for text analysis. The high-level workflow is as follows:\n\nWebhook Trigger (n8n): A user initiates the process by sending an HTTP request to n8n’s webhook with two inputs: (a) a simple text defining the entity categories of interest (for example, \"PER, ORG, LOC\"), and (b) the text to analyze (either included in the request or accessible via a provided file URL). This trigger node captures the input and starts the automation.\nPrompt Construction (n8n): The workflow builds a structured prompt for the LLM. Based on the webhook input, it prepares the system instructions listing each entity type and guidelines, then appends the user’s text. Essentially, n8n will merge the entity definitions into a pre-defined prompt template (the one derived from the paper’s method). This can be done using a Function node or an LLM Prompt node in n8n to ensure the text and instructions are combined correctly.\nLLM Inference (Ollama + LLM): n8n then passes the prompt to an Ollama Chat Model node, which communicates with the Ollama server’s API. The Ollama daemon hosts the selected 14B model on the local GPU and returns the model’s completion. In our case, the completion will be the original text with NER tags inserted around the entities (e.g. &lt;&lt;PER John Doe /PER&gt;&gt; went to &lt;&lt;LOC Berlin /LOC&gt;&gt; ...). This step harnesses the A100 GPU to generate results quickly, using the chosen model’s weights locally.\nOutput Processing (n8n): The tagged text output from the LLM can be handled in two ways. The simplest is to return the tagged text directly as the response to the webhook call – allowing the user to see their original text with all entities highlighted by tags. Alternatively, n8n can post-process the tags to extract a structured list of entities (e.g. a JSON array of {\"entity\": \"John Doe\", \"type\": \"PER\"} objects). This parsing can be done with a Regex or code node, but given our focus on correctness, we often trust the model’s tagging format to be consistent (the paper reported the format was reliably followed when instructed clearly). Finally, an HTTP Response node sends the results back to the user (or stores them), completing the workflow.\n\nWorkflow Structure: In n8n’s interface, the workflow might look like a sequence of connected nodes: Webhook → Function (build prompt) → AI Model (Ollama) → Webhook Response. If using n8n’s new AI Agent feature, some steps (like prompt templating) can be configured within the AI nodes themselves. The key is that the Ollama model node is configured to use the local server (usually at http://127.0.0.1:11434 by default) and the specific model name. We assume the base pipeline (available on GitHub) already includes most of this structure – our task is to slot in the custom prompt and model configuration for the NER use case.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#setup-and-infrastructure-requirements",
    "href": "Writing/ner4all-case-study.html#setup-and-infrastructure-requirements",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "Setup and Infrastructure Requirements",
    "text": "Setup and Infrastructure Requirements\nTo reproduce this solution, you will need a machine with an NVIDIA GPU and the following software components installed:\n\nn8n (v1.x** or later)** – the workflow automation tool. You can install n8n via npm, Docker, or use the desktop app. For a server environment, Docker is convenient. For example, to run n8n with Docker:\ndocker run -it --rm \\\n           -p 5678:5678 \\\n           -v ~/.n8n:/home/node/.n8n \\\n           n8nio/n8n:latest\nThis exposes n8n on http://localhost:5678 for the web interface. (If you use Docker and plan to connect to a host-running Ollama, start the container with --network=host to allow access to the Ollama API on localhost.)\nOllama (v0.x*) – an LLM runtime that serves models via an HTTP API. Installing Ollama is straightforward: download the installer for your OS from the official site (Linux users can run the one-line script curl -sSL https://ollama.com/install.sh | sh). After installation, start the Ollama server (daemon) by running:\nollama serve\nThis will launch the service listening on port 11434. You can verify it’s running by opening http://localhost:11434 in a browser – it should respond with “Ollama is running”. Note: Ensure your system has recent NVIDIA drivers and CUDA support if using GPU. Ollama supports NVIDIA GPUs with compute capability ≥5.0 (the A100 is well above this). Use nvidia-smi to confirm your GPU is recognized. If everything is set up, Ollama will automatically use the GPU for model inference (falling back to CPU if none available).\nLLM Model (14B class): Finally, download at least one large language model to use for NER. You have a few options here, and you can “pull” them via Ollama’s CLI:\n\nDeepSeek-R1 14B: A 14.8B-parameter model distilled from larger reasoning models (based on Qwen architecture). It’s optimized for reasoning tasks and compares to OpenAI’s models in quality. Pull it with:\nollama pull deepseek-r1:14b\nThis downloads ~9 GB of data (the quantized weights). If you have a very strong GPU (e.g. A100 80GB), you could even try deepseek-r1:70b (~43 GB), but 14B is a good balance for our use-case. DeepSeek-R1 is licensed MIT and designed to run locally with no restrictions.\nCogito 14B: A 14B “hybrid reasoning” model by Deep Cogito, known for excellent instruction-following and multilingual capability. Pull it with:\nollama pull cogito:14b\nCogito-14B is also ~9 GB (quantized) and supports an extended context window up to 128k tokens – which is extremely useful if you plan to analyze very long documents without chunking. It’s trained in 30+ languages and tuned to follow complex instructions, which can help in structured output tasks like ours.\nOthers: Ollama offers many models (LLaMA 2 variants, Mistral, etc.). For instance, ollama pull llama2:13b would get a LLaMA-2 13B model. These can work, but for best results in NER with no fine-tuning, we suggest using one of the above well-instructed models. If your hardware is limited, you could try a 7-8B model (e.g., deepseek-r1:7b or cogito:8b), which download faster and use ~4–5 GB VRAM, at the cost of some accuracy. In CPU-only scenarios, even a 1.5B model is available – it will run very slowly and likely miss more entities, but it proves the pipeline can work on minimal hardware.\n\n\nHardware Requirements: Our case assumes an NVIDIA A100 GPU (40 GB), which comfortably hosts a 14B model in memory and accelerates inference. In practice, any modern GPU with ≥10 GB memory can run a 13–14B model in 4-bit quantization. For example, an RTX 3090 or 4090 (24 GB) could handle it, and even smaller GPUs (or Apple Silicon with 16+ GB RAM) can run 7B models. Ensure you have sufficient system RAM as well (at least as much as the model size, plus overhead for n8n – 16 GB RAM is a safe minimum for 14B). Disk space of ~10 GB per model is needed. If using Docker for n8n, allocate CPU and memory generously to avoid bottlenecks when the LLM node processes large text.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#building-the-n8n-workflow",
    "href": "Writing/ner4all-case-study.html#building-the-n8n-workflow",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "Building the n8n Workflow",
    "text": "Building the n8n Workflow\nWith the environment ready, we now construct the n8n workflow that ties everything together. We outline each component with instructions:\n\n1. Webhook Input for Entities and Text\nStart by creating a Webhook trigger node in n8n. This will provide a URL (endpoint) that you can send a request to. Configure it to accept a POST request containing the necessary inputs. For example, we expect the request JSON to look like:\n{\n  \"entities\": \"PER, ORG, LOC\",\n  \"text\": \"John Doe visited Berlin in 1921 and met with the Board of Acme Corp.\"\n}\nHere, \"entities\" is a simple comma-separated string of entity types (you could also accept an array or a more detailed schema; for simplicity we use the format used in the paper: PER for person, LOC for location, ORG for organization). The \"text\" field contains the content to analyze. In a real scenario, the text could be much longer or might be sent as a file. If it’s a file, one approach is to send it as form-data and use n8n’s Read Binary File + Move Binary Data nodes to get it into text form. Alternatively, send a URL in the JSON and use an HTTP Request node in the workflow to fetch the content. The key is that by the end of this step, we have the raw text and the list of entity labels available in the n8n workflow as variables.\n\n\n2. Constructing the LLM Prompt\nNext, add a node to build the prompt that will be fed to the LLM. You can use a Function node (JavaScript code) or the “Set” node to template a prompt string. We will create two pieces of prompt content: a system instruction (the role played by the system prompt in chat models) and the user message (which will contain the text to be processed).\nAccording to the method, our system prompt should incorporate the following:\n\nPersona/Context: e.g. “You are a historian and archivist analyzing a historical document. The language may be old or have archaic spellings. You have extensive knowledge of people, places, and organizations relevant to the context.” This establishes domain expertise in the model.\nTask Definition: e.g. “Your task is to perform Named Entity Recognition. Identify all occurrences of the specified entity types in the given text and annotate them with the corresponding tags.”\nEntity Definitions: List the entity categories provided by the user, with a brief definition if needed. For example: “The entity types are: PER (persons or fictional characters), ORG (organizations, companies, institutions), LOC (locations such as cities, countries, landmarks).” If the user already provided definitions in the webhook, include those; otherwise a generic definition as shown is fine.\nTagging Instructions: Clearly explain the tagging format. We adopt the format from the paper: each entity should be wrapped in &lt;&lt;TYPE ... /TYPE&gt;&gt;. So instruct: “Enclose each entity in double angle brackets with its type label. For example: &lt;&lt;PER John Doe /PER&gt;&gt; for a person named John Doe. Do not alter any other text – only insert tags. Ensure every opening tag has a closing tag.” Also mention that tags can nest or overlap if necessary (though that’s rare).\nOutput Expectations: Emphasize that the output should be the exact original text, verbatim, with tags added and nothing else. For example: “Repeat the input text exactly, adding the tags around the entities. Do not add explanations or remove any content. The output should look like the original text with markup.” This is crucial to prevent the model from omitting or rephrasing text. The paper’s prompt literally had a line: “Repeat the given text exactly. Be very careful to ensure that nothing is added or removed apart from the annotations.”.\nCompliance & Thoughtfulness: We can borrow the trick of telling the model to take its time and be precise. For instance: “Before answering, take a deep breath and think step by step. Make sure you find all entities. You will be rewarded for each correct tag.” While the notion of reward is hypothetical, such phrasing has been observed to sharpen the model’s focus. This is optional but can be useful for complex texts.\n\nOnce this system prompt is assembled as a single string, it will be sent as the system role content to the LLM. Now, for the user prompt, we simply supply the text to be analyzed. In many chat-based LLMs, the user message would contain the text on which the assistant should perform the task. We might prefix it with something like “Text to analyze:” for clarity, or just include the raw text. (Including a prefix is slightly safer to distinguish it from any instructions, but since the system prompt already set the task, the user message can be just the document text.)\nIn n8n, if using the Basic LLM Chain node, you can configure it to use a custom system prompt. For example, connect the Function/Set node output into the LLM node, and in the LLM node’s settings choose “Mode: Complete” or similar, then under System Instructions put an expression that references the constructed prompt text (e.g., { $json[\"prompt\"] } if the prompt was output to that field). The User Message can similarly be fed from the input text field (e.g., { $json[\"text\"] }). Essentially, we map our crafted instruction into the system role, and the actual content into the user role.\n\n\n3. Configuring the Local LLM (Ollama Model Node)\nNow configure the LLM node to use the Ollama backend and your downloaded model. n8n provides an “Ollama Chat Model” integration, which is a sub-node of the AI Agent system. In the n8n editor, add or open the LLM node (if using the AI Agent, this might be inside a larger agent node), and look for model selection. Select Ollama as the provider. You’ll need to set up a credential for Ollama API access – use http://127.0.0.1:11434 as the host (instead of the default localhost, to avoid any IPv6 binding issues). No API key is needed since it’s local. Once connected, you should see a dropdown of available models (all the ones you pulled). Choose the 14B model you downloaded, e.g. deepseek-r1:14b or cogito:14b.\nDouble-check the parameters for generation. By default, Ollama models have their own preset for max tokens and temperature. For an extraction task, we want the model to stay focused and deterministic. It’s wise to set a relatively low temperature (e.g. 0.2) to reduce randomness, and a high max tokens so it can output the entire text with tags (set max tokens to at least the length of your input in tokens plus 10-20% for tags). If using Cogito with its 128k context, you can safely feed very long text; with other models (often ~4k context), ensure your text isn’t longer than the model’s context limit or use a model variant with extended context. If the model supports “tools” or functions, you won’t need those here – this is a single-shot prompt, not a multi-step agent requiring tool usage, so just the chat completion mode is sufficient.\nAt this point, when the workflow runs to this node, n8n will send the system and user messages to Ollama and wait for the response. The heavy lifting is done by the LLM on the GPU, which will generate the tagged text. On an A100, a 14B model can process a few thousand tokens of input and output in just a handful of seconds (exact time depends on the model and input size).\n\n\n4. Returning the Results\nAfter the LLM node, add a node to handle the output. If you want to present the tagged text directly, you can pass the LLM’s output to the final Webhook Response node (or if using the built-in n8n chat UI, you would see the answer in the chat). The tagged text will look something like:\n&lt;&lt;PER John Doe /PER&gt;&gt; visited &lt;&lt;LOC Berlin /LOC&gt;&gt; in 1921 and met with the Board\nof &lt;&lt;ORG Acme Corp /ORG&gt;&gt;.\nThis format highlights each identified entity. It is immediately human-readable with the tags, and trivial to post-process if needed. For example, one could use a regex like &lt;&lt;(\\w+) (.*?) /\\1&gt;&gt; to extract all type and entity pairs from the text. In n8n, a quick approach is to use a Function node to find all matches of that pattern in item.json[\"data\"] (assuming the LLM output is in data). Then one could return a JSON array of entities. However, since our focus is on correctness and ease, you might simply return the marked-up text and perhaps document how to parse it externally if the user wants structured data.\nFinally, use an HTTP Response node (if the workflow was triggered by a Webhook) to send back the results. If the workflow was triggered via n8n’s chat trigger (in the case of interactive usage), you would instead rely on the chat UI output. For a pure API workflow, the HTTP response will contain either the tagged text or a JSON of extracted entities, which the user’s script or application can then use.\nNote: If you plan to run multiple analyses or have an ongoing service, you might want to persist the Ollama server (don’t shut it down between runs) and perhaps keep the model loaded in VRAM for performance. Ollama will cache the model in memory after the first request, so subsequent requests are faster. On an A100, you could even load two models (if you plan to experiment with which gives better results) but be mindful of VRAM usage if doing so concurrently.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#model-selection-considerations",
    "href": "Writing/ner4all-case-study.html#model-selection-considerations",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "Model Selection Considerations",
    "text": "Model Selection Considerations\nWe provided two example 14B models (DeepSeek-R1 and Cogito) to use with this pipeline. Both are good choices, but here are some considerations and alternatives:\n\nAccuracy vs. Speed: Larger models (like 14B or 30B) generally produce more accurate and coherent results, especially for complex instructions, compared to 7B models. Since our aim is correctness of NER output, the A100 allows us to use a 14B model which offers a sweet spot. In preliminary tests, these models can correctly tag most obvious entities and even handle some tricky cases (e.g. person names with titles, organizations that sound like person names, etc.) thanks to their pretrained knowledge. If you find the model is making mistakes, you could try a bigger model (Cogito 32B or 70B, if resources permit). Conversely, if you need faster responses and are willing to trade some accuracy, a 7-8B model or running the 14B at a higher quantization (e.g. 4-bit) on CPU might be acceptable for smaller texts.\nDomain of the Text: The paper dealt with historical travel guide text (1920s era). These open models have been trained on large internet corpora, so they likely have seen a lot of historical names and terms, but their coverage might not be as exhaustive as GPT-4. If your text is in a specific domain (say, ancient mythology or very obscure local history), the model might miss entities that it doesn’t recognize as famous. The prompt’s context can help (for example, adding a note like “Note: Mythological characters should be considered PERSON entities.” as they did for Greek gods). For extremely domain-specific needs, one could fine-tune a model or use a specialized one, but that moves beyond the zero-shot philosophy.\nLanguage: If your texts are not in English, ensure the chosen model is multilingual. Cogito, for instance, was trained in over 30 languages, so it can handle many European languages (the paper also tested German prompts). If using a model that’s primarily English (like some LLaMA variants), you might get better results by writing the instructions in English but letting it output tags in the original text. The study found English prompts initially gave better recall even on German text, but with prompt tweaks the gap closed. For our pipeline, you can simply provide the definitions in English and the text in the foreign language – a capable model will still tag the foreign entities. For example, Cogito or DeepSeek should tag a German sentence’s “Herr Schmidt” as &lt;&lt;PER Herr Schmidt /PER&gt;&gt;. Always test on a small sample if in doubt.\nExtended Context: If your input text is very long (tens of thousands of words), you should chunk it into smaller segments (e.g. paragraph by paragraph) and run the model on each, then merge the outputs. This is because most models (including DeepSeek 14B) have a context window of 2048–8192 tokens. However, Cogito’s 128k context capability is a game-changer – in theory you could feed an entire book and get a single output. Keep in mind the time and memory usage will grow with very large inputs, and n8n might need increased timeout settings for such long runs. For typical use (a few pages of text at a time), the standard context is sufficient.\n\nIn our implementation, we encourage experimenting with both DeepSeek-R1 and Cogito models. Both are open-source and free for commercial use (Cogito uses an Apache 2.0 license, DeepSeek MIT). They represent some of the best 14B-class models as of early 2025. You can cite these models in any academic context if needed, or even switch to another model with minimal changes to the n8n workflow (just pull the model and change the model name in the Ollama node).",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#example-run",
    "href": "Writing/ner4all-case-study.html#example-run",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "Example Run",
    "text": "Example Run\nLet’s run through a hypothetical example to illustrate the output. Suppose a historian supplies the following via the webhook:\n\nEntities: PER, ORG, LOC\nText: “Baron Münchhausen was born in Bodenwerder and served in the Russian military under Empress Anna. Today, the Münchhausen Museum in Bodenwerder is operated by the town council.”\n\nWhen the workflow executes, the LLM receives instructions to tag people (PER), organizations (ORG), and locations (LOC). With the prompt techniques described, the model’s output might look like:\n&lt;&lt;PER Baron Münchhausen /PER&gt;&gt; was born in &lt;&lt;LOC Bodenwerder /LOC&gt;&gt; and served\nin the Russian military under &lt;&lt;PER Empress Anna /PER&gt;&gt;. Today, the &lt;&lt;ORG\nMünchhausen Museum /ORG&gt;&gt; in &lt;&lt;LOC Bodenwerder /LOC&gt;&gt; is operated by the town\ncouncil.\nAll person names (Baron Münchhausen, Empress Anna) are enclosed in &lt;&lt;PER&gt;&gt; tags, the museum is marked as an organization, and the town Bodenwerder is marked as a location (twice). The rest of the sentence remains unchanged. This output can be returned as-is to the user. They can visually verify it or programmatically parse out the tagged entities. The correctness of outputs is high: each tag corresponds to a real entity mention in the text, and there are no hallucinated tags. If the model were to make an error (say, tagging “Russian” as LOC erroneously), the user could adjust the prompt (for example, clarify that national adjectives are not entities) and re-run.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#limitations-and-solutions",
    "href": "Writing/ner4all-case-study.html#limitations-and-solutions",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "Limitations and Solutions",
    "text": "Limitations and Solutions\nWhile this pipeline makes NER easier to reproduce, it’s important to be aware of its limitations and how to mitigate them:\n\nModel Misclassifications: A local 14B model may not match GPT-4’s level of understanding. It might occasionally tag something incorrectly or miss a subtle entity. For instance, in historical texts, titles or honorifics (e.g. “Dr. John Smith”) might confuse it, or a ship name might be tagged as ORG when it’s not in our categories. Solution: Refine the prompt with additional guidance. You can add a “Note” section in the instructions to handle known ambiguities (the paper did this with notes about Greek gods being persons, etc.). Also, a quick manual review or spot-check is recommended for important outputs. Since the output format is simple, a human or a simple script can catch obvious mistakes (e.g., if “Russian” was tagged LOC, a post-process could remove it knowing it’s likely wrong). Over time, if you notice a pattern of mistakes, update the prompt instructions accordingly.\nText Reproduction Issues: We instruct the model to output the original text verbatim with tags, but LLMs sometimes can’t resist minor changes. They may “correct” spelling or punctuation, or alter spacing. The paper noted this tendency and used fuzzy matching when evaluating. In our pipeline, minor format changes usually don’t harm the extraction, but if preserving text exactly is important (say for downstream alignment), this is a concern. Solution: Emphasize fidelity in the prompt (we already do). If needed, do a diff between the original text and tagged text and flag differences. Usually differences will be small (e.g., changing an old spelling to modern). You can then either accept them or attempt a more rigid approach (like asking for a JSON list of entity offsets – though that introduces other complexities and was intentionally avoided by the authors). In practice, we found the tag insertion approach with strong instructions yields nearly identical text apart from the tags.\nLong Inputs and Memory: Very large documents may exceed the model’s input capacity or make the process slow. The A100 GPU can handle a lot, but n8n itself might have default timeouts for a single workflow execution. Solution: For long texts, break the input into smaller chunks (maybe one chapter or section at a time). n8n can loop through chunks using the Split In Batches node or simply by splitting the text in the Function node and feeding the LLM node multiple times. You’d then concatenate the outputs. If chunking, ensure that if an entity spans a chunk boundary, it might be missed – usually rare in well-chosen chunk boundaries (paragraph or sentence). Alternatively, use Cogito for its extended context to avoid chunking. Make sure to increase n8n’s execution timeout if needed (via environment variable N8N_DEFAULT_TIMEOUT or in the workflow settings).\nConcurrent Usage: If multiple users or processes hit the webhook simultaneously, they would be sharing the single LLM instance. Ollama can queue requests, but the GPU will handle them one at a time (unless running separate instances with multiple GPUs). For a research setting with one user at a time, this is fine. If offering this as a service to others, consider queuing requests or scaling out (multiple replicas of this workflow on different GPU machines). The stateless design of the prompt makes each run independent.\nn8n Learning Curve: For historians new to n8n, setting up the workflow might be unfamiliar. However, n8n’s no-code interface is fairly intuitive with a bit of guidance. This case study provides the logic; one can also import pre-built workflows. In fact, the n8n community has template workflows (for example, a template for chatting with local LLMs) that could be adapted. We assume the base pipeline from the paper’s authors is available on GitHub – using that as a starting point, one mostly needs to adjust nodes as described. If needed, one can refer to n8n’s official docs or community forum for help on creating a webhook or using function nodes. Once set up, running the workflow is as easy as sending an HTTP request or clicking “Execute Workflow” in n8n.\nOutput Verification: Since we prioritize correctness, you may want to evaluate how well the model did, especially if you have ground truth annotations. While benchmarking is out of scope here, note that you can integrate evaluation into the pipeline too. For instance, if you had a small test set with known entities, you could compare the model output tags with expected tags using a Python script (n8n has an Execute Python node) or use an NER evaluation library like nervaluate for precision/recall. This is exactly what the authors did to report performance, and you could mimic that to gauge your chosen model’s accuracy.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#conclusion",
    "href": "Writing/ner4all-case-study.html#conclusion",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "Conclusion",
    "text": "Conclusion\nBy following this guide, we implemented the NER4All paper’s methodology with a local, reproducible setup. We used n8n to handle automation and prompt assembly, and a local LLM (via Ollama) to perform the heavy-duty language understanding. The result is a flexible NER pipeline that requires no training data or API access – just a well-crafted prompt and a powerful pretrained model. We demonstrated how a user can specify custom entity types and get their text annotated in one click or API call. The approach leverages the strengths of LLMs (vast knowledge and language proficiency) to adapt to historical or niche texts, aligning with the paper’s finding that a bit of context and expert prompt design can unlock high NER performance.\nImportantly, this setup is easy to reproduce: all components are either open-source or freely available (n8n, Ollama, and the models). A research engineer or historian can run it on a single machine with sufficient resources, and it can be shared as a workflow file for others to import. By removing the need for extensive data preparation or model training, this lowers the barrier to extracting structured information from large text archives.\nMoving forward, users can extend this case study in various ways: adding more entity types (just update the definitions input), switching to other LLMs as they become available (perhaps a future 20B model with even better understanding), or integrating the output with databases or search indexes for further analysis. With the rapid advancements in local AI models, we anticipate that such pipelines will become even more accurate and faster over time, continually democratizing access to advanced NLP for all domains.\nSources: This implementation draws on insights from [1] for the prompt-based NER method, and uses tools like n8n and Ollama as documented in their official guides. The chosen models (DeepSeek-R1[2] and Cogito[3]) are described in their respective releases. All software and models are utilized in accordance with their licenses for a fully local deployment.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Writing/ner4all-case-study.html#about-llms-as-authors",
    "href": "Writing/ner4all-case-study.html#about-llms-as-authors",
    "title": "Case Study: Local LLM-Based NER with n8n and Ollama",
    "section": "About LLMs as ‘authors’",
    "text": "About LLMs as ‘authors’\n\nThe initial draft was created using “Deep-Research” from gpt-4.5 (preview). Final proofreading/content review/layouting by Nicole Dresselhaus. Do not fear that this is some LLM-BS to get views on the homepage. I read everything multiple times and would have written it with this content - just in worse words.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "Case Study: Local LLM-Based NER with n8n and Ollama"
    ]
  },
  {
    "objectID": "Uni/Lernerfolg_an_der_Uni.html",
    "href": "Uni/Lernerfolg_an_der_Uni.html",
    "title": "Wie lerne ich richtig an der Uni?",
    "section": "",
    "text": "Dies ist eine gute Frage. Da ich im laufe der Zeit einige Antworten gesammelt habe, wollte ich diese mal hier niederschreiben. Vorweg eine Warnung: All das hier spiegelt nur meine persönlichen Erfahrungen aus Gesprächen wieder. Es kann sein, dass die z.B. für euren Fachbereich nicht gilt. Da wir das nun aus dem Weg haben, geht es auch gleich los.",
    "crumbs": [
      "Home",
      "Serious",
      "Uni",
      "Wie lerne ich richtig an der Uni?"
    ]
  },
  {
    "objectID": "Uni/Lernerfolg_an_der_Uni.html#uni-ist-nicht-schule",
    "href": "Uni/Lernerfolg_an_der_Uni.html#uni-ist-nicht-schule",
    "title": "Wie lerne ich richtig an der Uni?",
    "section": "Uni ist nicht Schule",
    "text": "Uni ist nicht Schule\nEinige mögen sagen: “duh!”, aber es ist erschreckend, wie viele Leute meinen, dass ihnen die Uni etwas schuldet oder das Dozenten und Tutoren dafür verantwortlich sind, dass man hier etwas lernt. Studium ist eine komplett freiwillige Veranstaltung. Man kann jederzeit sagen: “Passt mir nicht. Ich gehe.” An der Uni wird erwartet, dass man sich ggf. einarbeitet, wenn man etwas nicht weiss; dass man Sekundärliteratur fragt (z.B. in Mathe auch mal in Bücher schaut um eine andere Erklärung zu bekommen, als der Prof an die Tafel geklatscht hat).",
    "crumbs": [
      "Home",
      "Serious",
      "Uni",
      "Wie lerne ich richtig an der Uni?"
    ]
  },
  {
    "objectID": "Uni/Lernerfolg_an_der_Uni.html#etwas-lerntheorie",
    "href": "Uni/Lernerfolg_an_der_Uni.html#etwas-lerntheorie",
    "title": "Wie lerne ich richtig an der Uni?",
    "section": "Etwas Lerntheorie",
    "text": "Etwas Lerntheorie\nEs gibt einen sehr schönen Talk von Edwand Kmett in dem er über seine Erfahrungen berichtet. Kurzum: Man lernt durch stete Wiederholung. Und der beste Moment etwas zu wiederholen ist, kurz bevor man es vergisst. Das stimmt ziemlich genau mit meiner Erfahrung überein.\n\nAuswendig lernen\nGrade die oben genannte Theorie steht beim Auswendiglernen im Vordergrund. Wenn man etwas langfristig auswendig lernen will (Fremdsprachen, etc.), dann gibt es hierzu Software, die herausfindet, wann es der beste Zeitpunkt ist, dich wieder abzufragen: Anki gibt es für jede Platform kostenlos (außer iPhone - hier 25$, weil Apple so viel Geld für das einstellen im AppStore haben will). Anki ist dazu gedacht, dass man zu jedem Thema einen Stapel hat (z.b. Klausurfragen, Sprachen, …) und jeden Tag lernt. Nach einiger Zeit wird die vorhersage der Lernzeit ziemlich genau. Anfangs beantwortet man noch viele Fragen täglich, aber je häufiger man die Antworten kennt, desto weiter hinten landen sie im Stapel. Schlussendlich kommt dieselbe Frage dann nur noch 1x/Monat oder noch seltener.\nIch benutze dies insbesondere zum Auswendiglernen von Fakten, Formeln, Fachbegriffen etc. Bei Mathe bietet sich zum Beispiel an einen Stapel mit allen Definitionen zu haben; in der Biologie eine Liste der Schema und Kreisläufe etc.\nMan kann auch einen Hardcore-Lernmarathon machen. Meine letzten beiden Klausuren waren nur auf “bestehen” - also ohne Note. Ich habe mir eine alte Klausur organisiert (mehr genaues unten) und dann daraus Karten erstellt. Dies hat nur wenige Stunden gedauert (2-3 verteilt auf 2 Tage). Damit habe ich dann am Tag vor der Klausur 2x gelernt (1x nach dem Aufstehen, 1x vorm schlafengehen; jeweils nach 30 Minuten hatte ich alle Fragen min. 1x korrekt beantwortet). Am Morgen der Klausur hab ich die Fragen vor dem Aufstehen noch einmal durchgemacht (wieder 25-30 min), habe mir zur Klausur fertig gemacht und bin 30 Min vor der Klausur die Fragen nochmals durchgegangen (15-30 min), aber konnte sie mittlerweile alle auswendig. Insgesamt habe ich mit Anki so für die Klausur effektiv 2h gelernt (+2-3h für das erstellen der Karten), habe die Klausur geschrieben und mit einer 3.0 bestanden (also wäre 3.0 gewesen, wenn es nicht unbenotet gewesen wäre). Kommilitonen, die sich (nach eigener Aussage) 1-2 Wochen auf die Klausur vorbereitet haben und eine Note wollten, schnitten teilweise schlechter ab (viele aber auch viel besser).\n\n\nMethodik lernen\nIm Gegensatz zum plumpen auswendig lernen gibt es dann auch Anforderungen, wo es darum geht Methoden und Anwendungen zu verstehen. Inbesondere ist dies in Vorbereitung auf z.B. mündliche Prüfungen der Fall. Hier steht eher die Theorie im Vordergrund.\nUm solche Konzepte zu verstehen braucht es leider Zeit. Hier hilft kein 48h-Lernmarathon um das “mal eben” auf die Kette zu kriegen. Am besten bereitet man sich das gesamte Semester über vor (haha! Als ob! :p). Das “Geheimnis” hier liegt in einer Kombination der Ansätze. Zum einen muss man natürlich verstehen, worum es geht. Hier hilft es Definitionen und Fachbegriffe z.B. mit Anki zu lernen. Allerdings muss man sich zusätzlich noch nach jeder(!) Vorlesung hinsetzen und versuchen den Inhalt zu verdauen. Dies können nur 10 Minuten sein oder auch 2h. Hier kommen dann Dinge zum Tragen, wie Sekundärliteratur, Wikipedia, Google, … Man muss die Zusammenhänge einmal verstehen - da kommt man nicht drumherum. ABER: Unser Gehirn arbeitet Assoziativ. Zusammenhänge sind meist logisch oder krass widersprüchlich. Hieraus kann man dann z.B. “Stichwortketten” bauen, von denen man nur das erste auswendig lernt und von da aus sich an den Rest “erinnert”.\nKleines Beispiel aus der Welt der Mathematik:\nVektorraum -&gt; Ist zu einer Basis definiert\n               -&gt; Basis ist die größtmögliche Zahl lin. unabh. Vektoren. Lin. Hülle der Basis ist der VR\n                   -&gt; Lin. Hülle ist jede Lin.-Komb. von Vektoren\n           -&gt; Hat eine Vektoraddition und skalare Multiplikation\n           -&gt; Wird über einem Körper aufgespannt\n               -&gt; Körper sind 2 abelsche Gruppen mit Distributivgesetz\n                   -&gt; abelsche Gruppe ist Menge mit K.A.I.N.\n                       -&gt; ....\nSo kann man sich über 5-6 Stichwörter fast am gesamten Stoff der Vorlesung entlanghangeln und merkt schnell, wo es hakt. Hier kann man dann nochmal gezielt nachhaken. Auch kann man bei so einer Struktur aus jedem “a -&gt; b -&gt; c” Anki-Karten machen mit “a” auf der Vorderseite, “b” auf der Rückseite bzw. “b” auf der Vorderseite und “c” auf der Rückseite und so gezielt diese “Ketten” trainieren. Grade in einer mündlichen Prüfung hangeln sich Prüfer ebenfalls an diesen Ketten entlang.",
    "crumbs": [
      "Home",
      "Serious",
      "Uni",
      "Wie lerne ich richtig an der Uni?"
    ]
  },
  {
    "objectID": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-klausur",
    "href": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-klausur",
    "title": "Wie lerne ich richtig an der Uni?",
    "section": "Vorbereiten auf eine Klausur",
    "text": "Vorbereiten auf eine Klausur\n\nHerausfinden, um was für eine Art von Klausur es sich handelt\n\nAnkreuzklausur?\nAuswendiglern-Klausur?\nPraktische Klausur (z.b. fast 1:1 Übungsaufgaben, feste Schema, ..)?\nOpen-Book?\nAnnotation von Grafiken?\n\nKlausuren von der Fachschaft organisieren\n\nFalls keine Vorhanden: Altfachschaftler fragen, wie die Klausur bei ihnen war\nNeue Klausur mit in die FS bringen, falls möglich (z.b. schreiend rausrennen und Klausur dabei mitnehmen, bevor man offiziell registriert wurde)\n\n\nJe nach Klausurtyp dann mit Anki stumpf Karten machen und auswendig lernen (z.b. Ankreuzklausur, Grafik-annotations-Klausur, ..) oder Übungsaufgaben/Altklausuren durchrechnen",
    "crumbs": [
      "Home",
      "Serious",
      "Uni",
      "Wie lerne ich richtig an der Uni?"
    ]
  },
  {
    "objectID": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-mündliche-prüfung",
    "href": "Uni/Lernerfolg_an_der_Uni.html#vorbereiten-auf-eine-mündliche-prüfung",
    "title": "Wie lerne ich richtig an der Uni?",
    "section": "Vorbereiten auf eine mündliche Prüfung",
    "text": "Vorbereiten auf eine mündliche Prüfung\n\nProtokolle aus der Fachschaft organisieren\n\nHäufig gegen Pfand, dass man bei Abgabe eines Protokolls wieder bekommt\nWenn keins vorhanden für die nachfolgede Generation eins ausfüllen\n\n\nWenn ihr einen Reihe von Protokollen vorliegen habt, dann schreibt alle Fragen heraus und notiert, wie häufig diese Frage gestellt wurde. So findet ihr heraus, auf welche Punkte der Prüfer besonders Wert legt (z.B. häufig sein eigenes Forschungsfeld). Diese Fragen dann restlos klären und zu Anki-Karten verarbeiten. Das reicht meistens für ein Bestehen. Wenn ihr auf eine gute Note wert legt, dann solltet ihr auch noch die Vorlesung, wie im Bereich “Methodik lernen” erwähnt, nacharbeiten. Insbesondere helfen hier die Assoziationsketten weiter den Stoff auch in der Prüfung in der richtigen Reihenfolge abzurufen. Vielleicht erkennt ihr solche Ketten schon aus den Prüfungsprotokollen und könnt euch ausmalen, wie man z.b. von da aus auf andere Themen der Vorlesung kommt (die z.b. neu sind oder überarbeitet wurden).\n\nUnterschiede mündliche Bachelor/Master-Prüfungen\nEinige Dozenten machen unterschiedliche Anforderungen, ob sie einen Bachelor oder einen Master-Studenten prüfen. Abgesehen von der anderen Prüfungszeit (15-30min bei bachelor, 25-45 bei Master) ist hier auch das Vorgehen anders. Bei einem Bachelor wird klassischerweise alles oberflächlich abgefragt und nur wenig in die Tiefe gegangen. Bei einem Master wir nur noch stichpunktartig gefragt, dafür aber bis ins Detail.\nBeispiel: Ich hatte eine mündliche Masterprüfung, bei der in der Vorlesung 7 verschiedene Themen behandelt wurden. In der Prüfung wurden dann nur die Themenübersicht abgefragt und bei 2 Themen komplett in die Tiefe gegangen - inkl. Formeln, Bedeutung, Übertragung auf in der Vorlesung nicht angesprochene Aspekte etc. Die anderen 5 Themen kamen nicht dran. Bei meinen Bachelorprüfungen war das eher umgekehrt: Hier wurde sich grob an der Vorlesung entlang gehangelt und zumindest alles einmal kurz angetestet, ob die zentralen Inhalte der Vorlesung verstanden wurden.\nDies hat häufig auch damit zu tun, dass man im Bachelor eher Grundlagen hört und somit ein grobes Verständnis aller Dinge wichtig ist, während im Master auf die Aneignung von Tiefenwissen ankommt.",
    "crumbs": [
      "Home",
      "Serious",
      "Uni",
      "Wie lerne ich richtig an der Uni?"
    ]
  },
  {
    "objectID": "Uni/Lernerfolg_an_der_Uni.html#prüfungsangt",
    "href": "Uni/Lernerfolg_an_der_Uni.html#prüfungsangt",
    "title": "Wie lerne ich richtig an der Uni?",
    "section": "Prüfungsangt",
    "text": "Prüfungsangt\nZu guter Letzt noch ein paar Worte zum Thema Prüfungsangst. Es ist normal, dass man vor einer Prüfung angespannt ist. Es ist nicht normal, wenn die Anspannung so ausartet, dass man sich übergibt, Krämpfe bekommt oder ähnlich starke Symptome zeigt. Ich leide selbst an solchen Problemen und habe mich schon mehrfach vor Prüfungen übergeben. Eine klassische Konfrontationstherapie funktioniert aufgrund der Seltenheit der Prüfungen nicht oder nur sehr schwer. Ich habe mich an meinen Arzt gewendet und habe nun genau für solche Situationen ein Medikament. 1-2h vor einer Prüfung nehme ich das und komme in einen komischen Zustand. Ich merke zwar noch, dass ich Angespannt bin und eigentlich Angst hätte, aber es “stört” mich nicht wirklich. Es versetzt mich nicht in Panik oder sonstwas. Es schaltet mein Gehirn nicht aus oder hat andere negative Effekte. Natürlich geht das auch mit Nachteilen einher: ein paar Tage keinen Alkohol, kein Auto fahren, etc. - Aber meist ist das ja nur 2-3x/Semester der Fall. Wenn man nicht so stark betroffen ist, dann ist davon allerdings abzuraten. Das Medikament gleicht die Panik durch Gelassenheit aus - wenn man keine Panik hat, dann wird man hierdurch so “gelassen” dass man mehrere Stunden einschläft - was in einer Prüfung vielleicht nicht ganz so gut ist ;)\nEs gibt auch zahlreiche Regularien und Rechtsansprüche, die ihr bei sowas habt. Ihr habt zum Beispiel (sofern ein (Amts?-)Arzt eine Prüfungsangst bestätigt hat) Anspruch auf mehr Prüfungszeit, die Prüfung alleine abzulegen (z.b. bei einem Mitarbeiter, während andere im Hörsaal schreiben), eine mündliche durch eine schriftliche zu tauschen (oder umgekehrt), etc. Das kann man individuell mit dem Prüfer absprechen. Ich weiss nicht, wie das in anderen Fakultäten läuft - aber in der Technischen Fakultät hat fast jeder Prüfer dafür volles Verständnis (einige litten sogar früher selbst an sowas).\nDie kostenlose psychologische Beratung an der Uni (aka. “Das rote Sofa” im X) bietet hier auch Hilfestellung bei und vermittelt in schwereren Fällen auch gleich noch eine Therapie/Ärzte. Hier kann man z.b. Prüfungssimulationen abhalten oder sich Hilfe holen, wenn ein Dozent sich querstellt. Die Mitarbeiter begleiten einen z.B. auch zu einer Prüfung (nach Absprache mit dem Veranstalter), falls das hilft, etc.\nEs ist keine Schande so ein Problem zu haben und es gibt genug, die sich damit rumschlagen. Aber man ist hier an der Uni auch nicht alleine damit. Es gibt zahlreiche Hilfsangebote.",
    "crumbs": [
      "Home",
      "Serious",
      "Uni",
      "Wie lerne ich richtig an der Uni?"
    ]
  },
  {
    "objectID": "Uni/Lernerfolg_an_der_Uni.html#schlusswort",
    "href": "Uni/Lernerfolg_an_der_Uni.html#schlusswort",
    "title": "Wie lerne ich richtig an der Uni?",
    "section": "Schlusswort",
    "text": "Schlusswort\nViel Erfolg bei euren Prüfungen. Falls euch dieser Artikel geholfen hat oder ihr noch Anregungen/Verbessenguswünsche habt, schreibt mir einfach.",
    "crumbs": [
      "Home",
      "Serious",
      "Uni",
      "Wie lerne ich richtig an der Uni?"
    ]
  },
  {
    "objectID": "Opinions/Keyboard-Layout.html",
    "href": "Opinions/Keyboard-Layout.html",
    "title": "Keyboard-Layout",
    "section": "",
    "text": "Since around 2006 i basically write only using the NEO2-Layout. There are many advantages that are not obvious to an onlooker right away.\nDon’t get me wrong. I still can type QWERTZ - just because you learn an additional layout does not mean that you forget everything from before.\nThe secret sauce lies in the deeper layers. Especially layer 3 having all the “hard to reach” things like brackets, braces, etc. right on the home row. And the 4th layer is magic for text-navigation. Left hand has the full navigation, right hand has the complete Numpad - even on laptop-keyboards that are lacking those.\nFor me as a person having the usual German Keyboard with AltGr this just means:\n\nPutting the thumb down on AltGr - it is above there anyway.\nUse left hand as normal arrow-keys (that work EVERYWHERE because they are just arrow keys)\nAlso use Home/End/PgUp/PgDown/…\n\nBefore i always had to switch over or hope that a thing had support for vi-style “hjkl”.\nThat’s why i also prefer Neovim as my primary editor - just not having to touch your mouse at any time for anything is such a godsend :)\nBest thing: If you don’t want to switch, there is also a “Neo-QWERTZ”-variant .. where you can just try the deeper layers while not leaving your QWERTZ-layout behind. But i have just seen and never tried it. Your experience may be sub-par.",
    "crumbs": [
      "Home",
      "Fun",
      "Opinions",
      "Keyboard-Layout"
    ]
  },
  {
    "objectID": "Coding/Haskell/Advantages.html",
    "href": "Coding/Haskell/Advantages.html",
    "title": "Talks und Posts zu Haskell",
    "section": "",
    "text": "Gründe Haskell zu nutzen und wo Vorteile liegen.\n\n\n\nThe Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness\n\n\n\n\n\nPapers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers\n\n\n\n\n\nTom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)\n\n\n\n\n\nFunctional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett\n\n\n\n\n\nHaskell fast and hard\nCounterexamples for Typeclasses",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Talks und Posts zu Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Advantages.html#talks",
    "href": "Coding/Haskell/Advantages.html#talks",
    "title": "Talks und Posts zu Haskell",
    "section": "",
    "text": "The Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Talks und Posts zu Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Advantages.html#bücherpaper",
    "href": "Coding/Haskell/Advantages.html#bücherpaper",
    "title": "Talks und Posts zu Haskell",
    "section": "",
    "text": "Papers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Talks und Posts zu Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Advantages.html#funny-talks",
    "href": "Coding/Haskell/Advantages.html#funny-talks",
    "title": "Talks und Posts zu Haskell",
    "section": "",
    "text": "Tom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Talks und Posts zu Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Advantages.html#unsortedunseen",
    "href": "Coding/Haskell/Advantages.html#unsortedunseen",
    "title": "Talks und Posts zu Haskell",
    "section": "",
    "text": "Functional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Talks und Posts zu Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Advantages.html#tutorials",
    "href": "Coding/Haskell/Advantages.html#tutorials",
    "title": "Talks und Posts zu Haskell",
    "section": "",
    "text": "Haskell fast and hard\nCounterexamples for Typeclasses",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Talks und Posts zu Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/FFPiH.html",
    "href": "Coding/Haskell/FFPiH.html",
    "title": "Fortgeschrittene funktionale Programmierung in Haskell",
    "section": "",
    "text": "FFPiH ist eine Vorlesung, die ich zusammen mit einem Kommilitonen im Sommer 2015 erstmals erstellt und gehalten haben.\nInsgesamt haben wir die Vorlesung 3x gehalten, wobei von der ersten zur zweiten Iteration der Inhalt massiv überarbeitet wurde und bei der Iteration von der zweiten zur dritten Vorlesung die Übungen komplett neu erstellt wurden.\nDie gesamten Übungen sind unter anderem in der FFPiH-Organisation in meinem gitea hinterlegt: https://gitea.dresselhaus.cloud/FFPiH\nEinige der aktualisierten Übungen sind privat geschaltet, da diese iterativ aufeinander aufbauen und jeweils die Musterlösung der vorherigen enthalten.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Fortgeschrittene funktionale Programmierung in Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/FFPiH.html#aufbau-der-vorlesung",
    "href": "Coding/Haskell/FFPiH.html#aufbau-der-vorlesung",
    "title": "Fortgeschrittene funktionale Programmierung in Haskell",
    "section": "Aufbau der Vorlesung",
    "text": "Aufbau der Vorlesung\nVorausgesetzt wurde, dass die Studierenden das erste Semester abgeschlossen hatten und somit bereits leichte Grundlagen in Haskell kannten (aber z.b. Dinge wie Functor/Applicative/Monad noch nicht wirklich erklärt bekommen haben).\nStück für Stück werden die Studis dann zunächst in abstrakte Konstrukte eingeführt, aber diese werden dann schnell in die Praxis umgesetzt. Etwa mit dem Schreiben eines eigenen Parsers.\nSchlussendlich gibt es dann einen “Rundumschlag” durch die gesamte Informatik. Erstellung eines Spieles (auf basis einer kleinen Grundlage), erstellung von WebApps mit Yesod, Parallelisierung und Nebenläufigkeit für rechenintensive Anwendungen inkl. synchronisation mittels STM.\nOptional gab es weitere Übungen zu dingen wie “verteiltes Rechnen”.\nZiel hierbei war nicht, diese ganzen Themen in der Tiefe beizubringen, sondern aufzuzeigen, wie sie sehr schnell abstrakte Konstrukte, die ihnen ggf. 3 Semester später erst begegnen bugfrei benutzen können, da Haskell hier in sehr vielen Fällen einfach nur die “richtige” Lösung kompilieren lässt und alle gängigen Fallen schlicht ausschließt. Beispiel ist z.b. STM innerhalb von STM, Mischen von DB-Monade, Handler-Monade und Template-Engine in Yesod, Process () statt IO () in der Nutzung von CloudHaskell, etc. pp.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Fortgeschrittene funktionale Programmierung in Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/FFPiH.html#studentisches-feedback",
    "href": "Coding/Haskell/FFPiH.html#studentisches-feedback",
    "title": "Fortgeschrittene funktionale Programmierung in Haskell",
    "section": "Studentisches Feedback",
    "text": "Studentisches Feedback\nSehr gutes Feedback von den Studenten bekamen wir insbesondere für Übungen wie:\nÜbung 2, Aufgabe 2, weil hier durch “einfaches” umformen hin zu Abstraktionen und mit den Regeln dieser im ersten Fall die Laufzeit (vor Compileroptimierungen) von O(n²) auf O(0) ändert.\nÜbung 4, welche ein komplett fertigen (sehr rudimentären und simplen) Dungeon-Crawler bereitstellt, der “nur” 1-2 bugs hat und “wie ein echtes Projekt” erweitert werden muss. Diese Übung hat sich dann über 4 weitere Übungen gestreckt, wobei folgende Aufgaben gelöst werden müssen:\n\nEinarbeitung in QuickCheck zur Behebung eines Bugs im Test\nUmschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful Lenses\nContinuation-Basierendes Event-System\nHinzufügen eines Parsers für Level, Items & deren Effekte und implementation dieser\nÄndern des GUI-Parts von CLI auf 2D GL mittels gloss\nÄndern von StateT World auf RWST GameConfig Log World und somit nutzen von individuellen Konfigurationen für z.b. Keybindings",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Fortgeschrittene funktionale Programmierung in Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html",
    "href": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html",
    "title": "Webapp-Example: MyService/Types.hs",
    "section": "",
    "text": "Anleitung siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass        #-}\n{-# LANGUAGE DeriveFunctor         #-}\n{-# LANGUAGE DeriveGeneric         #-}\n{-# LANGUAGE DerivingVia           #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts      #-}\n{-# LANGUAGE FlexibleInstances     #-}\n{-# LANGUAGE GADTs                 #-}\n{-# LANGUAGE LambdaCase            #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings     #-}\n{-# LANGUAGE RankNTypes            #-}\n{-# LANGUAGE RecordWildCards       #-}\nmodule MyService.Types where\n\nimport           Data.Aeson                     (FromJSON, ToJSON)\nimport           Data.Text\nimport           Data.Time.Clock\nimport           GHC.Generics\nimport           System.Envy\nimport           Text.PrettyPrint               (text)\nimport           Text.PrettyPrint.GenericPretty\n\n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n  doc = text . unpack\n  docPrec i a = text $ showsPrec i a \"\"\n\ninstance Out UTCTime where\n  doc = text . show\n  docPrec i a = text $ showsPrec i a \"\"\n\n-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.\ndata ServerConfig = ServerConfig\n                  { myserviceHost              :: String -- ^ Environment: $MYSERVICE_HOST\n                  , myservicePort              :: Int    -- ^ Environment: $MYSERVICE_PORT\n                  , myserviceMaxTimeout        :: Int    -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n                  , myserviceInternalProxyUrl  :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n                  , myserviceInternalProxyPort :: Int    -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n                  , myserviceExternalProxyUrl  :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n                  , myserviceExternalProxyPort :: Int    -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n                  , myserviceActivemqHost      :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n                  , myserviceActivemqPort      :: Int    -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n                  , myserviceActivemqUsername  :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n                  , myserviceActivemqPassword  :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n                  , myserviceMongoUsername     :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n                  , myserviceMongoPassword     :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n                  , myserviceDebug             :: Bool   -- ^ Environment: $MYSERVICE_DEBUG\n                  } deriving (Show, Eq, Generic)\n\n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n  defConfig = ServerConfig \"0.0.0.0\" 8080 20\n                           \"\"\n                           \"\"\n                           \"\"\n                           0\n                           \"\"\n                           0\n                           \"\"\n                           0\n                           \"\"\n                           \"\"\n                           \"\"\n                           \"\"\n                           False\n\n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n\n\ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Webapp-Development in Haskell",
      "Webapp-Example: MyService/Types.hs"
    ]
  },
  {
    "objectID": "Coding/Haskell/Code Snippets/Morphisms.html",
    "href": "Coding/Haskell/Code Snippets/Morphisms.html",
    "title": "*-Morpisms",
    "section": "",
    "text": "Note\n\n\n\nBackup eines Blogposts eines Kommilitonen\n\n\nThis weekend I spend some time on Morphisms.\nKnowing that this might sound daunting to many dabbling Haskellers (like I am), I decided to write a real short MergeSort hylomorphism quickstarter.\n\nFor those who need a refresher: MergeSort works by creating a balanced binary tree from the input list and directly collapsing it back into itself while treating the children as sorted lists and merging these with an O(n) algorithm.\n\nFirst the usual prelude:\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE TypeFamilies #-}\n\nimport Data.Functor.Foldable\nimport Data.List (splitAt, unfoldr)\n\nWe will use a binary tree like this. Note that there is no explicit recursion used, but NodeF has two holes. These will eventually filled later.\ndata TreeF c f = EmptyF | LeafF c | NodeF f f\n  deriving (Eq, Show, Functor)\n\nAside: We could use this as a normal binary tree by wrapping it in Fix: type Tree a = Fix (TreeF a) But this would require us to write our tree like Fix (NodeF (Fix (LeafF 'l')) (Fix (LeafF 'r'))) which would get tedious fast. Luckily Edward build a much better way to do this into recursion-schemes. I will touch on this later.\n\nWithout further ado we start to write a Coalgebra, which in my book is just a scary name for “function that is used to construct datastructures”.\nunflatten :: [a] -&gt; TreeF a [a]\nunflatten (  []) = EmptyF\nunflatten (x:[]) = LeafF x\nunflatten (  xs) = NodeF l r   where (l,r) = splitAt (length xs `div` 2) xs\nFrom the type signature it’s immediately obvious, that we take a list of ’a’s and use it to create a part of our tree.\nThe nice thing is that due to the fact that we haven’t commited to a type in our tree nodes we can just put lists in there.\n\nAside: At this point we could use this Coalgebra to construct (unsorted) binary trees from lists:\nexample1 = ana unflatten [1,3] == Fix (NodeF (Fix (LeafF 1)) (Fix (LeafF 3)))\n\nOn to our sorting, tree-collapsing Algebra. Which again is just a creepy word for “function that is used to deconstruct datastructures”.\nThe function mergeList is defined below and just merges two sorted lists into one sorted list in O(n), I would probably take this from the ordlist package if I were to implement this for real.\nAgain we see that we can just construct our sorted output list from a TreeF that apparently contains just lists.\nflatten :: Ord a =&gt; TreeF a [a] -&gt; [a]\nflatten EmptyF      = []\nflatten (LeafF c)   = [c]\nflatten (NodeF l r) = mergeLists l r\n\nAside: We could use a Coalgebra to deconstruct trees:\nexample2 = cata flatten (Fix (NodeF (Fix (LeafF 3)) (Fix (LeafF 1)))) == [1,3]\n\nNow we just combine the Coalgebra and the Algebra with one from the functions from Edwards recursion-schemes library:\nmergeSort :: Ord a =&gt; [a] -&gt; [a]\nmergeSort = hylo flatten unflatten\n\nexample3 = mergeSort [5,2,7,9,1,4] == [1,2,4,5,7,9]\n\nWhat have we gained?\nWe have implemented a MergeSort variant in 9 lines of code, not counting the mergeLists function below. Not bad, but this implementation is not much longer.\nOn the other hand the morphism based implementation cleanly describes what happens during construction and deconstruction of our intermediate structure.\nMy guess is that, as soon as the algortihms get more complex, this will really make a difference.\n\nAt this point I wasn’t sure if this was useful or remotely applicable. Telling someone “I spend a whole weekend learning about Hylomorphism” isn’t something the cool developer kids do.\nIt appeared to me that maybe I should have a look at the Core to see what the compiler finally comes up with (edited for brevity):\n    mergeSort :: [Integer] -&gt; [Integer]\n    mergeSort =\n      \\ (x :: [Integer]) -&gt;\n        case x of wild {\n          [] -&gt; [];\n          : x1 ds -&gt;\n            case ds of _ {\n              [] -&gt; : x1 ([]);\n              : ipv ipv1 -&gt;\n                unfoldr\n                  lvl9\n                  (let {\n                     p :: ([Integer], [Integer])\n                     p =\n                       case $wlenAcc wild 0 of ww { __DEFAULT -&gt;\n                       case divInt# ww 2 of ww4 { __DEFAULT -&gt;\n                       case tagToEnum# (&lt;# ww4 0) of _ {\n                         False -&gt;\n                           case $wsplitAt# ww4 wild of _ { (# ww2, ww3 #) -&gt; (ww2, ww3) };\n                         True -&gt; ([], wild)\n                       }\n                       }\n                       } } in\n                   (case p of _ { (x2, ds1) -&gt; mergeSort x2 },\n                    case p of _ { (ds1,  y) -&gt; mergeSort y  }))\n            }\n        }\n    end Rec }\nWhile I am not really competent in reading Core and this is actually the first time I bothered to try, it is immediately obvious that there is no trace of any intermediate tree structure.\nThis is when it struck me. I was dazzled and amazed. And am still. Although we are writing our algorithm as if we are working on a real tree structure the library and the compiler are able to just remove the whole intermediate step.\n\nAftermath:\nIn the beginning I promised a way to work on non-functor data structures. Actually that was how I began to work with the recursion-schemes library.\nWe are able to create a ‘normal’ version of our tree from above:\ndata Tree c = Empty | Leaf c | Node (Tree c) (Tree c)\n  deriving (Eq, Show)\nBut we can not use this directly with our (Co-)Algebras. Luckily Edward build a little bit of type magic into the library:\ntype instance Base (Tree c) = (TreeF c)\n\ninstance Unfoldable (Tree c) where\n  embed EmptyF      = Empty\n  embed (LeafF c)   = Leaf c\n  embed (NodeF l r) = Node l r\n\ninstance Foldable (Tree c) where\n  project Empty      = EmptyF\n  project (Leaf c)   = LeafF c\n  project (Node l r) = NodeF l r\nWithout going into detail by doing this we establish a relationship between Tree and TreeF and teach the compiler how to translate between these types.\nNow we can use our Alebra on our non functor type:\nexample4 = cata flatten (Node (Leaf 'l') (Leaf 'r')) == \"lr\"\nThe great thing about this is that, looking at the Core output again, there is no traces of the TreeF structure to be found. As far as I can tell, the algorithm is working directly on our Tree type.\n\nLiterature:\n\nUnderstanding F-Algebras\nRecursion Schemes by Example\nRecursion Schemes: A Field Guide\nThis StackOverflow question\n\n\nAppendix:\nmergeLists :: Ord a =&gt; [a] -&gt; [a] -&gt; [a]\nmergeLists = curry $ unfoldr c where\n  c ([], [])     = Nothing\n  c ([], y:ys)   = Just (y, ([], ys))\n  c (x:xs, [])   = Just (x, (xs, []))\n  c (x:xs, y:ys) | x &lt;= y = Just (x, (xs, y:ys))\n                 | x &gt; y  = Just (y, (x:xs, ys))",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Code Snippets",
      "*-Morpisms"
    ]
  },
  {
    "objectID": "About/index.html",
    "href": "About/index.html",
    "title": "About me",
    "section": "",
    "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js",
    "crumbs": [
      "Home",
      "Info",
      "About me"
    ]
  },
  {
    "objectID": "About/index.html#work",
    "href": "About/index.html#work",
    "title": "About me",
    "section": "",
    "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js",
    "crumbs": [
      "Home",
      "Info",
      "About me"
    ]
  },
  {
    "objectID": "About/index.html#education",
    "href": "About/index.html#education",
    "title": "About me",
    "section": "Education",
    "text": "Education\n\nBachelor “Kognitive Informatik” (Cognitive Informatics) in Bielefeld 2010-2014\nMaster “Naturwissenschaftliche Informatik” (Informatics in the natural sciences) 2014-2018\n\n\nExtraordinary grades (Excerpt of my Transcript)\nNote: Scale of grades in Germany is 1.0 to 4.0 with 1.0 being best, 4.0 being passing grade, 5.0 being failed grade\n\n1.0 in Modern Data Analysis\n\nMaster course on data-analysis (time-series, core-vector-machines, gaussian processes, …)\n\n1.0 in Computergraphics\n\nRaytracing, Modern OpenGL\n\n1.3 in Computer-Animation\n\nDual-Quarternion-Skinning, Character-Animation, FACS-Poses, etc.\n\n1.3 in GPU-Computing (CUDA)\n\noriginally a 1.7 by timing (task was de-mosaicing on images, grade was measured in ms, whereby 400ms equated to 4.0 and 100ms equated to 1.0), but because my deep knowledge was visible in the code i was given a 1.3 after oral presentation.\n\n1.0 in Parallel Algorithms and Data-Structures\nEthical Hacking\n\nReverse Engineering with IDApro",
    "crumbs": [
      "Home",
      "Info",
      "About me"
    ]
  },
  {
    "objectID": "About/index.html#further-information",
    "href": "About/index.html#further-information",
    "title": "About me",
    "section": "Further information",
    "text": "Further information\n\nMore details on my work-experience\nMore details of my coding\nMore details of things i did beside studying at University",
    "crumbs": [
      "Home",
      "Info",
      "About me"
    ]
  },
  {
    "objectID": "About/Work.html",
    "href": "About/Work.html",
    "title": "Work-Experience",
    "section": "",
    "text": "Work-Experience\n\nMar. 2023 to Sep. 2023:\n\nDeveloper for 2Lambda.co. Role migrated from just coding stuff to architecting and rewriting the whole software from the ground up using a small modular approach instead of the shaky one-off systems in place.\nWas later a “nanny for everything”.\nDid a lot of work to have self-documenting code (i.e. generate documentation from the actual values used in the program, not some comments that always get out of date)\nSetting up a knowledge-base (Zettelkasten-approach) to track experiments and hyperlink them to the documentation generated above (and due to Zettelkasten you then get “this thing was used in Experiments a, b and c” automatically\nTechnologies used:\n\nClojure\n\nComplete application was written in Clojure\nNever touched that language before March - got up to speed in just 2 days, poked the expert on the team detailed questions about the runtime-system after 1 month (like inlining-behavior, allocation-things, etc.)\n\nEmanote\n\nautogenerated & linked documentation of internal modules\nintegrated with manual written tutorials/notes\ncrosslinking documentation of experiments with documentation of modules\n\nWeb of knowledge\nbidirectional discovery of things tried/done in the past to optimize finding of new strategies (meta-optimizing the decisions on what to optimize/try)\n\n\nInfrastructure\n\nOrganized and co-administrated the 4 Root-Servers we had\nSet up Kubernetes, Nexus, Docker, Nginx, letsencrypt-certs, dns-entries, etc..\n\n\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\nExtraction/Classification of sentences from JobAds (Requirements, Benefits, Tasks, …)\nExtraction of Information from JobAds (Location of company, Location of workplay, contact-details, application-procedure, etc.) including geocoding of those information (backed by OpenStreetMap)\nEmbedding of JobAds into a meaningful space (i.e. “get me similar ads. btw. i dislike ad a, b, c”).\nAnalyse & predict search-queries of users on the webpage and offer likely but distinct queries (i.e. similar when typo or complete different words (synonyms, hyponyms, etc.))\n\nTechnologies used:\n\nHaskell (currently GHC 8.6, soon GHC 8.8)\n\nstack + stackage-lts\nfixplate (recursion-schemes-implementation)\nmany usual technologies like lens, http-simple, mtl, ..\ngolden-testing via tasty\nseveral inhouse-developments:\n\ntemplating based on text-replacement via generics (fieldname in Template-Type == variable replaced in template)\nactiveMQ/Kibana-bridge for logging via hs-stomp\ngeneric internal logging-framework\n\n\nPython\n\ntensorflow\npytorch\nsklearn\nnltk\n\n\n\n2013-2018:\n\nseveral jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “[[FFPiH|Intermediate Functional Programming in Haskell]]”\n\nOriginally developed as student-project in cooperation with Jonas Betzendahl\nFirst held in Summer 2015\nDue to high demand held again in Summer 2016 and 2017\nWas awarded Lecturer-Award “silver Chalk” in 2016\n\nFirst time that this award was given to students\nMany lecturers at our faculty never get any teaching-award until retirement\n\n\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader “Workgroup Computer Graphics”) using Pandoc & reveal.js\n\nFramework: https://github.com/mbotsch/revealSlides\nExample: https://github.com/mbotsch/eLearning\nPandoc-Filters: https://github.com/mbotsch/pandoc-slide-filter",
    "crumbs": [
      "Home",
      "Info",
      "About me",
      "Work-Experience"
    ]
  },
  {
    "objectID": "About/Experience.html",
    "href": "About/Experience.html",
    "title": "Highlights of my experiences in the programming world",
    "section": "",
    "text": "(as far as NDA and other things allow it)\n\n\n\nLearning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell\n\n\n\n\n\nI also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -&gt; Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D",
    "crumbs": [
      "Home",
      "Info",
      "About me",
      "Highlights of my experiences in the programming world"
    ]
  },
  {
    "objectID": "About/Experience.html#haskell-enthusiast",
    "href": "About/Experience.html#haskell-enthusiast",
    "title": "Highlights of my experiences in the programming world",
    "section": "",
    "text": "Learning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell",
    "crumbs": [
      "Home",
      "Info",
      "About me",
      "Highlights of my experiences in the programming world"
    ]
  },
  {
    "objectID": "About/Experience.html#gitea",
    "href": "About/Experience.html#gitea",
    "title": "Highlights of my experiences in the programming world",
    "section": "",
    "text": "I also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -&gt; Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D",
    "crumbs": [
      "Home",
      "Info",
      "About me",
      "Highlights of my experiences in the programming world"
    ]
  },
  {
    "objectID": "About/Extracurricular.html",
    "href": "About/Extracurricular.html",
    "title": "Studium generale / University-Life",
    "section": "",
    "text": "(What I did at university besides studying :sunglasses: )\n\n\n\nStudent Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs\n\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#",
    "crumbs": [
      "Home",
      "Info",
      "About me",
      "Studium generale / University-Life"
    ]
  },
  {
    "objectID": "About/Extracurricular.html#committees-student-body",
    "href": "About/Extracurricular.html#committees-student-body",
    "title": "Studium generale / University-Life",
    "section": "",
    "text": "Student Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs",
    "crumbs": [
      "Home",
      "Info",
      "About me",
      "Studium generale / University-Life"
    ]
  },
  {
    "objectID": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog",
    "href": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog",
    "title": "Studium generale / University-Life",
    "section": "",
    "text": "Fortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#",
    "crumbs": [
      "Home",
      "Info",
      "About me",
      "Studium generale / University-Life"
    ]
  },
  {
    "objectID": "Coding/Haskell/Code Snippets/Monoid.html",
    "href": "Coding/Haskell/Code Snippets/Monoid.html",
    "title": "Monoid? Da war doch was…",
    "section": "",
    "text": "Stellen wir uns vor, dass wir eine Funktion schreiben, die einen String bekommt (mehrere Lines mit ACSII-Text) und dieses Wort-für-Wort rückwärts ausgeben soll. Das ist ein einfacher Einzeiler:\nWas passiert hier an Vodoo? Und was machen die ganzen wilden Zeichen da?\nGehen wir die Main zeilenweise durch: Wir lesen die Datei, die im ersten Kommandozeilen-Argument gegeben wird. getArgs hat folgende Signatur:\nWir bekommen als eine Liste der Argumente. Wir wollen nur das erste. Also machen wir head getArgs. Allerdings fliegt uns dann ein Fehler. head sieht nämlich so aus:\nIrgendwie müssen wird as in das IO bekommen. Hierzu gibt es fmap. Somit ist\nEin inline-Alias (um die Funktion links und das Argument rechts zu schreiben und sich ne Menge Klammern zu sparen) ist &lt;$&gt;. Somit ist schlussendlich der Inhalt der Datei aus dem ersten Argument (lazy) in ls.\nEine andere Möglichkeit sich das (in diesem Fall) zu merken, bzw. drauf zu kommen ist, dass [] AUCH ein Funktor (sogar eine Monade) ist. Man könnte das also auch so schreiben:\nfmap “packt” die Funktion quasi 1 Umgebung (Funktor, Monade, ..) weiter rein - Sei es nun in Maybe, Either oder irgendwas anderes.\nAlternatives (ausführliches) Beispiel am Ende.\nWenn wir uns die Signatur ansehen, dann haben wir nun\nreadFile will aber nun ein String haben. Man kann nun\nkann man auch “inline” mit =&lt;&lt; die Sachen “auspacken”.\nDie 2. Zeile lesen wir nun einfach “von hinten”, wie man das meistens tun sollte. Hier ist ein\nwas uns den Inhalt der Datei zeilenweise gibt. Mit jeder Zeile möchten wir nun folgendes machen:\nWenn wir uns die Signatur ansehen:\nDas mag im ersten Moment verwirren, daher noch die Signaturen der Einzelfunktionen:\nDa wir am Ende in der IO-Monade landen müssen wir das auf unsere Zeilen mit mapM statt map anwenden. Dies sorgt auch dafür, dass die Liste der reihe nach durchgegangen wird. mapM mit unserer Funktion schaut dann so aus:\neek! Das [IO ()] sieht ekelig aus. Wir haben eine Liste von IO-gar nichts. Das können wir eigentlich entsorgen. Da wir innerhalb der main-Funktion in einer IO-Monade sind, wollen wir IO () anstatt [IO ()] zurück haben.\nWenn wir uns jetzt erinnern, dass [] auch nur eine Monade ist und dass jede Monade ein Monoid ist, dann ist die Lösung einfach. Monoide haben eine “append”-funktion (mappend oder (&lt;&gt;) genannt). Wenn wir “nichts” an “nichts” anhängen, dann erhalten wir …. Trommelwirbel “nichts”! Wir müssen die [IO ()]-Liste also “nur noch” mit mappend falten. Hierzu gibt es schon eine vorgefertigte Funktion:\nWas genau die gewünschte Faltung macht. Wir müssen nun wieder fmap nehmen, da wir die Liste selbst falten wollen - und nicht map, welches auf den IO () innerhalb der Liste arbeiten würde. Durch die Faltung fällt die Liste nun auf IO () zusammen.\nViel Voodoo in wenig Code, aber wenn man sich dran gewöhnt hat, sind Monaden in Monaden auch nicht schlimm. Man muss sich immer nur richtig “rein” fmap’en.\nKleinen Tipp gab es noch: mapM_ macht genau das, was oben mit mconcat erreicht werden sollte. Somit kann man auch\nschreiben. Ich hab es aber mal wegen der klarheit oben so gelassen.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Code Snippets",
      "Monoid? Da war doch was..."
    ]
  },
  {
    "objectID": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel",
    "href": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel",
    "title": "Monoid? Da war doch was…",
    "section": "Alternatives fmap-Beispiel",
    "text": "Alternatives fmap-Beispiel\nNehmen wir als alternatives Beispiel mal an:\na :: IO Maybe State t\nUm Funktionen vom Typ\nf :: IO a -&gt; IO a\nf a             -- valide\nzu nehmen, brauchen wir nichts machen. Bei\nf' :: Maybe a -&gt; Maybe a\nbrauchen wir 1 fmap, also ein\nf' a            -- error\nf' &lt;$&gt; a\num eine Funktion\nf'' :: State t -&gt; State t\nzu benutzen folglich:\nf'' a           -- error\nf'' &lt;$&gt; a       -- error\nfmap f'' &lt;$&gt; a",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Code Snippets",
      "Monoid? Da war doch was..."
    ]
  },
  {
    "objectID": "Coding/Haskell/Webapp-Example/Main.hs.html",
    "href": "Coding/Haskell/Webapp-Example/Main.hs.html",
    "title": "Webapp-Example: Main.hs",
    "section": "",
    "text": "Wie man das verwendet, siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts    #-}\n{-# LANGUAGE LambdaCase          #-}\n{-# LANGUAGE OverloadedStrings   #-}\n{-# LANGUAGE RankNTypes          #-}\n{-# LANGUAGE RecordWildCards     #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport           Codec.MIME.Type\nimport           Configuration.Dotenv                 as Dotenv\nimport           Control.Concurrent                   (forkIO, threadDelay)\nimport           Control.Concurrent.Async\nimport           Control.Concurrent.STM\nimport           Control.Monad\nimport           Control.Monad.Catch\nimport           Control.Monad.Except\nimport           Conversion\nimport           Conversion.Text                      ()\nimport           Data.Binary.Builder\nimport           Data.String                          (IsString (..))\nimport           Data.Time\nimport           Data.Time.Clock\nimport           Data.Time.Format\nimport           Data.Default\nimport           Network.HostName\nimport           Network.HTTP.Client                  as HTTP hiding\n                                                               (withConnection)\nimport           Network.HTTP.Types                   (Status, statusCode)\nimport           Network.Mom.Stompl.Client.Queue\nimport           Network.Wai                          (Middleware)\nimport           Network.Wai.Logger\nimport           Network.Wai.Middleware.Cors\nimport           Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n                                                       logStdout,\n                                                       mkRequestLogger,\n                                                       outputFormat)\nimport           Servant.Client                       (mkClientEnv,\n                                                       parseBaseUrl)\nimport           System.Directory\nimport           System.Envy\nimport           System.IO\nimport           System.Log.FastLogger\nimport           Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString                      as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy                 as LBS\nimport qualified Network.HTTP.Client.TLS              as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue      as AMQ\nimport qualified Network.Wai                          as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport           MyService.Handler                    as H  -- handler der H.myApiEndpointV1Post implementiert\nimport           MyService.Types                            -- weitere Type (s. nächste box)\nimport           MyServiceGen.API                     as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n  -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n  void $ loadFile $ Dotenv.Config [\".env\"] [] False\n  -- Config holen (defaults + overrides aus dem Environment)\n  sc@ServerConfig{..} &lt;- decodeWithDefaults defConfig\n  -- Backend-Setup\n  -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n  let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n      createBackend url proxy = do\n        manager &lt;- newManager . managerSetProxy proxy\n          $ defaultHTTPSSettings\n        url' &lt;- parseBaseUrl url\n        return (mkClientEnv manager url')\n      internalProxy = case myserviceInternalProxyUrl of\n                        \"\"  -&gt; noProxy\n                        url -&gt; useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n      -- externalProxy = case myserviceExternalProxyUrl of\n      --                   \"\"  -&gt; noProxy\n      --                   url -&gt; useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n  -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n  calls &lt;- (,)\n           &lt;$&gt; createBackend myserviceAUri internalProxy\n           &lt;*&gt; createBackend myserviceBUri internalProxy\n\n  -- Logging-Setup\n  hSetBuffering stdout LineBuffering\n  hSetBuffering stderr LineBuffering\n\n\n  -- Infos holen, brauchen wir später\n  myName &lt;- getHostName\n  today &lt;- formatTime defaultTimeLocale \"%F\" . utctDay &lt;$&gt; getCurrentTime\n\n\n  -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n  amqPost &lt;- newTQueueIO\n\n\n  -- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.\n  bracket\n   -- logfiles öffnen\n   (LogFiles &lt;$&gt; openFile (\"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".info\") AppendMode\n             &lt;*&gt; openFile (if myserviceDebug then \"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".debug\" else \"/dev/null\") AppendMode\n             &lt;*&gt; openFile (\"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".error\") AppendMode\n             &lt;*&gt; openFile (\"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".timings\") AppendMode\n   )\n   -- und bei exception/beendigung schlißen.h\n   (\\(LogFiles a b c d) -&gt; mapM_ hClose [a,b,c,d])\n   $ \\logfiles -&gt; do\n\n\n    -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n    let log = printLogFiles logfiles :: MonadIO m =&gt; [LogItem] -&gt; m ()\n        iolog = printLogFilesIO logfiles :: [LogItem] -&gt; IO ()\n\n\n        -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n        -- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion\n        -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n        server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n                                   }\n        config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n    iolog . pure . Info $ \"Using Server configuration:\"\n    iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n                                    , myserviceMongoPassword = \"******\"\n                                    }\n    -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n    void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n    -- logging-Framework erzeugen\n    loggingMW &lt;- loggingMiddleware\n    -- server starten\n    if myserviceDebug\n       then runMyServiceMiddlewareServer config (cors (\\_ -&gt; Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n       else runMyServiceMiddlewareServer config (cors (\\_ -&gt; Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -&gt; ([LogItem] -&gt; IO ()) -&gt; TQueue BS.ByteString -&gt; IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n  res &lt;- handle (\\(e :: SomeException) -&gt; do\n      printLog . pure . Error $ \"Exception in AMQ-Thread: \"&lt;&gt;show e\n      return $ Right ()\n    ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n        printLog . pure . Info $ \"AMQ: connecting...\"\n        withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n                                                                   , OTmo (30*1000) {- 30 sec timeout -}\n                                                                   ]\n                                                                   [] $ \\c -&gt; do\n          let oconv = return\n          printLog . pure . Info $ \"AMQ: connected\"\n          withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -&gt; do\n            printLog . pure . Info $ \"AMQ: queue created\"\n            let postfun = writeQ writer (Type (Application \"json\") []) []\n            void $ race\n              (forever $ atomically (readTQueue var) &gt;&gt;= postfun)\n              (threadDelay (600*1000*1000)) -- wait 10 Minutes\n          -- close writer\n        -- close connection\n  -- get outside of all try/handle/...-constructions befor recursing.\n  case res of\n    Left ex -&gt; do\n      printLog . pure . Error $ \"AMQ: \"&lt;&gt;show ex\n      keepActiveMQConnected sc printLog var\n    Right _ -&gt; keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n  where\n    out :: ZonedDate -&gt; WAI.Request -&gt; Status -&gt; Maybe Integer -&gt; NominalDiffTime -&gt; [BS.ByteString] -&gt; Builder -&gt; LogStr\n    out _ r status _ _ payload _\n      | statusCode status &lt; 300 = \"\"\n      | statusCode status &gt; 399 && statusCode status &lt; 500 = \"Error code \"&lt;&gt;toLogStr (statusCode status) &lt;&gt;\" sent. Request-Payload was: \"&lt;&gt; mconcat (toLogStr &lt;$&gt; payload) &lt;&gt; \"\\n\"\n      | otherwise = toLogStr (show r) &lt;&gt; \"\\n\"",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Webapp-Development in Haskell",
      "Webapp-Example: Main.hs"
    ]
  },
  {
    "objectID": "Coding/Haskell/Webapp-Example/index.html",
    "href": "Coding/Haskell/Webapp-Example/index.html",
    "title": "Webapp-Development in Haskell",
    "section": "",
    "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -&gt; Response-Model erfassen.\nUm die Anforderungen und Möglichkeiten des jeweiligen Services sauber zu erfassen und automatisiert zu prüfen, dummy-implementationen zu bekommen und vieles andere mehr, empfiehlt es sich den Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach der Definition, was man am Ende haben möchte, muss man sich entscheiden, in welcher Sprache man weiter entwickelt. Ich empfehle aus verschiedenen Gründen primär 2 Sprachen: Python-Microservices (weil die ML-Libraries sehr gut sind, allerdings Änderungen meist schwer sind und der Code wenig robust - meist nur 1 API-Endpunkt pro service) und Haskell (Stabilität, Performace, leicht zu ändern, gut anzupassen).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Webapp-Development in Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Webapp-Example/index.html#definition-der-api",
    "href": "Coding/Haskell/Webapp-Example/index.html#definition-der-api",
    "title": "Webapp-Development in Haskell",
    "section": "",
    "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -&gt; Response-Model erfassen.\nUm die Anforderungen und Möglichkeiten des jeweiligen Services sauber zu erfassen und automatisiert zu prüfen, dummy-implementationen zu bekommen und vieles andere mehr, empfiehlt es sich den Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach der Definition, was man am Ende haben möchte, muss man sich entscheiden, in welcher Sprache man weiter entwickelt. Ich empfehle aus verschiedenen Gründen primär 2 Sprachen: Python-Microservices (weil die ML-Libraries sehr gut sind, allerdings Änderungen meist schwer sind und der Code wenig robust - meist nur 1 API-Endpunkt pro service) und Haskell (Stabilität, Performace, leicht zu ändern, gut anzupassen).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Webapp-Development in Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell",
    "href": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell",
    "title": "Webapp-Development in Haskell",
    "section": "Startprojekt in Haskell",
    "text": "Startprojekt in Haskell\n\nErstellen eines neuen Projektes\nZunächst erstellen wir in normales Haskell-Projekt ohne Funktionalität & Firlefanz:\nstack new myservice\nDies erstellt ein neues Verzeichnis und das generelle scaffolding. Nach einer kurzen Anpassung der stack.yaml (resolver auf unserer setzen; aktuell: lts-17.4) fügen wir am Ende der Datei\nallow-newer: true\nghc-options:\n  \"$locals\": -fwrite-ide-info\nein. Anschließend organisieren™ wir uns noch eine gute .gitignore und initialisieren das git mittels git init; git add .; git commit -m \"initial scaffold\"\n\n\nGenerierung der API\nDa die API immer wieder neu generiert werden kann (und sollte!) liegt sich in einem unterverzeichnis des Hauptprojektes.\nInitial ist es das einfachste ein leeres temporäres Verzeichnis woanders zu erstellen, die api-doc.yml hinein kopieren und folgendes ausführen:\nopenapi-generator generate -g haskell -o . -i api-doc.yml\nDieses erstellt einem dann eine komplette library inkl. Datentypen. Wichtig: Der Name in der api-doc sollte vom Namen des Services (oben myservice) abweichen - entweder in Casing oder im Namen direkt. Suffixe wie API schneidet der Generator hier leider ab. (Wieso das ganze? Es entstehen nachher 2 libraries, foo & fooAPI. Da der generator das API abschneidet endet man mit foo & foo und der compiler meckert, dass er nicht weiß, welche lib gemeint ist).\ndanach: wie gewohnt git init; git add .; git commit -m \"initial\". Auf dem Server der Wahl (github, gitea, gitlab, …) nun ein Repository erstellen (am Besten: myserviceAPI - nach Konvention ist alles auf API endend autogeneriert!) und den Anweisungen nach ein remote hinzufügen & pushen.\n\nWieder zurück im Haskell-Service\nIn unserem eigentlichen Service müssen wir nun die API einbinden. Dazu erstellen wir ein Verzeichnis libs (Konvention) und machen ein git submodule add &lt;repository-url&gt; libs/myserviceAPI\nGit hat nun die API in das submodul gepackt und wir können das oben erstellte temporäre Verzeichnis wieder löschen.\nAnschließend müssen wir stack noch erklären, dass wir die API da nun liegen haben und passen wieder die stack.yaml an, indem wir das Verzeichnis unter packages hinzufügen.\npackages:\n  - .\n  - libs/myserviceAPI # &lt;&lt;\nNun können wir in der package.yaml (oder myservice.cabal, falls kein hpack verwendet wird) unter den dependencies unsere API hinzufügen (name wie die cabal-Datei in libs/myserviceAPI).\n\n\n\nEinbinden anderer Microservices\nFunktioniert komplett analog zu dem vorgehen oben (ohne das generieren natürlich :grin:). stack.yaml editieren und zu den packages hinzufügen:\npackages:\n  - .\n  - libs/myserviceAPI\n  - libs/myCoolMLServiceAPI\nin der package.yaml (oder der cabal) die dependencies hinzufügen und schon haben wir die Features zur Verfügung und können gegen diese Services reden.\n\n\nEntfernen von anderen Technologien/Microservices\nIn git ist das entfernen von Submodules etwas frickelig, daher hier ein copy&paste der GitHub-Antwort:\n## Remove the submodule entry from .git/config\ngit submodule deinit -f path/to/submodule\n\n## Remove the submodule directory from the superproject's .git/modules directory\nrm-rf .git/modules/path/to/submodule\n\n## Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule\ngit rm-f path/to/submodule\nFalls das nicht klappt, gibt es alternative Vorschläge unter dem Link oben.\n\n\nWoher weiss ich, was wo liegt? Dokumentation? Halloo??\nKeine Panik. Ein stack haddock --open hilft da. Das generiert die Dokumentation für alle in der package.yaml (oder cabal-file) eingetragenen dependencies inkl. aller upstream-dependencies. Man bekommt also eine komplette lokale Dokumentation von allem. Geöffnet wird dann die Paket-Startseite inkl. der direkten dependencies:\nEs gibt 2 wichtige Pfade im Browser:\n\n...../all/index.html - hier sind alle Pakete aufgeführt\n...../index.html - hier sind nur die direkten dependencies aufgeführt.\n\nWenn man einen lokalen Webserver startet kann man mittels “s” auch die interaktive Suche öffnen (Suche nach Typen, Funktionen, Signaturen, etc.). In Bash mit python3 geht das z.b. einfach über:\ncd $(stack path --local-doc-root)\npython3 -m SimpleHTTPServer 8000\nfirefox \"http://localhost:8000\"\n\n\nImplementation des Services und Start\n\nLoader/Bootstrapper\nGenerelles Vorgehen:\n\nin app/Main.hs: Hier ist quasi immer nur eine Zeile drin: main = myServiceMain\nGrund: Applications tauchen nicht im Haddock auf. Also haben wir ein “src”-Modul, welches hier nur geladen & ausgeführt wird.\nin src/MyService.hs: myServiceMain :: IO () definieren\n\nFür die Main kann man prinzipiell eine Main andere Services copy/pasten. Im folgenden eine Annotierte main-Funktion - zu den einzelnen Voraussetzungen kommen wir im Anschluss.\nMain.hs anzeigen\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts    #-}\n{-# LANGUAGE LambdaCase          #-}\n{-# LANGUAGE OverloadedStrings   #-}\n{-# LANGUAGE RankNTypes          #-}\n{-# LANGUAGE RecordWildCards     #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport           Codec.MIME.Type\nimport           Configuration.Dotenv                 as Dotenv\nimport           Control.Concurrent                   (forkIO, threadDelay)\nimport           Control.Concurrent.Async\nimport           Control.Concurrent.STM\nimport           Control.Monad\nimport           Control.Monad.Catch\nimport           Control.Monad.Except\nimport           Conversion\nimport           Conversion.Text                      ()\nimport           Data.Binary.Builder\nimport           Data.String                          (IsString (..))\nimport           Data.Time\nimport           Data.Time.Clock\nimport           Data.Time.Format\nimport           Data.Default\nimport           Network.HostName\nimport           Network.HTTP.Client                  as HTTP hiding\n                                                               (withConnection)\nimport           Network.HTTP.Types                   (Status, statusCode)\nimport           Network.Mom.Stompl.Client.Queue\nimport           Network.Wai                          (Middleware)\nimport           Network.Wai.Logger\nimport           Network.Wai.Middleware.Cors\nimport           Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n                                                       logStdout,\n                                                       mkRequestLogger,\n                                                       outputFormat)\nimport           Servant.Client                       (mkClientEnv,\n                                                       parseBaseUrl)\nimport           System.Directory\nimport           System.Envy\nimport           System.IO\nimport           System.Log.FastLogger\nimport           Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString                      as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy                 as LBS\nimport qualified Network.HTTP.Client.TLS              as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue      as AMQ\nimport qualified Network.Wai                          as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport           MyService.Handler                    as H  -- handler der H.myApiEndpointV1Post implementiert\nimport           MyService.Types                            -- weitere Type (s. nächste box)\nimport           MyServiceGen.API                     as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n  -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n  void $ loadFile $ Dotenv.Config [\".env\"] [] False\n  -- Config holen (defaults + overrides aus dem Environment)\n  sc@ServerConfig{..} &lt;- decodeWithDefaults defConfig\n  -- Backend-Setup\n  -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n  let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n      createBackend url proxy = do\n        manager &lt;- newManager . managerSetProxy proxy\n          $ defaultHTTPSSettings\n        url' &lt;- parseBaseUrl url\n        return (mkClientEnv manager url')\n      internalProxy = case myserviceInternalProxyUrl of\n                        \"\"  -&gt; noProxy\n                        url -&gt; useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n      -- externalProxy = case myserviceExternalProxyUrl of\n      --                   \"\"  -&gt; noProxy\n      --                   url -&gt; useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n  -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n  calls &lt;- (,)\n           &lt;$&gt; createBackend myserviceAUri internalProxy\n           &lt;*&gt; createBackend myserviceBUri internalProxy\n\n  -- Logging-Setup\n  hSetBuffering stdout LineBuffering\n  hSetBuffering stderr LineBuffering\n\n\n  -- Infos holen, brauchen wir später\n  myName &lt;- getHostName\n  today &lt;- formatTime defaultTimeLocale \"%F\" . utctDay &lt;$&gt; getCurrentTime\n\n\n  -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n  amqPost &lt;- newTQueueIO\n\n\n  -- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.\n  bracket\n   -- logfiles öffnen\n   (LogFiles &lt;$&gt; openFile (\"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".info\") AppendMode\n             &lt;*&gt; openFile (if myserviceDebug then \"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".debug\" else \"/dev/null\") AppendMode\n             &lt;*&gt; openFile (\"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".error\") AppendMode\n             &lt;*&gt; openFile (\"/logs/myservice-\"&lt;&gt;myName&lt;&gt;\"-\"&lt;&gt;today&lt;&gt;\".timings\") AppendMode\n   )\n   -- und bei exception/beendigung schlißen.h\n   (\\(LogFiles a b c d) -&gt; mapM_ hClose [a,b,c,d])\n   $ \\logfiles -&gt; do\n\n\n    -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n    let log = printLogFiles logfiles :: MonadIO m =&gt; [LogItem] -&gt; m ()\n        iolog = printLogFilesIO logfiles :: [LogItem] -&gt; IO ()\n\n\n        -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n        -- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion\n        -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n        server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n                                   }\n        config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n    iolog . pure . Info $ \"Using Server configuration:\"\n    iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n                                    , myserviceMongoPassword = \"******\"\n                                    }\n    -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n    void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n    -- logging-Framework erzeugen\n    loggingMW &lt;- loggingMiddleware\n    -- server starten\n    if myserviceDebug\n       then runMyServiceMiddlewareServer config (cors (\\_ -&gt; Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n       else runMyServiceMiddlewareServer config (cors (\\_ -&gt; Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -&gt; ([LogItem] -&gt; IO ()) -&gt; TQueue BS.ByteString -&gt; IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n  res &lt;- handle (\\(e :: SomeException) -&gt; do\n      printLog . pure . Error $ \"Exception in AMQ-Thread: \"&lt;&gt;show e\n      return $ Right ()\n    ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n        printLog . pure . Info $ \"AMQ: connecting...\"\n        withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n                                                                   , OTmo (30*1000) {- 30 sec timeout -}\n                                                                   ]\n                                                                   [] $ \\c -&gt; do\n          let oconv = return\n          printLog . pure . Info $ \"AMQ: connected\"\n          withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -&gt; do\n            printLog . pure . Info $ \"AMQ: queue created\"\n            let postfun = writeQ writer (Type (Application \"json\") []) []\n            void $ race\n              (forever $ atomically (readTQueue var) &gt;&gt;= postfun)\n              (threadDelay (600*1000*1000)) -- wait 10 Minutes\n          -- close writer\n        -- close connection\n  -- get outside of all try/handle/...-constructions befor recursing.\n  case res of\n    Left ex -&gt; do\n      printLog . pure . Error $ \"AMQ: \"&lt;&gt;show ex\n      keepActiveMQConnected sc printLog var\n    Right _ -&gt; keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n  where\n    out :: ZonedDate -&gt; WAI.Request -&gt; Status -&gt; Maybe Integer -&gt; NominalDiffTime -&gt; [BS.ByteString] -&gt; Builder -&gt; LogStr\n    out _ r status _ _ payload _\n      | statusCode status &lt; 300 = \"\"\n      | statusCode status &gt; 399 && statusCode status &lt; 500 = \"Error code \"&lt;&gt;toLogStr (statusCode status) &lt;&gt;\" sent. Request-Payload was: \"&lt;&gt; mconcat (toLogStr &lt;$&gt; payload) &lt;&gt; \"\\n\"\n      | otherwise = toLogStr (show r) &lt;&gt; \"\\n\"\n{{&lt; dend &gt;}}\n\n\nWeitere Instanzen und Definitionen, die der Generator (noch) nicht macht\nIn der Myservice.Types werden ein paar hilfreiche Typen und Typ-Instanzen definiert. Im Folgenden geht es dabei um Dinge für:\n\nEnvy\n\nLaden von $ENV_VAR in Datentypen\nDefinitionen für Default-Settings\n\nServerConfig\n\nDefinition der Server-Konfiguration & Benennung der Environment-Variablen\n\nExtraTypes\n\nggf. Paketweite extra-Typen, die der Generator nicht macht, weil sie nicht aus der API kommen (z.B. cache)\n\nOut/BSON-Instanzen\n\nDer API-Generator generiert nur wenige Instanzen automatisch (z.B. aeson), daher werden hier die fehlenden definiert.\nBSON: Kommunikation mit MongoDB\nOut: pretty-printing im Log\n\nNur nötig, wenn man pretty-printing via Out statt über Generics wie z.b. pretty-generic oder die automatische Show-Instanz via prerryShow macht.\n\n\n\nTypes.hs anzeigen\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass        #-}\n{-# LANGUAGE DeriveFunctor         #-}\n{-# LANGUAGE DeriveGeneric         #-}\n{-# LANGUAGE DerivingVia           #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts      #-}\n{-# LANGUAGE FlexibleInstances     #-}\n{-# LANGUAGE GADTs                 #-}\n{-# LANGUAGE LambdaCase            #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings     #-}\n{-# LANGUAGE RankNTypes            #-}\n{-# LANGUAGE RecordWildCards       #-}\nmodule MyService.Types where\n \nimport           Data.Aeson                     (FromJSON, ToJSON)\nimport           Data.Text\nimport           Data.Time.Clock\nimport           GHC.Generics\nimport           System.Envy\nimport           Text.PrettyPrint               (text)\nimport           Text.PrettyPrint.GenericPretty\n \n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n  doc = text . unpack\n  docPrec i a = text $ showsPrec i a \"\"\n \ninstance Out UTCTime where\n  doc = text . show\n  docPrec i a = text $ showsPrec i a \"\"\n \n-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.\ndata ServerConfig = ServerConfig\n                  { myserviceHost              :: String -- ^ Environment: $MYSERVICE_HOST\n                  , myservicePort              :: Int    -- ^ Environment: $MYSERVICE_PORT\n                  , myserviceMaxTimeout        :: Int    -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n                  , myserviceInternalProxyUrl  :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n                  , myserviceInternalProxyPort :: Int    -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n                  , myserviceExternalProxyUrl  :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n                  , myserviceExternalProxyPort :: Int    -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n                  , myserviceActivemqHost      :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n                  , myserviceActivemqPort      :: Int    -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n                  , myserviceActivemqUsername  :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n                  , myserviceActivemqPassword  :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n                  , myserviceMongoUsername     :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n                  , myserviceMongoPassword     :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n                  , myserviceDebug             :: Bool   -- ^ Environment: $MYSERVICE_DEBUG\n                  } deriving (Show, Eq, Generic)\n \n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n  defConfig = ServerConfig \"0.0.0.0\" 8080 20\n                           \"\"\n                           \"\"\n                           \"\"\n                           0\n                           \"\"\n                           0\n                           \"\"\n                           0\n                           \"\"\n                           \"\"\n                           \"\"\n                           \"\"\n                           False\n \n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n \n \ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).\n{{&lt; dend &gt;}}\n\n\nWas noch zu tun ist\nDen Service implementieren. Einfach ein neues Modul aufmachen (z.B. MyService.Handler oder MyService.DieserEndpunktbereich/MyService.JenerEndpunktbereich) und dort die Funktion implementieren, die man in der Main.hs benutzt hat. In dem Handler habt ihr dann keinen Stress mehr mit Validierung, networking, logging, etc. pp. weil alles in der Main abgehandelt wurde und ihr nur noch den “Happy-Case” implementieren müsst. Beispiel für unseren Handler oben:\nmyApiEndpointV1Post :: MonadIO m =&gt; ServerConfig -&gt; (ClientEnv,ClientEnv) -&gt; TQueue BS.ByteString -&gt; ([LogItem] -&gt; IO ()) -&gt; Request -&gt; m Response\nmyApiEndpointV1Post sc calls amqPost log req = do\n    liftIO . log $ [Info $ \"recieved \"&lt;&gt;pretty req]                                                                   -- input-logging\n    liftIO . atomically . writeTQueue . LBS.toStrict $ \"{\\\"hey Kibana, i recieved:\\\"\" &lt;&gt; A.encode (pretty req) &lt;&gt; \"}\" -- log in activeMQ/Kibana\n\n\n    --- .... gaaaanz viel komplizierter code um die Response zu erhalten ;)\n    let ret = Response 1337 Nothing -- dummy-response ;)\n    -- gegeben wir haben eine gültige mongodb-pipe;\n    -- mehr logik will ich in die Beispiele nicht packen.\n    -- Man kann die z.b. als weiteren Wert in einer TMVar (damit man sie ändern & updaten kann) an die Funktion übergeben.\n    liftIO . access pipe master \"DatabaseName\" $ do\n        ifM (auth (myServiceMongoUsername sc) (myServiceMongoPassword sc)) (return ()) (liftIO . printLog . pure . Error $ \"MongoDB: Login failed.\")\n        save \"DatabaseCollection\" [\"_id\" =: 1337, \"entry\" =: ret] -- selbe id wie oben ;)\n    return ret\nDiese dummy-Antwort führt auf, wie gut man die ganzen Sachen mischen kann.\n\nLogging in die Dateien/stdout - je nach Konfiguration\nLogging von Statistiken in Kibana\nSpeichern der Antwort in der MongoDB\nGenerieren einer Serverantwort und ausliefern dieser über die Schnittstelle\n\n\n\nTipps & Tricks\n\nDateien, die statisch ausgeliefert werden sollen\nHierzu erstellt man ein Verzeichnis static/ (Konvention; ist im generator so generiert, dass das ausgeliefert wird). Packt man hier z.b. eine index.html rein, erscheint die, wenn man den Service ansurft.\n\n\nWie bekomme ich diese fancy Preview hin?\nDer Editor, der ganz am Anfang zum Einsatz gekommen ist, braucht nur die api-doc.yml um diese Ansicht zu erzeugen. Daher empfiehlt sich hier ein angepasster Fork davon indem die Pfade in der index.html korrigiert sind. Am einfachsten (und von den meisten services so benutzt): In meiner Implementation liegt dann nach dem starten auf http://localhost:PORT/ui/ und kann direkt dort getestet werden.\n\n\nWie sorge ich für bessere Warnungen, damit der Compiler meine Bugs fängt?\nstack build --file-watch --ghc-options '-freverse-errors -W -Wall -Wcompat' --interleaved-output\nWas tut das?\n\n--file-watch: automatisches (minimales) kompilieren bei dateiänderungen\n--ghc-options\n\n-freverse-errors: Fehlermeldungen in umgekehrter Reihenfolge (Erster Fehler ganz unten; wenig scrollen )\n-W: Warnungen an\n-Wall: Alle sinnvollen Warnungen an (im gegensatz zu -Weverything, was WIRKLICH alles ist )\n-Wcompat: Warnungen für Sachen, die in der nächsten Compilerversion kaputt brechen werden & vermieden werden sollten\n\n--interleaved-output: stack-log direkt ausgeben & nicht in Dateien schreiben und die dann am ende zusammen cat'en.\n\nUm pro Datei Warnungen auszuschalten (z.B. weil man ganz sicher weiss, was man tut -.-): {-# OPTIONS_GHC -Wno-whatsoever #-} als pragma in die Datei.\nIdealerweise sollte das Projekt keine Warnungen erzeugen.\n\n\n\n\nDeployment\nAls Beispiel sei hier ein einfaches Docker-Build mit Jenkins-CI gezeigt, weil ich das aus Gründen rumliegen hatte. Kann man analog in fast alle anderen CI übersetzen.\n\nDocker\nDie angehängten Scripte gehen von einer Standard-Einrichtung aus (statische Sachen in static, 2-3 händische Anpassungen auf das eigene Projekt nach auspacken). Nachher liegt dann auch unter static/version die gebaute Versionsnummer & kann abgerufen werden. In der Dockerfile.release und der Jenkinsfile müssen noch Anpassungen gemacht werden. Konkret:\n\nin der Dockerfile.release: alle &lt;&lt;&lt;HIER&gt;&gt;&gt;-Stellen sinnvoll befüllen\nin der Jenkinsfile die defs für “servicename” und “servicebinary” ausfüllen. Binary ist das, was bei stack exec aufgerufen wird; name ist der Image-Name für das docker-repository.\n\n\n\nJenkins\nÄnderungen die dann noch gemacht werden müssen:\n\ngit-repository URL anpassen\nEnvironment-Vars anpassen ($BRANCH = test & live haben keine zusatzdinger im docker-image-repository; ansonsten hat das image $BRANCH im Namen)\n\nWenn das fertig gebaut ist, liegt im test/live-repository ein docker-image namens servicename:version.\n\n\n\nOMG! Ich muss meine API ändern. Was mache ich nun?\n\napi-doc.yml bearbeiten, wie gewünscht\nmittels generator die Api & submodule neu generieren\nggf. custom Änderungen übernehmen (:Gitdiffsplit hilft)\nAlle Compilerfehler + Warnungen in der eigentlichen Applikation fixen\nIf it comipilez, ship it! (Besser nicht :grin:)",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Webapp-Development in Haskell"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html",
    "href": "Coding/Haskell/Lenses.html",
    "title": "Lenses",
    "section": "",
    "text": "Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen\nkann. Also einfachen Datenstruktur kann man einen Record mit der entsprechenden\nSyntax nehmen.\n\n\ndata Person = P { name :: String\n                , addr :: Address\n                , salary :: Int }\ndata Address = A { road :: String\n                 , city :: String\n                 , postcode :: String }\n-- autogeneriert unten anderem: addr :: Person -&gt; Address\n\n    setName :: String -&gt; Person -&gt; Person\n    setName n p = p { name = n } --record update notation\n\n    setPostcode :: String -&gt; Person -&gt; Person\n    setPostcode pc p\n        = p { addr = addr p { postcode = pc } }\n    -- update of a record inside a record\n\n\n\nProblem mit diesem Code:\n\nfür 1-Dimensionale Felder ist die record-syntax ok.\ntiefere Ebenen nur umständlich zu erreichen\neigentlich wollen wir nur pe in p setzen, müssen aber über addr etc. gehen.\nwir brauchen wissen über die “Zwischenstrukturen”, an denen wir nicht\ninteressiert sind\n\n\n\n\ndata Person = P { name :: String\n                , addr :: Address\n                , salary :: Int }\n-- a lens for each field\nlname   :: Lens' Person String\nladdr   :: Lens' Person Adress\nlsalary :: Lens' Person Int\n-- getter/setter for them\nview    :: Lens' s a -&gt; s -&gt; a\nset     :: Lens' s a -&gt; a -&gt; s -&gt; s\n-- lens-composition\ncomposeL :: Lens' s1 s2 -&gt; Lens s2 a -&gt; Lens' s1 a\n\n\n\nMit diesen Dingen (wenn wir sie hätten) könnte man dann\ndata Person = P { name :: String\n                , addr :: Address\n                , salary :: Int }\ndata Address = A { road :: String\n                 , city :: String\n                 , postcode :: String }\nsetPostcode :: String -&gt; Person -&gt; Person\nsetPostcode pc p\n    = set (laddr `composeL` lpostcode) pc p\nmachen und wäre fertig.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#wofür-brauchen-wir-das-überhaupt",
    "href": "Coding/Haskell/Lenses.html#wofür-brauchen-wir-das-überhaupt",
    "title": "Lenses",
    "section": "",
    "text": "Die Idee dahinter ist, dass man Zugriffsabstraktionen über Daten verknüpfen\nkann. Also einfachen Datenstruktur kann man einen Record mit der entsprechenden\nSyntax nehmen.\n\n\ndata Person = P { name :: String\n                , addr :: Address\n                , salary :: Int }\ndata Address = A { road :: String\n                 , city :: String\n                 , postcode :: String }\n-- autogeneriert unten anderem: addr :: Person -&gt; Address\n\n    setName :: String -&gt; Person -&gt; Person\n    setName n p = p { name = n } --record update notation\n\n    setPostcode :: String -&gt; Person -&gt; Person\n    setPostcode pc p\n        = p { addr = addr p { postcode = pc } }\n    -- update of a record inside a record\n\n\n\nProblem mit diesem Code:\n\nfür 1-Dimensionale Felder ist die record-syntax ok.\ntiefere Ebenen nur umständlich zu erreichen\neigentlich wollen wir nur pe in p setzen, müssen aber über addr etc. gehen.\nwir brauchen wissen über die “Zwischenstrukturen”, an denen wir nicht\ninteressiert sind\n\n\n\n\ndata Person = P { name :: String\n                , addr :: Address\n                , salary :: Int }\n-- a lens for each field\nlname   :: Lens' Person String\nladdr   :: Lens' Person Adress\nlsalary :: Lens' Person Int\n-- getter/setter for them\nview    :: Lens' s a -&gt; s -&gt; a\nset     :: Lens' s a -&gt; a -&gt; s -&gt; s\n-- lens-composition\ncomposeL :: Lens' s1 s2 -&gt; Lens s2 a -&gt; Lens' s1 a\n\n\n\nMit diesen Dingen (wenn wir sie hätten) könnte man dann\ndata Person = P { name :: String\n                , addr :: Address\n                , salary :: Int }\ndata Address = A { road :: String\n                 , city :: String\n                 , postcode :: String }\nsetPostcode :: String -&gt; Person -&gt; Person\nsetPostcode pc p\n    = set (laddr `composeL` lpostcode) pc p\nmachen und wäre fertig.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#trivialer-ansatz",
    "href": "Coding/Haskell/Lenses.html#trivialer-ansatz",
    "title": "Lenses",
    "section": "Trivialer Ansatz",
    "text": "Trivialer Ansatz\n\nGetter/Setter also Lens-Methoden\ndata LensR s a = L { viewR :: s -&gt; a\n                   , setR  :: a -&gt; s -&gt; s }\n\ncomposeL (L v1 u1) (L v2 u2)\n  = L (\\s -&gt; v2 (v1 s))\n      (\\a s -&gt; u1 (u2 a (v1 s)) s)\n\n\nWieso ist das schlecht?\n\nextrem ineffizient\n\nAuslesen traversiert die Datenstruktur, dann wird die Function angewendet und\nzum setzen wird die Datenstruktur erneut traversiert:\nover :: LensR s a -&gt; (a -&gt; a) -&gt; s -&gt; s\nover ln f s = setR l (f (viewR l s)) s\n\nLösung: modify-funktion hinzufügen\n\ndata LensR s a\n   = L { viewR :: s -&gt; a\n       , setR  :: a -&gt; s -&gt; s\n       , mod   :: (a-&gt;a) -&gt; s -&gt; s\n       , modM  :: (a-&gt;Maybe a) -&gt; s -&gt; Maybe s\n       , modIO :: (a-&gt;IO a) -&gt; s -&gt; IO s }\nNeues Problem: Für jeden Spezialfall muss die Lens erweitert werden.\n\n\nSomething in common\nMan kann alle Monaden abstrahieren. Functor reicht schon:\ndata LensR s a\n   = L { viewR :: s -&gt; a\n       , setR  :: a -&gt; s -&gt; s\n       , mod   :: (a-&gt;a) -&gt; s -&gt; s\n       , modF  :: Functor f =&gt; (a-&gt;f a) -&gt; s -&gt; f s }\nIdee: Die 3 darüberliegenden durch modF ausdrücken.\n\n\nTyp einer Lens\nWenn man das berücksichtigt, dann hat einen Lens folgenden Typ:\ntype Lens' s a = forall f. Functor f\n                           =&gt; (a -&gt; f a) -&gt; s -&gt; f s\nAllerdings haben wir dann noch unseren getter/setter:\ndata LensR s a = L { viewR :: s -&gt; a\n                   , setR :: a -&gt; s -&gt; s }\nStellt sich raus: Die sind isomorph! Auch wenn die von den Typen her komplett\nanders aussehen.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#benutzen-einer-lens-also-setter",
    "href": "Coding/Haskell/Lenses.html#benutzen-einer-lens-also-setter",
    "title": "Lenses",
    "section": "Benutzen einer Lens also Setter",
    "text": "Benutzen einer Lens also Setter\nset :: Lens' s a -&gt; (a -&gt; s -&gt; s)\nset ln a s = --...umm...\n--:t ln =&gt; (a -&gt; f a) -&gt; s -&gt; f s\n--            =&gt; get s out of f s to return it\nWir können für f einfach die “Identity”-Monade nehmen, die wir nachher wegcasten\nkönnen.\nnewtype Identity a = Identity a\n-- Id :: a -&gt; Identity a\n\nrunIdentity :: Identity s -&gt; s\nrunIdentity   (Identity x) = x\n\ninstance Functor Identity where\n    fmap f (Identity x) = Identity (f x)\nsomit ist set einfach nur\nset :: Lens' s a -&gt; (a -&gt; s -&gt; s)\nset ln x s\n   = runIdentity (ls set_fld s)\n   where\n     set_fld :: a -&gt; Identity a\n     set_fld _ = Identity x\n     -- a was the OLD value.\n     -- We throw that away and set the new value\noder kürzer (für nerds wie den Author der Lens-Lib)\nset :: Lens' s a -&gt; (a -&gt; s -&gt; s)\nset ln x = runIdentity . ln (Identity . const x)",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#benutzen-einer-lens-also-modify",
    "href": "Coding/Haskell/Lenses.html#benutzen-einer-lens-also-modify",
    "title": "Lenses",
    "section": "Benutzen einer Lens also Modify",
    "text": "Benutzen einer Lens also Modify\nDasselbe wie Set, nur dass wir den Parameter nicht entsorgen, sondern in die\nmitgelieferte Function stopfen.\nover :: Lens' s a -&gt; (a -&gt; a) -&gt; s -&gt; s\nover ln f = runIdentity . ln (Identity . f)",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#benutzen-einer-lens-also-getter",
    "href": "Coding/Haskell/Lenses.html#benutzen-einer-lens-also-getter",
    "title": "Lenses",
    "section": "Benutzen einer Lens also Getter",
    "text": "Benutzen einer Lens also Getter\nview :: Lens' s a -&gt; (s -&gt; a)\nview ln s = --...umm...\n--:t ln =&gt; (a -&gt; f a) -&gt; s -&gt; f s\n--            =&gt; get a out of the (f s) return-value\n--            Wait, WHAT?\nAuch hier gibt es einen netten Funktor. Wir packen das “a” einfach in das “f”\nund werfen das “s” am End weg.\nnewtype Const v a = Const v\n\ngetConst :: Const v a -&gt; v\ngetConst (Const x) = x\n\ninstance Functor (Const v) where\n    fmap f (Const x) = Const x\n    -- throw f away. Nothing changes our const!\nsomit ergibt sich\nview :: Lens' s a -&gt; (s -&gt; a)\nview ln s\n  = getConst (ln Const s)\n              -- Const :: s -&gt; Const a s\noder nerdig\nview :: Lens' s a -&gt; (s -&gt; a)\nview ln = getConst . ln Const",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#lenses-bauen",
    "href": "Coding/Haskell/Lenses.html#lenses-bauen",
    "title": "Lenses",
    "section": "Lenses bauen",
    "text": "Lenses bauen\nNochmal kurz der Typ:\ntype Lens' s a = forall f. Functor f\n                     =&gt; (a -&gt; f a) -&gt; s -&gt; f s\nFür unser Personen-Beispiel vom Anfang:\ndata Person = P { _name :: String, _salary :: Int }\n\nname :: Lens' Person String\n-- name :: Functor f =&gt; (String -&gt; f String)\n--                    -&gt; Person -&gt; f Person\n\nname elt_fn (P n s)\n  = fmap (\\n' -&gt; P n' s) (elt_fn n)\n-- fmap :: Functor f =&gt; (a-&gt;b) -&gt; f a -&gt; f b - der Funktor, der alles verknüpft\n-- \\n' -&gt; .. :: String -&gt; Person - Funktion um das Element zu lokalisieren (WO wird ersetzt/gelesen/...)\n-- elt_fn n  :: f String         - Funktion um das Element zu verändern (setzen, ändern, ...)\nDie Lambda-Funktion ersetzt einfach den Namen. Häufig sieht man auch\nname elt_fn (P n s)\n  = (\\n' -&gt; P n' s) &lt;$&gt; (elt_fn n)\n--  |    Focus    |     |Function|",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#wie-funktioniert-das-intern",
    "href": "Coding/Haskell/Lenses.html#wie-funktioniert-das-intern",
    "title": "Lenses",
    "section": "Wie funktioniert das intern?",
    "text": "Wie funktioniert das intern?\nview name (P {_name=\"Fred\", _salary=100})\n   -- inline view-function\n= getConst (name Const (P {_name=\"Fred\", _salary=100})\n   -- inline name\n= getConst (fmap (\\n' -&gt; P n' 100) (Const \"Fred\"))\n   -- fmap f (Const x) = Const x - Definition von Const\n= getConst (Const \"Fred\")\n   -- getConst (Const x) = x\n= \"Fred\"\nDieser Aufruf hat KEINE Runtime-Kosten, weil der Compiler direkt die Address des\nFeldes einsetzen kann. Der gesamte Boilerplate-Code wird vom Compiler\nwegoptimiert.\nDies gilt für jeden Funktor mit newtype, da das nur ein Typalias ist.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#composing-lenses-und-deren-benutzung",
    "href": "Coding/Haskell/Lenses.html#composing-lenses-und-deren-benutzung",
    "title": "Lenses",
    "section": "Composing Lenses und deren Benutzung",
    "text": "Composing Lenses und deren Benutzung\nWie sehen denn die Typen aus?\nWir wollen ein\n\nLens’ s1 s2 -&gt; Lens’ s2 a -&gt; Lens’ s1 a\n\nWir haben 2 Lenses\n\nln1 :: (s2 -&gt; f s2) -&gt; (s1 -&gt; f s1)\nln2 :: (a -&gt; f a) -&gt; (s2 -&gt; f s2)\n\nwenn man scharf hinsieht, kann man die verbinden\n\nln1 . ln2 :: (a -&gt; f s) -&gt; (s1 -&gt; f s1)\n\nund erhält eine Lens. Sogar die Gewünschte!\nSomit ist Lens-Composition einfach nur Function-Composition (.).",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#automatisieren-mit-template-haskell",
    "href": "Coding/Haskell/Lenses.html#automatisieren-mit-template-haskell",
    "title": "Lenses",
    "section": "Automatisieren mit Template-Haskell",
    "text": "Automatisieren mit Template-Haskell\nDer Code um die Lenses zu bauen ist für records immer Identisch:\ndata Person = P { _name :: String, _salary :: Int }\n\nname :: Lens' Person String\nname elt_fn (P n s) = (\\n' -&gt; P n' s) &lt;$&gt; (elt_fn n)\nDaher kann man einfach\nimport Control.Lens.TH\ndata Person = P { _name :: String, _salary :: Int }\n\n$(makeLenses ''Person)\nnehmen, was einem eine Lens für “name” und eine Lens für “salary” generiert.\nMit anderen Templates kann man auch weitere Dinge steuern (etwa wofür Lenses\ngeneriert werden, welches Prefix (statt _) man haben will etc. pp.).\nWill man das aber haben, muss man selbst in den Control.Lens.TH-Code schauen.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#lenses-für-den-beispielcode",
    "href": "Coding/Haskell/Lenses.html#lenses-für-den-beispielcode",
    "title": "Lenses",
    "section": "Lenses für den Beispielcode",
    "text": "Lenses für den Beispielcode\nimport Control.Lens.TH\n\ndata Person = P { _name :: String\n                , _addr :: Address\n                , _salary :: Int }\ndata Address = A { _road :: String\n                 , _city :: String\n                 , _postcode :: String }\n\n$(makeLenses ''Person)\n$(makeLenses ''Address)\n\nsetPostcode :: String -&gt; Person -&gt; Person\nsetPostcode pc p = set (addr . postcode) pc p",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#shortcuts-mit-line-noise",
    "href": "Coding/Haskell/Lenses.html#shortcuts-mit-line-noise",
    "title": "Lenses",
    "section": "Shortcuts mit “Line-Noise”",
    "text": "Shortcuts mit “Line-Noise”\n-- ...\n\nsetPostcode :: String -&gt; Person -&gt; Person\nsetPostcode pc p = addr . postcode .~ pc     $ p\n--                 |   Focus     |set|to what|in where\n\ngetPostcode :: Person -&gt; String\ngetPostcode p = p   ^. $ addr . postcode\n--            |from|get|    Focus       |\nEs gibt drölf-zillionen weitere Infix-Operatoren (für Folds,\nListenkonvertierungen, -traversierungen, …)",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#virtuelle-felder",
    "href": "Coding/Haskell/Lenses.html#virtuelle-felder",
    "title": "Lenses",
    "section": "Virtuelle Felder",
    "text": "Virtuelle Felder\nMan kann mit Lenses sogar Felder emulieren, die gar nicht da sind. Angenommen\nfolgender Code:\ndata Temp = T { _fahrenheit :: Float }\n\n$(makeLenses ''Temp)\n-- liefert Lens: fahrenheit :: Lens Temp Float\n\ncentigrade :: Lens Temp Float\ncentigrade centi_fn (T faren)\n  = (\\centi' -&gt; T (cToF centi'))\n    &lt;$&gt; (centi_fn (fToC faren))\n-- cToF & fToC as Converter-Functions defined someplace else\nHiermit kann man dann auch Funktionen, die auf Grad-Celsius rechnen auf Daten\nanwenden, die eigenlich nur Fahrenheit speichern, aber eine Umrechnung\nbereitstellen. Analog kann man auch einen Zeit-Datentypen definieren, der\nintern mit Sekunden rechnet (und somit garantiert frei von Fehlern wie -3\nMinuten oder 37 Stunden ist)",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#non-record-strukturen",
    "href": "Coding/Haskell/Lenses.html#non-record-strukturen",
    "title": "Lenses",
    "section": "Non-Record Strukturen",
    "text": "Non-Record Strukturen\nDas ganze kann man auch parametrisieren und auf Non-Record-Strukturen anwenden.\nBeispielhaft an einer Map verdeutlicht:\n-- from Data.Lens.At\nat :: Ord k =&gt; k -&gt; Lens' (Map k v) (Maybe v)\n\n-- oder identisch, wenn man die Lens' auflöst:\nat :: Ord k, forall f. Functor f =&gt; k -&gt; (Maybe v -&gt; f Maybe v) -&gt; Map k v -&gt; f Map k v\n\nat k mb_fn m\n  = wrap &lt;$&gt; (mb_fn mv)\n  where\n    mv = Map.lookup k m\n\n    wrap :: Maybe v -&gt; Map k v\n    wrap (Just v') = Map.insert k v' m\n    wrap Nothing   = case mv of\n                       Nothing -&gt; m\n                       Just _  -&gt; Map.delete k m\n\n-- mb_fn :: Maybe v -&gt; f Maybe v",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#weitere-beispiele",
    "href": "Coding/Haskell/Lenses.html#weitere-beispiele",
    "title": "Lenses",
    "section": "Weitere Beispiele",
    "text": "Weitere Beispiele\n\nBitfields auf Strukturen die Bits haben (Ints, …) in Data.Bits.Lens\nWeb-scraper in Package hexpat-lens\np ^.. _HTML' . to allNodes\n             . traverse . named \"a\"\n             . traverse . ix \"href\"\n             . filtered isLocal\n             . to trimSpaces\nZieht alle externen Links aus dem gegebenen HTML-Code in p um weitere ziele\nfürs crawlen zu finden.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#erweiterungen",
    "href": "Coding/Haskell/Lenses.html#erweiterungen",
    "title": "Lenses",
    "section": "Erweiterungen",
    "text": "Erweiterungen\nBisher hatten wir Lenses nur auf Funktoren F. Die nächstmächtigere Klasse ist\nApplicative.\ntype Traversal' s a = forall f. Applicative f\n                             =&gt; (a -&gt; f a) -&gt; (s -&gt; f s)\nDa wir den Container identisch lassen (weder s noch a wurde angefasst) muss sich\netwas anderes ändern. Statt eines einzelnen Focus erhalten wir viele Foci.\nWas ist ein Applicative überhaupt? Eine schwächere Monade (nur 1x Anwendung und\nkein Bind - dafür kann man die beliebig oft hintereinanderhängen).\nclass Functor f =&gt; Applicative f where\n  pure  :: a -&gt; f a\n  (&lt;*&gt;) :: f (a -&gt; b) -&gt; f a -&gt; f b\n\n-- Monade als Applicative:\npure = return\nmf &lt;*&gt; mx = do { f &lt;- mf; x &lt;- mx; return (f x) }\nRecap: Was macht eine Lens:\ndata Adress = A { _road :: String\n                , _city :: String\n                , _postcode :: String }\n\nroad :: Lens' Adress String\nroad elt_fn (A r c p) = (\\r' -&gt; A r' c p) &lt;$&gt; (elt_fn r)\n--                      |    \"Hole\"     |     | Thing to put in|\nWenn man nun road & city gleichzeitig bearbeiten will:\naddr_strs :: Traversal' Address String\naddr_strs elt_fn (A r c p)\n  = ... (\\r' c' -&gt; A r' c' p)    .. (elt_fn r) .. (elt_fn c) ..\n--      | function with 2 \"Holes\"|  first Thing |  second Thing\nfmap kann nur 1 Loch stopfen, aber nicht mit n Löchern umgehen. Applicative mit\n&lt;*&gt; kann das.\nSomit gibt sich\naddr_strs :: Traversal' Address String\naddr_strs elt_fn (A r c p)\n  = pure           (\\r' c' -&gt; A r' c' p)  &lt;*&gt; (elt_fn r) &lt;*&gt; (elt_fn c)\n--  lift in Appl. | function with 2 \"Holes\"|  first Thing |  second Thing\n-- oder kürzer\naddr_strs :: Traversal' Address String\naddr_strs elt_fn (A r c p)\n  = (\\r' c' -&gt; A r' c' p)  &lt;$&gt; (elt_fn r) &lt;*&gt; (elt_fn c)\n-- pure x &lt;*&gt; y == x &lt;$&gt; y\nWie würd eine modify-funktion aussehen?\nover :: Lens' s a -&gt; (a -&gt; a) -&gt; s -&gt; s\nover ln f = runIdentity . ln (Identity . f)\n\nover :: Traversal' s a -&gt; (a -&gt; a) -&gt; s -&gt; s\nover ln f = runIdentity . ln (Identity . f)\nDer Code ist derselbe - nur der Typ ist generischer. Auch die anderen Dinge\nfunktioniert diese Erweiterung (für Identity und Const muss man noch ein paar\ndummy-Instanzen schreiben um sie von Functor auf Applicative oder Monad zu heben\n\nkonkret reicht hier die Instanzierung von Monoid). In der Lens-Library ist\ndaher meist Monad m statt Functor f gefordert.",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#wozu-dienen-die-erweiterungen",
    "href": "Coding/Haskell/Lenses.html#wozu-dienen-die-erweiterungen",
    "title": "Lenses",
    "section": "Wozu dienen die Erweiterungen?",
    "text": "Wozu dienen die Erweiterungen?\nMan kann mit Foci sehr selektiv vorgehen. Auch kann man diese durch Funktionen\nsteuern. Beispisweise eine Function anwenden auf\n\nJedes 2. Listenelement\nAlle graden Elemente in einem Baum\nAlle Namen in einer Tabelle, deren Gehalt &gt; 10.000€ ist\n\nTraversals und Lenses kann man trivial kombinieren (lens . lens =&gt; lens,\nlens . traversal =&gt; traversal etc.)",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Coding/Haskell/Lenses.html#wie-es-in-lens-wirklich-aussieht",
    "href": "Coding/Haskell/Lenses.html#wie-es-in-lens-wirklich-aussieht",
    "title": "Lenses",
    "section": "Wie es in Lens wirklich aussieht",
    "text": "Wie es in Lens wirklich aussieht\nIn diesem Artikel wurde nur auf Monomorphic Lenses eingegangen. In der richtigen\nLibrary ist eine Lens\ntype Lens' s a = Lens s s a a\ntype Lens s t a b = forall f. Functor f =&gt; (a -&gt; f b) -&gt; (s -&gt; f t)\nsodass sich auch die Typen ändern können um z.B. automatisch einen Konvertierten\n(sicheren) Typen aus einer unsicheren Datenstruktur zu geben.\nDie modify-Funktion over ist auch\n&gt; over :: Profunctor p =&gt; Setting p s t a b -&gt; p a b -&gt; s -&gt; t\n\nEdward is deeply in thrall to abstractionitis - Simon Peyton Jones\n\nLens alleine definiert 39 newtypes, 34 data-types und 194 Typsynonyme…\nAusschnitt\n-- traverseOf :: Functor f =&gt; Iso s t a b           -&gt; (a -&gt; f b) -&gt; s -&gt; f t\n-- traverseOf :: Functor f =&gt; Lens s t a b          -&gt; (a -&gt; f b) -&gt; s -&gt; f t\n-- traverseOf :: Applicative f =&gt; Traversal s t a b -&gt; (a -&gt; f b) -&gt; s -&gt; f t\n\ntraverseOf :: Over p f s t a b -&gt; p a (f b) -&gt; s -&gt; f t\ndafuq?",
    "crumbs": [
      "Home",
      "Serious",
      "Coding",
      "Haskell",
      "Lenses"
    ]
  },
  {
    "objectID": "Health/Issues.html",
    "href": "Health/Issues.html",
    "title": "Mental Health",
    "section": "",
    "text": "In modern times many people struggle with mental health issues - and a am by no means an exception. The main issue is, that most people just don’t talk about it and suffer alone, thinking they are alone, and everyone else is just doing fine in this hellscape of a modern society. At least that is what you see on several social media platforms like Instagram etc.\nSo even despite my exceptional1 successes that can be seen in my work i always struggled with issues even to the point of total breakdown. Of course i am also guilty of painting a rosy picture of me - just look at a summary of my experiences or the awesome things i did at university. If you only read that it is hard to believe that i basically had to delay my studies from 2007 to 2010 because i wasn’t even really able to leave the house.\nOnly thanks to the not-that-awful system in Germany and massive financial help from my parents i was even able to pursue this way.",
    "crumbs": [
      "Home",
      "Serious",
      "Health",
      "Mental Health"
    ]
  },
  {
    "objectID": "Health/Issues.html#what-are-my-issues",
    "href": "Health/Issues.html#what-are-my-issues",
    "title": "Mental Health",
    "section": "What are my issues?",
    "text": "What are my issues?\nWell.. after 15 long years of therapy i finally get a hang of all my issues. Many of those are related, some are just the result of coping mechanisms of ignoring other issues.\nCurrently i am successfully diagnosed with\n\nsocial anxiety\nADHD\ntransgenderism\n\nand i got a big suspician of\n\nautism\n\nAll in all: when i feel well, am good rested and have nothing critical coming up i am more of what i would call a “high functioning Autist, but not THAT far on the spectrum”. But it is funny that while finding out who i really am, i met more people who basically had the same issue and a similar biography like mine. Some of them get the autism-diagnosis first, others the ADHD one - since until some time ago those diagnosis were mutually exclusive.\nThat’s also why many people like me are only really diagnosed as adults, because autism hides many effects of ADHD and vice-versa - depending on which one is currently dominating. It is basically 2 modes: do everything all at once and start everything that grabs your attention - or do a deep dive into a single thing. And the exact opposite: The ADHD part being bored by the autism-project, the autism-part is completely overwhelmed by the ADHD chaos. Both then leading to exhaustion, not being able to do anything .. and basically feeling guilty for the things you did not manage to finish.\nToday i finally found myself. I currently have a great partner (with 3 kids) - and all of them have similar issues. Like i said: I best get along with similar people - and also fall in love with those.. and as AuDHD runs in the genes all offspring has a good chance of catching it to varies degrees, too.\nI think the most important thing was the ADHD-Diagnosis - as this enabled me to get metylphenidate to basically get into a “3-4 hours focused as long as the pill works” and total chaos afterwards. This enables me to have productive days/times where i can do all the boring-work that my ADHD-Part wants to sit out and the autism part is overwhelmed from even starting.",
    "crumbs": [
      "Home",
      "Serious",
      "Health",
      "Mental Health"
    ]
  },
  {
    "objectID": "Health/Issues.html#the-early-days",
    "href": "Health/Issues.html#the-early-days",
    "title": "Mental Health",
    "section": "The early days",
    "text": "The early days\nTo be continued …",
    "crumbs": [
      "Home",
      "Serious",
      "Health",
      "Mental Health"
    ]
  },
  {
    "objectID": "Opinions/Editors.html",
    "href": "Opinions/Editors.html",
    "title": "Editors",
    "section": "",
    "text": "Better said: “neovim is currently the best™ editor” ;)\n\n\nYou can find my current Config along with other things in my gitea snippet-git.",
    "crumbs": [
      "Home",
      "Fun",
      "Opinions",
      "Editors"
    ]
  },
  {
    "objectID": "Opinions/Editors.html#editors",
    "href": "Opinions/Editors.html#editors",
    "title": "Editors",
    "section": "",
    "text": "Better said: “neovim is currently the best™ editor” ;)\n\n\nYou can find my current Config along with other things in my gitea snippet-git.",
    "crumbs": [
      "Home",
      "Fun",
      "Opinions",
      "Editors"
    ]
  },
  {
    "objectID": "Opinions/Editors.html#references",
    "href": "Opinions/Editors.html#references",
    "title": "Editors",
    "section": "References",
    "text": "References\n\nLearning Vim in 2014: Vim as Language - Herding Lions\nvi - What is your most productive shortcut with Vim? - Stack Overflow",
    "crumbs": [
      "Home",
      "Fun",
      "Opinions",
      "Editors"
    ]
  },
  {
    "objectID": "Stuff/Bielefeldverschwoerung.html",
    "href": "Stuff/Bielefeldverschwoerung.html",
    "title": "Die Bielefeld-Verschwörung",
    "section": "",
    "text": "Kopie des vermutlichen Originals von (vermutlich) Achim Held aus 1994.\nWarnung: Diese Seite enthält Material, von dem SIE nicht wollen, dass es bekannt wird. Speichern Sie diese Seite nicht auf Ihrer lokalen Platte ab, denn sonst sind Sie auch dran, wenn SIE plötzlich bei Ihnen vor der Tür stehen; und das passiert schneller als man denkt. Auch sollten Sie versuchen, alle Hinweise darauf, dass Sie diese Seite jemals gelesen haben, zu vernichten. Tragen Sie diese Seite auf keinen Fall in ihre Hotlist/Bookmarks/etc… ein!\nVielen Dank für die Beachtung aller Sicherheitsvorschriften.",
    "crumbs": [
      "Home",
      "Fun",
      "Stuff",
      "Die Bielefeld-Verschwörung"
    ]
  },
  {
    "objectID": "Stuff/Bielefeldverschwoerung.html#die-geschichte-der-entdeckung",
    "href": "Stuff/Bielefeldverschwoerung.html#die-geschichte-der-entdeckung",
    "title": "Die Bielefeld-Verschwörung",
    "section": "Die Geschichte der Entdeckung",
    "text": "Die Geschichte der Entdeckung\nVor einigen Jahren fiel es einigen Unerschrockenen zum ersten Mal auf, dass in den Medien immer wieder von einer Stadt namens ‘Bielefeld’ die Rede war, dass aber niemand jemanden aus Bielefeld kannte, geschweige denn selbst schon einmal dort war. Zuerst hielten sie dies für eine belanglose Sache, aber dann machte es sie doch neugierig. Sie unterhielten sich mit anderen darüber, ohne zu ahnen, dass dies bereits ein Fehler war: Aus heutiger Sicht steht fest, dass jemand geplaudert haben muss, denn sofort darauf wurden SIE aktiv. Plötzlich tauchten Leute auf, die vorgaben, schon einmal in Bielefeld gewesen zu sein; sogar Personen, die vormals noch laut Zweifel geäußert hatten, berichteten jetzt davon, sich mit eigenen Augen von der Existenz vergewissert zu haben - immer hatten diese Personen bei ihren Berichten einen seltsam starren Blick. Doch da war es schon zu spät - die Saat des Zweifels war gesät. Weitere Personen stießen zu der Kerngruppe der Zweifler, immer noch nicht sicher, was oder wem man da auf der Spur war.\nDann, im Oktober 1993, der Durchbruch: Auf der Fahrt von Essen nach Kiel auf der A2 erhielten vier der hartnäckigsten Streiter für die Aufdeckung der Verschwörung ein Zeichen: Jemand hatte auf allen Schildern den Namen ‘Bielefeld’ mit orangem Klebeband durchgestrichen. Da wußte die Gruppe: Man ist nicht alleine, es gibt noch andere, im Untergrund arbeitende Zweifler, womöglich über ganz Deutschland verteilt, die auch vor spektakulären Aktionen nicht zurückschrecken. Von da an war uns klar: Wir müssen diese Scharade aufdecken, koste es, was es wolle! Das Ausmaß der Verschwörung\nDer Aufwand, mit dem die Täuschung der ganzen Welt betrieben wird, ist enorm. Die Medien, von denen ja bekannt ist, dass sie unter IHRER Kontrolle stehen, berichten tagaus, tagein von Bielefeld, als sei dies eine Stadt wie jede andere, um der Bevölkerung das Gefühl zu geben, hier sei alles ganz normal. Aber auch handfestere Beweise werden gefälscht: SIE kaufen hunderttausende von Autos, versehen sie mit gefälschten ’BI-’Kennzeichen und lassen diese durch ganz Deutschland fahren. SIE stellen, wie bereits oben geschildert, entlang der Autobahnen große Schilder auf, auf denen Bielefeld erwähnt wird. SIE veröffentlichen Zeitungen, die angeblich in Bielefeld gedruckt werden. Anscheinend haben SIE auch die Deutsche Post AG in Ihrer Hand, denn auch im PLZB findet man einen Eintrag für Bielefeld; und ebenso wird bei der Telekom ein komplettes Ortsnetz für Bielefeld simuliert. Einige Leute behaupten sogar in Bielefeld studiert zu haben und können auch gut gefälschte Diplome u.ä. der angeblich existenten Uni Bielefeld vorweisen. Auch Bundeskanzler Gerhard Schröder behauptet, 1965 das “Westfalen-Kolleg” in Bielefeld besucht zu haben, wie seinem Lebenslauf unter dem Link Bildungsweg zu entnehmen ist.\nAber auch vor dem Internet machen SIE nicht halt. SIE vergeben Mail-Adressen für die Domain uni-bielefeld.de, und SIE folgen auch den neuesten Trends: SIE bieten im WWW eine “Stadtinfo über Bielefeld” an, sogar mit Bildern; das Vorgarten-Foto, das dem Betrachter als “Botanischer Garten” verkauft werden sollte, ist nach der Entlarvung auf dieser Seite jedoch inzwischen wieder entfernt worden. Aber auch die noch vorhandenen Bilder sind sogar für den Laien als Fotomontagen zu erkennen. Wir sind noch nicht dahinter gekommen, wo der Rechner steht, auf dem die Domains .bielefeld.de und uni-bielefeld.de gefälscht werden; wir arbeiten daran. Inzwischen wurde auch von einem IHRER Agenten - der Täter ist uns bekannt - versucht, diese WWW-Seite zu sabotieren, ich konnte den angerichteten Schaden jedoch zum Glück wieder beheben.\nEin anonymer Informant, der ganz offensichtlich zu IHNEN zu gehören scheint oder zumindest gute Kontakte zu IHNEN hat, hat mich kürzlich in einer Mail auf die nächste Stufe IHRER Planung hingewiesen: “Ich schätze, spätestens in 10 Jahren wird es heißen: Bielefeld muss Hauptstadt werden.” Was das bedeutet, muss ja wohl nicht extra betont werden.\nDie schrecklichste Maßnahme, die SIE ergriffen haben, ist aber zweifelsohne immer noch die Gehirnwäsche, der immer wieder harmlose Menschen unterzogen werden, die dann anschließend auch die Existenz von Bielefeld propagieren. Immer wieder verschwinden Menschen, gerade solche, die sich öffentlich zu ihren Bielefeldzweifeln bekannt haben, nur um dann nach einiger Zeit wieder aufzutauchen und zu behaupten, sie seien in Bielefeld gewesen. Womöglich wurden einige Opfer sogar mit Telenosestrahlen behandelt. Diesen armen Menschen konnten wir bisher nicht helfen. Wir haben allerdings inzwischen einen Verdacht, wo diese Gehirnwäsche durchgeführt wird: Im sogenannten Bielefeld-Zentrum, wobei SIE sogar die Kaltblütigkeit besitzen, den Weg zu diesem Ort des Schreckens von der Autobahn aus mit großen Schildern auszuschildern. Wir sind sprachlos, welchen Einfluß SIE haben.\nInzwischen sind - wohl auch durch mehrere Berichte in den wenigen nicht von IHNEN kontrollierten Medien - mehr und mehr Leute wachsamer geworden und machen uns auf weitere Aspekte der Verschwörung aufmerksam. So berichtet zum Beispiel Holger Blaschka:\n“Auch der DFB ist in diesen gewaltigen Skandal verwickelt, spielt in der ersten Liga doch ein Verein, den SIE Arminia Bielefeld getauft haben, der innert 2 Jahren aus dem Nichts der Amateur-Regionen im bezahlten Fußball auftauchte und jetzt im Begriff ist, sich zu IHRER besten Waffe gegen all die Zweifler zu entwickeln. Den Gästefans wird vorgetäuscht mit ihren Bussen nach Bielefeld zu kommen, wo sie von IHNEN abgefangen werden, um direkt ins Stadion geleitet zu werden. Es besteht keine Chance sich die Stadt näher anzuschauen, und auch die Illusion des Heimpublikums wird durch eine größere Menge an bezahlten Statisten aufrechterhalten. Selbst ehemalige Top-Spieler, die Ihren Leistungszenit bei weitem überschritten haben, werden zu diesem Zweck von IHNEN mißbraucht. Mit genialen Manövern, u.a. vorgetäuschten Faustschlägen und Aufständen gegen das Präsidium eines baldigen Drittligisten wurde von langer Hand die wohl aufwendigste Täuschung aller Zeiten inszeniert. Es gibt noch mehr Beweise: Das sich im Rohbau befindende Stadion, das gefälschte und verpanschte Bier und nicht zuletzt die Tatsache, dass dieser Verein nur einen Sponsor hat. SIE, getarnt als Modefirma Gerry Weber.”",
    "crumbs": [
      "Home",
      "Fun",
      "Stuff",
      "Die Bielefeld-Verschwörung"
    ]
  },
  {
    "objectID": "Stuff/Bielefeldverschwoerung.html#was-steckt-dahinter",
    "href": "Stuff/Bielefeldverschwoerung.html#was-steckt-dahinter",
    "title": "Die Bielefeld-Verschwörung",
    "section": "Was steckt dahinter?",
    "text": "Was steckt dahinter?\nDies ist die Frage, auf die wir auch nach jahrelangen Untersuchungen immer noch keine befriedigende Antwort geben können. Allerdings gibt es einige Indizien, die auf bestimmte Gruppierungen hinweisen:\n\nEs könnte eine Gruppe um den Sternenbruder und Weltenlehrer Ashtar Sheran dahinterstecken, die an der Stelle, an der Bielefeld liegen soll, ihre Landung vorbereiten, die - einschlägiger Fachliteratur zufolge - kurz bevorsteht. Zu dieser Gruppe sollen auch Elvis und Kurt Cobain gehören, die beide - vom schwedischen Geheimdienst gedeckt - noch am Leben sind.\nAn der Stelle, an der Bielefeld liegen soll, hält die CIA John F. Kennedy seit dem angeblichen Attentat versteckt, damit er nichts über die vorgetäuschte Mondlandung der NASA erzählen kann. Inwieweit die Reichsflugscheibenmacht von ihrer Mond- oder Marsbasis aus da mitspielt, können wir nicht sagen, da alle Beweise beim Abschuß der schwer bewaffneten Marssonde Observer vernichtet wurden. Informationen hierüber besitzt vielleicht der Vatikan, der seit den 50er Jahren regelmäßig mit tachyonenangetriebenen Schiffen zum Mars fliegt.\nDer MOSSAD in Zusammenarbeit mit dem OMEGA-Sektor planen an dieser Stelle die Errichtung eines geheimen Forschungslabors, weil sich genau an diesem Ort zwei noch nicht dokumentierte Ley-Linien kreuzen. Dort könnte auch der Jahrtausende alte Tunnel nach Amerika und Australien (via Atlantis) seinen Eingang haben. Wichtige Mitwisser, namentlich Uwe Barschel und Olof Palme, wurden von den mit dem MOSSAD zusammenarbeitenden Geheimdiensten, darunter der Stasi und der weniger bekannten ‘Foundation’, frühzeitig ausgeschaltet.\nAn der Stelle liegt die Höhle eines der schlafenden Drachen aus dem Vierten Zeitalter, die auf das Erwachen der Magie am 24. Dezember 2011 (siehe hierzu den Maya-Kalender) warten. Beschützt wird diese Stelle von den Rittern des Ordenskreuzes AAORRAC, die sich inzwischen mit der Herstellung von programmiertem Wasser beschäftigen - nach einen Rezept, das sie unter brutaler Folter von Ann Johnson bekommen haben. Diese hatte es bekanntlich von hohen Lichtwesen aus dem All erhalten, um die Menschheit vor außerirdischen Implantaten bis Stufe 3 zu schützen.",
    "crumbs": [
      "Home",
      "Fun",
      "Stuff",
      "Die Bielefeld-Verschwörung"
    ]
  },
  {
    "objectID": "Stuff/Bielefeldverschwoerung.html#was-können-wir-tun",
    "href": "Stuff/Bielefeldverschwoerung.html#was-können-wir-tun",
    "title": "Die Bielefeld-Verschwörung",
    "section": "Was können wir tun?",
    "text": "Was können wir tun?\nZum einen können wir alle an den Bundestag, das Europaparlament und die UNO schreiben, um endlich zu erreichen, dass SIE nicht mehr von den Politikern gedeckt werden. Da aber zu befürchten ist, dass SIE die Politik - so wie auch das organisierte Verbrechen und die großen Weltreligionen - unter Kontrolle haben, sind die Erfolgschancen dieses Weges doch eher zweifelhaft.\nEine weitere Möglichkeit besteht darin, dass sich alle Bielefeldzweifler treffen und gemeinsam durch transzendentale Meditation (TM) soviel positive Ausstrahlung erzeugen, dass der Schwindel auffliegt. Eine ähnliche Vorgehensweise hat in Washington, D.C. für eine Senkung der Verbrechensrate um über 20% gesorgt. Besonders effektiv ist dies im Zusammenwirken mit Hopi-Kerzen im Ohr und Yogischem Schweben.\nAb und zu nimmt in einer der eigentlich von IHNEN kontrollierten Zeitungen ein Redakteur allen Mut zusammen und riskiert es, in einer der Ausgaben zumindest andeutungsweise auf die Verschwörung hinzuweisen. So wurde in der FAZ Bielefeld als “Die Mutter aller Un-Städte” bezeichnet, und die taz überschrieb einen Artikel mit “Das Bermuda-Dreieck bei Bielefeld”. Auf Nachfrage bekommt man dann natürlich zu hören, das habe man alles ganz anders gemeint, bei der taz hieß es sogar, es hätte in Wirklichkeit “Bitterfeld” heißen sollen, aber für einen kurzen Moment wurden die Leser darauf aufmerksam gemacht, dass mit Bielefeld etwas nicht stimmt. An dem Mut dieser Redakteure, über deren weiteres Schicksal uns leider nichts bekannt ist, sollten wir uns alle ein Beispiel nehmen.\nDas, was wir alle aber für uns im kleinen tun können, ist folgendes: Kümmert euch um die bedauernswerten Opfer der Gehirnwäsche, umsorgt sie, macht ihnen behutsam klar, dass sie einer Fehlinformation unterliegen. Und, bekennt euch alle immer offen, damit SIE merken, dass wir uns nicht länger täuschen lassen: Bielefeld gibt es nicht!!!",
    "crumbs": [
      "Home",
      "Fun",
      "Stuff",
      "Die Bielefeld-Verschwörung"
    ]
  },
  {
    "objectID": "index.html",
    "href": "index.html",
    "title": "Nicole Dresselhaus",
    "section": "",
    "text": "Unsortierte Einsichten und Erfahrungen. Archiviert zum verlinken, späteren Überdenken oder Diskutieren.\nKeine Garantie auf Richtigkeit oder Trollfreiheit :&gt;"
  },
  {
    "objectID": "index.html#letzte-posts",
    "href": "index.html#letzte-posts",
    "title": "Nicole Dresselhaus",
    "section": "Letzte Posts",
    "text": "Letzte Posts"
  },
  {
    "objectID": "Writing/Obsidian-RAG.html",
    "href": "Writing/Obsidian-RAG.html",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "",
    "text": "Der Nutzer verfügt über eine Obsidian-Wissensdatenbank, in der Markdown-Dateien mit typisierten Inhalten (FileClasses wie Person, Projekt, Deliverable etc.) verwaltet werden. Die Notizen enthalten strukturierte YAML-Metadaten (unterstützt durch Plugins wie Metadata Menu) und sind durch viele Wiki-Links miteinander vernetzt. Standardisierte Templates (via Templater) sorgen dafür, dass z.B. Personenseiten immer ähnliche Felder (Name, ORCID, etc.) aufweisen.\nZiel ist es, mithilfe eines Language Models (LLM) wiederkehrende Aufgaben zu erleichtern, zum Beispiel: automatisch YAML-Felder ausfüllen (etwa fehlende ORCID iDs bei Personen ergänzen), neue Entitätsseiten anhand von Templates befüllen oder sinnvolle Verlinkungen zwischen Notizen vorschlagen. Dabei reicht ein tägliches Neu-Einlesen der Obsidian-Daten (via Cronjob o.Ä.) aus – eine Echtzeit-Synchronisation ist optional. Die Obsidian-internen Wikilinks ([[...]]) müssen im LLM-Ausgabeformat nicht unbedingt klickbar sein (es genügt, wenn sie referenziert werden).\nUm diese Funktionen umzusetzen, bieten sich verschiedene technische Ansätze an. Im Folgenden werden fünf Optionen untersucht: (1) Nutzung eines Vektorspeichers für semantische Suche, (2) Aufbau eines Knowledge Graph aus den Notizen, (3) eine Hybrid-Lösung aus Graph und Vektor, (4) Extraktion & Normalisierung der YAML-Metadaten und (5) existierende Tools/Workflows zur Automatisierung. Jede Option wird mit Funktionsweise, Vorund Nachteilen, Aufwand, Integrationsmöglichkeiten (insb. mit lokalen LLMs wie LLaMA, Deepseek, Cogito etc.) sowie konkreten Tool-Empfehlungen dargestellt.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#hintergrund-und-zielsetzung",
    "href": "Writing/Obsidian-RAG.html#hintergrund-und-zielsetzung",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "",
    "text": "Der Nutzer verfügt über eine Obsidian-Wissensdatenbank, in der Markdown-Dateien mit typisierten Inhalten (FileClasses wie Person, Projekt, Deliverable etc.) verwaltet werden. Die Notizen enthalten strukturierte YAML-Metadaten (unterstützt durch Plugins wie Metadata Menu) und sind durch viele Wiki-Links miteinander vernetzt. Standardisierte Templates (via Templater) sorgen dafür, dass z.B. Personenseiten immer ähnliche Felder (Name, ORCID, etc.) aufweisen.\nZiel ist es, mithilfe eines Language Models (LLM) wiederkehrende Aufgaben zu erleichtern, zum Beispiel: automatisch YAML-Felder ausfüllen (etwa fehlende ORCID iDs bei Personen ergänzen), neue Entitätsseiten anhand von Templates befüllen oder sinnvolle Verlinkungen zwischen Notizen vorschlagen. Dabei reicht ein tägliches Neu-Einlesen der Obsidian-Daten (via Cronjob o.Ä.) aus – eine Echtzeit-Synchronisation ist optional. Die Obsidian-internen Wikilinks ([[...]]) müssen im LLM-Ausgabeformat nicht unbedingt klickbar sein (es genügt, wenn sie referenziert werden).\nUm diese Funktionen umzusetzen, bieten sich verschiedene technische Ansätze an. Im Folgenden werden fünf Optionen untersucht: (1) Nutzung eines Vektorspeichers für semantische Suche, (2) Aufbau eines Knowledge Graph aus den Notizen, (3) eine Hybrid-Lösung aus Graph und Vektor, (4) Extraktion & Normalisierung der YAML-Metadaten und (5) existierende Tools/Workflows zur Automatisierung. Jede Option wird mit Funktionsweise, Vorund Nachteilen, Aufwand, Integrationsmöglichkeiten (insb. mit lokalen LLMs wie LLaMA, Deepseek, Cogito etc.) sowie konkreten Tool-Empfehlungen dargestellt.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#vektorbasierter-ansatz-semantic-search-mit-embeddings",
    "href": "Writing/Obsidian-RAG.html#vektorbasierter-ansatz-semantic-search-mit-embeddings",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "1. Vektorbasierter Ansatz: Semantic Search mit Embeddings",
    "text": "1. Vektorbasierter Ansatz: Semantic Search mit Embeddings\n\nPrinzip\nAlle Markdown-Notizen (bzw. deren Inhalt) werden in kleinere Chunks zerlegt und durch einen Embedding-Modell in hochdimensionale Vektoren umgewandelt. Diese Vektoren werden in einem Vektorstore (wie ChromaDB oder Weaviate) gespeichert. Bei Anfragen des LLM (z.B. “Welche Projekte hat Person X?” oder “Erstelle eine neue Organisation XYZ basierend auf ähnlichen Einträgen”), können mittels ähnlichkeitssuche semantisch passende Notiz-Abschnitte abgerufen und dem LLM als Kontext mitgegeben werden (Retrieval-Augmented Generation).\n\n\nImplementierung\nIn der Praxis ließe sich z.B. ein Workflow mit Ollama + Nomic Embeddings + Chroma1 aufbauen. Ollama stellt ein lokales LLM-Serving bereit und bietet auch eine API für Embeddins [1]. Man könnte ein spezialisiertes Embeddin-Modell wie nomic-embed-text verwenden, welches kompakte 1024-dimensionale Textvektoren liefert [1]. Die Notizen des Obsidian Vault würden per Skript täglich eingelesen, in Sinnabschnitte (Chunks) aufgeteilt (z.B. nach Überschriften oder einer festen Token-Länge) und über Ollamas Embedding-API in Vektoren umgewandelt [1]. Diese Vektoren speichert man in einer lokalen DB wie Chroma. Anfragen an das LLM werden dann zunächst an den Vektorstore gestellt, um die relevantesten Notiz-Abschnitte zu finden, welche dann zusammen mit der eigentlichen Frage an das LLM gegeben werden (klassischer RAG-Pipeline). Dieses Verfahren ist vergleichbar mit dem Smart Connections Obsidian-Plugin: Dort wird ebenfalls ein “Text Embedding Model” auf den Vault angewendet, um zu einer Nutzerfrage automatisch thematisch passende Notizen zu finden und dem LLM bereitzustellen [2]. So konnte im Beispiel ein lokales LLaMA-basiertes Modell Fragen zum eigenen Vault korrekt beantworten, indem es zuvor den passenden Ausschnitt (hier: eine Styleguide-Notiz) über Embeddings gefunden hatte [2].\n1 Alle diese Teile laufen bereits individuell in der Arbeitsgruppe bzw. werden schon genutzt.\n\nIntegration mit lokalen LLMs\nEin Vorteil dieses Ansatzes ist, dass er schon heute mit lokalen Open-Source-LLMs funktioniert. Beispielsweise ließ sich in Smart Connections ein lokal gehostetes LLaMA-Model (3B Instruct) via text-generation-webui einbinden [2]. Alternativ kann man auch LLM-as-a-service Tools wie Ollama nutzen, um ein Modell wie Llama 2 bereitzustellen. Die Open-Source-Tools LangChain oder LlamaIndex bieten Module, um Vektorstores anzubinden und mit LLM-Abfragen zu kombinieren – dies kann man auch mit lokal eingebundenen Modellen (z.B. über LlamaCpp oder GPT4All) verwenden. Zahlreiche fertige Projekte demonstrieren dieses Vorgehen: z.B. privateGPT kombiniert LangChain, GPT4All (lokales LLM) und Chroma, um komplett offline Fragen über lokale Dateien zu beantworten [3]. Auch Khoj verfolgt einen ähnlichen Pfad: Es indexiert den Vault und erlaubt semantische Natürliche Sprache Suche über Markdown-Inhalte sowie “ähnliche Notizen finden” [4].\n\n\nLeistung\nDank moderner Embedding-Modelle können semantisch ähnliche Inhalte gefunden werden, selbst wenn die Schlagwörter nicht exakt übereinstimmen. Das löst das in Obsidian bekannte Problem, dass die eingebaute Suche nur exakte Worttreffer findet [5]. Der Ansatz skaliert auch auf größere Wissensbasen; Vektordatenbanken wie Weaviate oder Chroma sind für zehntausende Einträge ausgelegt. Eine tägliche Aktualisierung ist machbar, da nur neue/geänderte Notizen re-embedded werden müssen.\n\n\nNachteile und Aufwand\nDie Einrichtung erfordert mehrere Komponenten. Man benötigt Pipeline-Schritte für das Chunking, Embedding und das Handling des Vektorstores – dies bedeutet anfängliche Komplexität und Rechenaufwand [5]. Insbesondere das Generieren der Embeddings kann bei großen Vaults zeitund speicherintensiv sein (je nach Modell und Hardware) [5]. Laufende Kosten sind bei rein lokaler Verarbeitung allerdings kein Thema außer CPU/GPU-Last. Ein potenzieller Nachteil ist, dass rein embeddings-basierte Suche keine strukturierte Abfrage erlaubt – das Modell findet zwar thematisch passende Textpassagen, aber um z.B. eine bestimmte Eigenschaft (wie eine fehlende ORCID) gezielt abzufragen, müsste man dennoch im Text suchen oder zusätzliche Logik anwenden. Das LLM kann aus den gefundenen Texten zwar implizit Fakten entnehmen, hat aber kein explizites Wissen über die Datenstruktur. Zudem können irrelevante Kontextstücke eingebunden werden, wenn das semantische Matching fehlerhaft ist (dies erfordert ggf. Feintuning der Chunk-Größe oder Filtern per Dateityp/-klasse)2.\n2 Und diese Nachteile machen dies zu einem Deal-Breaker. Gerade in Tabellen oder Auflistungen kann der Attention-Mechanismus der LLM schnell zu einem Mischen oder Verwechseln von präsentierten Informationen führen. Besonders kleine Netze (meist bis ~7b) sind hier anfällig.\n\nZusammenfassung – Ansatz 1: Vektordatenbank (Embeddings)\n\n\n\n\n\n\n\n\nDetails\n\n\n\n\nVorgehen\nInhalte aller Markdown-Dateien in semantische Vektoren kodieren (z.B. mit nomic-embed-text ([1])) und in einer Vektor-DB speichern. LLM-Anfragen per Similarity Search mit relevantem Kontext anreichern.\n\n\nStärken\nSemantische Suche (findet thematisch passende Infos, nicht nur exakte Worttreffer) [5]. Skaliert auf große Textmengen. Bereits heute mit lokalen LLMs erprobt (z.B. Smart Connections Plugin) [2]. Gut geeignet für Q&A, Textzusammenfassungen und Link-Vorschläge basierend auf Ähnlichkeit.\n\n\nSchwächen\nKomplexeres Setup (Embedding-Model + DB + Pipeline) [5]. Hoher Rechenaufwand für Embeddings bei großen Vaults. Kein explizites Modell von Beziehungen/Metadaten – strukturierte Abfragen (z.B. “zeige alle Personen ohne ORCID”) nur mit Zusatzlogik. Kontexttreffer können ungenau sein (erfordert ggf. Feinjustierung).\n\n\nIntegrations-Optionen\nLokale LLM-Einbindung möglich (z.B. LLaMA 2 über Ollama-API). Tools: ChromaDB, Weaviate oder FAISS als Vektorstore; LangChain/LlamaIndex für Pipeline-Management. Obsidian-Plugins: Smart Connections (komplett integriert mit lokalem Embedder+LLM) [2]; Khoj (separater Suchassistent mit Embeddings) [4].\n\n\n\n\n\n\n\n\ngraph LR\n    A[Obsidian Vault] --&gt; B[Chunking]\n    B --&gt; C[Embedding Model]\n    C --&gt; D[(Vector Database)]\n    E[User Query] --&gt; F[Query Embedding]\n    F --&gt; G[Similarity Search]\n    D --&gt; G\n    G --&gt; H[Relevant Chunks]\n    H --&gt; I[LLM]\n    E --&gt; I\n    I --&gt; J[Response]",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#knowledge-graph-ansatz-strukturierte-graphdatenbank",
    "href": "Writing/Obsidian-RAG.html#knowledge-graph-ansatz-strukturierte-graphdatenbank",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "2. Knowledge-Graph-Ansatz: Strukturierte Graphdatenbank",
    "text": "2. Knowledge-Graph-Ansatz: Strukturierte Graphdatenbank\n\nPrinzip\nStatt (oder zusätzlich zu) freiem Text wird der Informationsgehalt des Vaults als Graph modelliert. Jede Notiz entspricht einem Knoten im Graphen (mit Typ-Label gemäß FileClass, z.B. Person, Projekt etc.). Relationen zwischen Notizen – implizit durch Obsidian-Wikilinks gegeben – werden zu expliziten Kanten im Graph (z.B. eine Person “arbeitet in” Organisation, ein Projekt “liefert” ein Deliverable). Auch Metadaten aus YAML können als Knoten oder Properties modelliert werden (z.B. ORCID als Attribut eines Person-Knotens, Tags als Relationen “hat Schlagwort” usw.). Das Ergebnis ist ein Wissensgraph, der ähnlich einem klassischen RDF-Triple-Store oder Neo4j-Property-Graph komplexe Abfragen und Analysen ermöglicht.\n\n\nErstellung des Graphen\nEine Möglichkeit ist die Obsidian-Daten nach RDF zu exportieren. So beschreibt Pavlyshyn (2023) ein Verfahren, einen Vault ins RDF-Format zu überführen, um “komplexe Abfragen mit klassischen Semantic-Tools” zu ermöglichen [6]. Alternativ kann man direkt in einer Graphdatenbank wie Neo4j modellieren. Ein Community-Plugin (obsidian-neo4j-stream) hat beispielsweise versucht, den Obsidian-Linkgraph in Neo4j importierbar zu machen [7]. Konkret würde man pro Markdown-Datei einen Node mit dessen YAML-Feldern als Properties anlegen. Bestehende Wiki-Links zwischen Dateien werden als ungerichtete oder gerichtete Edges abgebildet (hier kann man, sofern man mehr Semantik will, Link-Typen einführen – z.B. im Text [[Albert Einstein|Autor]] könnte der Alias “Autor” als Kanten-Label genutzt werden). Da Obsidian standardmäßig keine typisierten Kanten unterstützt, bleiben Relationstypen begrenzt – Plugins wie Juggl oder Graph-Link-Types erlauben allerdings das Hinzufügen von Link-Metadaten, was für eine genauere Graph-Modellierung hilfreich sein könnte [8]. YAML-Inhalte, die auf andere Notizen referenzieren, können ebenfalls als Kanten kodiert werden (Beispiel: In einer Projekt-Notiz listet das YAML-Feld team: mehrere Personen – diese Verweise werden im Graph als Kanten Projekt —hatTeam→ Person umgesetzt). Nicht referenzielle Metadaten (etwa ein ORCID-Wert) bleiben einfach als Datenfeld am Knoten.\n\n\nNutzung für LLM-Aufgaben\nEin solcher Graph erlaubt strukturierte Abfragen und Schlussfolgerungen. Für wiederkehrende Aufgaben kann man den Graph gezielt auswerten. Beispielsweise ließen sich “alle Personen ohne ORCID” mittels einer einfachen Graph-Query ermitteln. Das LLM könnte diese Liste als Input erhalten und dann (ggf. mittels Tools oder Wissensbasis) die fehlenden IDs ergänzen. Auch Link-Vorschläge können aus dem Graph gezogen werden: Durch Graph-Analysen wie das Finden von gemeinsamen Nachbarn oder kürzesten Pfaden entdeckt man Verbindungen, die im Vault noch nicht als direkte Links existieren. So könnte man z.B. feststellen, dass zwei Personen an vielen gleichen Meetings teilgenommen haben und dem Nutzer vorschlagen, diese Personen direkt miteinander zu verknüpfen. Oder man erkennt durch link prediction Algorithmen neue mögliche Beziehungen. Forschung und Community sehen hier großes Potential: Eine AI-gestützte Graphanalyse kann helfen, verborgene Zusammenhänge im eigenen Zettelkasten zu finden [9]. Mit Graph-basiertem Reasoning ließe sich sogar neues Wissen entdecken oder logisch konsistente Antworten generieren [8] – etwas, das rein embeddings-basierte Ansätze so nicht leisten.\n\n\nIntegration mit LLMs\nDie Integration eines Graphen erfordert meist eine Zwischenschicht. Ein LLM kann nicht direkt “in” einer Neo4j-Datenbank suchen, aber man kann ihm eine Schnittstelle anbieten. Zwei Strategien sind denkbar:\n\nVerbalize & Prompt: Informationen aus dem Graph gezielt ins Prompt einbetten. Z.B. könnte man bei einer Frage wie “In welcher Organisation arbeitet Alice?” erst eine Graphdatenbank-Anfrage (z.B. in Cypher oder SPARQL) ausführen und das Ergebnis (etwa: “Alice – arbeitetBei → AcmeCorp”) in Textform dem Modell vorgeben, bevor es antwortet. Solche Abfragen könnte ein LLM theoretisch sogar selbst generieren (LangChain bietet z.B. Agents, die Cypher-Queries formulieren und ausführen können). Für definierte Use-Cases kann man aber auch feste Query-Vorlagen verwenden.\nLLM-in-the-Loop Graph Reasoning: Neuere Libraries wie LlamaIndex ermöglichen es, LLMs als Reasoner über Graphen einzusetzen. Der Graph wird dabei intern z.B. als Tripel-Liste gehalten, und das LLM kann mittels promptbasierter Logik Kettenschlüsse durchführen. Allerdings muss der Graph dafür in das Prompt passen (bei sehr vielen Knoten unrealistisch) – es ist also eher für Teilgraphen oder summarische Beziehungen geeignet3.\n\n3 Via ‘Tool Use’ in Modernen LLM könnte das LLM selbst eine Suche auslösen und so den Teilgraphen wählen. Aber alleine die formulierung der Suche führt dann direkt zu dem hybriden Ansatz unten.Eine andere interessante Möglichkeit ist die Nutzung graphbasierter KI-Modelle (Graph Neural Networks o.ä.), die aber in unserem Kontext (persönlicher Vault) noch experimentell sind. Erwähnenswert ist z.B. MyKin.ai, ein Projekt, das einen privaten KI-Assistenten baut, der gemeinsam mit dem Nutzer einen persönlichen Wissensgraphen aufbaut und nutzt [8]. Hier übernimmt die KI das “heavy lifting” der Graph-Pflege, während der Nutzer chattet – ein hybrider Ansatz aus Conversation und Graphaufbau. Für unseren Anwendungsfall wäre jedoch eher ein statischer Graph sinnvoll, den wir periodisch aktualisieren.\n\n\nZusammenfassung - Ansatz 2: Graphdatenbank\n\n\n\n\n\n\n\n\nDetails\n\n\n\n\nVorgehen\nKonvertiere Vault-Inhalte in einen strukturierten Graphen (Knoten = Notizen/Entitäten; Kanten = Obsidian-Links oder abgeleitete Relationen). Nutzen von Graph-DB (Neo4j, RDF-Store) für Abfragen und Analysen.\n\n\nStärken\nExplizite Struktur: ermöglicht genaue Abfragen, z.B. Finde fehlende Werte oder alle Verknüpfungen eines Knotens auf einen Blick. Logische Inferenzen möglich (Graph Reasoning) – unterstützt Link-Empfehlungen und Konsistenzprüfungen [8]. Gute Ergänzung zu YAML-Typisierung: FileClass-Struktur wird vollständig nutzbar. Persistenz: Graph kann unabhängig von Obsidian analysiert, versioniert, mit anderen Daten gemappt werden (z.B. ORCID-Abgleich via externen Datensatz).\n\n\nSchwächen\nErheblicher Initialaufwand: Datenmodell entwerfen, Export-Skripte schreiben oder Tools einrichten [7]. Keine fertige Out-of-the-box-Lösung für Obsidian↔︎Graph (bislang nur Ansätze in der Community). Laufende Synchronisation nötig (Vault-Änderungen -&gt; Graph-Update). Die LLM-Integration ist komplexer – erfordert Query-Tool oder das Einbetten von Graph-Daten ins Prompt. Für offene Fragen (Freitext) allein nicht ausreichend, da der Graph primär Fakten repräsentiert, nicht Fließtext.\n\n\nIntegrations-Optionen\nNeo4j (mit APOC/Neosemantics für RDF-Unterstützung) eignet sich für Property-Graph-Modell; Apache Jena oder GraphDB für RDF-Triple-Store. LangChain bietet Memory/Agent, um Wissensgraphen abzufragen (z.B. ConversationKGMemory). LlamaIndex hat einen KnowledgeGraphIndex, der Tripel extrahieren und durchs LLM traversieren kann. Diese Lösungen sind aber noch experimentell. Evtl. Kombination mit Obsidian-Plugin: Ein früher Plugin-Prototyp streamte Obsidian-Daten nach Neo4j [7] – dieser könnte als Ausgangspunkt dienen.\n\n\n\n\n\n\n\n\ngraph LR\n    A[Obsidian Vault] --&gt; B[Entity Extraction]\n    A --&gt; C[Relationship Extraction]\n    B --&gt; D[Graph Construction]\n    C --&gt; D\n    D --&gt; E[(Graph Database)]\n    F[User Query] --&gt; G[Query Parser]\n    G --&gt; H[Graph Traversal]\n    E --&gt; H\n    H --&gt; I[Structured Facts]\n    I --&gt; J[LLM]\n    F --&gt; J\n    J --&gt; K[Response]\n\n\n\n\n\n\n\n\nFazit Graphdatenbank\nEin Wissensgraph spielt seine Stärken vor allem bei strukturbezogenen Aufgaben aus. Für das automatische Ausfüllen von YAML-Feldern oder das Prüfen von Verlinkungen ist er ideal, da solche Fragen direkte Graphabfragen ermöglichen. Auch für neuartige Verknüpfungen (Link-Vorschläge) lässt sich ein Graph analytisch nutzen (z.B. “Link Prediction” auf Basis von Graph-Nachbarschaft). Allerdings ist die Umsetzung deutlich komplexer als beim Vektorstore, und viele RAG-Anwendungsfälle (Zusammenfassungen, inhaltliche Q&A) erfordern trotzdem den Rückgriff auf die eigentlichen Texte – was wiederum den Vektoransatz benötigt. Daher bietet sich oft eine Kombination beider Methoden an.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#hybrid-ansatz-kombination-aus-graph-und-vektor-rag",
    "href": "Writing/Obsidian-RAG.html#hybrid-ansatz-kombination-aus-graph-und-vektor-rag",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "3. Hybrid-Ansatz: Kombination aus Graph und Vektor-RAG",
    "text": "3. Hybrid-Ansatz: Kombination aus Graph und Vektor-RAG\nDieser Ansatz versucht, semantische Textsuche und strukturierte Graph-Abfragen zu vereinen, um die Vorteile beider Welten auszuschöpfen. In der Praxis gibt es mehrere Möglichkeiten, wie ein hybrides System ausgestaltet sein kann:\n\nParallelbetrieb mit separaten Pipelines: Vektorstore und Knowledge Graph werden beide gepflegt. Je nach Anfrage oder Teilaufgabe wird das eine oder andere genutzt. Beispiel: Für eine Q&A-Frage holt das System erst relevante Text-Passagen via Vektorstore, und prüft zusätzlich im Graph, welche Entitäten darin vorkommen und ruft deren Beziehungen ab. Das LLM bekäme dann sowohl inhaltliche Ausschnitte als Kontext als auch strukturierte Fakten (z.B. “Alice arbeitetBei AcmeCorp”) als Knowledge-Panel. Für die Aufgabe Link-Vorschläge könnte das System sowohl einen Embedding-Vergleich zwischen Notizen nutzen (um thematisch ähnliche Notes zu finden), als auch den Graphen auswerten (um strukturell nahe, aber unverbundene Knoten zu entdecken). Die finalen Vorschläge wären die Schnittmenge bzw. Union beider Methoden – das erhöht Präzision und Reichweite der Empfehlungen.\nIntegration innerhalb einer Datenplattform: Moderne Vector-Datenbanken wie Weaviate erlauben es, semantische Vektorsuche mit symbolischen Filtern zu kombinieren. Man kann Objekte (hier: Notizen) mit ihren strukturierten Feldern in Weaviate speichern und neben dem Vektorindex auch die Metadaten abfragen. Z.B. könnte man eine Query formulieren: “Gib mir die 5 ähnlichsten Notizen zu [Text], die vom Typ Projekt sind und nach 2020 erstellt wurden.” – Weaviate würde erst nach Ähnlichkeit filtern, dann die Metadaten-Bedingungen anwenden. So eine hybride Suche könnte man nutzen, um etwa bei Template-Befüllung nur vergleichbare Objekte zum Prompt hinzuzufügen (z.B. nur andere Organisationen, keine Meeting-Notizen). Auch ChromaDB arbeitet an Feature-Filterfunktionen, die so etwas erlauben würden. Alternativ kann man den Graphen selbst mit Vektor-Embeddings anreichern: Man könnte jedem Knotentyp einen eigenen Vektor zuordnen, der den gesamten Inhalt der zugehörigen Notiz(en) repräsentiert. Diese Vektoren ließen sich im Graphen als Attribut halten und für Ähnlichkeitssuchen zwischen Knoten verwenden (knowledge graph embeddings). Allerdings ist das experimentell – man müsste z.B. bei Kanten-Traversierung dynamisch Nachbarschaftsvektoren kombinieren, was nicht trivial ist.\nLLM als Orchestrator: Hier steuert das LLM, wann welcher Ansatz gezogen wird. Beispielsweise könnte man ein System bauen, in dem das LLM zunächst entscheidet: “Brauche ich strukturiertes Wissen?” Wenn ja, könnte es per Tool-Use einen Graph-Query durchführen (z.B. via Cypher) – eine Technik, die mit LangChain Agents umsetzbar wäre. Danach würde es ggf. einen zweiten Schritt machen: “Benötige ich noch Detailinformationen oder Zitate?” – dann die Vektor-Datenbank abfragen, relevante Textstücke holen, und schließlich alles in einer konsolidierten Antwort formulieren. Dieser agentenbasierte Ansatz ist sehr flexibel, aber auch am anspruchsvollsten in der Implementierung (er erfordert zuverlässig trainierte/verfeinerte LLM-Prompts, die wissen, wann und wie die jeweiligen Werkzeuge zu benutzen sind).\n\n\nVor-/Nachteile\nDie Hybridlösung verspricht maximale Abdeckung der Anwendungsfälle. Strukturierte Fakten und unstrukturierte Inhalte können gemeinsam dem LLM präsentiert werden, was sowohl präzise Faktenkenntnis als auch reichhaltigen Kontext ermöglicht. Gerade für komplexe Aufgaben – etwa das automatisierte Erstellen einer neuen Entitätenseite – wären wohl beide Aspekte wichtig: das LLM müsste sich an vorhandenen ähnlichen Seiten inhaltlich orientieren (Vektorsuche nach ähnlichen Organisations-Beschreibungen) und zugleich korrekte Verknüpfungen setzen (Graph checken, ob z.B. die neue Organisation bereits Personen im Vault hat, die als Mitarbeiter verknüpft werden sollten). Ein solches System könnte also dem Nutzer sehr viel Arbeit abnehmen und dabei konsistente, vernetzte Notizen erzeugen.\nDem steht jedoch ein hoher Architekturund Wartungsaufwand gegenüber. Man muss im Grunde zwei Systeme aufbauen und aktuell halten. Zudem ist die Logik, wie die Ergebnisse zusammenfließen, nicht trivial. Ohne gutes Design kann es passieren, dass der Graph-Teil und der Vektor-Teil widersprüchliche oder redundante Informationen liefern. Auch muss man Performance beachten – doppelte Abfragen kosten mehr Zeit. In vielen Fällen mag auch ein einzelner Ansatz ausreichen, sodass die Zusatzkomplexität nicht immer gerechtfertigt ist.\n\n\nIntegrationsmöglichkeiten\nAuf technischer Seite ist so ein hybrides System durchaus machbar. Beispielsweise ließe sich LlamaIndex verwenden, um unterschiedliche Indexe (VectorIndex, KnowledgeGraphIndex) zu kombinieren – es gibt Konzepte wie “Composable Indices”, mit denen man hierarchische Abfragen bauen kann. So könnte man erst den Graph nach relevanten Knoten filtern und dann nur die zugehörigen Dokumente vektor-suchen (oder umgekehrt). Weaviate als All-in-one-Lösung wurde bereits erwähnt. In kleineren Umgebungen kann man auch pragmatisch vorgehen: Ein Python-Skript, das bei bestimmten Fragen zuerst einen Neo4j-Query absetzt und dessen Ergebnis dem LLM als Teil des Prompts voranstellt, während es parallel eine Chroma-Query macht, wäre eine einfache implementierbare Variante.\n\n\nZusammenfassung – Ansatz 3: Hybrid-Lösung\n\n\n\n\n\n\n\n\nDetails\n\n\n\n\nVorgehen\nKombination beider Ansätze: Pflege einer Graph-Struktur und eines Vektorindex. Nutzung je nach Bedarf – entweder separat oder durch orchestrierte Abfragen, um sowohl strukturiertes Wissen als auch relevante Texte bereitzustellen.\n\n\nStärken\nSehr leistungsfähig – deckt sowohl faktische als auch kontextuelle Fragen ab. Kann die höchste Antwortqualität liefern (Konsistenz durch Graph-Fakten, Detail durch Textauszüge). Hilft, sowohl “Known-item” Suchen (explizite Werte) als auch “Open-ended” Suchen (Texte) zu bedienen. Für Link-Vorschläge ideal: Kombination aus semantischer Ähnlichkeit und Graph-Nachbarschaft erhöht Trefferquote sinnvoll.\n\n\nSchwächen\nSehr komplex in Umsetzung und Wartung. Erfordert doppelte Infrastruktur. Koordination zwischen Graph und Vectorstore nötig – potenziell fehleranfällig. Höhere Latenz durch Mehrfach-Abfragen. Nur lohnend, wenn wirklich vielfältige Aufgaben automatisiert werden sollen; für rein textliche Q&A overkill.\n\n\nIntegrations-Optionen\nWeaviate (Vectors + strukturierte Class-Properties in einem System), oder Kombination aus Neo4j + Chroma. LangChain Agents könnten Graphund Vektor-Tools parallel nutzen. LlamaIndex bietet experimentell kombinierbare Indizes. Workflows müssen sorgfältig entworfen werden (z.B. zuerst Graph-Query, dann Vector-Query auf Untermenge).\n\n\n\n\n\n\n\n\ngraph LR\n    A[Obsidian Vault] --&gt; B[Chunking]\n    A --&gt; C[Entity Extraction]\n    B --&gt; D[Embedding Model]\n    C --&gt; E[Graph Construction]\n    D --&gt; F[(Vector Database)]\n    E --&gt; G[(Graph Database)]\n    H[User Query] --&gt; I[Query Embedding]\n    H --&gt; J[Graph Query]\n    I --&gt; K[Vector Search]\n    J --&gt; L[Graph Traversal]\n    F --&gt; K\n    G --&gt; L\n    K --&gt; M[Text Context]\n    L --&gt; N[Structured Facts]\n    M --&gt; O[Combined Context]\n    N --&gt; O\n    H --&gt; P[LLM]\n    O --&gt; P\n    P --&gt; Q[Enriched Response]\n\n\n\n\n\n\n\n\nFazit Hybrid-Lösung\nDie Hybrid-Lösung ist die ambitionierteste, aber auch zukunftsträchtigste Option. Sie empfiehlt sich, wenn sowohl inhaltliche Assistenz (Texte zusammenfassen, beantworten) als auch datenbankartige Operationen (Felder validieren, Beziehungen auswerten) gefragt sind – was hier der Fall ist. Oft kann man auch schrittweise vorgehen: zunächst mit einem Vektor-RAG starten (geringerer Aufwand) und dann gezielt Graph-Features ergänzen, sobald z.B. Link-Empfehlungen oder Konsistenzprüfungen wichtiger werden.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren",
    "href": "Writing/Obsidian-RAG.html#datenaufbereitung-yaml-metadaten-extrahieren-und-normalisieren",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "4. Datenaufbereitung: YAML-Metadaten extrahieren und normalisieren",
    "text": "4. Datenaufbereitung: YAML-Metadaten extrahieren und normalisieren\nUnabhängig vom gewählten Retrieval-Ansatz ist es essenziell, die in YAML front matter steckenden strukturierten Informationen effektiv zu nutzen. Die Obsidian-Plugins Metadata Menu und Templater stellen sicher, dass viele wichtige Daten bereits sauber in den Notizen vorliegen (z.B. hat eine Personenseite Felder wie fullname, birthdate, ORCID usw.). Ein LLM könnte zwar theoretisch auch direkt im Markdown nach diesen Mustern suchen, aber es ist deutlich effizienter, die Daten einmalig zu extrahieren und in einer leichter nutzbaren Form vorzuhalten.\n\nExtraktion\nEin möglicher Schritt im täglichen Refresh ist ein Skript, das alle Dateien durchläuft und die YAML-Blöcke parst (z.B. mit einem YAML-Parser in Python oder JavaScript). Die extrahierten Felder können dann in eine normale Datenbank (SQLite/CSV/JSON) oder direkt als Knoten/Properties in den Knowledge Graph überführt werden. Damit erhält man z.B. eine Tabelle aller Personen mit ihren ORCID-IDs, eine Liste aller Projekte mit Start-/Enddatum etc.\n\n\nNormalisierung\nOft müssen die Rohwerte etwas vereinheitlicht werden. Beispielsweise sollten Datumsangaben ein konsistentes Format haben, Personennamen evtl. in Vor-/Nachname zerlegt, und fehlende Felder explizit als null markiert werden. Außerdem kann man hier foreign-key-Bezüge auflösen: Wenn z.B. im YAML einer Publikation author: \"[[Doe, John]]\" steht, könnte das Skript erkennen, dass dies die Person mit UID XYZ ist, und entsprechend in der extrahierten Struktur statt des Link-Codes einen eindeutigen Verweis (auf die Person John Doe) speichern. Diese Normalisierung erleichtert nachfolgende Analysen enorm – insbesondere kann man einfache Regeln ableiten, die dann vom LLM geprüft oder genutzt werden. Zum Beispiel: “Wenn person.ORCID leer ist, schlage vor, ihn zu ergänzen” – das kann das LLM dann direkt als Aufforderung bekommen. Oder: “Beim Erstellen einer neuen Person fülle Felder X,Y nach Vorlage aus” – hier weiß man aus der YAML-Definition bereits, welche Felder existieren müssen.\n\n\nNutzung durch LLM\nDer aufbereitete YAML-Datensatz kann auf zwei Weisen eingebunden werden:\n\nInline im Prompt: Für bestimmte Aufgaben kann man dem LLM direkt Ausschnitte aus dieser strukturierten Sammlung geben. Etwa: “In unserer Datenbank fehlt für Person[42] der ORCID. Hier ist eine Liste aller Personennamen mit ORCID, finde anhand des Namens den passenden ORCID und trage ihn ein.” – Falls die Person woanders erwähnt wurde, könnte das Modell es herausfinden. (Eher unwahrscheinlich ohne Internetzugriff – ORCID erfordert eher einen API-Call, aber zumindest könnte das LLM erkennen, dass es fehlt und ggf. den Nutzer nach der ID fragen). Für Link-Empfehlungen könnte man dem LLM eine Liste aller Titel geben – oder besser direkt die Graph-Info wie “Person A und Person B haben 3 gemeinsame Projekte” – siehe Hybrid-Ansatz.\nProgrammatisch außerhalb des LLMs: Viele Routineaufgaben lassen sich erkennen, ohne das LLM zu bemühen. Man könnte einen Teil der Automatisierung rein mit Skripten vorab erledigen. Z.B. neue Links: Ein Skript könnte alle Personennamen im Fließtext durchsuchen und prüfen, ob sie bereits als [[Link]] markiert sind; wenn nicht, die Stelle hervorheben und dem LLM als “Kandidat für Verlinkung” präsentieren. Oder bei einer neuen Organisation könnten automatisch Felder aus externen APIs gezogen und ins Template eingetragen werden (sofern erlaubt). Das LLM hätte dann eher die Rolle, die zusammengestellten Infos in schönen Prosa-Text zu gießen, anstatt die Fakten selbst zu suchen.\n\n\n\nBeispiel-Workflows\n\nYAML-Exports lassen sich mit vorhandenen Tools unterstützen. Es gibt z.B. das Obsidian-Plugin Dataview, welches Abfragen auf YAML ermöglichen kann – allerdings nur innerhalb Obsidian. Man könnte aber ein Dataview JS-Skript4 schreiben, das alle Einträge eines Typs ausgibt, und diese Output-Datei dann weiterverarbeiten. Alternativ direkt auf Dateisystemebene arbeiten: Python mit os und pyyaml kann alle .md Files scannen.\nDie extrahierten Daten kann man mit dem Graph-Ansatz koppeln: etwa alle Personen ohne ORCID als Cypher-Query generieren lassen und automatisch in eine “ToDo”-Liste (Obsidian Note) schreiben, die vom LLM oder Nutzer geprüft wird.\nDurch Templates sind die Felder pro FileClass ja bekannt. Diese Knowledge kann ins Prompt fließen: “Eine Organisation hat die Felder Name, Typ, Beschreibung, Mitarbeiter, etc. Fülle basierend auf den folgenden Infos…” Das Modell weiß dann genau, welche YAML-Spalten es ausgeben soll.\n\n4 oder Plugins wie Dataview-Publisher benutzen, die die Ergebnisse als Markdown-Tabell in ein Dokument schreiben\n\nVor- & Nachteile\nDie Vorteile der strukturierten Extraktion liegen auf der Hand – Performance und Präzision. Man muss nicht jedes Mal den gesamten Markdown-Text durchsuchen, um z.B. den Wert eines bestimmten Feldes zu finden; man hat ihn direkt. Außerdem reduziert es die Abhängigkeit vom LLM für einfache Aufgaben (Daten finden, vergleichen). Für die meisten Menschen ist es auch leichter zu verstehen und zu prüfen, wenn man z.B. eine CSV mit allen ORCIDs hat, als wenn man dem LLM blind glauben muss.\nAls Nachteil kann gesehen werden, dass es zusätzlicher Implementierungsaufwand ist und eine gewisse Duplizierung der Daten (die YAML-Inhalte leben dann in zwei Formen: im Markdown und in der extrahierten Sammlung). Die Synchronisation muss bei Änderungen immer gewährleistet sein (Cronjob). Allerdings ist das, verglichen mit dem Aufwand der LLM-Integration, relativ gering und gut automatisierbar.\n\n\n\n\n\ngraph LR\n    A[Obsidian Vault] --&gt; B[FileClass Detection]\n    B --&gt; C[Type-Specific Extraction]\n    C --&gt; D[YAML Parser]\n    D --&gt; E[Data Validation]\n    E --&gt; F[Type Normalization]\n    F --&gt; G[(Typed Collections)]\n    H[Task Request] --&gt; I[Schema Lookup]\n    I --&gt; J[Targeted Data Fetch]\n    G --&gt; J\n    J --&gt; K[Context Assembly]\n    H --&gt; K\n    K --&gt; L[LLM Processing]\n    L --&gt; M[Schema-Aware Output]\n\n\n\n\n\n\n\n\nZusammenfassung – Ansatz 4: Extraktion\nIn jedem Fall sollte man eine Pipeline vorsehen, die die YAML-Metadaten extrahiert und in eine strukturierte Form bringt. Diese bildet das Rückgrat für den Knowledge-Graph-Ansatz (ohne diese wären die Knoten nackte Titel ohne Attribute) und ist auch für Vektor-RAG nützlich (z.B. als Filter oder zur post-processing der LLM-Antworten). Insbesondere dank der FileClass-Typisierung im Vault kann man hier sehr zielgerichtet vorgehen – etwa nur definierte Entitätstypen verarbeiten. In Community-Diskussionen wurde vorgeschlagen, YAML-Metadaten zu nutzen, um AI-Aufgaben einzuschränken: z.B. NER-Modelle nur auf bestimmten Notizen laufen zu lassen, die laut YAML einen bestimmten Typ haben [9]. Solche Optimierungen werden durch saubere strukturelle Aufbereitung erst möglich.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#automatisierungstools-und-workflows",
    "href": "Writing/Obsidian-RAG.html#automatisierungstools-und-workflows",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "5. Automatisierungstools und Workflows",
    "text": "5. Automatisierungstools und Workflows\nFür die Umsetzung der oben beschriebenen Ansätze gibt es bereits einige Tools, Projekte und Best Practices, die man nutzen oder von denen man lernen kann. Hier eine strukturierte Übersicht samt Empfehlungen:\n\nObsidian-Plugins (In-App KI-Features)\n\nSmart Connections: Plugin, das innerhalb Obsidian mit lokalen Embeddings arbeitet, um ähnliche Notizen zu finden, und einen Chatbot bereitstellt. Es kann ein lokales LLM (oder OpenAI API) einbinden und versorgt es automatisch mit Kontext aus dem Vault [2]. Vorteil: einfache Installation, enge Vault-Integration (Antworten können direkt als Notiz eingefügt werden). Nachteil: begrenzt anpassbar – der Workflow ist vordefiniert (hauptsächlich Q&A Chat). Für den Start aber exzellent, um ein Gefühl für RAG im eigenen Vault zu bekommen.\nKhoj: Ein Open-Source Projekt, bestehend aus einem lokalen Backend [4] und Obsidian-Plugin. Ermöglicht natürliche Sprachsuche und Chat über die eigenen Notizen [4]. Es kann sowohl online-Modelle (GPT-4 etc.) als auch lokale Modelle nutzen [10]. Khoj fokussiert auf schnelle semantische Suche; der Chat-Teil ist vor allem QA-orientiert. Als persönlicher Suchassistent ist es sehr interessant – etwa um via Obsidian Command Palette Fragen ans Vault zu stellen. Es ist weniger darauf ausgelegt, automatisch Links zu erzeugen oder YAML zu verändern (dafür wäre wiederum ein LLM mit Schreibrechten nötig).\nObsidian Copilot / GPT-Assistant: Es existieren mehrere Plugins, die GPT-3/4 in Obsidian integrieren (teils auch lokal via LLaMA). Diese sind im Prinzip UI-Verbesserungen, um das LLM “im Editor” zu nutzen. Für RAG kann man sie einsetzen, indem man manuell Kontext reinkopiert, aber automatisches Retrieval bieten sie nicht ohne weiteres.\nObsidian Neo4j Plugin (Experimentell): Das erwähnte obsidian-neo4j-stream von @HEmile [7] könnte als Ausgangspunkt dienen, falls man die Graph-Route ausprobieren will. Es war dazu gedacht, den Vault als kontinuierlichen Stream in Neo4j zu spiegeln. Leider wurde es nicht fertiggestellt/maintained. Dennoch ließe sich der Code evtl. anpassen, um zumindest einmalig einen Export durchzuführen. Alternativ: Im Obsidian-Forum gibt es auch Beispiele, wie man mit ein paar Skriptzeilen alle Links extrahieren kann. Zusammen mit den YAML-Daten könnte man so einen Basic-Graphen schon bekommen.\n\n\n\nExterne Anwendungen / Skripte\n\nLlamaIndex (GPT Index): Diese Python-Bibliothek ist eine Schweizer Taschenmesser für RAG. Man kann Dokumente laden (Markdown wird unterstützt), unterschiedliche Indizes erstellen (Vector, List, KnowledgeGraph etc.) und Abfragen mit LLM orchestrieren. Sie eignet sich, um schnell Prototypen zu bauen. Beispielsweise könnte man einen KnowledgeGraphIndex erstellen, der mittels Instruct-LLM Tripel aus den Notizen extrahiert (z.B. “Person X – arbeitet für – Organisation Y”). Anschließend kann man Abfragen in natürlicher Sprache stellen, die vom LLM in Graph-Traversals übersetzt werden. Oder man nutzt den simpleren VectorIndex auf Markdown-Chunks. LlamaIndex kann auch Komposition: man könnte pro FileClass einen Index bauen (z.B. alle Personen in einem VectorIndex, alle Projekte in einem anderen) und dann einen übergeordneten Query laufen lassen. Diese Flexibilität ist mächtig – aber es erfordert eben etwas Programmierung. Für einen produktiven Workflow (täglicher Cronjob) müsste man ein eigenes Python-Skript schreiben, das die Indizes aktualisiert.\nLangChain: Ein Framework v.a. für komplexere Chains und Agenten. Es liefert Bausteine, um z.B. eine Tool-using Agent zu bauen, die mit einer Vector DB Suche und einer Graph-DB Abfrage als Tools ausgestattet ist. Damit ließe sich ein Dialogsystem kreieren, das je nach Frage entscheidet, ob es den Neo4j-Graph oder den Chroma-Vektorindex konsultiert. Allerdings setzt dies einiges an Prompt Engineering voraus, damit der Agent zuverlässig funktioniert. Alternativ kann man LangChain auch einfach nutzen, um entweder Vector-search oder Graph-DB-Queries einzeln bequemer zu machen (es gibt z.B. vorgefertigte Neo4j Retriever-Klassen etc.).\nHaystack: Das von deepset (evtl. in der Frage mit “Deepseek” gemeint) entwickelte Open-Source-Toolkit Haystack ist ebenfalls auf Dokumenten-QA spezialisiert. Es unterstützt das Indexieren von Markdown, verschiedene Vector-Backends und kann auch Knowledge-Graph-Komponenten integrieren. Zudem hat es Pipeline-Knoten zum z.B. Fragenklassifizieren, dass bestimmte Fragen an bestimmte Reader geleitet werden. Für einen produktiven Einsatz mit lokalem UI ggf. eine Option. Allerdings eher heavy-weight und auf QA fokussiert, weniger auf Wissensbasis-Pflege.\nprivateGPT / llama.cpp based scripts: Für einfache Frage-Antwort-Systeme auf dem eigenen Vault kann man vorhandene Lösungen wie privateGPT oder GPT4All (mit UI) verwenden [3]. Diese bringen einen Großteil der Vector+LLM Pipeline schon fertig mit. Sie indexieren Ordner voller Dokumente (auch Markdown) und erlauben dann Queries an ein lokales Modell. Der Anpassungsspielraum (z.B. andere Tasks als reines QA) ist aber gering. Als Baseline sind sie nützlich – man könnte damit z.B. testen, wie gut ein LLM mit den eingebetteten Obsidian-Notizen Fragen beantwortet, und daraus Anforderungen ableiten.\nBasic Memory (basicmachines): Ein innovativer Ansatz ist hier zu erwähnen: Basic Memory speichert AI-Konversationen als Markdown in Obsidian und baut daraus sukzessive einen semantischen Wissensgraph [11]. D.h. wenn man mit dem LLM chatbasiert arbeitet, erstellt das Tool automatisch Notizen und verbindet sie (z.B. werden erkannte Entitäten verlinkt). Es ist quasi das Gegenstück zu unserem Problem – statt einen bestehenden Vault zu nutzen, erzeugt es einen Vault. Dennoch kann man sich dort Konzepte abschauen: z.B. wie strukturierte Notizen aus LLM-Ausgaben generiert werden können, oder wie man bi-direktional arbeitet (User editiert Notiz, KI liest Änderungen beim nächsten Mal). Basic Memory setzt auf lokale Dateien und betont Privatsphäre, was dem hiesigen Anforderungsprofil ähnelt. Für die konkreten Aufgaben (ORCID-Suche, Link-Vorschlag) liefert es zwar keine fertige Lösung, aber die Idee, KI beim Nutzer Notizen anlegen/ändern zu lassen, ist hier praktisch umgesetzt.\nExterne APIs / Datenquellen:\nFür bestimmte Felder wie ORCID wird ein rein lokales LLM kaum die Werte erraten können, sofern sie nicht schon irgendwo im Vault stehen. Falls Internetzugriff eine Option ist, könnte man ein Plugin oder einen Workflow integrieren, der ORCID API Abfragen durchführt (z.B. über den Namen der Person) und die ID zurückliefert. Ein LLM-Agent könnte auch so einen API-Call ausführen (via Tools in LangChain). Alternativ: Alle bekannten ORCID-IDs der eigenen Personen könnte man in einer Datei sammeln; wenn das LLM eine Lücke findet, bittet es den Nutzer um Input. Hier muss man die Limitierungen eines LLM realistisch sehen und ggf. klassische Automatisierung (API-Skripte) kombinieren.\n\n\n\n\n\n\ngraph LR\n    subgraph Obsidian\n    A[Vault] --&gt; B[Plugins]\n    B --&gt; C[Templater]\n    B --&gt; D[Metadata Menu]\n    B --&gt; E[AI Assistant]\n    end\n\n    subgraph External Processing\n    A --&gt; F[Daily Export]\n    F --&gt; G[Data Processing]\n    G --&gt; H[LLM Analysis]\n    H --&gt; I[Automation Scripts]\n    end\n\n    subgraph Integration\n    I --&gt; J[Change Proposals]\n    J --&gt; K[User Review]\n    K --&gt; L[Accepted Changes]\n    L --&gt; M[Vault Updates]\n    M --&gt; A\n    end",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#zusammenfassende-empfehlung",
    "href": "Writing/Obsidian-RAG.html#zusammenfassende-empfehlung",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "Zusammenfassende Empfehlung",
    "text": "Zusammenfassende Empfehlung\nFür einen ersten Prototypen empfiehlt es sich, mit dem Vektorstore-Ansatz (1) zu beginnen, da dieser am schnellsten sichtbare Erfolge bringt. Man kann z.B. mit ChromaDB + einem lokalen LLM experimentieren, oder direkt das Smart-Connections-Plugin ausprobieren, um ein Gefühl für semantische Suche im Vault zu bekommen. Die YAML-Daten sollte man von Anfang an mit-extrahieren (4), da sie die Grundlage für weitere Strukturierungsmaßnahmen bilden. Anschließend kann man gezielt Graph-Features (2) ergänzen: etwa den exportierten Vault in Neo4j laden und ein paar Abfragen formulieren, um Missing Links oder fehlende Felder aufzuspüren. Mittelfristig dürfte eine Kombination (3) notwendig sein, um sowohl Inhalt als auch Struktur abzudecken – dies kann man Schritt für Schritt angehen (z.B. zunächst Vector-RAG für inhaltliche Fragen, und separate Tools/Reports für strukturierte Checks; später dann Integration zu einem einheitlichen KI-Assistenten). Unterstützend sollte man vorhandene Tools (5) nutzen, wo möglich – z.B. Khoj für ad-hoc Fragen, oder LlamaIndex für schnelle Implementierung von Prototypen. Generell gilt: lokale LLMs sind inzwischen leistungsfähig genug für solche Aufgaben, wie die genannten Beispiele zeigen (Chat mit Vault über LLaMA etc.). Wichtig ist es, die Vault-Organisation konsequent weiterzuführen (FileClasses, Templates), da ein sauber strukturiertes Wissen die Grundlage für jede erfolgreiche RAG-Lösung ist – egal ob Vektor, Graph oder hybrid.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#quellen",
    "href": "Writing/Obsidian-RAG.html#quellen",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "Quellen",
    "text": "Quellen\nDie Analyse basiert auf aktuellen Erkenntnissen aus der Obsidian-Community und KI-Fachwelt, u.a. Erfahrungen mit semantischer Suche [2], Diskussionen zu Knowledge Graphs in PKM [9] und Berichten über lokale RAG-Implementierungen [2].",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  },
  {
    "objectID": "Writing/Obsidian-RAG.html#methodik-llms-als-autoren",
    "href": "Writing/Obsidian-RAG.html#methodik-llms-als-autoren",
    "title": "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze",
    "section": "Methodik / LLMs als ‘Autoren’",
    "text": "Methodik / LLMs als ‘Autoren’\n\nErstellt wurde der initial draft mittels Websuche und “Deep-Research” von gpt-4.5 (preview). Systematische Überarbeitungen (Extraktion Bibliographie, Überarbeitung Metadaten) mittels cogito-v0.1 im Editor. Übernahme nach manueller Prüfung. Erstellung der Mermaid-Diagramme mittels Claude 3.7 Sonnet. Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting durch Nicole Dresselhaus.",
    "crumbs": [
      "Home",
      "Serious",
      "Writing",
      "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
    ]
  }
]