11 KiB
Mindnet v2.2 – Technische Architektur
Datei: docs/mindnet_technical_architecture_v2.2.md
Stand: 2025-12-08
Status: FINAL (Integrierter Stand WP01–WP04c)
Quellen: mindnet_technical_architecture.md, chunking_strategy.md, Handbuch.md, wp04_retriever_scoring.md.
Ziel dieses Dokuments: Vollständige, konsolidierte Beschreibung der aktuellen technischen Architektur von Mindnet v2.2. Es definiert die Datenstrukturen in Qdrant, die Verarbeitungspipelines (Importer, Chunker, Edges) und die Retrieval-Logik. Es bildet den aktuellen Implementierungsstand ab.
1. Systemüberblick
1.1 Architektur-Zielbild
Mindnet ist ein persönliches Wissensnetz. Technisch bedeutet das:
- 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.
Das System arbeitet deterministisch (stabile IDs) und ist konfigurationsgetrieben (types.yaml, retriever.yaml).
1.2 Verzeichnisstruktur & Komponenten
Die Codebasis ist modular aufgebaut.
/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 (QueryHit, Explanation, FeedbackRequest)
│ ├── routers/
│ │ ├── query.py # Such-Endpunkt
│ │ ├── feedback.py # Feedback-Endpunkt (WP04c)
│ │ └── ...
│ ├── services/
│ │ ├── feedback_service.py # Logging-Logik (WP04c)
│ │ └── embeddings_client.py
├── config/
│ ├── types.yaml # Typ-Definitionen (Import-Zeit)
│ └── retriever.yaml # Scoring-Gewichte (Laufzeit)
├── 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. 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:
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:
scoring: semantic_weight: 1.0 edge_weight: 0.7 centrality_weight: 0.5
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) realisiert die Logik hinter /query.
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. Feedback & Logging Architektur (WP04c)
Mindnet implementiert ein "Data Flywheel" zur späteren Optimierung (Self-Tuning).
6.1 Komponenten
- Feedback Service (
app/services/feedback_service.py): Kapselt die Schreibzugriffe. - Storage: Lokales Dateisystem (
data/logs/), Format JSONL (Line-delimited JSON).
6.2 Log-Dateien
search_history.jsonl:- Speichert jede Anfrage an
/query. - 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
6.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.
7. Indizes & Performance
Damit Qdrant performant bleibt, sind Payload-Indizes essenziell.
Erforderliche Indizes:
- Notes:
note_id,type,tags. - Chunks:
note_id,chunk_id. - Edges:
source_id,target_id,kind,scope,note_id.
Validierung erfolgt über tests/ensure_indexes_and_show.py.
8. 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.