12 KiB
| doc_type | audience | scope | status | version | context |
|---|---|---|---|---|---|
| concept | architect, developer | architecture, design_patterns, modularization | active | 2.9.1 | 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 Diskingestion_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.yamldefiniert, nicht im Code - Prompt-Templates sind in
prompts.yamlkonfigurierbar - Edge-Typen werden in
edge_vocabulary.mdverwaltet - 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:
# 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:
# 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:
# 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:
- API empfängt Request (
/ingest/save) - Datei wird sofort auf Disk geschrieben
- API antwortet mit
status: "queued" - Background Task startet Ingestion asynchron
- User kann weiterarbeiten
Code:
@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:
# 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:
- Cloud (OpenRouter/Gemini): Schnell, aber abhängig von Provider
- Rate-Limit-Handling: Automatische Retries bei HTTP 429
- Lokaler Fallback (Ollama): Langsam, aber immer verfügbar
Code:
# 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:
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
bodyundfull - Deduplizierung: Kanten werden anhand Identität erkannt
Code:
# 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:
# 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:
# 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):
risk:
retriever_weight: 0.90
edge_defaults: ["blocks"]
2. Strategie-Ebene (decision_engine.yaml):
DECISION:
inject_types: ["value", "risk"]
3. Kognitive Ebene (prompts.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
- Graph-Logik: Siehe Graph-Logik
- KI-Persönlichkeit: Siehe KI-Persönlichkeit
- Developer Guide: Siehe Developer Guide
Letzte Aktualisierung: 2025-01-XX
Version: 2.9.1