356 lines
17 KiB
Markdown
356 lines
17 KiB
Markdown
# 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)**.
|
||
|
||
---
|
||
<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. |
|
||
| `type` | Keyword | **Kopie des Note-Typs** (Denormalisiert für Filter). |
|
||
| `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`, `type`.
|
||
* **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. |