mindnet/docs/mindnet_technical_architecture.md
2025-12-09 17:11:03 +01:00

355 lines
17 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-09
**Status:** **FINAL** (Integrierter Stand WP01WP06)
**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)**.
---
<details>
<summary>📖 <b>Inhaltsverzeichnis (Klicken zum Öffnen)</b></summary>
- [Mindnet v2.2 Technische Architektur](#mindnet-v22--technische-architektur)
- [](#)
- [1. Systemüberblick](#1-systemüberblick)
- [1.1 Architektur-Zielbild](#11-architektur-zielbild)
- [1.2 Verzeichnisstruktur \& Komponenten (Post-WP06)](#12-verzeichnisstruktur--komponenten-post-wp06)
- [2. Datenmodell \& Collections (Qdrant)](#2-datenmodell--collections-qdrant)
- [2.1 Notes Collection (`<prefix>_notes`)](#21-notes-collection-prefix_notes)
- [2.2 Chunks Collection (`<prefix>_chunks`)](#22-chunks-collection-prefix_chunks)
- [2.3 Edges Collection (`<prefix>_edges`)](#23-edges-collection-prefix_edges)
- [3. Konfiguration](#3-konfiguration)
- [3.1 Typ-Registry (`config/types.yaml`)](#31-typ-registry-configtypesyaml)
- [3.2 Retriever-Config (`config/retriever.yaml`)](#32-retriever-config-configretrieveryaml)
- [3.3 Decision Engine (`config/decision_engine.yaml`)](#33-decision-engine-configdecision_engineyaml)
- [3.4 Prompts (`config/prompts.yaml`)](#34-prompts-configpromptsyaml)
- [3.5 Environment (`.env`)](#35-environment-env)
- [4. Import-Pipeline (Markdown → Qdrant)](#4-import-pipeline-markdown--qdrant)
- [4.1 Verarbeitungsschritte](#41-verarbeitungsschritte)
- [5. Retriever-Architektur \& Scoring](#5-retriever-architektur--scoring)
- [5.1 Betriebsmodi](#51-betriebsmodi)
- [5.2 Scoring-Formel (WP04a)](#52-scoring-formel-wp04a)
- [5.3 Explanation Layer (WP04b)](#53-explanation-layer-wp04b)
- [5.4 Graph-Expansion](#54-graph-expansion)
- [6. RAG \& Chat Architektur (WP06 Hybrid Router)](#6-rag--chat-architektur-wp06-hybrid-router)
- [6.1 Architektur-Pattern: Intent Router](#61-architektur-pattern-intent-router)
- [6.2 Schritt 1: Intent Detection (Hybrid)](#62-schritt-1-intent-detection-hybrid)
- [6.3 Schritt 2: Strategy Resolution (Late Binding)](#63-schritt-2-strategy-resolution-late-binding)
- [6.4 Schritt 3: Multi-Stage Retrieval](#64-schritt-3-multi-stage-retrieval)
- [6.5 Schritt 4: Generation \& Response](#65-schritt-4-generation--response)
- [7. Feedback \& Logging Architektur (WP04c)](#7-feedback--logging-architektur-wp04c)
- [7.1 Komponenten](#71-komponenten)
- [7.2 Log-Dateien](#72-log-dateien)
- [7.3 Traceability](#73-traceability)
- [8. Indizes \& Performance](#8-indizes--performance)
- [9. Offene Punkte / Known Limitations](#9-offene-punkte--known-limitations)
</details>
---
## 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`, `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_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 Decision Engine (`config/decision_engine.yaml`)
**Neu in WP06:** Steuert den Intent-Router.
* Definiert Strategien (`DECISION`, `EMPATHY`, `CODING`, `FACT`).
* Definiert `trigger_keywords` und `inject_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
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 (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.
1. **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).
2. **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`, `CODING` oder `FACT` (Default).
### 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"]` bei `DECISION`).
* **prompt_template:** Schlüssel für das Template in `prompts.yaml` (z.B. `decision_template`, `empathy_template`).
### 6.4 Schritt 3: Multi-Stage Retrieval
1. **Primary Retrieval:** Hybride Suche nach der User-Query (findet Fakten).
2. **Strategic Retrieval (Conditional):** Wenn `inject_types` definiert sind, erfolgt eine zweite Suche, die explizit auf diese Typen filtert.
3. **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 `LLMService` fü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
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.