# Mindnet v2.2 – Technische Architektur **Datei:** `docs/mindnet_technical_architecture_v2.2.md` **Stand:** 2025-12-07 **Status:** **FINAL** (Integrierter Stand WP01–WP04a) **Quellen:** `mindnet_technical_architecture.md`, `chunking_strategy.md`, `Handbuch.md`, `wp04_retriever_scoring.md`, `Überarbeitungshinweise_WP03.md`, `Überarbeitungshinweise_WP04.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: 1. **Source:** Markdown-Notizen in einem Vault (Obsidian-kompatibel). 2. **Pipeline:** Ein Python-Importer transformiert diese in **Notes**, **Chunks** und **Edges**. 3. **Storage:** Speicherung in **Qdrant** (Vektor-Datenbank) in drei getrennten Collections. 4. **Service:** Eine FastAPI-Anwendung stellt Endpunkte für **Semantische** und **Hybride Suche** 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) │ │ └── retriever.py # Scoring & Graph-Expansion (WP04) │ ├── models/ # Pydantic DTOs (QueryRequest, etc.) │ └── routers/ # API Routen ├── config/ │ ├── types.yaml # Typ-Definitionen (Import-Zeit) │ └── retriever.yaml # Scoring-Gewichte (Laufzeit) ├── 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 (`_notes`) Repräsentiert die Metadaten einer Datei. * **Zweck:** Filterung, Metadaten-Haltung, Vererbung von Eigenschaften an Chunks. * **Schema (Payload):** | Feld | Datentyp | Beschreibung | Herkunft | | :--- | :--- | :--- | :--- | | `note_id` | Keyword | Stabile ID (UUIDv5 oder Slug). | Frontmatter | | `title` | Text | Titel der Notiz. | Frontmatter | | `type` | Keyword | Logischer Typ (z.B. `concept`). | `types.yaml` Resolver | | `retriever_weight` | Float | Wichtigkeit im Retrieval. | `types.yaml` | | `chunk_profile` | Keyword | Genutztes Chunking-Profil. | `types.yaml` | | `edge_defaults` | List[Str] | Aktive Default-Relationen. | `types.yaml` | | `tags` | List[Kw] | Tags zur Filterung. | Frontmatter | | `updated` | Integer | Zeitstempel (z.B. YYYYMMDD...). | File Stats | | `fulltext` | Text | Gesamter Inhalt (für Export/Rekonstruktion). | Body | ### 2.2 Chunks Collection (`_chunks`) Die atomaren Sucheinheiten. * **Zweck:** Vektorsuche (Embeddings), Granulares Ergebnis. * **Schema (Payload):** | Feld | Datentyp | Beschreibung | | :--- | :--- | :--- | | `chunk_id` | Keyword | Deterministisch: `{note_id}#c{index:02d}`. | | `note_id` | Keyword | Referenz zur Note. | | `text` | Text | **Reiner Inhalt** (ohne Overlap). Anzeige-Text. | | `window` | Text | **Kontext-Fenster** (mit Overlap). Embedding-Basis. | | `ord` | Integer | Sortierreihenfolge (1..N). | | `retriever_weight` | Float | Vererbt von Note. | | `chunk_profile` | Keyword | Vererbt von Note. | | `neighbors_prev` | List[Str] | ID des Vorgänger-Chunks. | | `neighbors_next` | List[Str] | ID des Nachfolger-Chunks. | ### 2.3 Edges Collection (`_edges`) Gerichtete Kanten. Massiv erweitert in WP03 für Provenienz-Tracking. * **Zweck:** Graph-Traversal, Kontext-Anreicherung. * **Schema (Payload):** | Feld | Datentyp | Beschreibung | Wertebereich (Bsp.) | | :--- | :--- | :--- | :--- | | `edge_id` | Keyword | Hash aus (src, dst, kind). | | | `source_id` | Keyword | ID des Start-Chunks. | | | `target_id` | Keyword | ID des Ziel-Chunks (oder Note-Titel). | | | `note_id` | Keyword | Note, die diese Kante definiert. | | | `kind` | Keyword | Art der Beziehung. | `references`, `depends_on`, `next` | | `scope` | Keyword | Geltungsbereich. | Immer `"chunk"` (v2.2). | | `rule_id` | Keyword | Herkunftsregel. | `explicit:wikilink`, `inline:rel` | | `confidence` | Float | 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:** ```yaml 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:** ```yaml 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 1. **Discovery & Parsing:** * Einlesen der `.md` Dateien. Hash-Vergleich (Body/Frontmatter) zur Erkennung von Änderungen. 2. **Typauflösung:** * Laden der `types.yaml`. Bestimmen des effektiven Typs und der `edge_defaults`. 3. **Chunking:** * Zerlegung via `chunker.py` basierend auf `chunk_profile` (z.B. `by_heading`, `short`, `long`). * Trennung von `text` (Kern) und `window` (Embedding-Kontext). 4. **Kantenableitung (Edge Derivation):** Die `derive_edges.py` erzeugt Kanten in strikter Reihenfolge: 1. **Inline-Edges:** `[[rel:depends_on X]]` → `kind=depends_on`, `rule_id=inline:rel`, `conf=0.95`. 2. **Callout-Edges:** `> [!edge] related_to: [[X]]` → `kind=related_to`, `rule_id=callout:edge`, `conf=0.90`. 3. **Explizite Referenzen:** `[[X]]` → `kind=references`, `rule_id=explicit:wikilink`, `conf=1.0`. 4. **Typ-Defaults:** Für jede Referenz werden Zusatzkanten gemäß `edge_defaults` erzeugt (z.B. `project` -> `depends_on`). `rule_id=edge_defaults:...`, `conf=0.7`. 5. **Struktur:** `belongs_to`, `next`, `prev` (automatisch). 5. **Upsert:** * Schreiben in Qdrant. Nutzung von `--purge-before-upsert` für saubere Updates. --- ## 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 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`-Graphen im Speicher, auf dem Boni berechnet werden. --- ## 6. 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`. --- ## 7. Offene Punkte / Known Limitations 1. **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]]`. 2. **Unresolved Targets:** * Kanten zu Notizen, die noch nicht existieren, werden mit `target_id="Titel"` angelegt. * Heilung durch `scripts/resolve_unresolved_references.py` möglich. 3. **Vektor-Konfiguration für Edges:** * `mindnet_edges` hat aktuell keine Vektoren (`vectors = null`). Eine semantische Suche *auf Kanten* ist noch nicht möglich.