All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 3s
457 lines
12 KiB
Markdown
457 lines
12 KiB
Markdown
---
|
|
doc_type: concept
|
|
audience: architect, developer
|
|
scope: architecture, design_patterns, modularization
|
|
status: active
|
|
version: 2.9.1
|
|
context: "Architektur-Patterns, Design-Entscheidungen und modulare Struktur von Mindnet. Basis für Verständnis und Erweiterungen."
|
|
---
|
|
|
|
# Architektur-Patterns & Design-Entscheidungen
|
|
|
|
Dieses Dokument beschreibt die zentralen Architektur-Patterns und Design-Entscheidungen, die Mindnet prägen. Es dient als Referenz für Entwickler und Architekten, um das System zu verstehen und konsistent zu erweitern.
|
|
|
|
## 1. Kern-Paradigmen
|
|
|
|
### 1.1 Filesystem First (Source of Truth)
|
|
|
|
**Prinzip:** Markdown-Dateien im Vault sind die einzige Quelle der Wahrheit. Die Datenbank (Qdrant) ist ein abgeleiteter Index.
|
|
|
|
**Implikationen:**
|
|
- Dateien werden immer direkt von der Festplatte gelesen (z.B. im Editor)
|
|
- Datenbank-Inhalte können aus Markdown rekonstruiert werden
|
|
- Änderungen erfolgen primär im Vault, nicht in der DB
|
|
- Die Datenbank ist ein Cache, kein primärer Speicher
|
|
|
|
**Code-Beispiele:**
|
|
- `ui_callbacks.py`: Liest Dateien direkt von Disk
|
|
- `ingestion_processor.py`: Schreibt zuerst auf Disk, dann in DB
|
|
|
|
### 1.2 Late Binding (Späte Semantik)
|
|
|
|
**Prinzip:** Struktur und Interpretation werden in Konfigurationen definiert, nicht im Code.
|
|
|
|
**Implikationen:**
|
|
- Neue Note-Typen werden in `types.yaml` definiert, nicht im Code
|
|
- Prompt-Templates sind in `prompts.yaml` konfigurierbar
|
|
- Edge-Typen werden in `edge_vocabulary.md` verwaltet
|
|
- Die "Persönlichkeit" ist Config, kein Code
|
|
|
|
**Vorteile:**
|
|
- Erweiterbarkeit ohne Code-Änderungen
|
|
- A/B-Testing von Prompt-Strategien
|
|
- Anpassung an verschiedene Use-Cases
|
|
|
|
### 1.3 Virtual Schema Layer
|
|
|
|
**Prinzip:** Markdown-Dateien benötigen nur minimale Frontmatter-Angaben. Komplexität wird zur Laufzeit injiziert.
|
|
|
|
**Beispiel:**
|
|
```yaml
|
|
# Minimal im Frontmatter
|
|
---
|
|
id: 20250101-test
|
|
title: Test
|
|
type: project
|
|
---
|
|
|
|
# Im Code wird zur Laufzeit ergänzt:
|
|
# - chunking_profile aus types.yaml
|
|
# - retriever_weight aus types.yaml
|
|
# - edge_defaults aus types.yaml
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Robuste Markdown-Dateien (weniger Breaking Changes)
|
|
- Zentrale Verwaltung von Logik
|
|
- Einfache Migration zwischen Versionen
|
|
|
|
---
|
|
|
|
## 2. Modulare Architektur (WP-14)
|
|
|
|
### 2.1 Paket-Struktur
|
|
|
|
Seit WP-14 ist die Core-Logik in spezialisierte Pakete unterteilt:
|
|
|
|
```
|
|
app/core/
|
|
├── chunking/ # Text-Segmentierung
|
|
│ ├── chunking_strategies.py # Sliding/Heading-Strategien
|
|
│ ├── chunking_processor.py # Orchestrierung
|
|
│ └── chunking_propagation.py # Edge-Vererbung
|
|
├── database/ # Qdrant-Infrastruktur
|
|
│ ├── qdrant.py # Client & Config
|
|
│ └── qdrant_points.py # Point-Mapping
|
|
├── graph/ # Graph-Logik
|
|
│ ├── graph_subgraph.py # Expansion
|
|
│ └── graph_weights.py # Scoring
|
|
├── ingestion/ # Import-Pipeline
|
|
│ ├── ingestion_processor.py # Two-Pass Workflow
|
|
│ └── ingestion_validation.py # Mistral-safe Parsing
|
|
├── parser/ # Markdown-Parsing
|
|
│ ├── parsing_markdown.py # Frontmatter/Body
|
|
│ └── parsing_scanner.py # File-Scan
|
|
└── retrieval/ # Suche & Scoring
|
|
├── retriever.py # Orchestrator
|
|
└── retriever_scoring.py # Mathematik
|
|
```
|
|
|
|
### 2.2 Design-Pattern: Proxy-Adapter (Abwärtskompatibilität)
|
|
|
|
**Problem:** Nach WP-14 Modularisierung müssen alte Import-Pfade weiter funktionieren.
|
|
|
|
**Lösung:** Proxy-Module leiten Anfragen an neue Pakete weiter.
|
|
|
|
**Beispiel:**
|
|
```python
|
|
# app/core/retriever.py (Proxy)
|
|
from .retrieval.retriever import (
|
|
Retriever, hybrid_retrieve, semantic_retrieve
|
|
)
|
|
__all__ = ["Retriever", "hybrid_retrieve", "semantic_retrieve"]
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Keine Breaking Changes für bestehenden Code
|
|
- Graduelle Migration möglich
|
|
- Alte Scripts funktionieren weiter
|
|
|
|
### 2.3 Design-Pattern: Singleton (Edge Registry)
|
|
|
|
**Problem:** Edge Registry muss konsistent über alle Services sein.
|
|
|
|
**Lösung:** Singleton-Pattern mit Lazy Loading.
|
|
|
|
**Code:**
|
|
```python
|
|
# app/services/edge_registry.py
|
|
class EdgeRegistry:
|
|
_instance = None
|
|
|
|
def __new__(cls):
|
|
if cls._instance is None:
|
|
cls._instance = super().__new__(cls)
|
|
return cls._instance
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Einheitliche Validierung
|
|
- Keine Duplikation von Vokabular-Daten
|
|
- Thread-safe Initialisierung
|
|
|
|
---
|
|
|
|
## 3. Asynchrone Verarbeitung
|
|
|
|
### 3.1 Background Tasks Pattern
|
|
|
|
**Problem:** Ingestion kann lange dauern (LLM-Calls, Embeddings). Blockiert API nicht.
|
|
|
|
**Lösung:** FastAPI Background Tasks für non-blocking Verarbeitung.
|
|
|
|
**Flow:**
|
|
1. API empfängt Request (`/ingest/save`)
|
|
2. Datei wird sofort auf Disk geschrieben
|
|
3. API antwortet mit `status: "queued"`
|
|
4. Background Task startet Ingestion asynchron
|
|
5. User kann weiterarbeiten
|
|
|
|
**Code:**
|
|
```python
|
|
@router.post("/save")
|
|
async def save_note(req: SaveRequest, background_tasks: BackgroundTasks):
|
|
# Sofortige Persistenz
|
|
write_to_disk(req.markdown_content)
|
|
|
|
# Async Processing
|
|
background_tasks.add_task(run_ingestion_task, ...)
|
|
|
|
return SaveResponse(status="queued", ...)
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Keine Timeouts bei großen Dateien
|
|
- Bessere User Experience
|
|
- System bleibt responsiv
|
|
|
|
### 3.2 Traffic Control (Semaphore Pattern)
|
|
|
|
**Problem:** Parallele LLM-Calls können System überlasten.
|
|
|
|
**Lösung:** Semaphore begrenzt parallele Hintergrund-Tasks.
|
|
|
|
**Code:**
|
|
```python
|
|
# app/services/llm_service.py
|
|
background_semaphore = asyncio.Semaphore(
|
|
settings.BACKGROUND_LIMIT # Default: 2
|
|
)
|
|
|
|
async def generate(...):
|
|
if priority == "background":
|
|
async with background_semaphore:
|
|
return await _call_llm(...)
|
|
else: # realtime
|
|
return await _call_llm(...) # Keine Limitierung
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Schutz vor API-Quoten-Überschreitung
|
|
- Stabiler Betrieb bei hoher Last
|
|
- Priorisierung von Echtzeit-Anfragen
|
|
|
|
---
|
|
|
|
## 4. Resilienz-Patterns
|
|
|
|
### 4.1 Provider-Kaskade (Fallback-Kette)
|
|
|
|
**Problem:** Cloud-Provider können ausfallen oder Quoten überschreiten.
|
|
|
|
**Lösung:** Dreistufige Kaskade mit intelligentem Fallback.
|
|
|
|
**Stufen:**
|
|
1. **Cloud (OpenRouter/Gemini):** Schnell, aber abhängig von Provider
|
|
2. **Rate-Limit-Handling:** Automatische Retries bei HTTP 429
|
|
3. **Lokaler Fallback (Ollama):** Langsam, aber immer verfügbar
|
|
|
|
**Code:**
|
|
```python
|
|
# app/services/llm_service.py
|
|
async def generate(...):
|
|
try:
|
|
return await _call_cloud(...)
|
|
except RateLimitError:
|
|
await asyncio.sleep(LLM_RATE_LIMIT_WAIT)
|
|
return await _call_cloud(...) # Retry
|
|
except Exception:
|
|
return await _call_ollama(...) # Fallback
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Hohe Verfügbarkeit
|
|
- Kostenoptimierung (Cloud für Speed, Lokal für Fallback)
|
|
- Resilienz gegen Provider-Ausfälle
|
|
|
|
### 4.2 Deep Fallback (Kognitiver Fallback)
|
|
|
|
**Problem:** Cloud liefert technisch erfolgreiche, aber inhaltlich leere Antworten (Silent Refusal).
|
|
|
|
**Lösung:** Validierung der Antwort-Qualität, nicht nur HTTP-Status.
|
|
|
|
**Code:**
|
|
```python
|
|
response = await _call_cloud(...)
|
|
if is_valid_json(response):
|
|
return response
|
|
else:
|
|
# Deep Fallback: Cloud hat blockiert, lokaler Fallback
|
|
return await _call_ollama(...)
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Erkennung von Policy-Violations
|
|
- Datenintegrität trotz Cloud-Filter
|
|
- Lokale Souveränität
|
|
|
|
---
|
|
|
|
## 5. Datenfluss-Patterns
|
|
|
|
### 5.1 Two-Pass Workflow (WP-15b)
|
|
|
|
**Problem:** Smart Edge Validation benötigt globalen Kontext (alle Notizen).
|
|
|
|
**Lösung:** Zwei-Phasen-Import mit Pre-Scan.
|
|
|
|
**Pass 1: Pre-Scan**
|
|
- Schnelles Scannen aller Dateien
|
|
- Aufbau von `LocalBatchCache` (IDs, Titel, Summaries)
|
|
- Keine LLM-Calls
|
|
|
|
**Pass 2: Semantic Processing**
|
|
- Nur für geänderte Dateien
|
|
- Binäre Validierung gegen Cache
|
|
- Effiziente Nutzung von LLM-Quoten
|
|
|
|
**Vorteile:**
|
|
- Reduktion von LLM-Calls (nur Validierung, keine Extraktion)
|
|
- Konsistente Validierung (globaler Kontext)
|
|
- Schnellerer Import
|
|
|
|
### 5.2 Idempotenz-Pattern
|
|
|
|
**Prinzip:** Mehrfache Imports derselben Datei führen zum gleichen Ergebnis.
|
|
|
|
**Mechanismen:**
|
|
- **Deterministische IDs:** UUIDv5 basierend auf Datei-Inhalt
|
|
- **Hash-basierte Change Detection:** Multi-Hash für `body` und `full`
|
|
- **Deduplizierung:** Kanten werden anhand Identität erkannt
|
|
|
|
**Code:**
|
|
```python
|
|
# Deterministische ID
|
|
note_id = uuid.uuid5(NAMESPACE_URL, f"{file_path}#{content_hash}")
|
|
|
|
# Change Detection
|
|
if current_hash == stored_hash:
|
|
skip_processing() # Idempotent
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Sicherheit bei Re-Imports
|
|
- Keine Duplikate
|
|
- Konsistente Datenbank
|
|
|
|
---
|
|
|
|
## 6. Frontend-Patterns
|
|
|
|
### 6.1 Active Inspector Pattern
|
|
|
|
**Problem:** Graph-Re-Renders bei Knoten-Selektion führen zu Flackern.
|
|
|
|
**Lösung:** CSS-Klassen statt State-Änderungen für Selektion.
|
|
|
|
**Code:**
|
|
```python
|
|
# ui_graph_cytoscape.py
|
|
stylesheet = [
|
|
{
|
|
"selector": ".inspected",
|
|
"style": {"border-width": 6, "border-color": "#FFC300"}
|
|
}
|
|
]
|
|
# Selektion ändert nur CSS-Klasse, nicht React-Key
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Stabiles UI (kein Re-Render)
|
|
- Bessere Performance
|
|
- Smooth User Experience
|
|
|
|
### 6.2 Resurrection Pattern
|
|
|
|
**Problem:** Streamlit vergisst Eingaben bei Re-Runs.
|
|
|
|
**Lösung:** Aggressive Synchronisation in `session_state`.
|
|
|
|
**Code:**
|
|
```python
|
|
# ui_editor.py
|
|
if widget_key not in st.session_state:
|
|
st.session_state[widget_key] = restore_from_data_key()
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Texteingaben überleben Tab-Wechsel
|
|
- Keine Datenverluste
|
|
- Bessere UX
|
|
|
|
---
|
|
|
|
## 7. Erweiterbarkeit: "Teach-the-AI" Paradigma
|
|
|
|
Mindnet lernt durch **Konfiguration**, nicht durch Training.
|
|
|
|
### 7.1 Drei-Ebenen-Erweiterung
|
|
|
|
**1. Daten-Ebene (`types.yaml`):**
|
|
```yaml
|
|
risk:
|
|
retriever_weight: 0.90
|
|
edge_defaults: ["blocks"]
|
|
```
|
|
|
|
**2. Strategie-Ebene (`decision_engine.yaml`):**
|
|
```yaml
|
|
DECISION:
|
|
inject_types: ["value", "risk"]
|
|
```
|
|
|
|
**3. Kognitive Ebene (`prompts.yaml`):**
|
|
```yaml
|
|
risk_definition:
|
|
ollama: "Ein Risiko ist eine potenzielle Gefahr..."
|
|
```
|
|
|
|
**Vorteile:**
|
|
- Keine Code-Änderungen nötig
|
|
- Schnelle Iteration
|
|
- A/B-Testing möglich
|
|
|
|
---
|
|
|
|
## 8. Design-Entscheidungen & Trade-offs
|
|
|
|
### 8.1 Qdrant als Vektor-DB
|
|
|
|
**Entscheidung:** Qdrant statt Pinecone/Weaviate
|
|
|
|
**Gründe:**
|
|
- Self-hosted (Privacy First)
|
|
- Open Source
|
|
- Gute Performance bei lokaler Installation
|
|
|
|
**Trade-off:** Mehr Wartungsaufwand als Managed Service
|
|
|
|
### 8.2 Hybrid Retrieval (Semantik + Graph)
|
|
|
|
**Entscheidung:** Kombination von Vektor-Suche und Graph-Expansion
|
|
|
|
**Gründe:**
|
|
- Semantik findet ähnliche Inhalte
|
|
- Graph findet strukturelle Verbindungen
|
|
- Kombination liefert bessere Relevanz
|
|
|
|
**Trade-off:** Höhere Komplexität, mehr Berechnungsaufwand
|
|
|
|
### 8.3 Background Tasks statt Queue-System
|
|
|
|
**Entscheidung:** FastAPI Background Tasks statt Redis/RabbitMQ
|
|
|
|
**Gründe:**
|
|
- Einfacher Setup (keine zusätzliche Infrastruktur)
|
|
- Ausreichend für Single-User-Szenario
|
|
- Weniger Moving Parts
|
|
|
|
**Trade-off:** Keine Persistenz bei Server-Neustart (Tasks gehen verloren)
|
|
|
|
---
|
|
|
|
## 9. Konsistenz-Garantien
|
|
|
|
### 9.1 Deterministische IDs
|
|
|
|
**Prinzip:** UUIDv5 basierend auf Datei-Inhalt
|
|
|
|
**Vorteile:**
|
|
- Reproduzierbare IDs
|
|
- Keine Duplikate bei Re-Import
|
|
- Graph ist aus Markdown rekonstruierbar
|
|
|
|
### 9.2 Provenance-Hierarchie
|
|
|
|
**Prinzip:** Kanten haben Qualitätsstufen (explicit > smart > rule)
|
|
|
|
**Vorteile:**
|
|
- Menschliche Intention hat Vorrang
|
|
- System-Heuristiken können überschrieben werden
|
|
- Transparente Gewichtung
|
|
|
|
---
|
|
|
|
## 10. Weitere Informationen
|
|
|
|
- **Vision & Strategie:** Siehe [Vision & Strategie](../00_General/00_vision_and_strategy.md)
|
|
- **Graph-Logik:** Siehe [Graph-Logik](02_concept_graph_logic.md)
|
|
- **KI-Persönlichkeit:** Siehe [KI-Persönlichkeit](02_concept_ai_personality.md)
|
|
- **Developer Guide:** Siehe [Developer Guide](../05_Development/05_developer_guide.md)
|
|
|
|
---
|
|
|
|
**Letzte Aktualisierung:** 2025-01-XX
|
|
**Version:** 2.9.1
|
|
|