mindnet/docs/mindnet_technical_architecture.md
Lars 9ee39277ae
All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 3s
docs/mindnet_technical_architecture.md aktualisiert
2025-12-08 09:08:12 +01:00

11 KiB
Raw Blame History

Mindnet v2.2 Technische Architektur

Datei: docs/mindnet_technical_architecture_v2.2.md Stand: 2025-12-08 Status: FINAL (Integrierter Stand WP01WP04c) Quellen: mindnet_technical_architecture.md, chunking_strategy.md, Handbuch.md, wp04_retriever_scoring.md.

Ziel dieses Dokuments: Vollständige, konsolidierte Beschreibung der aktuellen technischen Architektur von Mindnet v2.2. Es definiert die Datenstrukturen in Qdrant, die Verarbeitungspipelines (Importer, Chunker, Edges) und die Retrieval-Logik. Es bildet den aktuellen Implementierungsstand ab.


1. Systemüberblick

1.1 Architektur-Zielbild

Mindnet ist ein persönliches Wissensnetz. Technisch bedeutet das:

  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.

Das System arbeitet deterministisch (stabile IDs) und ist konfigurationsgetrieben (types.yaml, retriever.yaml).

1.2 Verzeichnisstruktur & Komponenten

Die Codebasis ist modular aufgebaut.

/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 (QueryHit, Explanation, FeedbackRequest)
│   ├── routers/
│   │   ├── query.py            # Such-Endpunkt
│   │   ├── feedback.py         # Feedback-Endpunkt (WP04c)
│   │   └── ...
│   ├── services/
│   │   ├── feedback_service.py # Logging-Logik (WP04c)
│   │   └── embeddings_client.py
├── config/
│   ├── types.yaml              # Typ-Definitionen (Import-Zeit)
│   └── retriever.yaml          # Scoring-Gewichte (Laufzeit)
├── 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:
    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:
    scoring:
      semantic_weight:   1.0
      edge_weight:       0.7
      centrality_weight: 0.5
    

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) realisiert die Logik hinter /query.

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. Feedback & Logging Architektur (WP04c)

Mindnet implementiert ein "Data Flywheel" zur späteren Optimierung (Self-Tuning).

6.1 Komponenten

  • Feedback Service (app/services/feedback_service.py): Kapselt die Schreibzugriffe.
  • Storage: Lokales Dateisystem (data/logs/), Format JSONL (Line-delimited JSON).

6.2 Log-Dateien

  1. search_history.jsonl:
    • Speichert jede Anfrage an /query.
    • 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?").

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


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


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