All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 3s
700 lines
23 KiB
Markdown
700 lines
23 KiB
Markdown
# Mindnet – Technische Architektur (V2, Stand: 2025-11-xx)
|
||
|
||
> **Ziel dieses Dokuments:**
|
||
> Vollständige, konsolidierte Beschreibung der aktuellen technischen Architektur von **Mindnet V2** – insbesondere der Verarbeitung von Markdown-Notizen, des Chunkings, der Kantenableitung (Edges) inkl. `rule_id` / `confidence` und der Speicherung in Qdrant.
|
||
> Das Dokument bildet den **aktuellen Implementierungsstand** ab, nicht mehr gültige Annahmen sind bereinigt, neue Konzepte (Typ-Defaults, Inline-Relationen, Edges-Schema) sind integriert.
|
||
|
||
---
|
||
|
||
## 1. Systemüberblick
|
||
|
||
### 1.1 Zielbild
|
||
|
||
Mindnet ist ein **persönliches Wissensnetz**. Technisch bedeutet das:
|
||
|
||
- Markdown-Notizen in einem Vault (Obsidian-kompatibel) werden
|
||
- geparst (Frontmatter + Body),
|
||
- in **Notes**, **Chunks** und **Edges** überführt,
|
||
- in **Qdrant** als drei Collections abgelegt:
|
||
- `<prefix>_notes`
|
||
- `<prefix>_chunks`
|
||
- `<prefix>_edges`.
|
||
|
||
- Das System ist so konzipiert, dass es:
|
||
- **deterministisch** arbeitet (IDs, Chunks, Kanten),
|
||
- über `types.yaml` **konfigurierbar** ist,
|
||
- später von einem LLM-basieren Agenten als **Graph-Retriever** genutzt werden kann.
|
||
|
||
---
|
||
|
||
### 1.2 Verzeichnisstruktur (Server)
|
||
|
||
/home/llmadmin/mindnet/:
|
||
|
||
- **/app**
|
||
- `main.py` – FastAPI-App / Einstieg
|
||
- **/app/core**
|
||
- `qdrant.py` – QdrantConfig, Client-Factory, Collection-Management
|
||
- `qdrant_points.py` – Upserts / Deletes / Queries auf Punktebene
|
||
- `note_payload.py` – Bau von Note-Payloads
|
||
- `chunk_payload.py` – Bau von Chunk-Payloads
|
||
- `chunker.py` – Chunking-Logik
|
||
- `edges.py` – Datenstruktur & Hilfsfunktionen für Edges
|
||
- `derive_edges.py` – Ableitung von Edges aus Text und Typen
|
||
- **/app/models**
|
||
- `dto.py` – DTOs für API / Services
|
||
- **/app/routers**
|
||
- `qdrant_router.py` – API-Layer Richtung Qdrant
|
||
- **/app/services**
|
||
- `llm_ollama.py` – Anbindung an Ollama-LLM
|
||
- `embeddings_client.py` – Embedding-Service (z. B. Ollama / lokaler Dienst)
|
||
- **/config**
|
||
- `types.yaml` – Typdefinitionen inkl. Chunk-Profilen und Edge-Defaults
|
||
- **/scripts**
|
||
- `import_markdown.py` – Hauptimporter für Vault
|
||
- `reset_qdrant.py` – Collections löschen/neu anlegen (inkl. Indizes)
|
||
- `setup_mindnet_collections.py` – Collection-Setup (historisch / Hilfsskript)
|
||
- diverse Diagnose- und Testskripte, z. B.
|
||
- `payload_dryrun.py`
|
||
- `edges_full_check.py`
|
||
- **/tests**
|
||
- `test_edges_smoke.py` – einfache Edge-Smoke-Tests
|
||
- `test_edges_all.py` – umfassendere Checks (Counts, Plausibilität)
|
||
- `ensure_indexes_and_show.py`, `assert_payload_schema.py` – Schema/Index-Checks
|
||
- **/vault**
|
||
- produktives Vault (Obsidian)
|
||
- **/mindnet_v2_test_vault**
|
||
- Test-Vault für V2-Funktionen (Chunking, Edges, Typ-Defaults)
|
||
|
||
---
|
||
|
||
## 2. Datenmodell & Collections
|
||
|
||
### 2.1 Note, Chunk, Edge – Begriffe
|
||
|
||
- **Note**
|
||
Entspricht einer Markdown-Datei mit YAML-Frontmatter (`id`, `title`, `type`, …).
|
||
In Qdrant als ein Punkt in `<prefix>_notes`.
|
||
|
||
- **Chunk**
|
||
Ein Abschnitt einer Note (z. B. Absatz, Überschrifts-Block, Sliding Window).
|
||
In Qdrant als Punkt in `<prefix>_chunks`.
|
||
|
||
- **Edge**
|
||
Eine gerichtete Kante, die eine Beziehung zwischen:
|
||
- zwei Chunks,
|
||
- Chunk ↔ Note,
|
||
- oder (konzeptionell) Note ↔ Note beschreibt.
|
||
Gespeichert in `<prefix>_edges`.
|
||
|
||
---
|
||
|
||
### 2.2 Qdrant Collections (Stand V2)
|
||
|
||
**Namenskonvention:**
|
||
Prefix aus ENV (`COLLECTION_PREFIX`, z. B. `mindnet`):
|
||
|
||
- `mindnet_notes`
|
||
- `mindnet_chunks`
|
||
- `mindnet_edges`
|
||
|
||
Die Collections werden über:
|
||
|
||
```bash
|
||
python3 -m scripts.reset_qdrant --mode wipe --prefix "$COLLECTION_PREFIX"
|
||
```
|
||
|
||
**vollständig neu angelegt**, inkl. Payload-Indizes (Details in 6.1).
|
||
|
||
---
|
||
|
||
### 2.3 Notes – Payload & Felder
|
||
|
||
Minimale, indexierte Felder (Auszug, Schema laut `diag_payload_indexes`):
|
||
|
||
- `note_id` (keyword) – stabile Note-ID (Frontmatter `id`)
|
||
- `title` (text) – Notiz-Titel
|
||
- `type` (keyword) – logischer Typ (z. B. `concept`, `project`, `profile`, `journal`, `source`)
|
||
- `tags` (keyword[]) – Tags aus Frontmatter
|
||
- `updated` (integer) – Zeitstempel (z. B. `YYYYMMDDHHMMSS` oder epoch; Implementierungsdetail)
|
||
|
||
Weitere Felder (nicht alle indexiert, aber im Payload vorhanden):
|
||
|
||
- `retriever_weight` (float) – wie stark diese Note/Chunks im Retrieval gewichtet werden
|
||
- `chunk_profile` (string) – Name des verwendeten Chunk-Profils
|
||
- `edge_defaults` (string[]) – Liste von Default-Relationen pro Typ (z. B. `["references","related_to"]`)
|
||
- `confidentiality`, `visibility`, weitere Metadaten aus Frontmatter
|
||
|
||
Diese Felder werden in `app/core/note_payload.py` aus Frontmatter + `types.yaml` konstruiert.
|
||
|
||
---
|
||
|
||
### 2.4 Chunks – Payload & Felder
|
||
|
||
Indexierte Kernfelder:
|
||
|
||
- `note_id` (keyword) – Referenz auf die übergeordnete Note
|
||
- `chunk_id` (keyword) – deterministische Chunk-ID:
|
||
`"{note_id}#c{index:02d}"` (z. B. `20251110-ollama-llm-9f0a12#c02`)
|
||
- `index` (integer) – 0-basiert; Position in der Note
|
||
- `type` (keyword) – Note-Typ (vom Note geerbt)
|
||
- `tags` (keyword[]) – Tags (vom Note geerbt)
|
||
|
||
Weitere wichtige Payload-Felder:
|
||
|
||
- `ord` (integer) – 1-basiertes Pendant zu `index` (für menschliche Lesbarkeit)
|
||
- `text` (string) – eigentlicher Chunk-Text (ggf. gereinigt/normalisiert)
|
||
- `window` (string) – Kontext-Fenster (z. B. Chunk ± Nachbarn)
|
||
- `retriever_weight` (float) – gültiger Wert aus `types.yaml` (oder Default)
|
||
- `chunk_profile` (string) – verwendetes Profil, z. B. `short`, `medium`, `long`, `by_heading`
|
||
- `neighbors_prev` (string[]) – Liste von `chunk_id`s direkter Vorgänger (i. d. R. 0 oder 1 ID)
|
||
- `neighbors_next` (string[]) – Liste von `chunk_id`s direkter Nachfolger
|
||
|
||
Die Konstruktion erfolgt in `app/core/chunk_payload.py` auf Basis der Chunks aus `chunker.py` und der aufgelösten Typ-Defaults.
|
||
|
||
---
|
||
|
||
### 2.5 Edges – Payload & Felder
|
||
|
||
Indexierte Felder:
|
||
|
||
- `note_id` (keyword) – Note, zu der der **Quell-Chunk** gehört
|
||
- `source_id` (keyword) – i. d. R. `chunk_id` des Quell-Chunks (z. B. `2025…#c00`)
|
||
- `target_id` (keyword) – Zielobjekt (z. B.:
|
||
- `chunk_id` eines anderen Chunks
|
||
- Note-Titel (`"Embeddings 101"`) bei **konzeptionellen Referenzen**
|
||
- `kind` (keyword) – Kantenart:
|
||
- Struktur: `belongs_to`, `next`, `prev`
|
||
- Explizit: `references`
|
||
- Inline: z. B. `similar_to`, `depends_on`, `related_to`
|
||
- Typ-Defaults: ebenfalls z. B. `depends_on`, `related_to` (aus `edge_defaults`)
|
||
- `scope` (keyword) – aktuell immer `"chunk"` (Kante wird auf Chunk-Ebene geführt)
|
||
- `chunk_id` (keyword) – `chunk_id` des Quell-Chunks
|
||
|
||
Weitere Payload-Felder:
|
||
|
||
- `rule_id` (string) – Herkunftsregel:
|
||
- Struktur:
|
||
- `structure:belongs_to`
|
||
- `structure:next`
|
||
- `structure:prev`
|
||
- Explizite Wiki-Links:
|
||
- `explicit:wikilink`
|
||
- Inline-Relationen:
|
||
- `inline:rel`
|
||
- Typ-Defaults aus `types.yaml`:
|
||
- `edge_defaults:<type>:<relation>`
|
||
z. B. `edge_defaults:concept:related_to`
|
||
- `confidence` (float) – Stärke / Vertrauensgrad:
|
||
- Struktur-Kanten: `1.0`
|
||
- Explizite Wiki-Links: `1.0`
|
||
- Inline-Relationen (`[[rel:… …]]`): z. B. `0.95`
|
||
- Typ-Defaults: z. B. `0.7`
|
||
|
||
Edges werden in `app/core/derive_edges.py` erzeugt und in `mindnet_edges` gespeichert.
|
||
|
||
---
|
||
|
||
## 3. Typ-Konfiguration (`config/types.yaml`)
|
||
|
||
### 3.1 Struktur des Files
|
||
|
||
`types.yaml` (vereinfachtes Beispiel):
|
||
|
||
```yaml
|
||
version: 1.0
|
||
default:
|
||
retriever_weight: 1.0
|
||
chunk_profile: default
|
||
|
||
types:
|
||
concept:
|
||
chunk_profile: medium
|
||
edge_defaults: ["references", "related_to"]
|
||
retriever_weight: 0.35
|
||
|
||
task:
|
||
chunk_profile: short
|
||
edge_defaults: ["depends_on", "belongs_to"]
|
||
retriever_weight: 0.8
|
||
|
||
experience:
|
||
chunk_profile: medium
|
||
edge_defaults: ["derived_from", "inspired_by"]
|
||
retriever_weight: 0.9
|
||
|
||
project:
|
||
chunk_profile: long
|
||
edge_defaults: ["references", "depends_on"]
|
||
retriever_weight: 0.97
|
||
|
||
profile:
|
||
chunk_profile: long
|
||
edge_defaults: ["references", "depends_on"]
|
||
retriever_weight: 0.66
|
||
```
|
||
|
||
### 3.2 Typauflösung – Reihenfolge der Prioritäten
|
||
|
||
Für eine Note mit `type` und optionalen Frontmatter Feldern `retriever_weight`, `chunk_profile` gilt aktuell:
|
||
|
||
1. **Typ-spezifische Defaults** aus `types.yaml`:
|
||
- `retriever_weight` ← `types.<type>.retriever_weight` (falls vorhanden)
|
||
- `chunk_profile` ← `types.<type>.chunk_profile` (falls vorhanden)
|
||
2. **Globaler Default** `default` in `types.yaml`, falls Typ-Eintrag fehlt.
|
||
3. **Frontmatter `retriever_weight` / `chunk_profile`** werden aktuell *nicht* mehr als Konfigurationsebene verwendet, sondern eher als historische Relikte. (Dies kann zukünftig wieder aktiviert werden, ist aber im aktuellen Stand bewusst abgeschaltet zugunsten klarer, zentraler Konfiguration über `types.yaml`.)
|
||
|
||
Das Ergebnis dieser Auflösung wird im Payload als:
|
||
|
||
- `note_payload["retriever_weight"]`
|
||
- `note_payload["chunk_profile"]`
|
||
|
||
gesetzt und **1:1 an alle Chunks vererbt**.
|
||
|
||
---
|
||
|
||
## 4. Import-Pipeline (Markdown → Qdrant)
|
||
|
||
### 4.1 Runbook
|
||
|
||
Standard-Import (mit Purge-Before-Upsert):
|
||
|
||
```bash
|
||
export COLLECTION_PREFIX=mindnet
|
||
export MINDNET_TYPES_FILE="$(pwd)/config/types.yaml"
|
||
|
||
python3 -m scripts.import_markdown \
|
||
--vault ./mindnet_v2_test_vault \
|
||
--apply \
|
||
--purge-before-upsert \
|
||
--prefix "$COLLECTION_PREFIX"
|
||
```
|
||
|
||
Nur Sync-Deletes (entfernte Dateien in Qdrant aufräumen):
|
||
|
||
```bash
|
||
python3 -m scripts.import_markdown \
|
||
--vault ./mindnet_v2_test_vault \
|
||
--sync-deletes \
|
||
--apply \
|
||
--prefix "$COLLECTION_PREFIX"
|
||
```
|
||
|
||
---
|
||
|
||
### 4.2 Verarbeitungsschritte
|
||
|
||
**1. Discovery & Parsing**
|
||
|
||
- `import_markdown.py` traversiert das Vault‐Verzeichnis.
|
||
- Für jede `.md`:
|
||
- YAML-Frontmatter extrahieren (id, title, type, created, updated, tags, …).
|
||
- Body (Markdown) als String.
|
||
|
||
**2. Typauflösung & Payload-Bau (Note)**
|
||
|
||
- Laden & Auflösen von `types.yaml` (über ENV `MINDNET_TYPES_FILE` oder Default `./config/types.yaml`).
|
||
- Bestimmung des effektiven Typs (`type` aus Frontmatter; ggf. Fallback).
|
||
- Anwendung der Typdefaults:
|
||
- `retriever_weight` und `chunk_profile` werden aus `types.yaml` gelesen.
|
||
- Konstruktion des Note-Payloads via `make_note_payload` (in `note_payload.py`):
|
||
- Felder wie `note_id`, `title`, `type`, `tags`, `updated`, `retriever_weight`, `chunk_profile`, `edge_defaults`, …
|
||
|
||
**3. Chunking**
|
||
|
||
- Übergabe des Note-Bodies + `chunk_profile` an `chunker.py`.
|
||
- `chunker.py` nutzt `chunk_config.py`, das definierte Profile bereitstellt, z. B.:
|
||
|
||
```python
|
||
CHUNK_PROFILES = {
|
||
"short": {"max_tokens": 128, "overlap": 32},
|
||
"medium": {"max_tokens": 256, "overlap": 64},
|
||
"long": {"max_tokens": 512, "overlap": 128},
|
||
"by_heading": {"strategy": "by_heading", "max_chars": 2000},
|
||
"default": {"max_tokens": 256, "overlap": 64},
|
||
}
|
||
```
|
||
|
||
- Ergebnis: Liste geordneter Chunks mit:
|
||
- `chunk_id` (`note_id#cXX`),
|
||
- `index` (0..N-1),
|
||
- `ord` (1..N),
|
||
- `text`,
|
||
- `window` (inkl. Nachbartexte),
|
||
- `neighbors_prev` / `neighbors_next`.
|
||
|
||
**4. Chunk-Payloads**
|
||
|
||
- In `chunk_payload.py` werden für jeden Chunk die Payloads gebaut:
|
||
- Vererbung von `note_id`, `type`, `tags`, `retriever_weight`, `chunk_profile`.
|
||
- Setzen von `index`, `ord`, `neighbors_prev`, `neighbors_next`.
|
||
- Persistieren von `text` und `window`.
|
||
|
||
**5. Kantenableitung (Edges)**
|
||
|
||
- `derive_edges.py` wird mit Note + Chunks + Typinformationen aufgerufen.
|
||
- Es entstehen mehrere Gruppen von Kanten:
|
||
1. **Struktur-Kanten**:
|
||
- `belongs_to`: Chunk → Note
|
||
- `next`: Chunk[i] → Chunk[i+1]
|
||
- `prev`: Chunk[i] → Chunk[i-1]
|
||
2. **Explizite Kanten aus Text**:
|
||
- Wiki-Links: `[[Target Title]]` → `kind="references"`, `rule_id="explicit:wikilink"`, `confidence=1.0`.
|
||
- Inline-Relationen: `[[rel:depends_on Embeddings 101]]`:
|
||
- `kind="depends_on"`
|
||
- `rule_id="inline:rel"`
|
||
- `confidence≈0.95`
|
||
- `target_id="Embeddings 101"` (Titel, nicht ID – Auflösung erfolgt später im Agenten).
|
||
3. **Typ-basierte Defaults (`edge_defaults`)**:
|
||
- Aus `types.yaml` je `type`: Liste von Relations-Kinds, z. B.:
|
||
- `concept.edge_defaults = ["references","related_to"]`
|
||
- `project.edge_defaults = ["references","depends_on"]`
|
||
- Für jede explizite Referenz Quelle→Ziel (z. B. `references`, `similar_to`) werden **zusätzliche Kanten** erzeugt, z. B.:
|
||
- `edge_defaults:project:depends_on`
|
||
- `edge_defaults:concept:related_to`
|
||
- `confidence≈0.7`.
|
||
4. **Deduplication**:
|
||
- Alle Kanten werden in einem Set anhand eines Keys wie
|
||
- `(kind, scope, source_id, target_id, rule_id)`
|
||
dedupliziert (idempotent).
|
||
|
||
**6. Upsert nach Qdrant**
|
||
|
||
- Punkte (Notes, Chunks, Edges) werden via `qdrant_points.py` in die entsprechenden Collections upserted.
|
||
- Option `--purge-before-upsert` sorgt dafür, dass alte Punkte mit denselben IDs entfernt werden, bevor neue geschrieben werden (stabiler Reimport).
|
||
- `--sync-deletes` entfernt Punkte, deren zugehörige Dateien im Vault nicht mehr existieren.
|
||
|
||
---
|
||
|
||
## 5. Edges im Detail – Semantik & Regeln
|
||
|
||
### 5.1 Struktur-Kanten
|
||
|
||
Erzeugt für **jede Note** mit >0 Chunks:
|
||
|
||
- **belongs_to**
|
||
- pro Chunk genau eine Kante:
|
||
- `source_id = chunk_id`
|
||
- `target_id = note_id` *oder* (konzeptionell) ein Note-Identifikator
|
||
- `kind = "belongs_to"`
|
||
- `scope = "chunk"`
|
||
- `rule_id = "structure:belongs_to"`
|
||
- `confidence = 1.0`
|
||
- **next / prev**
|
||
- für Index i (`0..N-2`):
|
||
- `next`: `chunk_i → chunk_{i+1}`
|
||
- `prev`: `chunk_{i+1} → chunk_i`
|
||
- `kind = "next"` bzw. `"prev"`
|
||
- `scope = "chunk"`
|
||
- `rule_id = "structure:next"` / `"structure:prev"`
|
||
- `confidence = 1.0`
|
||
|
||
Tests (`tests.test_edges_smoke` / `tests.test_edges_all`) stellen sicher:
|
||
|
||
- `belongs_to` == `#chunks`
|
||
- `next` == `prev` == `#chunks - 1`
|
||
- keine Duplikate der Strukturkanten.
|
||
|
||
---
|
||
|
||
### 5.2 Explizite Wiki-Links (`[[Target Title]]`)
|
||
|
||
- Parser in `derive_edges.py` sucht im Chunk-Text nach Mustern:
|
||
- `[[Some Page]]` (kein `rel:` Präfix)
|
||
- Erzeugt Kanten:
|
||
- `kind = "references"`
|
||
- `scope = "chunk"`
|
||
- `source_id = chunk_id`
|
||
- `target_id = "<Titel aus dem Link>"` (z. B. `"Vector DB Basics"`)
|
||
- `rule_id = "explicit:wikilink"`
|
||
- `confidence = 1.0`
|
||
|
||
Diese Kanten bilden das **explizite Referenznetz** des Nutzers ab (Obsidian-typische Wikilinks).
|
||
|
||
---
|
||
|
||
### 5.3 Inline-Relationen (`[[rel:… …]]`)
|
||
|
||
Aktuell **implementiert und funktional** ist folgende Form:
|
||
|
||
- **Format (aktuell unterstützt):**
|
||
|
||
```md
|
||
[[rel:depends_on Embeddings 101]]
|
||
[[rel:similar_to Vector DB Basics]]
|
||
[[rel:related_to Qdrant Vektordatenbank]]
|
||
```
|
||
|
||
- Parsing-Strategie:
|
||
- Link-Inhalt wird gesplittet:
|
||
- Erster Token: `rel:depends_on`
|
||
- Rest: Zielbezeichnung (`Embeddings 101`)
|
||
- `kind` wird aus dem Teil `depends_on` extrahiert (z. B. `similar_to`, `related_to`).
|
||
- `target_id` wird aus dem Rest (meist ein Titel) abgeleitet.
|
||
|
||
- Ergebnis-Kante:
|
||
- `kind = "<Relation aus rel:…>"`
|
||
- `scope = "chunk"`
|
||
- `source_id = chunk_id`
|
||
- `target_id = "<Ziel-Titel>"`
|
||
- `rule_id = "inline:rel"`
|
||
- `confidence ≈ 0.95`
|
||
|
||
- **Nicht unterstützt (Stand jetzt):**
|
||
|
||
```md
|
||
rel: depends_on [[Embeddings 101]] [[Vector DB Basics]]
|
||
```
|
||
|
||
Diese Syntax wird derzeit **nicht** erkannt.
|
||
Multi-Target-Inline-Relationen (`rel: similar_to [[A]] [[B]]`) sind ein **offener Task**.
|
||
|
||
---
|
||
|
||
### 5.4 Typ-basierte Default-Edges (`edge_defaults`)
|
||
|
||
Zweck:
|
||
|
||
- Für bestimmte Typen (vor allem `concept`, `project`, `profile`) sollen zusätzliche Kanten automatisch ergänzt werden, um das **Semantiknetz zu verdichten**, ohne dass der Nutzer jede Relation explizit pflegen muss.
|
||
|
||
Konfiguration:
|
||
|
||
- In `types.yaml` definiert, z. B.:
|
||
|
||
```yaml
|
||
types:
|
||
concept:
|
||
edge_defaults: ["references", "related_to"]
|
||
project:
|
||
edge_defaults: ["references", "depends_on"]
|
||
```
|
||
|
||
Aktuelle Implementierung:
|
||
|
||
- Für jede **explizite** Referenz (Wiki-Link oder Inline-Relation) von Chunk → Ziel:
|
||
- `kind="references"` (Wiki) oder die Inline-Relation (`depends_on`, `similar_to`, …)
|
||
- Auf Basis des Note-Typs (`type`) wird pro Default-Relation eine zusätzliche Kante erzeugt:
|
||
|
||
Beispiel:
|
||
|
||
- Note-Typ: `project`
|
||
- `edge_defaults: ["references","depends_on"]`
|
||
- Explizit: `references` von `Edge Index` → `Embeddings 101`
|
||
- Abgeleitete Kanten:
|
||
- `kind="references"`, `rule_id="edge_defaults:project:references"`, `confidence=0.7`
|
||
- `kind="depends_on"`, `rule_id="edge_defaults:project:depends_on"`, `confidence=0.7`
|
||
|
||
- Symmetrische Kanten:
|
||
- Für einige Relations-Typen (z. B. `related_to`) wird zusätzlich eine **spiegelbildliche** Kante Ziel → Quelle erzeugt.
|
||
- Dies erklärt in Tests z. B. Paare wie:
|
||
- `related_to (Source=Ollama LLM, Target=Qdrant Vektordatenbank)`
|
||
- `related_to (Source=Qdrant Vektordatenbank, Target=Ollama LLM)`
|
||
|
||
- Deduplication:
|
||
- Es wird verhindert, dass mehrfach identische Kanten geschrieben werden.
|
||
- Schlüssel: `(kind, scope, source_id, target_id, rule_id)`.
|
||
|
||
---
|
||
|
||
### 5.5 `rule_id` & `confidence` – Bedeutung
|
||
|
||
**`rule_id`** kodiert **Herkunft** und **Regel**, z. B.:
|
||
|
||
- `structure:belongs_to`, `structure:next`, `structure:prev`
|
||
- `explicit:wikilink`
|
||
- `inline:rel`
|
||
- `edge_defaults:<type>:<relation>`
|
||
|
||
**`confidence`** dient als späterer Gewichtungsfaktor im Retrieval / Ranking:
|
||
|
||
- `1.0` → harte Struktur- oder explizite Kante (vom Nutzer direkt gesetzt)
|
||
- `0.95` → Inline-Relation (stark, aber nicht so „hart“ wie die reine Referenz)
|
||
- `0.7` → Typ-Default-Edges (abgeleitete Hypothesen aus Typwissen)
|
||
|
||
Ein künftiger LLM-Agent kann daraus z. B.:
|
||
|
||
- Kanten mit hoher `confidence` priorisieren,
|
||
- Default-Edges als schwächere Hinweise betrachten,
|
||
- Inline-Relationen gezielt als semantische Aussagen über die Beziehung interpretieren.
|
||
|
||
---
|
||
|
||
## 6. Qdrant-Schema & Indizes
|
||
|
||
### 6.1 Erstellung & Indizes
|
||
|
||
Die Erstellung erfolgt über:
|
||
|
||
```bash
|
||
python3 -m scripts.reset_qdrant --mode wipe --prefix "$COLLECTION_PREFIX"
|
||
```
|
||
|
||
Intern:
|
||
|
||
- `qdrant.py` / `setup_mindnet_collections.py`:
|
||
- Collections anlegen, ggf. mit Vektor-Konfiguration (Single-Vector; Named-Vectors sind vorbereitet aber optional).
|
||
- `ensure_payload_indexes()`:
|
||
- Legt für jede Collection die relevanten Payload-Indizes an.
|
||
|
||
Verifizierbar durch:
|
||
|
||
```bash
|
||
python3 -m tests.ensure_indexes_and_show
|
||
```
|
||
|
||
Beispielauszug (bereits real so gesehen):
|
||
|
||
- **mindnet_notes**
|
||
|
||
```json
|
||
{
|
||
"tags": { "data_type": "keyword", "points": 8 },
|
||
"note_id":{ "data_type": "keyword", "points": 8 },
|
||
"title": { "data_type": "text", "points": 8 },
|
||
"updated":{ "data_type": "integer", "points": 0 },
|
||
"type": { "data_type": "keyword", "points": 8 }
|
||
}
|
||
```
|
||
|
||
- **mindnet_chunks**
|
||
|
||
```json
|
||
{
|
||
"note_id": { "data_type": "keyword", "points": 44 },
|
||
"chunk_id":{ "data_type": "keyword", "points": 44 },
|
||
"index": { "data_type": "integer", "points": 44 },
|
||
"type": { "data_type": "keyword", "points": 44 },
|
||
"tags": { "data_type": "keyword", "points": 44 }
|
||
}
|
||
```
|
||
|
||
- **mindnet_edges**
|
||
|
||
```json
|
||
{
|
||
"scope": { "data_type": "keyword", "points": 135 },
|
||
"kind": { "data_type": "keyword", "points": 135 },
|
||
"chunk_id":{ "data_type": "keyword", "points": 135 },
|
||
"target_id":{ "data_type": "keyword", "points": 135 },
|
||
"note_id": { "data_type": "keyword", "points": 135 },
|
||
"source_id":{ "data_type": "keyword", "points": 135 }
|
||
}
|
||
```
|
||
|
||
### 6.2 Schema-Validierung
|
||
|
||
`tests/assert_payload_schema.py` prüft:
|
||
|
||
- ob die Minimalfelder (`note_id`, `type`, `title`, `updated`, `tags` etc.) vorhanden sind,
|
||
- berichtet fehlende Felder.
|
||
|
||
Nach Korrektur der Indexanlage sollte:
|
||
|
||
- `ok = true` sein, bzw. zumindest alle Kernfelder unter `present` geführt werden.
|
||
(Im aktuellen Stand werden sie als Indizes geführt; Qdrant speichert zusätzliche Felder ohne explizite Schema-Zeile, was technisch unproblematisch ist.)
|
||
|
||
---
|
||
|
||
## 7. Nutzung im Retrieval (Ausblick)
|
||
|
||
### 7.1 Typbewusstes Chunk-Retrieval
|
||
|
||
Ein typischer Query-Pfad eines LLM-Agents:
|
||
|
||
1. Query → Embedding
|
||
2. Suche in `mindnet_chunks` mit Filter, z. B.:
|
||
|
||
```python
|
||
f = rest.Filter(
|
||
must=[
|
||
rest.FieldCondition(key="type", match=rest.MatchValue(value="concept")),
|
||
rest.FieldCondition(key="tags", match=rest.MatchAny(any=["ki","ollama"]))
|
||
]
|
||
)
|
||
```
|
||
|
||
3. Treffer werden nach:
|
||
- Vektor-Score,
|
||
- `retriever_weight`,
|
||
- ggf. Edges (z. B. Projekte mit `depends_on` → Konzepte) gewichtet.
|
||
|
||
### 7.2 Graph-orientierte Navigation
|
||
|
||
Basierend auf `mindnet_edges` kann der Agent:
|
||
|
||
- ausgehend von einem relevanten Chunk:
|
||
- Struktur-Kanten (`next`, `prev`) zur Kontextvergrößerung nutzen,
|
||
- `references` / `depends_on` / `related_to` interpretieren,
|
||
- `edge_defaults` als semantische Ausdehnung (z. B. von `project` zu seinen `concept`-Bausteinen) nutzen.
|
||
|
||
Die Bedeutung der Kanten ist dabei durch `rule_id` und `confidence` klar nachvollziehbar.
|
||
|
||
---
|
||
|
||
## 8. Offene Punkte / Known Limitations
|
||
|
||
1. **Multi-Target Inline-Relationen**
|
||
Syntax wie:
|
||
|
||
```md
|
||
[[rel:similar_to Vector DB Basics]] [[rel:similar_to Embeddings 101]]
|
||
```
|
||
funktioniert (zwei separate Inline-Links).
|
||
**Nicht** unterstützt (Stand jetzt):
|
||
|
||
```md
|
||
[[rel:similar_to Vector DB Basics]] [[Embeddings 101]]
|
||
rel: similar_to [[Vector DB Basics]] [[Embeddings 101]]
|
||
```
|
||
|
||
→ Offener Task: Parser erweitern, um mehrere Ziele in einer Inline-Relation zu unterstützen.
|
||
|
||
2. **Callout-Edges (`[!edge]`)**
|
||
- Konzeptionell vorgesehen, z. B.:
|
||
|
||
```md
|
||
> [!edge] related_to: [[Vector DB Basics]]
|
||
```
|
||
|
||
- Im aktuellen Implementierungsstand werden Callouts entweder:
|
||
- gar nicht, oder
|
||
- nur teilweise verarbeitet (in den aktuellen Test-Runs war `callout_total = 0`).
|
||
→ Offener Task: Callout-Parser robust implementieren und mit Tests absichern.
|
||
|
||
3. **Frontmatter-Overrides für `retriever_weight` / `chunk_profile`**
|
||
- Aktuell **nicht** wirksam.
|
||
- Typdefaults aus `types.yaml` sind der **Single Source of Truth**.
|
||
- Später können kontrollierte Overrides (z. B. für Sonderfälle) eingeführt werden.
|
||
|
||
4. **Vektor-Konfiguration für Edges**
|
||
- `mindnet_edges` wird derzeit ohne Vektoren betrieben (`vectors = null`).
|
||
- Später könnte z. B. eine semantische Repräsentation von Kanten eingeführt werden (z. B. Embedding von `(source_chunk_text + relation + target_title)`).
|
||
|
||
---
|
||
|
||
## 9. Zusammenfassung
|
||
|
||
- Die Mindnet-V2-Architektur implementiert einen **dreistufigen Speicher**:
|
||
- Notes (`mindnet_notes`),
|
||
- Chunks (`mindnet_chunks`),
|
||
- Edges (`mindnet_edges`).
|
||
|
||
- **`types.yaml`** steuert:
|
||
- `retriever_weight` und `chunk_profile` je Typ (inkl. Default),
|
||
- `edge_defaults` je Typ (automatische Relations-Ableitung).
|
||
|
||
- Das **Chunking** ist profilbasiert (short/medium/long/by_heading) und deterministisch.
|
||
|
||
- Die **Kantenableitung** in `derive_edges.py` erzeugt:
|
||
- strukturelle Kanten (belongs_to / next / prev),
|
||
- explizite Kanten aus Wiki-Links (`[[Target]]`),
|
||
- semantische Inline-Relationen (`[[rel:depends_on Target]]`),
|
||
- typbasierte Default-Kanten (z. B. `edge_defaults:project:depends_on`).
|
||
|
||
- `rule_id` und `confidence` machen die Herkunft jeder Kante transparent und ermöglichen später eine **gewichtete Auswertung** durch LLM-basierte Agenten.
|
||
|
||
- Qdrant-Collections und Payload-Indizes sind konsistent eingerichtet und durch Tests verifiziert.
|
||
|
||
Dieses Dokument bildet damit das aktuelle technische Gesamtbild von Mindnet V2 ab, inkl. aller relevanten Mechanismen rund um Typen, Chunking, Edges und Qdrant-Schema.
|
||
|