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

17 KiB
Raw Blame History

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).


📖 Inhaltsverzeichnis (Klicken zum Öffnen)
---

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.