mindnet/docs/mindnet_technical_architecture.md
2025-12-08 16:14:52 +01:00

295 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Mindnet v2.2 Technische Architektur
**Datei:** `docs/mindnet_technical_architecture_v2.2.md`
**Stand:** 2025-12-08
**Status:** **FINAL** (Integrierter Stand WP01WP05)
**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 (LLM/Chat)**.
---
## 1. Systemüberblick
### 1.1 Architektur-Zielbild
Mindnet ist ein **lokales RAG-System (Retrieval Augmented Generation)**.
1. **Source:** Markdown-Notizen in einem Vault (Obsidian-kompatibel).
2. **Pipeline:** Ein Python-Importer transformiert diese in **Notes**, **Chunks** und **Edges**.
3. **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).
4. **Service:** Eine FastAPI-Anwendung stellt Endpunkte für **Semantische** und **Hybride Suche** sowie **Feedback** bereit.
5. **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`, `prompts.yaml`).
### 1.2 Verzeichnisstruktur & Komponenten (Post-WP05)
/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, ChatRequest)
│ ├── routers/
│ │ ├── query.py # Such-Endpunkt
│ │ ├── chat.py # RAG-Chat Endpunkt (WP05)
│ │ ├── feedback.py # Feedback-Endpunkt (WP04c)
│ │ └── ...
│ ├── services/
│ │ ├── llm_service.py # Ollama Client (WP05)
│ │ ├── feedback_service.py # JSONL Logging (WP04c)
│ │ └── embeddings_client.py
├── config/
│ ├── types.yaml # Typ-Definitionen (Import-Zeit)
│ ├── retriever.yaml # Scoring-Gewichte (Laufzeit)
│ └── prompts.yaml # LLM System-Prompts & Templates (WP05)
├── 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_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 (`<prefix>_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 (`<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_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 (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 Prompts (`config/prompts.yaml`)
Steuert die LLM-Persönlichkeit (WP05).
* **Inhalt (Beispiel):**
system_prompt: |
Du bist 'mindnet', das KI-Gedächtnis.
rag_template: |
QUELLEN (WISSEN): {context_str}
### 3.4 Environment (`.env`)
Erweiterung für LLM-Steuerung:
MINDNET_LLM_MODEL=phi3:mini
MINDNET_OLLAMA_URL=http://127.0.0.1:11434
---
## 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`) 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...")
* **Output:** `QueryHit` enthält ein `explanation` Objekt mit menschenlesbaren `reasons` und `related_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 (WP05)
Der Flow für eine Chat-Anfrage (`/chat`) ist eine Pipeline aus vier Schritten.
### 6.1 Schritt 1: Intent & Retrieval
* Der `ChatRouter` ruft den `Retriever` im **Hybrid-Modus** auf.
* Dies stellt sicher, dass logisch verknüpfte Notizen (z.B. Entscheidungen zu einem Projekt) gefunden werden.
### 6.2 Schritt 2: Context Enrichment (Das "Context Intelligence" Pattern)
* Der Router (`_build_enriched_context`) reichert die Chunks mit Metadaten an:
* **Typ-Injection:** `[DECISION]`, `[PROJECT]`, `[CONCEPT]`.
* **Score-Transparenz:** `(Score: 0.75)`.
* **Formatierung:** Markdown-Header pro Quelle.
* *Ziel:* Kleine Modelle (SLMs wie Phi-3) benötigen diese expliziten Signale, um komplexe Zusammenhänge ("Warum nutzen wir X?") aus den Texten abzuleiten.
### 6.3 Schritt 3: Generation (LLM Service)
* **Service:** `app/services/llm_service.py` nutzt `httpx` (async).
* **Backend:** Ollama (lokal).
* **Modell:** `phi3:mini` (3.8B Parameter).
* **Timeout:** Erhöht auf 300s für CPU-Inference Sicherheit.
### 6.4 Schritt 4: Response & Traceability
* Die Antwort enthält die generierte Nachricht **und** die verwendeten Quellen (`sources`).
* Eine `query_id` wird generiert und durchgereicht, um späteres Feedback (WP04c) zu ermöglichen.
---
## 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
1. **`search_history.jsonl`**:
* Speichert jede Anfrage an `/query` und `/chat`.
* Enthält: `query_id`, `query_text`, `timestamp`, `hits` (inkl. `score_breakdown` Snapshots).
* Zweck: Trainingsdaten ("Was hat das System gesehen?").
2. **`feedback.jsonl`**:
* Speichert User-Reaktionen an `/feedback`.
* Enthält: `query_id`, `node_id`, `score` (1-5), `comment`.
* Zweck: Labels ("War es gut?").
### 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`.
* **Edges:** `source_id`, `target_id`, `kind`, `scope`, `note_id`.
Validierung erfolgt über `tests/ensure_indexes_and_show.py`.
---
## 9. 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.