mindnet/docs/02_concepts/02_concept_architecture_patterns.md
Lars 5225090490
All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 3s
Dokumentationsaupdate
2025-12-28 10:56:34 +01:00

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 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:

# 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:

  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:

@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:

  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:

# 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 body und full
  • 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


Letzte Aktualisierung: 2025-01-XX
Version: 2.9.1