initial
This commit is contained in:
748
Writing/Obsidian-RAG.qmd
Normal file
748
Writing/Obsidian-RAG.qmd
Normal file
@ -0,0 +1,748 @@
|
||||
---
|
||||
tags:
|
||||
- Writing
|
||||
aliases:
|
||||
- "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
|
||||
cssclasses:
|
||||
- table-wide
|
||||
- table-wrap
|
||||
authors:
|
||||
- name: GPT-4.5
|
||||
url: https://chatgpt.com
|
||||
affiliation:
|
||||
- name: OpenAI
|
||||
url: https://openai.com
|
||||
- name: cogito-v1-preview
|
||||
url: https://www.deepcogito.com/research/cogito-v1-preview
|
||||
affiliation:
|
||||
- name: DeepCogito
|
||||
url: https://www.deepcogito.com
|
||||
- name: Claude 3.7 Sonnet
|
||||
url: https://claude.ai
|
||||
affiliation:
|
||||
- name: Antrhopic
|
||||
url: https://www.anthropic.com
|
||||
- name: Nicole Dresselhaus
|
||||
affiliation:
|
||||
- name: Humboldt-Universität zu Berlin
|
||||
url: https://hu-berlin.de
|
||||
orcid: 0009-0008-8850-3679
|
||||
date: 2025-04-24
|
||||
categories:
|
||||
- Article
|
||||
- RAG
|
||||
- ML
|
||||
fileClass: authored
|
||||
lang: de
|
||||
linter-yaml-title-alias:
|
||||
"RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
|
||||
title: "RAG für eine Obsidian-Wissensdatenbank: Technische Ansätze"
|
||||
bibliography:
|
||||
- obsidian-rag.bib
|
||||
citation-style: springer-humanities-brackets
|
||||
image: ../thumbs/writing_obsidian-rag.png
|
||||
---
|
||||
|
||||
## Hintergrund und Zielsetzung
|
||||
|
||||
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.
|
||||
|
||||
**Ziel** 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).
|
||||
|
||||
Um 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.
|
||||
|
||||
## 1. Vektorbasierter Ansatz: Semantic Search mit Embeddings
|
||||
|
||||
### Prinzip
|
||||
|
||||
Alle 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).
|
||||
|
||||
### Implementierung
|
||||
|
||||
In der Praxis ließe sich z.B. ein Workflow mit **Ollama** + **Nomic
|
||||
Embeddings** + **Chroma**^[Alle diese Teile laufen bereits individuell in der
|
||||
Arbeitsgruppe bzw. werden schon genutzt.] aufbauen. Ollama stellt ein lokales
|
||||
LLM-Serving bereit und bietet auch eine API für Embeddins
|
||||
[@ollama_chroma_cookbook]. Man könnte ein spezialisiertes Embeddin-Modell wie
|
||||
`nomic-embed-text` verwenden, welches kompakte 1024-dimensionale Textvektoren
|
||||
liefert [@ollama_chroma_cookbook]. 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 [@ollama_chroma_cookbook]. 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 [@smart_connections_plugin]. 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 [@smart_connections_plugin].
|
||||
|
||||
### Integration mit lokalen LLMs
|
||||
|
||||
Ein 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 [@smart_connections_plugin]. 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
|
||||
[@second_brain_assistant_with_obsidian]. Auch **Khoj** verfolgt einen ähnlichen
|
||||
Pfad: Es indexiert den Vault und erlaubt semantische **Natürliche Sprache
|
||||
Suche** über Markdown-Inhalte sowie _"ähnliche Notizen finden"_ [@khoj_plugin].
|
||||
|
||||
### Leistung
|
||||
|
||||
Dank 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 [@supercharging_obsidian_search]. 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.
|
||||
|
||||
### Nachteile und Aufwand
|
||||
|
||||
Die 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 [@supercharging_obsidian_search].
|
||||
Insbesondere das Generieren der Embeddings kann bei großen Vaults zeitund
|
||||
speicherintensiv sein (je nach Modell und Hardware)
|
||||
[@supercharging_obsidian_search]. 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)^[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.].
|
||||
|
||||
### Zusammenfassung – Ansatz 1: Vektordatenbank (Embeddings)
|
||||
|
||||
| | **Details** |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Vorgehen** | Inhalte aller Markdown-Dateien in semantische Vektoren kodieren (z.B. mit `nomic-embed-text` ([@ollama_chroma_cookbook])) und in einer Vektor-DB speichern. LLM-Anfragen per Similarity Search mit relevantem Kontext anreichern. |
|
||||
| **Stärken** | _Semantische Suche_ (findet thematisch passende Infos, nicht nur exakte Worttreffer) [@supercharging_obsidian_search]. Skaliert auf große Textmengen. Bereits heute mit lokalen LLMs erprobt (z.B. _Smart Connections_ Plugin) [@smart_connections_plugin]. Gut geeignet für Q&A, Textzusammenfassungen und Link-Vorschläge basierend auf Ähnlichkeit. |
|
||||
| **Schwächen** | Komplexeres Setup (Embedding-Model + DB + Pipeline) [@supercharging_obsidian_search]. 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). |
|
||||
| **Integrations-Optionen** | Lokale 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) [@smart_connections_plugin]; _Khoj_ (separater Suchassistent mit Embeddings) [@khoj_plugin]. |
|
||||
|
||||
```{mermaid}
|
||||
%%| column: screen-inset-right
|
||||
graph LR
|
||||
A[Obsidian Vault] --> B[Chunking]
|
||||
B --> C[Embedding Model]
|
||||
C --> D[(Vector Database)]
|
||||
E[User Query] --> F[Query Embedding]
|
||||
F --> G[Similarity Search]
|
||||
D --> G
|
||||
G --> H[Relevant Chunks]
|
||||
H --> I[LLM]
|
||||
E --> I
|
||||
I --> J[Response]
|
||||
```
|
||||
|
||||
## 2. Knowledge-Graph-Ansatz: Strukturierte Graphdatenbank
|
||||
|
||||
### Prinzip
|
||||
|
||||
Statt (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.
|
||||
|
||||
### Erstellung des Graphen
|
||||
|
||||
Eine 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
|
||||
[@export_obsidian_to_rdf]. 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
|
||||
[@export_to_common_graph_formats]. 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
|
||||
[@personal_knowledge_graphs_in_obsidian]. 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.
|
||||
|
||||
### Nutzung für LLM-Aufgaben
|
||||
|
||||
Ein 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 [@ai_empowered_zettelkasten_with_ner_and_graph_llm]. Mit
|
||||
Graph-basiertem Reasoning ließe sich sogar **neues Wissen entdecken** oder
|
||||
logisch konsistente Antworten generieren
|
||||
[@ai_empowered_zettelkasten_with_ner_and_graph_llm,
|
||||
@personal_knowledge_graphs_in_obsidian] – etwas, das rein embeddings-basierte
|
||||
Ansätze so nicht leisten.
|
||||
|
||||
### Integration mit LLMs
|
||||
|
||||
Die 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:
|
||||
|
||||
1. **Verbalize & 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.
|
||||
1. **LLM-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 geeignet^[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
|
||||
[@personal_knowledge_graphs_in_obsidian]. 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.
|
||||
|
||||
### Zusammenfassung - Ansatz 2: Graphdatenbank
|
||||
|
||||
| | **Details** |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Vorgehen** | Konvertiere 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. |
|
||||
| **Stärken** | Explizite **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 [@ai_empowered_zettelkasten_with_ner_and_graph_llm, @personal_knowledge_graphs_in_obsidian]. 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). |
|
||||
| **Schwächen** | Erheblicher **Initialaufwand**: Datenmodell entwerfen, Export-Skripte schreiben oder Tools einrichten [@export_to_common_graph_formats]. 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 -> 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. |
|
||||
| **Integrations-Optionen** | **Neo4j** (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 [@export_to_common_graph_formats] – dieser könnte als Ausgangspunkt dienen. |
|
||||
|
||||
```{mermaid}
|
||||
%%| column: screen-inset-right
|
||||
graph LR
|
||||
A[Obsidian Vault] --> B[Entity Extraction]
|
||||
A --> C[Relationship Extraction]
|
||||
B --> D[Graph Construction]
|
||||
C --> D
|
||||
D --> E[(Graph Database)]
|
||||
F[User Query] --> G[Query Parser]
|
||||
G --> H[Graph Traversal]
|
||||
E --> H
|
||||
H --> I[Structured Facts]
|
||||
I --> J[LLM]
|
||||
F --> J
|
||||
J --> K[Response]
|
||||
```
|
||||
|
||||
### Fazit Graphdatenbank
|
||||
|
||||
Ein 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.
|
||||
|
||||
## 3. Hybrid-Ansatz: Kombination aus Graph und Vektor-RAG
|
||||
|
||||
Dieser 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:
|
||||
|
||||
- **Parallelbetrieb 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.
|
||||
- **Integration 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.
|
||||
- **LLM 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).
|
||||
|
||||
### Vor-/Nachteile
|
||||
|
||||
Die 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.
|
||||
|
||||
Dem 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.
|
||||
|
||||
### Integrationsmöglichkeiten
|
||||
|
||||
Auf 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.
|
||||
|
||||
### Zusammenfassung – Ansatz 3: Hybrid-Lösung
|
||||
|
||||
| | **Details** |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Vorgehen** | Kombination 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. |
|
||||
| **Stärken** | Sehr **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. |
|
||||
| **Schwächen** | **Sehr 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. |
|
||||
| **Integrations-Optionen** | Weaviate (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). |
|
||||
|
||||
```{mermaid}
|
||||
%%| column: screen-inset-right
|
||||
graph LR
|
||||
A[Obsidian Vault] --> B[Chunking]
|
||||
A --> C[Entity Extraction]
|
||||
B --> D[Embedding Model]
|
||||
C --> E[Graph Construction]
|
||||
D --> F[(Vector Database)]
|
||||
E --> G[(Graph Database)]
|
||||
H[User Query] --> I[Query Embedding]
|
||||
H --> J[Graph Query]
|
||||
I --> K[Vector Search]
|
||||
J --> L[Graph Traversal]
|
||||
F --> K
|
||||
G --> L
|
||||
K --> M[Text Context]
|
||||
L --> N[Structured Facts]
|
||||
M --> O[Combined Context]
|
||||
N --> O
|
||||
H --> P[LLM]
|
||||
O --> P
|
||||
P --> Q[Enriched Response]
|
||||
```
|
||||
|
||||
### Fazit Hybrid-Lösung
|
||||
|
||||
Die 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.
|
||||
|
||||
## 4. Datenaufbereitung: YAML-Metadaten extrahieren und normalisieren
|
||||
|
||||
Unabhä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.
|
||||
|
||||
### Extraktion
|
||||
|
||||
Ein 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.
|
||||
|
||||
### Normalisierung
|
||||
|
||||
Oft 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.
|
||||
|
||||
### Nutzung durch LLM
|
||||
|
||||
Der aufbereitete YAML-Datensatz kann auf zwei Weisen eingebunden werden:
|
||||
|
||||
- **Inline 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.
|
||||
- **Programmatisch 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.
|
||||
|
||||
### Beispiel-Workflows
|
||||
|
||||
- YAML-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-Skript^[oder Plugins wie _Dataview-Publisher_ benutzen, die die Ergebnisse
|
||||
als Markdown-Tabell in ein Dokument schreiben] 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.
|
||||
- Die 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.
|
||||
- Durch 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.
|
||||
|
||||
### Vor- & Nachteile
|
||||
|
||||
Die **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.
|
||||
Als **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.
|
||||
|
||||
```{mermaid}
|
||||
%%| column: screen-inset-right
|
||||
graph LR
|
||||
A[Obsidian Vault] --> B[FileClass Detection]
|
||||
B --> C[Type-Specific Extraction]
|
||||
C --> D[YAML Parser]
|
||||
D --> E[Data Validation]
|
||||
E --> F[Type Normalization]
|
||||
F --> G[(Typed Collections)]
|
||||
H[Task Request] --> I[Schema Lookup]
|
||||
I --> J[Targeted Data Fetch]
|
||||
G --> J
|
||||
J --> K[Context Assembly]
|
||||
H --> K
|
||||
K --> L[LLM Processing]
|
||||
L --> M[Schema-Aware Output]
|
||||
```
|
||||
|
||||
### Zusammenfassung – Ansatz 4: Extraktion
|
||||
|
||||
In 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 [@ai_empowered_zettelkasten_with_ner_and_graph_llm]. Solche Optimierungen
|
||||
werden durch saubere strukturelle Aufbereitung erst möglich.
|
||||
|
||||
## 5. Automatisierungstools und Workflows
|
||||
|
||||
Fü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:
|
||||
|
||||
### Obsidian-Plugins (In-App KI-Features)
|
||||
|
||||
- _Smart 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 [@smart_connections_plugin]. 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.
|
||||
- _Khoj:_ Ein Open-Source Projekt, bestehend aus einem lokalen Backend
|
||||
[@khoj_plugin] und Obsidian-Plugin. Ermöglicht **natürliche Sprachsuche** und
|
||||
Chat über die eigenen Notizen [@khoj_plugin]. Es kann sowohl online-Modelle
|
||||
(GPT-4 etc.) als auch lokale Modelle nutzen
|
||||
[@build_your_second_brain_with_khoj_ai]. 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).
|
||||
- _Obsidian 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.
|
||||
- _Obsidian Neo4j Plugin (Experimentell):_ Das erwähnte _obsidian-neo4j-stream_
|
||||
von \@HEmile [@export_to_common_graph_formats] 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.
|
||||
|
||||
### Externe Anwendungen / Skripte
|
||||
|
||||
- _LlamaIndex (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.
|
||||
- _LangChain:_ 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.).
|
||||
- _Haystack:_ 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.
|
||||
- _privateGPT / 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 [@second_brain_assistant_with_obsidian]. 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.
|
||||
- _Basic 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
|
||||
[@basic_memory_ai_conversations_that_build_knowledge]. 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.
|
||||
- **Externe APIs / Datenquellen:**
|
||||
Fü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.
|
||||
|
||||
```{mermaid}
|
||||
%%| column: screen-inset-right
|
||||
graph LR
|
||||
subgraph Obsidian
|
||||
A[Vault] --> B[Plugins]
|
||||
B --> C[Templater]
|
||||
B --> D[Metadata Menu]
|
||||
B --> E[AI Assistant]
|
||||
end
|
||||
|
||||
subgraph External Processing
|
||||
A --> F[Daily Export]
|
||||
F --> G[Data Processing]
|
||||
G --> H[LLM Analysis]
|
||||
H --> I[Automation Scripts]
|
||||
end
|
||||
|
||||
subgraph Integration
|
||||
I --> J[Change Proposals]
|
||||
J --> K[User Review]
|
||||
K --> L[Accepted Changes]
|
||||
L --> M[Vault Updates]
|
||||
M --> A
|
||||
end
|
||||
```
|
||||
|
||||
## Zusammenfassende Empfehlung
|
||||
|
||||
Fü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.
|
||||
|
||||
## Quellen
|
||||
|
||||
Die Analyse basiert auf aktuellen Erkenntnissen aus der Obsidian-Community und
|
||||
KI-Fachwelt, u.a. Erfahrungen mit semantischer Suche
|
||||
[@smart_connections_plugin], Diskussionen zu Knowledge Graphs in PKM
|
||||
[@ai_empowered_zettelkasten_with_ner_and_graph_llm] und Berichten über lokale
|
||||
RAG-Implementierungen [@local_free_rag_with_question_generation,
|
||||
@smart_connections_plugin].
|
||||
|
||||
## Methodik / LLMs als 'Autoren' {.appendix}
|
||||
|
||||
Erstellt 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.
|
3
Writing/_metadata.yml
Normal file
3
Writing/_metadata.yml
Normal file
@ -0,0 +1,3 @@
|
||||
google-scholar: true
|
||||
execute:
|
||||
enable: false
|
69
Writing/documentation.bib
Normal file
69
Writing/documentation.bib
Normal file
@ -0,0 +1,69 @@
|
||||
@article{wilson2017good,
|
||||
title={Good enough practices in scientific computing},
|
||||
author={Wilson, Greg and Bryan, Jennifer and Cranston, Karen and Kitzes, Justin and Nederbragt, Lex and Teal, Tracy K},
|
||||
journal={PLoS computational biology},
|
||||
volume={13},
|
||||
number={6},
|
||||
pages={e1005510},
|
||||
year={2017},
|
||||
publisher={Public Library of Science}
|
||||
}
|
||||
|
||||
@article{prlic2012ten,
|
||||
title={Ten simple rules for documenting scientific software},
|
||||
author={Prli{\'c}, Andreas and Procter, James B},
|
||||
journal={PLoS Computational Biology},
|
||||
volume={8},
|
||||
number={12},
|
||||
pages={e1002802},
|
||||
year={2012},
|
||||
publisher={Public Library of Science}
|
||||
}
|
||||
|
||||
@article{smith2016software,
|
||||
title={Software citation principles},
|
||||
author={Smith, Arfon M and Katz, Daniel S and Niemeyer, Kyle E and FORCE11 Software Citation Working Group and others},
|
||||
journal={PeerJ Computer Science},
|
||||
volume={2},
|
||||
pages={e86},
|
||||
year={2016},
|
||||
publisher={PeerJ Inc.}
|
||||
}
|
||||
|
||||
@article{maria2019jupyter,
|
||||
title={Jupyter notebooks—a publishing format for reproducible computational workflows},
|
||||
author={Kluyver, Thomas and Ragan-Kelley, Benjamin and P{\'e}rez, Fernando and Granger, Brian and Bussonnier, Matthias and Frederic, Jonathan and Kelley, Kyle and Hamrick, Jessica B and Grout, Jason and Corlay, Sylvain and others},
|
||||
journal={Positioning and Power in Academic Publishing: Players, Agents and Agendas},
|
||||
volume={20},
|
||||
pages={87--90},
|
||||
year={2016},
|
||||
publisher={IOS Press}
|
||||
}
|
||||
|
||||
@misc{endings2020principles,
|
||||
title = {Endings Principles for Digital Longevity},
|
||||
author = {{Endings Project}},
|
||||
year = {2020},
|
||||
url = {https://endings.uvic.ca/principles.html}
|
||||
}
|
||||
|
||||
@article{katz2021open,
|
||||
title={The Journal of Open Source Software (JOSS)},
|
||||
author={Katz, Daniel S and Niemeyer, Kyle E and Smith, Arfon M},
|
||||
journal={PeerJ Computer Science},
|
||||
volume={7},
|
||||
pages={e432},
|
||||
year={2021},
|
||||
publisher={PeerJ Inc.}
|
||||
}
|
||||
|
||||
@article{lamprecht2020towards,
|
||||
title={Towards FAIR principles for research software},
|
||||
author={Lamprecht, Anna-Lena and Garcia, Leyla and Kuzak, Mateusz and Martinez, Carlos and Arcila, Ricardo and Martin Del Pico, Eva and others},
|
||||
journal={Data Science},
|
||||
volume={3},
|
||||
number={1},
|
||||
pages={37--59},
|
||||
year={2020},
|
||||
publisher={IOS Press}
|
||||
}
|
824
Writing/documentation.md
Normal file
824
Writing/documentation.md
Normal file
@ -0,0 +1,824 @@
|
||||
---
|
||||
tags:
|
||||
- Writing
|
||||
cssclasses:
|
||||
- table-wide
|
||||
- table-wrap
|
||||
title:
|
||||
"Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital
|
||||
Humanities)"
|
||||
description: |
|
||||
Ein Überblick und Best Practices für die Dokumantation von Forschungssoftware.
|
||||
abstract: |
|
||||
Diese Dokumentation fasst zusammen, welche wissenschaftlichen Konzepte,
|
||||
Algorithmen und Theorien hinter der Software stehen. Sie dient dazu, den
|
||||
Nutzer*innen zu helfen, die theoretischen Grundlagen nachvollziehbar zu machen.
|
||||
lang: de
|
||||
authors:
|
||||
- name: Nicole Dresselhaus
|
||||
affiliation:
|
||||
- name: Humboldt-Universität zu Berlin
|
||||
url: https://hu-berlin.de
|
||||
email: nicole.dresselhaus@hu-berlin.de
|
||||
correspondence: true
|
||||
orcid: 0009-0008-8850-3679
|
||||
roles:
|
||||
- Conceptualization
|
||||
- Supervision
|
||||
- Validation
|
||||
- "Writing – review & editing"
|
||||
- name: GPT-4.5
|
||||
url: https://chatgpt.com
|
||||
affiliation:
|
||||
- name: OpenAI
|
||||
url: https://openai.com
|
||||
roles:
|
||||
- investigation
|
||||
- "Writing – original draft"
|
||||
date: 2025-05-08
|
||||
categories:
|
||||
- Article
|
||||
- Best Practices
|
||||
citation: true
|
||||
google-scholar: true
|
||||
fileClass: authored
|
||||
bibliography:
|
||||
- documentation.bib
|
||||
image: ../thumbs/writing_documentation.png
|
||||
---
|
||||
|
||||
## Einleitung
|
||||
|
||||
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.
|
||||
|
||||
[Alle Empfehlungen stützen sich auf Literatur und etablierte Richtlinien
|
||||
[@prlic2012ten; @wilson2017good; @katz2021open;
|
||||
@endings2020principles].]{.aside}
|
||||
|
||||
Dieser 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
|
||||
[@endings2020principles]. 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.
|
||||
|
||||
## Inhaltliche Anforderungen an die Dokumentation
|
||||
|
||||
Ein 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:
|
||||
|
||||
### Ziel und Zweck der Software (Statement of Need)
|
||||
|
||||
Beschreiben 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).
|
||||
|
||||
### Input-/Output-Spezifikation und Datenbeschreibung
|
||||
|
||||
Dokumentieren 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.”_]{.aside}
|
||||
|
||||
### Code-Abhängigkeiten und technische Voraussetzungen
|
||||
|
||||
Listen 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.]{.aside}
|
||||
|
||||
- **Typische 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).
|
||||
[Faustregel: **Zeigen statt nur beschreiben** – konkrete Anwendungsfälle in
|
||||
der Doku verankern.]{.aside}
|
||||
|
||||
### Wissenschaftlicher Hintergrund und theoretischer Kontext
|
||||
|
||||
Da 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).]{.aside}
|
||||
|
||||
### Bekannte Limitationen, Annahmen und Fehlermeldungen
|
||||
|
||||
Geben 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.
|
||||
|
||||
### Weiterentwicklung und Beitragsmöglichkeiten
|
||||
|
||||
Obwohl 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.]{.aside}
|
||||
|
||||
### Projekt-Metadaten (Lizenz, Zitation, Version)
|
||||
|
||||
Teil 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[@smith2016software]. 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.
|
||||
|
||||
### Zusammenfassung der inhaltlichen Anforderungen
|
||||
|
||||
Zusammengefasst 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).
|
||||
|
||||
## Format und Struktur der Dokumentation
|
||||
|
||||
Fü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:
|
||||
|
||||
### `README.md` als zentrales Dokument
|
||||
|
||||
Die 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).
|
||||
|
||||
### Strukturierte Unterteilung in weitere Dateien/Abschnitte
|
||||
|
||||
::: {.column-margin}
|
||||
|
||||
```plain
|
||||
example-project/
|
||||
├── README.md
|
||||
├── CONTRIBUTING.md (optional)
|
||||
├── CHANGELOG.md (optional)
|
||||
├── CITATION.md (oder CITATION.cff)
|
||||
├── LICENSE
|
||||
├── data/ (optional)
|
||||
│ └── sample_data.csv
|
||||
├── docs/ (optional)
|
||||
│ ├── INSTALL.md
|
||||
│ └── USAGE.md
|
||||
├── examples/ (optional)
|
||||
│ └── example_workflow.ipynb
|
||||
└── src/
|
||||
├── script.py
|
||||
└── module/
|
||||
└── helper.py
|
||||
```
|
||||
|
||||
Beispielhafter Struktur eines Code-Repositories
|
||||
|
||||
:::
|
||||
|
||||
Sollte 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.:
|
||||
|
||||
- `README.md` – Überblick (Ziel, Installation, kurzes Beispiel, Lizenz/Zitation)
|
||||
- `docs/` Verzeichnis mit weiteren .md-Dateien für tiefergehende Dokumentation
|
||||
(optional)
|
||||
- `CONTRIBUTING.md` – Hinweise für Beiträger (falls relevant)
|
||||
- `LICENSE` – Lizenztext
|
||||
- `CITATION.cff` oder `CITATION.md` – wie zu zitieren.
|
||||
|
||||
Diese 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.
|
||||
|
||||
### Keine proprietären Formate oder Abhängigkeit von Werkzeugen
|
||||
|
||||
Um 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.**
|
||||
|
||||
### Übersichtlichkeit und Navigierbarkeit
|
||||
|
||||
Strukturieren 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.
|
||||
|
||||
### Beispiele, Codeblöcke und ggf. Abbildungen einbinden
|
||||
|
||||
Nutzen 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.
|
||||
|
||||
### Fazit Format und Struktur
|
||||
|
||||
Insgesamt 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.
|
||||
|
||||
## Umfang und Fokus der Dokumentation
|
||||
|
||||
Gerade 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.
|
||||
|
||||
Ein 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.
|
||||
|
||||
**Priorisierung:** 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).
|
||||
|
||||
Die 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 < 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.
|
||||
|
||||
Ein 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.
|
||||
|
||||
Zum 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.
|
||||
|
||||
Abschließ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.
|
||||
|
||||
## (Teil-)automatisierte Dokumentationswerkzeuge
|
||||
|
||||
Die 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:
|
||||
|
||||
### Docstrings und API-Dokumentationsgeneratoren
|
||||
|
||||
Nutzen 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.
|
||||
|
||||
::: {.column-margin}
|
||||
|
||||
Ä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.
|
||||
|
||||
:::
|
||||
|
||||
Der 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.
|
||||
|
||||
### Jupyter Notebooks und literate programming
|
||||
|
||||
Ein mächtiges Werkzeug – gerade in datengetriebenen Geisteswissenschaften – sind
|
||||
**Jupyter Notebooks** bzw. R Markdown Notebooks [@maria2019jupyter]. 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).
|
||||
|
||||
Notebooks 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.
|
||||
|
||||
::: {.callout-warning}
|
||||
|
||||
Notebooks 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.
|
||||
|
||||
:::
|
||||
|
||||
**Wann 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.
|
||||
|
||||
### Sphinx/MkDocs/Doxygen (statische Dokumentationswebseiten)
|
||||
|
||||
Fü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.]{.aside}
|
||||
|
||||
**Verpflichtend** 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 >
|
||||
einige tausend Zeilen oder > 5 Module lohnt es sich, eine generierte
|
||||
Dokumentation bereitzustellen, um den Überblick zu behalten.
|
||||
|
||||
### In-Code Hilfefunktionen und CL-Interface Doku
|
||||
|
||||
Falls 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.
|
||||
|
||||
Fü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.
|
||||
|
||||
### Versionskontrolle und kontinuierliche Dokumentationspflege
|
||||
|
||||
Eine 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.]{.aside}
|
||||
|
||||
### Spezialfälle
|
||||
|
||||
In 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.
|
||||
|
||||
### Wann ist was verpflichtend
|
||||
|
||||
Es 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.
|
||||
|
||||
## Best Practices, Vorlagen und Checklisten
|
||||
|
||||
Um 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:
|
||||
|
||||
### Orientierung an Nutzerbedürfnissen
|
||||
|
||||
Stellen 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.
|
||||
|
||||
### Checkliste für die Mindest-Dokumentation
|
||||
|
||||
Die 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:
|
||||
|
||||
1. **Zielklärung:** Ist der Zweck der Software klar benannt und der
|
||||
wissenschaftliche _Need_ begründet? (Falls nein, ergänzen: _Warum existiert
|
||||
dieses Tool?_)
|
||||
2. **Installation & 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)?
|
||||
3. **Grundlegende Nutzung:** Gibt es eine Anleitung oder Beispiele, wie man die
|
||||
Software verwendet (Eingabe -> Ausgaben)? Ist mindestens ein typischer
|
||||
Workflow beschrieben, idealerweise mit Beispielinput und -output?
|
||||
4. **Optionen & 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?
|
||||
5. **Validierung & Einschränkungen:** Werden Annahmen und Grenzen der Software
|
||||
genannt? Weiß ein*e Nutzer*in, welche Fälle nicht abgedeckt sind oder worauf
|
||||
zu achten ist (z. B. Datenqualität, maximale Größen)? Transparenz hier
|
||||
verhindert Frustration.
|
||||
6. **Hintergrund & 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.
|
||||
7. **Kontakt & 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?
|
||||
8. **Rechtliches & 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.
|
||||
9. **Aktualitä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.
|
||||
10. **Konsistenz & 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.
|
||||
|
||||
Diese 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.
|
||||
|
||||
### Positiv- und Negativbeispiele studieren
|
||||
|
||||
Ein 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.
|
||||
|
||||
### Prinzipien: FAIR und ENDINGS
|
||||
|
||||
Beachten 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.
|
||||
|
||||
### Kontinuierliche Verbesserung und Feedback
|
||||
|
||||
Dokumentation 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.
|
||||
|
||||
### Zusammenfassung Best Practices
|
||||
|
||||
Zusammenfassend 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.
|
||||
|
||||
## Fazit
|
||||
|
||||
Die 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.
|
||||
|
||||
Wissenschaftlich 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**.
|
||||
|
||||
### Tabellarische Übersicht der Dokumentations-Bestandteile
|
||||
|
||||
::: {.column-page-right}
|
||||
|
||||
Table: _Empfohlene 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.
|
||||
|
||||
| **Dokuelement** | **Inhalt/Purpose** | **Format/Ort** | **Umfang** |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ------------------------------------- |
|
||||
| **README (Hauptdoku)** | Zweck der Software; Kurzbeschreibung; Installationsanleitung; einfaches Nutzungsbeispiel; Lizenz- und Kontaktinfo | Markdown im Root des Repos (statisch versioniert) | 1–2 Seiten |
|
||||
| **Eingabe/Ausgabe-Guide** | Beschreibung der erwarteten Inputs (Datenformat, Parameter) und generierten Outputs (Dateien, Berichte) inkl. Beispielen | Teil der README oder separate Datei (z.B. USAGE.md) | 1 Seite (mit Beispielen) |
|
||||
| **Wissenschaftlicher Hintergrund** | Erläuterung der Methode, Theorie, Algorithmen; Verweise auf Literatur | README-Abschnitt "Hintergrund" oder separate Doku (BACKGROUND.md) | 0.5–1 Seite (plus Referenzen) |
|
||||
| **Bekannte Limitationen** | Auflistung von Einschränkungen, Annahmen, bekannten Problemen; ggf. Workarounds | README-Abschnitt "Limitations" oder FAQ.md | 0.5 Seite |
|
||||
| **Beispiel-Workflow (Tutorial)** | Schritt-für-Schritt Anleitung mit einem realistischen Anwendungsfall (ggf. mit Code und Screenshot) | Jupyter Notebook (`.ipynb`) im Repo `examples/` Ordner oder Markdown in docs/ | 1–3 Seiten / entsprechend Zellen |
|
||||
| **API-Referenz** | Technische Dokumentation von Funktionen/Klassen für Entwickler\*innen | Automatisch generiert aus Docstrings (z.B. Sphinx in `docs/` Ordner, HTML/PDF Ausgabe) | Je nach Codegröße (ggf. umfangreich) |
|
||||
| **CONTRIBUTING** | Anleitung für Beitragswillige: Code Style, Workflow, Tests, Kontakt | CONTRIBUTING.md im Repo | 0.5–1 Seite |
|
||||
| **LICENSE** / **CITATION** | Rechtliche Infos (Lizenztext); Zitationsleitfaden (Bevorzugte Zitierweise, DOI) | Jeweils eigene Datei im Repo (Plain Text/Markdown) | Kurz (Standardtext bzw. Referenz) |
|
||||
| **Release-Information** | Versionshinweise, Änderungsprotokoll (Changelog) | CHANGELOG.md oder Releases auf GitHub | fortlaufend pro Version (Stichpunkte) |
|
||||
|
||||
:::
|
||||
|
||||
### Schlusswort
|
||||
|
||||
Mit 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.
|
||||
|
||||
## Methodik / LLMs als 'Autoren' {.appendix}
|
||||
|
||||
Erstellt wurde der initial draft mittels Websuche und "Deep-Research" von
|
||||
`gpt-4.5 (preview)`. Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting
|
||||
durch Nicole Dresselhaus.
|
BIN
Writing/header.png
Normal file
BIN
Writing/header.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 625 KiB |
106
Writing/ner4all-case-study.bib
Normal file
106
Writing/ner4all-case-study.bib
Normal file
@ -0,0 +1,106 @@
|
||||
@misc{ollama_chroma_cookbook,
|
||||
title = {Ollama - Chroma Cookbook},
|
||||
url = {https://cookbook.chromadb.dev/integrations/ollama/embeddings/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = apr
|
||||
}
|
||||
|
||||
@misc{smart_connections_plugin,
|
||||
title = {Just wanted to mention that the smart connections plugin is incredible. : r/ObsidianMD},
|
||||
url = {https://www.reddit.com/r/ObsidianMD/comments/1fzmkdk/just_wanted_to_mention_that_the_smart_connections/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = oct
|
||||
}
|
||||
|
||||
@misc{khoj_plugin,
|
||||
title = {Khoj: An AI powered Search Assistant for your Second Brain - Share & showcase - Obsidian Forum},
|
||||
url = {https://forum.obsidian.md/t/khoj-an-ai-powered-search-assistant-for-you-second-brain/53756},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2023},
|
||||
month = jul
|
||||
}
|
||||
|
||||
@misc{supercharging_obsidian_search,
|
||||
title = {Supercharging Obsidian Search with AI and Ollama},
|
||||
author = {@airabbitX},
|
||||
url = {https://medium.com/@airabbitX/supercharging-obsidian-search-with-local-llms-a-personal-journey-1e008eb649a6},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = nov
|
||||
}
|
||||
|
||||
@misc{export_to_common_graph_formats,
|
||||
title = {Export to common graph formats - Plugins ideas - Obsidian Forum},
|
||||
url = {https://forum.obsidian.md/t/export-to-common-graph-formats/4138},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2020},
|
||||
month = feb
|
||||
}
|
||||
|
||||
@misc{personal_knowledge_graphs_in_obsidian,
|
||||
title = {Personal Knowledge Graphs in Obsidian},
|
||||
author = {Volodymyr Pavlyshyn},
|
||||
url = {https://volodymyrpavlyshyn.medium.com/personal-knowledge-graphs-in-obsidian-528a0f4584b9},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{export_obsidian_to_rdf,
|
||||
title = {How to export your Obsidian Vault to RDF},
|
||||
author = {Volodymyr Pavlyshyn},
|
||||
url = {https://volodymyrpavlyshyn.medium.com/how-to-export-your-obsidian-vault-to-rdf-00fb2539ed18},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{ai_empowered_zettelkasten_with_ner_and_graph_llm,
|
||||
title = {AI empowered Zettelkasten with NER and Graph LLM - Knowledge management - Obsidian Forum},
|
||||
url = {https://forum.obsidian.md/t/ai-empowered-zettelkasten-with-ner-and-graph-llm/79112},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{build_your_second_brain_with_khoj_ai,
|
||||
title = {Build your second brain with Khoj AI},
|
||||
url = {https://dswharshit.medium.com/build-your-second-brain-with-khoj-ai-high-signal-ai-2-87492730d7ce},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = jun
|
||||
}
|
||||
|
||||
@misc{second_brain_assistant_with_obsidian,
|
||||
title = {Second Brain Assistant with Obsidian},
|
||||
url = {https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2025},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{basic_memory_ai_conversations_that_build_knowledge,
|
||||
title = {Basic Memory | AI Conversations That Build Knowledge},
|
||||
url = {https://basicmachines.co/},
|
||||
note = {Accessed: 2025-04-23}
|
||||
}
|
||||
|
||||
@misc{local_free_rag_with_question_generation,
|
||||
title = {Local (Free) RAG with Question Generation using LM Studio, Nomic embeddings, ChromaDB and Llama 3.2 on a Mac mini M1},
|
||||
author = {Oscar Galvis},
|
||||
url = {https://lomaky.medium.com/local-free-rag-with-question-generation-using-lm-studio-nomic-embeddings-chromadb-and-llama-3-2-9758877e93b4},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = oct
|
||||
}
|
||||
|
||||
@misc{private_gpt_llama_cpp_based_scripts,
|
||||
title = {privateGPT / llama.cpp based scripts},
|
||||
url = {https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2025},
|
||||
month = mar
|
||||
}
|
||||
|
613
Writing/ner4all-case-study.md
Normal file
613
Writing/ner4all-case-study.md
Normal file
@ -0,0 +1,613 @@
|
||||
---
|
||||
tags:
|
||||
- Writing
|
||||
- table-wrap
|
||||
authors:
|
||||
- name: GPT-4.5
|
||||
url: https://chatgpt.com
|
||||
affiliation:
|
||||
- name: OpenAI
|
||||
url: https://openai.com
|
||||
- name: cogito-v1-preview
|
||||
url: https://www.deepcogito.com/research/cogito-v1-preview
|
||||
affiliation:
|
||||
- name: DeepCogito
|
||||
url: https://www.deepcogito.com
|
||||
- name: Nicole Dresselhaus
|
||||
affiliation:
|
||||
- name: Humboldt-Universität zu Berlin
|
||||
url: https://hu-berlin.de
|
||||
orcid: 0009-0008-8850-3679
|
||||
date: 2025-05-05
|
||||
categories:
|
||||
- Article
|
||||
- Case-study
|
||||
- ML
|
||||
- NER
|
||||
lang: en
|
||||
citation: true
|
||||
fileClass: authored
|
||||
title: "Case Study: Local LLM-Based NER with n8n and Ollama"
|
||||
abstract: |
|
||||
Named Entity Recognition (NER) is a foundational task in text analysis,
|
||||
traditionally addressed by training NLP models on annotated data. However, a
|
||||
recent study – _“NER4All or Context is All You Need”_ – showed that
|
||||
out-of-the-box Large Language Models (LLMs) can **significantly outperform**
|
||||
classical NER pipelines (e.g. spaCy, Flair) on historical texts by using clever
|
||||
prompting, without any model retraining. This case study demonstrates how to
|
||||
implement the paper’s method using entirely local infrastructure: an **n8n**
|
||||
automation workflow (for orchestration) and a **Ollama** server running a
|
||||
14B-parameter LLM on an NVIDIA A100 GPU. The goal is to enable research
|
||||
engineers and tech-savvy historians to **reproduce and apply this method
|
||||
easily** on their own data, with a focus on usability and correct outputs rather
|
||||
than raw performance.
|
||||
|
||||
We will walk through the end-to-end solution – from accepting a webhook input
|
||||
that defines entity types (e.g. Person, Organization, Location) to prompting a
|
||||
local LLM to extract those entities from a text. The solution covers setup
|
||||
instructions, required infrastructure (GPU, memory, software), model
|
||||
configuration, and workflow design in n8n. We also discuss potential limitations
|
||||
(like model accuracy and context length) and how to address them. By the end,
|
||||
you will have a clear blueprint for a **self-hosted NER pipeline** that
|
||||
leverages the knowledge encoded in LLMs (as advocated by the paper) while
|
||||
maintaining data privacy and reproducibility.
|
||||
|
||||
bibliography:
|
||||
- ner4all-case-study.bib
|
||||
citation-style: springer-humanities-brackets
|
||||
nocite: |
|
||||
@*
|
||||
image: ../thumbs/writing_ner4all-case-study.png
|
||||
---
|
||||
|
||||
## Background: LLM-Based NER Method Overview
|
||||
|
||||
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.
|
||||
|
||||
**Prompt Strategy:** The success of this approach hinges on careful prompt
|
||||
engineering. The final prompt used in the paper had multiple components:
|
||||
|
||||
- **Persona & 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.
|
||||
- **Task 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.”_
|
||||
- **Optional 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.
|
||||
- **Reiteration & 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.
|
||||
- **Prompt 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.
|
||||
- **Output 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 `<<PER ... /PER>>` to tag people, `<<LOC ... /LOC>>` 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.
|
||||
|
||||
**Why 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.
|
||||
|
||||
## Solution Architecture
|
||||
|
||||
Our 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:
|
||||
|
||||
1. **Webhook 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.
|
||||
2. **Prompt 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.
|
||||
3. **LLM 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.
|
||||
`<<PER John Doe /PER>> went to <<LOC Berlin /LOC>> ...`). This step harnesses
|
||||
the A100 GPU to generate results quickly, using the chosen model’s weights
|
||||
locally.
|
||||
4. **Output 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"}`{.json} 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.
|
||||
|
||||
**Workflow 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.
|
||||
|
||||
## Setup and Infrastructure Requirements
|
||||
|
||||
To reproduce this solution, you will need a machine with an **NVIDIA GPU** and
|
||||
the following software components installed:
|
||||
|
||||
- **n8n (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:
|
||||
|
||||
```bash
|
||||
docker run -it --rm \
|
||||
-p 5678:5678 \
|
||||
-v ~/.n8n:/home/node/.n8n \
|
||||
n8nio/n8n:latest
|
||||
```
|
||||
|
||||
This 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.)
|
||||
|
||||
- **Ollama (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:
|
||||
|
||||
```bash
|
||||
ollama serve
|
||||
```
|
||||
|
||||
This 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).
|
||||
|
||||
- **LLM 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:
|
||||
|
||||
- _DeepSeek-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:
|
||||
|
||||
```bash
|
||||
ollama pull deepseek-r1:14b
|
||||
```
|
||||
|
||||
This 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.
|
||||
|
||||
- _Cogito 14B:_ A 14B “hybrid reasoning” model by Deep Cogito, known for
|
||||
excellent instruction-following and multilingual capability. Pull it with:
|
||||
|
||||
```bash
|
||||
ollama pull cogito:14b
|
||||
```
|
||||
|
||||
Cogito-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.
|
||||
|
||||
- _Others:_ 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.
|
||||
|
||||
**Hardware 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.
|
||||
|
||||
## Building the n8n Workflow
|
||||
|
||||
With the environment ready, we now construct the n8n workflow that ties
|
||||
everything together. We outline each component with instructions:
|
||||
|
||||
### 1. Webhook Input for Entities and Text
|
||||
|
||||
Start 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"entities": "PER, ORG, LOC",
|
||||
"text": "John Doe visited Berlin in 1921 and met with the Board of Acme Corp."
|
||||
}
|
||||
```
|
||||
|
||||
Here, `"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.
|
||||
|
||||
### 2. Constructing the LLM Prompt
|
||||
|
||||
Next, 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).
|
||||
|
||||
According to the method, our **system prompt** should incorporate the following:
|
||||
|
||||
- **Persona/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.
|
||||
- **Task 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.”_
|
||||
- **Entity 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.
|
||||
- **Tagging Instructions:** Clearly explain the tagging format. We adopt the
|
||||
format from the paper: each entity should be wrapped in `<<TYPE ... /TYPE>>`.
|
||||
So instruct: _“Enclose each entity in double angle brackets with its type
|
||||
label. For example: <\<PER John Doe /PER>> 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).
|
||||
- **Output 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.”.
|
||||
- **Compliance & 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.
|
||||
|
||||
Once 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:\n” 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.)
|
||||
|
||||
In 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.
|
||||
|
||||
### 3. Configuring the Local LLM (Ollama Model Node)
|
||||
|
||||
Now 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`.
|
||||
|
||||
Double-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.
|
||||
|
||||
At 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).
|
||||
|
||||
### 4. Returning the Results
|
||||
|
||||
After 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:
|
||||
|
||||
```plain
|
||||
<<PER John Doe /PER>> visited <<LOC Berlin /LOC>> in 1921 and met with the Board
|
||||
of <<ORG Acme Corp /ORG>>.
|
||||
```
|
||||
|
||||
This 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 `<<(\w+) (.*?) /\1>>` 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.
|
||||
|
||||
Finally, 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.
|
||||
|
||||
**Note:** 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.
|
||||
|
||||
## Model Selection Considerations
|
||||
|
||||
We 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:
|
||||
|
||||
- **Accuracy 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.
|
||||
- **Domain 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.
|
||||
- **Language:** 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 `<<PER Herr Schmidt /PER>>`. Always test on a small sample
|
||||
if in doubt.
|
||||
- **Extended 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.
|
||||
|
||||
In 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).
|
||||
|
||||
## Example Run
|
||||
|
||||
Let’s run through a hypothetical example to illustrate the output. Suppose a
|
||||
historian supplies the following via the webhook:
|
||||
|
||||
- **Entities:** `PER, ORG, LOC`
|
||||
- **Text:** _"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."_
|
||||
|
||||
When 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:
|
||||
|
||||
```plain
|
||||
<<PER Baron Münchhausen /PER>> was born in <<LOC Bodenwerder /LOC>> and served
|
||||
in the Russian military under <<PER Empress Anna /PER>>. Today, the <<ORG
|
||||
Münchhausen Museum /ORG>> in <<LOC Bodenwerder /LOC>> is operated by the town
|
||||
council.
|
||||
```
|
||||
|
||||
All person names (Baron Münchhausen, Empress Anna) are enclosed in `<<PER>>`
|
||||
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.
|
||||
|
||||
## Limitations and Solutions
|
||||
|
||||
While this pipeline makes NER easier to reproduce, it’s important to be aware of
|
||||
its limitations and how to mitigate them:
|
||||
|
||||
- **Model 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.
|
||||
|
||||
- **Text 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.
|
||||
|
||||
- **Long 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`{.bash} or in the workflow settings).
|
||||
|
||||
- **Concurrent 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.
|
||||
|
||||
- **n8n 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.
|
||||
|
||||
- **Output 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.
|
||||
|
||||
## Conclusion
|
||||
|
||||
By 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.
|
||||
|
||||
Importantly, 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.
|
||||
|
||||
Moving 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.
|
||||
|
||||
**Sources:** This implementation draws on insights from Ahmed et al. (2025) for
|
||||
the prompt-based NER method, and uses tools like n8n and Ollama as documented in
|
||||
their official guides. The chosen models (DeepSeek-R1 and Cogito) are described
|
||||
in their respective releases. All software and models are utilized in accordance
|
||||
with their licenses for a fully local deployment.
|
||||
|
||||
## Methodik / LLMs als 'Autoren' {.appendix}
|
||||
|
||||
Erstellt wurde der initial draft mittels Websuche und "Deep-Research" von
|
||||
`gpt-4.5 (preview)`. Abschließendes Korrekturlesen/inhaltliche Prüfung/Layouting
|
||||
durch Nicole Dresselhaus.
|
106
Writing/obsidian-rag.bib
Normal file
106
Writing/obsidian-rag.bib
Normal file
@ -0,0 +1,106 @@
|
||||
@misc{ollama_chroma_cookbook,
|
||||
title = {Ollama - Chroma Cookbook},
|
||||
url = {https://cookbook.chromadb.dev/integrations/ollama/embeddings/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = apr
|
||||
}
|
||||
|
||||
@misc{smart_connections_plugin,
|
||||
title = {Just wanted to mention that the smart connections plugin is incredible. : r/ObsidianMD},
|
||||
url = {https://www.reddit.com/r/ObsidianMD/comments/1fzmkdk/just_wanted_to_mention_that_the_smart_connections/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = oct
|
||||
}
|
||||
|
||||
@misc{khoj_plugin,
|
||||
title = {Khoj: An AI powered Search Assistant for your Second Brain - Share & showcase - Obsidian Forum},
|
||||
url = {https://forum.obsidian.md/t/khoj-an-ai-powered-search-assistant-for-you-second-brain/53756},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2023},
|
||||
month = jul
|
||||
}
|
||||
|
||||
@misc{supercharging_obsidian_search,
|
||||
title = {Supercharging Obsidian Search with AI and Ollama},
|
||||
author = {@airabbitX},
|
||||
url = {https://medium.com/@airabbitX/supercharging-obsidian-search-with-local-llms-a-personal-journey-1e008eb649a6},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = nov
|
||||
}
|
||||
|
||||
@misc{export_to_common_graph_formats,
|
||||
title = {Export to common graph formats - Plugins ideas - Obsidian Forum},
|
||||
url = {https://forum.obsidian.md/t/export-to-common-graph-formats/4138},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2020},
|
||||
month = feb
|
||||
}
|
||||
|
||||
@misc{personal_knowledge_graphs_in_obsidian,
|
||||
title = {Personal Knowledge Graphs in Obsidian},
|
||||
author = {Volodymyr Pavlyshyn},
|
||||
url = {https://volodymyrpavlyshyn.medium.com/personal-knowledge-graphs-in-obsidian-528a0f4584b9},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{export_obsidian_to_rdf,
|
||||
title = {How to export your Obsidian Vault to RDF},
|
||||
author = {Volodymyr Pavlyshyn},
|
||||
url = {https://volodymyrpavlyshyn.medium.com/how-to-export-your-obsidian-vault-to-rdf-00fb2539ed18},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{ai_empowered_zettelkasten_with_ner_and_graph_llm,
|
||||
title = {AI empowered Zettelkasten with NER and Graph LLM - Knowledge management - Obsidian Forum},
|
||||
url = {https://forum.obsidian.md/t/ai-empowered-zettelkasten-with-ner-and-graph-llm/79112},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{build_your_second_brain_with_khoj_ai,
|
||||
title = {Build your second brain with Khoj AI},
|
||||
url = {https://dswharshit.medium.com/build-your-second-brain-with-khoj-ai-high-signal-ai-2-87492730d7ce},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = jun
|
||||
}
|
||||
|
||||
@misc{second_brain_assistant_with_obsidian,
|
||||
title = {Second Brain Assistant with Obsidian},
|
||||
url = {https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2025},
|
||||
month = mar
|
||||
}
|
||||
|
||||
@misc{basic_memory_ai_conversations_that_build_knowledge,
|
||||
title = {Basic Memory | AI Conversations That Build Knowledge},
|
||||
url = {https://basicmachines.co/},
|
||||
note = {Accessed: 2025-04-23}
|
||||
}
|
||||
|
||||
@misc{local_free_rag_with_question_generation,
|
||||
title = {Local (Free) RAG with Question Generation using LM Studio, Nomic embeddings, ChromaDB and Llama 3.2 on a Mac mini M1},
|
||||
author = {Oscar Galvis},
|
||||
url = {https://lomaky.medium.com/local-free-rag-with-question-generation-using-lm-studio-nomic-embeddings-chromadb-and-llama-3-2-9758877e93b4},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2024},
|
||||
month = oct
|
||||
}
|
||||
|
||||
@misc{private_gpt_llama_cpp_based_scripts,
|
||||
title = {privateGPT / llama.cpp based scripts},
|
||||
url = {https://www.ssp.sh/brain/second-brain-assistant-with-obsidian-notegpt/},
|
||||
note = {Accessed: 2025-04-23},
|
||||
year = {2025},
|
||||
month = mar
|
||||
}
|
||||
|
421
Writing/springer-humanities-brackets.csl
Normal file
421
Writing/springer-humanities-brackets.csl
Normal file
@ -0,0 +1,421 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0" demote-non-dropping-particle="sort-only" default-locale="en-US">
|
||||
<info>
|
||||
<title>Springer - Humanities (numeric, brackets)</title>
|
||||
<id>http://www.zotero.org/styles/springer-humanities-brackets</id>
|
||||
<link href="http://www.zotero.org/styles/springer-humanities-brackets" rel="self"/>
|
||||
<link href="http://www.zotero.org/styles/springer-humanities-author-date" rel="template"/>
|
||||
<link href="www.springer.com/cda/content/document/cda_downloaddocument/Key_Style_Points_Aug2012.pdf" rel="documentation"/>
|
||||
<author>
|
||||
<name>Sebastian Karcher</name>
|
||||
</author>
|
||||
<contributor>
|
||||
<name>Julian Onions</name>
|
||||
<email>julian.onions@gmail.com</email>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Richard Karnesky</name>
|
||||
<email>karnesky+zotero@gmail.com</email>
|
||||
<uri>http://arc.nucapt.northwestern.edu/Richard_Karnesky</uri>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Charles Parnot</name>
|
||||
<email>charles.parnot@gmail.com</email>
|
||||
<uri>http://twitter.com/cparnot</uri>
|
||||
</contributor>
|
||||
<category citation-format="numeric"/>
|
||||
<category field="generic-base"/>
|
||||
<category field="humanities"/>
|
||||
<summary>Style for Springer's humanities journals - the journals do look slightly different from each other, but this should work quite closely</summary>
|
||||
<updated>2019-10-01T00:21:45+00:00</updated>
|
||||
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
|
||||
</info>
|
||||
<locale>
|
||||
<terms>
|
||||
<term name="container-author" form="verb">by</term>
|
||||
</terms>
|
||||
</locale>
|
||||
<macro name="secondary-contributors">
|
||||
<choose>
|
||||
<if type="chapter paper-conference" match="none">
|
||||
<group delimiter=". ">
|
||||
<choose>
|
||||
<if variable="author">
|
||||
<names variable="editor">
|
||||
<label form="verb" text-case="capitalize-first" suffix=" " plural="never"/>
|
||||
<name and="text" delimiter=", "/>
|
||||
</names>
|
||||
</if>
|
||||
</choose>
|
||||
<choose>
|
||||
<if variable="author editor" match="any">
|
||||
<names variable="translator">
|
||||
<label form="verb" text-case="capitalize-first" suffix=" " plural="never"/>
|
||||
<name and="text" delimiter=", "/>
|
||||
</names>
|
||||
</if>
|
||||
</choose>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="container-contributors">
|
||||
<choose>
|
||||
<if type="chapter paper-conference" match="any">
|
||||
<group prefix=", " delimiter=", ">
|
||||
<choose>
|
||||
<if variable="author">
|
||||
<names variable="container-author editor" delimiter=", ">
|
||||
<label form="short" suffix=" " plural="never"/>
|
||||
<name and="text" delimiter=", " initialize="false" initialize-with=". "/>
|
||||
</names>
|
||||
</if>
|
||||
</choose>
|
||||
<choose>
|
||||
<if variable="author editor" match="any">
|
||||
<names variable="translator">
|
||||
<label form="short" plural="never" suffix=" "/>
|
||||
<name and="text" delimiter=", "/>
|
||||
</names>
|
||||
</if>
|
||||
</choose>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="recipient">
|
||||
<choose>
|
||||
<if type="personal_communication">
|
||||
<choose>
|
||||
<if variable="genre">
|
||||
<text variable="genre" text-case="capitalize-first"/>
|
||||
</if>
|
||||
<else>
|
||||
<text term="letter" text-case="capitalize-first"/>
|
||||
</else>
|
||||
</choose>
|
||||
</if>
|
||||
</choose>
|
||||
<names variable="recipient" delimiter=", ">
|
||||
<label form="verb" prefix=" " suffix=" "/>
|
||||
<name and="text" delimiter=", "/>
|
||||
</names>
|
||||
</macro>
|
||||
<macro name="contributors">
|
||||
<names variable="author">
|
||||
<name and="text" name-as-sort-order="first" sort-separator=", " delimiter=", " delimiter-precedes-last="always"/>
|
||||
<label form="short" plural="never" prefix=", "/>
|
||||
<substitute>
|
||||
<names variable="editor"/>
|
||||
<names variable="translator"/>
|
||||
<text macro="title"/>
|
||||
</substitute>
|
||||
</names>
|
||||
<text macro="recipient"/>
|
||||
</macro>
|
||||
<macro name="interviewer">
|
||||
<names variable="interviewer" delimiter=", ">
|
||||
<label form="verb" prefix=" " text-case="capitalize-first" suffix=" "/>
|
||||
<name and="text" delimiter=", "/>
|
||||
</names>
|
||||
</macro>
|
||||
<macro name="archive">
|
||||
<group delimiter=". ">
|
||||
<text variable="archive_location" text-case="capitalize-first"/>
|
||||
<text variable="archive"/>
|
||||
<text variable="archive-place"/>
|
||||
</group>
|
||||
</macro>
|
||||
<macro name="access">
|
||||
<group delimiter=". ">
|
||||
<choose>
|
||||
<if type="graphic report" match="any">
|
||||
<text macro="archive"/>
|
||||
</if>
|
||||
<else-if type="article-magazine article-newspaper bill book chapter graphic legal_case legislation motion_picture paper-conference report song thesis" match="none">
|
||||
<text macro="archive"/>
|
||||
</else-if>
|
||||
</choose>
|
||||
<text variable="DOI" prefix="https://doi.org/"/>
|
||||
<choose>
|
||||
<if variable="DOI issued" match="none">
|
||||
<choose>
|
||||
<if variable="URL accessed" match="all">
|
||||
<choose>
|
||||
<if type="legal_case" match="none">
|
||||
<text variable="URL"/>
|
||||
</if>
|
||||
</choose>
|
||||
<group delimiter=" ">
|
||||
<text term="accessed" text-case="capitalize-first"/>
|
||||
<date variable="accessed" delimiter=" ">
|
||||
<date-part name="month"/>
|
||||
<date-part name="day"/>
|
||||
</date>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</if>
|
||||
<else-if type="webpage">
|
||||
<date variable="issued" delimiter=" ">
|
||||
<date-part name="month"/>
|
||||
<date-part name="day"/>
|
||||
</date>
|
||||
</else-if>
|
||||
</choose>
|
||||
</group>
|
||||
</macro>
|
||||
<macro name="title">
|
||||
<choose>
|
||||
<if variable="title" match="none">
|
||||
<choose>
|
||||
<if type="personal_communication" match="none">
|
||||
<text variable="genre" text-case="capitalize-first"/>
|
||||
</if>
|
||||
</choose>
|
||||
</if>
|
||||
<else-if type="bill book graphic legal_case legislation motion_picture report song" match="any">
|
||||
<text variable="title" font-style="italic"/>
|
||||
</else-if>
|
||||
<else>
|
||||
<text variable="title"/>
|
||||
</else>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="edition">
|
||||
<choose>
|
||||
<if type="bill book graphic legal_case legislation motion_picture report song" match="any">
|
||||
<choose>
|
||||
<if is-numeric="edition">
|
||||
<group delimiter=" " prefix=". ">
|
||||
<number variable="edition" form="ordinal"/>
|
||||
<text term="edition" form="short"/>
|
||||
</group>
|
||||
</if>
|
||||
<else>
|
||||
<text variable="edition" prefix=". "/>
|
||||
</else>
|
||||
</choose>
|
||||
</if>
|
||||
<else-if type="chapter paper-conference" match="any">
|
||||
<choose>
|
||||
<if is-numeric="edition">
|
||||
<group delimiter=" " prefix=", ">
|
||||
<number variable="edition" form="ordinal"/>
|
||||
<text term="edition" form="short"/>
|
||||
</group>
|
||||
</if>
|
||||
<else>
|
||||
<text variable="edition" prefix=", "/>
|
||||
</else>
|
||||
</choose>
|
||||
</else-if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="locators">
|
||||
<choose>
|
||||
<if type="article-journal">
|
||||
<text variable="volume" prefix=" "/>
|
||||
</if>
|
||||
<else-if type="legal_case">
|
||||
<text variable="volume" prefix=", "/>
|
||||
<text variable="container-title" prefix=" "/>
|
||||
<text variable="page" prefix=" "/>
|
||||
</else-if>
|
||||
<else-if type="bill book graphic legal_case legislation motion_picture report song" match="any">
|
||||
<group prefix=". " delimiter=". ">
|
||||
<group>
|
||||
<text term="volume" form="short" text-case="capitalize-first" suffix=" "/>
|
||||
<number variable="volume" form="numeric"/>
|
||||
</group>
|
||||
<group>
|
||||
<number variable="number-of-volumes" form="numeric"/>
|
||||
<text term="volume" form="short" prefix=" " plural="true"/>
|
||||
</group>
|
||||
</group>
|
||||
</else-if>
|
||||
<else-if type="chapter paper-conference" match="any">
|
||||
<choose>
|
||||
<if variable="page" match="none">
|
||||
<group prefix=". ">
|
||||
<text term="volume" form="short" text-case="capitalize-first" suffix=" "/>
|
||||
<number variable="volume" form="numeric"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</else-if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="locators-chapter">
|
||||
<choose>
|
||||
<if type="chapter paper-conference" match="any">
|
||||
<choose>
|
||||
<if variable="page">
|
||||
<group prefix=", ">
|
||||
<text variable="volume" suffix=":"/>
|
||||
<text variable="page"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="locators-article">
|
||||
<choose>
|
||||
<if type="article-newspaper">
|
||||
<group prefix=", " delimiter=", ">
|
||||
<group delimiter=" ">
|
||||
<text variable="edition"/>
|
||||
<text term="edition"/>
|
||||
</group>
|
||||
<group>
|
||||
<text term="section" form="short" suffix=" "/>
|
||||
<text variable="section"/>
|
||||
</group>
|
||||
</group>
|
||||
</if>
|
||||
<else-if type="article-journal">
|
||||
<text variable="page" prefix=": "/>
|
||||
</else-if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="container-prefix">
|
||||
<text term="in" text-case="capitalize-first"/>
|
||||
</macro>
|
||||
<macro name="container-title">
|
||||
<choose>
|
||||
<if type="chapter paper-conference" match="any">
|
||||
<text macro="container-prefix" suffix=" "/>
|
||||
</if>
|
||||
</choose>
|
||||
<choose>
|
||||
<if type="legal_case" match="none">
|
||||
<text variable="container-title" font-style="italic"/>
|
||||
</if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="publisher">
|
||||
<group delimiter=": ">
|
||||
<text variable="publisher-place"/>
|
||||
<text variable="publisher"/>
|
||||
</group>
|
||||
</macro>
|
||||
<macro name="date">
|
||||
<choose>
|
||||
<if variable="issued">
|
||||
<date variable="issued">
|
||||
<date-part name="year"/>
|
||||
</date>
|
||||
</if>
|
||||
<else-if variable="accessed">
|
||||
<date variable="accessed">
|
||||
<date-part name="year"/>
|
||||
</date>
|
||||
</else-if>
|
||||
</choose>
|
||||
</macro>
|
||||
<macro name="day-month">
|
||||
<date variable="issued">
|
||||
<date-part name="month"/>
|
||||
<date-part name="day" prefix=" "/>
|
||||
</date>
|
||||
</macro>
|
||||
<macro name="collection-title">
|
||||
<text variable="collection-title" text-case="title"/>
|
||||
<text variable="collection-number" prefix=" "/>
|
||||
</macro>
|
||||
<macro name="event">
|
||||
<group>
|
||||
<text term="presented at" suffix=" "/>
|
||||
<text variable="event"/>
|
||||
</group>
|
||||
</macro>
|
||||
<macro name="description">
|
||||
<choose>
|
||||
<if type="interview">
|
||||
<group delimiter=". ">
|
||||
<text macro="interviewer"/>
|
||||
<text variable="medium" text-case="capitalize-first"/>
|
||||
</group>
|
||||
</if>
|
||||
<else>
|
||||
<text variable="medium" text-case="capitalize-first" prefix=". "/>
|
||||
</else>
|
||||
</choose>
|
||||
<choose>
|
||||
<if variable="title" match="none"/>
|
||||
<else-if type="thesis"/>
|
||||
<else>
|
||||
<group delimiter=" " prefix=". ">
|
||||
<text variable="genre" text-case="capitalize-first"/>
|
||||
<choose>
|
||||
<if type="report">
|
||||
<text variable="number"/>
|
||||
</if>
|
||||
</choose>
|
||||
</group>
|
||||
</else>
|
||||
</choose>
|
||||
<!--This is for computer programs only. Localization new to 1.0.1, so may be missing in many locales-->
|
||||
<group delimiter=" " prefix=" (" suffix=")">
|
||||
<text term="version"/>
|
||||
<text variable="version"/>
|
||||
</group>
|
||||
</macro>
|
||||
<macro name="issue">
|
||||
<choose>
|
||||
<if type="legal_case">
|
||||
<text variable="authority" prefix=". "/>
|
||||
</if>
|
||||
<else-if type="speech">
|
||||
<group prefix=" " delimiter=", ">
|
||||
<text macro="event"/>
|
||||
<text macro="day-month"/>
|
||||
<text variable="event-place"/>
|
||||
</group>
|
||||
</else-if>
|
||||
<else-if type="article-newspaper article-magazine" match="any">
|
||||
<text macro="day-month" prefix=", "/>
|
||||
</else-if>
|
||||
<else>
|
||||
<group prefix=". " delimiter=", ">
|
||||
<choose>
|
||||
<if type="thesis">
|
||||
<text variable="genre" text-case="capitalize-first"/>
|
||||
</if>
|
||||
</choose>
|
||||
<text macro="publisher"/>
|
||||
</group>
|
||||
</else>
|
||||
</choose>
|
||||
</macro>
|
||||
<citation collapse="citation-number">
|
||||
<sort>
|
||||
<key variable="citation-number"/>
|
||||
</sort>
|
||||
<layout prefix="[" suffix="]" delimiter=", ">
|
||||
<text variable="citation-number"/>
|
||||
</layout>
|
||||
</citation>
|
||||
<bibliography second-field-align="flush" et-al-min="11" et-al-use-first="7" entry-spacing="0">
|
||||
<layout suffix=".">
|
||||
<text variable="citation-number" suffix=". "/>
|
||||
<group delimiter=". ">
|
||||
<text macro="contributors"/>
|
||||
<text macro="date"/>
|
||||
<text macro="title"/>
|
||||
</group>
|
||||
<text macro="description"/>
|
||||
<text macro="secondary-contributors" prefix=". "/>
|
||||
<text macro="container-title" prefix=". "/>
|
||||
<text macro="container-contributors"/>
|
||||
<text macro="edition"/>
|
||||
<text macro="locators-chapter"/>
|
||||
<text macro="locators"/>
|
||||
<text macro="collection-title" prefix=". "/>
|
||||
<text macro="issue"/>
|
||||
<text macro="locators-article"/>
|
||||
<text macro="access" prefix=". "/>
|
||||
</layout>
|
||||
</bibliography>
|
||||
</style>
|
Reference in New Issue
Block a user