17 KiB
Mindnet v2.2 – Technische Architektur
Datei: docs/mindnet_technical_architecture_v2.2.md
Stand: 2025-12-09
Status: FINAL (Integrierter Stand WP01–WP06)
Quellen: Programmplan_V2.2.md, Handbuch.md, chunking_strategy.md, wp04_retriever_scoring.md.
Ziel dieses Dokuments: Vollständige Beschreibung der technischen Architektur inkl. Graph-Datenbank, Retrieval-Logik und der neuen RAG-Komponenten (Decision Engine & Hybrid Router).
📖 Inhaltsverzeichnis (Klicken zum Öffnen)
- Mindnet v2.2 – Technische Architektur
1. Systemüberblick
1.1 Architektur-Zielbild
Mindnet ist ein lokales RAG-System (Retrieval Augmented Generation).
- Source: Markdown-Notizen in einem Vault (Obsidian-kompatibel).
- Pipeline: Ein Python-Importer transformiert diese in Notes, Chunks und Edges.
- Storage:
- Qdrant: Vektor-Datenbank für Graph und Semantik (Collections: notes, chunks, edges).
- Local Files (JSONL): Append-Only Logs für Feedback und Search-History (Data Flywheel).
- Service: Eine FastAPI-Anwendung stellt Endpunkte für Semantische und Hybride Suche sowie Feedback bereit.
- Inference: Lokales LLM (Ollama: Phi-3 Mini) für RAG-Chat und Antwortgenerierung.
Das System arbeitet deterministisch (stabile IDs) und ist konfigurationsgetrieben (types.yaml, retriever.yaml, decision_engine.yaml, prompts.yaml).
1.2 Verzeichnisstruktur & Komponenten (Post-WP06)
/mindnet/
├── app/
│ ├── main.py # FastAPI Einstiegspunkt
│ ├── core/
│ │ ├── qdrant.py # Client-Factory & Connection
│ │ ├── qdrant_points.py # Low-Level Point Operations (Upsert/Delete)
│ │ ├── note_payload.py # Bau der Note-Objekte
│ │ ├── chunk_payload.py # Bau der Chunk-Objekte
│ │ ├── chunker.py # Text-Zerlegung (Profiling)
│ │ ├── edges.py # Edge-Datenstrukturen
│ │ ├── derive_edges.py # Logik der Kantenableitung (WP03)
│ │ ├── graph_adapter.py # Subgraph & Reverse-Lookup (WP04b)
│ │ └── retriever.py # Scoring, Expansion & Explanation (WP04a/b)
│ ├── models/ # Pydantic DTOs
│ ├── routers/
│ │ ├── query.py # Such-Endpunkt
│ │ ├── chat.py # Hybrid Router & Decision Engine (WP06)
│ │ ├── feedback.py # Feedback-Endpunkt (WP04c)
│ │ └── ...
│ ├── services/
│ │ ├── llm_service.py # Ollama Client mit Timeout & Raw-Mode
│ │ ├── feedback_service.py # JSONL Logging (WP04c)
│ │ └── embeddings_client.py
├── config/
│ ├── types.yaml # Typ-Definitionen (Import-Zeit)
│ ├── retriever.yaml # Scoring-Gewichte (Laufzeit)
│ ├── decision_engine.yaml # Strategien & Keywords (WP06)
│ └── prompts.yaml # LLM System-Prompts & Templates (WP06)
├── data/
│ └── logs/ # Lokale JSONL-Logs (WP04c)
├── scripts/
│ ├── import_markdown.py # Haupt-Importer CLI
│ ├── payload_dryrun.py # Diagnose: JSON-Generierung ohne DB
│ └── edges_full_check.py # Diagnose: Graph-Integrität
└── tests/ # Pytest Suite
2. Datenmodell & Collections (Qdrant)
Das Datenmodell verteilt sich auf drei Collections, definiert durch COLLECTION_PREFIX (Standard: mindnet).
2.1 Notes Collection (<prefix>_notes)
Repräsentiert die Metadaten einer Datei.
- Zweck: Filterung, Metadaten-Haltung, Vererbung von Eigenschaften an Chunks.
- Schema (Payload):
Feld Datentyp Beschreibung Herkunft note_idKeyword Stabile ID (UUIDv5 oder Slug). Frontmatter titleText Titel der Notiz. Frontmatter typeKeyword Logischer Typ (z.B. concept).types.yamlResolverretriever_weightFloat Wichtigkeit im Retrieval. types.yamlchunk_profileKeyword Genutztes Chunking-Profil. types.yamledge_defaultsList[Str] Aktive Default-Relationen. types.yamltagsList[Kw] Tags zur Filterung. Frontmatter updatedInteger Zeitstempel (z.B. YYYYMMDD...). File Stats fulltextText Gesamter Inhalt (für Export/Rekonstruktion). Body
2.2 Chunks Collection (<prefix>_chunks)
Die atomaren Sucheinheiten.
- Zweck: Vektorsuche (Embeddings), Granulares Ergebnis.
- Schema (Payload):
Feld Datentyp Beschreibung chunk_idKeyword Deterministisch: {note_id}#c{index:02d}.note_idKeyword Referenz zur Note. typeKeyword Kopie des Note-Typs (Denormalisiert für Filter). textText Reiner Inhalt (ohne Overlap). Anzeige-Text. windowText Kontext-Fenster (mit Overlap). Embedding-Basis. ordInteger Sortierreihenfolge (1..N). retriever_weightFloat Vererbt von Note. chunk_profileKeyword Vererbt von Note. neighbors_prevList[Str] ID des Vorgänger-Chunks. neighbors_nextList[Str] ID des Nachfolger-Chunks.
2.3 Edges Collection (<prefix>_edges)
Gerichtete Kanten. Massiv erweitert in WP03 für Provenienz-Tracking.
- Zweck: Graph-Traversal, Kontext-Anreicherung.
- Schema (Payload):
Feld Datentyp Beschreibung Wertebereich (Bsp.) edge_idKeyword Hash aus (src, dst, kind). source_idKeyword ID des Start-Chunks. target_idKeyword ID des Ziel-Chunks (oder Note-Titel). note_idKeyword Note, die diese Kante definiert. kindKeyword Art der Beziehung. references,depends_on,nextscopeKeyword Geltungsbereich. Immer "chunk"(v2.2).rule_idKeyword Herkunftsregel. explicit:wikilink,inline:relconfidenceFloat Vertrauenswürdigkeit (0.0-1.0). 1.0, 0.95, 0.7
3. Konfiguration
Die Logik ist ausgelagert in YAML-Dateien.
3.1 Typ-Registry (config/types.yaml)
Steuert den Import-Prozess.
-
Priorität: Frontmatter > Pfad > Default.
-
Inhalt (Beispiel):
types: concept: chunk_profile: medium edge_defaults: ["references", "related_to"] retriever_weight: 0.60
3.2 Retriever-Config (config/retriever.yaml)
Steuert das Scoring zur Laufzeit (WP04a).
-
Inhalt (Beispiel):
scoring: semantic_weight: 1.0 edge_weight: 0.7 centrality_weight: 0.5
3.3 Decision Engine (config/decision_engine.yaml)
Neu in WP06: Steuert den Intent-Router.
- Definiert Strategien (
DECISION,EMPATHY,CODING,FACT). - Definiert
trigger_keywordsundinject_types(Late Binding). - Definiert LLM-Router-Settings (
llm_fallback_enabled).
3.4 Prompts (config/prompts.yaml)
Steuert die LLM-Persönlichkeit und Templates.
- Enthält Templates für alle Strategien (z.B.
decision_template,empathy_template,technical_template).
3.5 Environment (.env)
Erweiterung für LLM-Steuerung:
MINDNET_LLM_MODEL=phi3:mini
MINDNET_OLLAMA_URL=http://127.0.0.1:11434
MINDNET_LLM_TIMEOUT=300.0 # Neu: Erhöht für CPU-Inference Cold-Starts
MINDNET_DECISION_CONFIG="config/decision_engine.yaml"
4. Import-Pipeline (Markdown → Qdrant)
Das Skript scripts/import_markdown.py orchestriert den Prozess.
4.1 Verarbeitungsschritte
- Discovery & Parsing:
- Einlesen der
.mdDateien. Hash-Vergleich (Body/Frontmatter) zur Erkennung von Änderungen.
- Einlesen der
- Typauflösung:
- Laden der
types.yaml. Bestimmen des effektiven Typs und deredge_defaults.
- Laden der
- Chunking:
- Zerlegung via
chunker.pybasierend aufchunk_profile(z.B.by_heading,short,long). - Trennung von
text(Kern) undwindow(Embedding-Kontext).
- Zerlegung via
- Kantenableitung (Edge Derivation):
Die
derive_edges.pyerzeugt Kanten in strikter Reihenfolge:- Inline-Edges:
[[rel:depends_on X]]→kind=depends_on,rule_id=inline:rel,conf=0.95. - Callout-Edges:
> [!edge] related_to: [[X]]→kind=related_to,rule_id=callout:edge,conf=0.90. - Explizite Referenzen:
[[X]]→kind=references,rule_id=explicit:wikilink,conf=1.0. - Typ-Defaults: Für jede Referenz werden Zusatzkanten gemäß
edge_defaultserzeugt (z.B.project->depends_on).rule_id=edge_defaults:...,conf=0.7. - Struktur:
belongs_to,next,prev(automatisch).
- Inline-Edges:
- Upsert:
- Schreiben in Qdrant. Nutzung von
--purge-before-upsertfür saubere Updates.
- Schreiben in Qdrant. Nutzung von
5. Retriever-Architektur & Scoring
Der Retriever (app/core/retriever.py) unterstützt zwei Modi. Für den Chat wird zwingend der Hybrid-Modus genutzt.
5.1 Betriebsmodi
- Semantic: Reine Vektorsuche. Schnell.
- Hybrid: Vektorsuche + Graph-Expansion (Tiefe N) + Re-Ranking.
5.2 Scoring-Formel (WP04a)
Die Relevanzberechnung ist nun eine gewichtete Summe:
TotalScore = (W_{sem} \cdot S_{sem} \cdot \max(W_{type}, 0)) + (W_{edge} \cdot B_{edge}) + (W_{cent} \cdot B_{cent})
- Komponenten:
S_{sem}: Semantic Score (Cosine Similarity).W_{type}:retriever_weight(aus Note/Chunk Payload).B_{edge}: Edge-Bonus (Summe der Konfidenzen eingehender relevanter Kanten).B_{cent}: Centrality-Bonus (im lokalen Subgraphen).
- Gewichte ($W$): Stammen aus
retriever.yaml.
5.3 Explanation Layer (WP04b)
Der Retriever kann Ergebnisse erklären (explain=True).
- Logik:
- Berechnung des
ScoreBreakdown(Anteile von Semantik, Graph, Typ). - Analyse des lokalen Subgraphen mittels
graph_adapter.py. - Incoming Edges (Authority): Wer zeigt auf diesen Treffer? (z.B. "Referenziert von...")
- Outgoing Edges (Hub): Worauf zeigt dieser Treffer? (z.B. "Verweist auf...")
- Berechnung des
- Output:
QueryHitenthält einexplanationObjekt mit menschenlesbarenreasonsundrelated_edges.
5.4 Graph-Expansion
Der Hybrid-Modus lädt dynamisch die Nachbarschaft der Top-K Vektor-Treffer ("Seeds") über graph_adapter.expand. Dies baut einen temporären NetworkX-artigen Graphen im Speicher (Klasse Subgraph), auf dem Boni berechnet werden.
6. RAG & Chat Architektur (WP06 Hybrid Router)
Der Flow für eine Chat-Anfrage (/chat) wurde in WP06 auf eine Configuration-Driven Architecture umgestellt. Der ChatRouter (app/routers/chat.py) fungiert als zentraler Dispatcher.
6.1 Architektur-Pattern: Intent Router
Die Behandlung einer Anfrage ist nicht mehr hartkodiert, sondern wird dynamisch zur Laufzeit entschieden.
- Input: User Message.
- Config:
config/decision_engine.yaml(Strategien & Keywords). - Komponenten:
- Fast Path: Keyword Matching (CPU-schonend).
- Slow Path: LLM-basierter Semantic Router (für subtile Intents).
6.2 Schritt 1: Intent Detection (Hybrid)
Der Router ermittelt die Absicht (Intent) des Nutzers.
- Keyword Scan (Fast Path):
- Iteration über alle Strategien in
decision_engine.yaml. - Prüfung auf
trigger_keywords. - Best Match: Bei mehreren Treffern gewinnt das längste/spezifischste Keyword (Robustheit gegen Shadowing).
- Iteration über alle Strategien in
- LLM Fallback (Slow Path):
- Nur aktiv, wenn
llm_fallback_enabled: true. - Greift, wenn keine Keywords gefunden wurden.
- Sendet die Query an das LLM mit einem Klassifizierungs-Prompt (
llm_router_prompt). - Ergebnis:
EMPATHY,DECISION,CODINGoderFACT(Default).
- Nur aktiv, wenn
6.3 Schritt 2: Strategy Resolution (Late Binding)
Basierend auf dem Intent lädt der Router die Parameter:
- inject_types: Liste der Notiz-Typen, die erzwungen werden sollen (z.B.
["value", "goal"]beiDECISION). - prompt_template: Schlüssel für das Template in
prompts.yaml(z.B.decision_template,empathy_template).
6.4 Schritt 3: Multi-Stage Retrieval
- Primary Retrieval: Hybride Suche nach der User-Query (findet Fakten).
- Strategic Retrieval (Conditional): Wenn
inject_typesdefiniert sind, erfolgt eine zweite Suche, die explizit auf diese Typen filtert. - Merge: Ergebnisse werden dedupliziert zusammengeführt.
6.5 Schritt 4: Generation & Response
- Context Enrichment: Metadaten (
[VALUE],[GOAL],[SCORE]) werden in den Context-String injiziert, um dem LLM die Rolle des Textstücks zu signalisieren. - Templating: Das LLM erhält den Prompt basierend auf dem gewählten Template.
- Execution: Der
LLMServiceführt den Call aus. Ein konfigurierbarer Timeout (MINDNET_LLM_TIMEOUT) fängt Cold-Start-Verzögerungen auf CPU-Hardware ab. - Response: Rückgabe enthält Antworttext, Quellenliste und den erkannten
intent(für Debugging/Frontend).
7. Feedback & Logging Architektur (WP04c)
Mindnet implementiert ein "Data Flywheel" zur späteren Optimierung (Self-Tuning).
7.1 Komponenten
- Feedback Service (
app/services/feedback_service.py): Kapselt die Schreibzugriffe. - Storage: Lokales Dateisystem (
data/logs/), Format JSONL (Line-delimited JSON).
7.2 Log-Dateien
search_history.jsonl:- Speichert jede Anfrage an
/queryund/chat. - Enthält:
query_id,query_text,timestamp,hits(inkl.score_breakdownSnapshots). - Zweck: Trainingsdaten ("Was hat das System gesehen?").
- Speichert jede Anfrage an
feedback.jsonl:- Speichert User-Reaktionen an
/feedback. - Enthält:
query_id,node_id,score(1-5),comment. - Zweck: Labels ("War es gut?").
- Speichert User-Reaktionen an
7.3 Traceability
Die query_id (UUIDv4) wird im /query Response generiert und muss vom Client beim /feedback Aufruf mitgesendet werden. Dies ermöglicht den Join zwischen Snapshot und Bewertung.
8. Indizes & Performance
Damit Qdrant performant bleibt, sind Payload-Indizes essenziell.
Erforderliche Indizes:
- Notes:
note_id,type,tags. - Chunks:
note_id,chunk_id,type. - Edges:
source_id,target_id,kind,scope,note_id.
Validierung erfolgt über tests/ensure_indexes_and_show.py.
9. Offene Punkte / Known Limitations
- Multi-Target Inline-Relations:
rel: similar_to [[A]] [[B]]wird aktuell parser-seitig nicht unterstützt.- Workaround: Zwei separate Inline-Links
[[rel:similar_to A]] [[rel:similar_to B]].
- Unresolved Targets:
- Kanten zu Notizen, die noch nicht existieren, werden mit
target_id="Titel"angelegt. - Heilung durch
scripts/resolve_unresolved_references.pymöglich.
- Kanten zu Notizen, die noch nicht existieren, werden mit
- Vektor-Konfiguration für Edges:
mindnet_edgeshat aktuell keine Vektoren (vectors = null). Eine semantische Suche auf Kanten ist noch nicht möglich.