diff --git a/docs/mindnet_technical_architecture.md b/docs/mindnet_technical_architecture.md index a0aefb9..c413773 100644 --- a/docs/mindnet_technical_architecture.md +++ b/docs/mindnet_technical_architecture.md @@ -1,474 +1,699 @@ -# Mindnet – Technische Architektur -**Version:** 2.0 • **Datum:** 2025-11-09 • **Autor:** Systemarchitektur (gemeinsam mit Owner) +# 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. --- -## 0. Zweck dieses Dokuments -Dieses Dokument beschreibt die **technische Architektur** von *mindnet* so präzise, dass ein erfahrener Engineer sie 1:1 implementieren kann. Es konsolidiert die bisherigen Überlegungen, eliminiert redundante/unklare Passagen und ergänzt fehlende Details (Datenmodell, Indizes, Import‑Pipelines, Scoring, Erklärbarkeit, Skalierung, Betrieb). Gültige Inhalte aus früheren Fassungen bleiben erhalten (siehe **Anhang A: Originalinhalt**). +## 1. Systemüberblick -> **Kernziele:** -> - Persönliches Wissensnetz mit langlebiger Semantik (Notizen, Erlebnisse, Entscheidungen). -> - Antworten, die **erklärbar** auf Erfahrungen, Konditionierung und Persönlichkeit beruhen. -> - **Konfigurierbare Regeln** (Zeitgewichtung, Relationstypen, Typ‑Profile). -> - **Lokal** betreibbar, später **skalierbar** (on‑prem → Rechenzentrum/Cloud). -> - **Deterministisch**, idempotent, auditierbar. +### 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: + - `_notes` + - `_chunks` + - `_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. Systemkontext (High Level) -- **Quellen:** Obsidian‑Vault (Markdown + Frontmatter), künftig MediaWiki/HTML/PDF/E-Mail/Images. -- **Ingestion:** Validator → Parser → Chunker (Profile) → Embedding → Graph‑& Payload‑Indexierung. -- **Speicher:** Qdrant als Vektorstore + Payload (JSON), ergänzt um Dateimetadaten. -- **APIs:** FastAPI (Read/Write), Tools (mindnet_query, mindnet_subgraph), n8n‑Glue. -- **Retrieval/Reasoning:** Hybrid (Vektor + Keyword + Graph‑Walk) mit konfigurierbaren Scoring‑Regeln und **Erklärpfad**. -- **Ops:** Deterministische IDs, Versions/Provenance‑Felder, Observability, Tests. +### 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. Zielarchitektur (Komponenten) -### 2.1 Ingestion Layer -1. **Frontmatter‑Validator** - - Erzwingt Pflichtfelder: `id`, `title`, `type`, `created`, `modified` - - Optionale Felder: `tags`, `visibility`, `relevance`, `time_weight_override`, `retriever_weight`, `chunk_profile`, `sources`, `aliases`, `edge_hints`. - - Validiert `type` gegen **types.yaml** (Konfiguration). +## 2. Datenmodell & Collections -2. **Parser** - - Extrahiert: reinen Text, Überschriften, Querverweise (`[[Note]]`, Markdown‑Links), Zitate/Quellen. - - Normalisiert Unicode, trennt Codeblöcke, Zitate, Aufzählungen. +### 2.1 Note, Chunk, Edge – Begriffe -3. **Chunker** (profilgesteuert) - - Profile: `short` (~300–500 Token), `medium` (~700–1000), `long` (~1200–1600). - - Regeln: Absatz‑, Überschriften‑, semantische Grenzen, Überschneidung ~10–15% für Kontextkohärenz. - - Chunk‑Header speichert: `{note_id, path, type, idx, section_hierarchy, tokens, chunk_profile}`. +- **Note** + Entspricht einer Markdown-Datei mit YAML-Frontmatter (`id`, `title`, `type`, …). + In Qdrant als ein Punkt in `_notes`. -4. **Embedding** - - Standard: MiniLM‑384d (lokal über Ollama/TEI), konfigurierbar (`embedding.model`, `dim`). - - Persistiert Vektor + **Payload**. +- **Chunk** + Ein Abschnitt einer Note (z. B. Absatz, Überschrifts-Block, Sliding Window). + In Qdrant als Punkt in `_chunks`. -5. **Edge‑Builder** - - **Declared Edges**: aus expliziten Links/Referenzen in der Quelle. - - **Inferred Edges (Rules)**: aus `types.yaml.edge_defaults` **und** Heuristiken (z. B. Abschnittstitel „Bezug“, „siehe auch“). - - Beide Ebenen als **Edges** persistiert mit `source='declared' | 'rule'`, **kein bloßer Dot‑Marker**. - -### 2.2 Storage Layer (Qdrant) -**Collections (empfohlen):** -- `mindnet_notes` (1 Vektor je Note, optional) -- `mindnet_chunks` (**primärer Suchraum**) -- `mindnet_edges` (Graph als Payload‑Dokumente) -- `mindnet_runs` (Import‑Läufe, Audit/Provenance) -- `mindnet_configs` (aktive Konfigurationen, z. B. `types.yaml` Schnappschuss) - -**Beispiel‑Schemas:** - -```yaml -# mindnet_chunks.payload -id: string # deterministic (UUIDv5 über prefix+path+idx) -note_id: string # FK mindnet_notes.id -type: string # z. B. concept|task|experience|project|event … -title: string -path: string # Pfad im Vault -collection: string # Präfix/Namespace -created: string # ISO‑Datum -modified: string # ISO‑Datum -chunk_idx: int -chunk_profile: string # short|medium|long -retriever_weight: float # aus types.yaml oder Note‑Override -time_weight: float # berechnet (Decay) oder Override -section: string # Überschrift/Gliederung -text: string # Rohtext des Chunks -tokens: int -hash: string # content_hash (idempotent) -visibility: string # public|internal|sensitive -source: { # Provenance - repo: string|null, - path: string, - commit: string|null, - import_run_id: string -} -``` - -```yaml -# mindnet_edges.payload -id: string # deterministic (UUIDv5 über src_id + dst_id + relation + source) -src_id: string # id (Note ODER Chunk) -dst_id: string # id (Note ODER Chunk) -src_kind: string # note|chunk -dst_kind: string # note|chunk -relation: string # references|related_to|depends_on|belongs_to|derived_from|inspired_by|mentions|contradicts|supports … -weight: float # Standard 1.0; typenspezifische Offsets möglich -source: string # declared|rule|ml -evidence: string|null # Textausschnitt/Link der Ableitung -created: string -import_run_id: string -visibility: string -``` - -**Indizes (Qdrant):** -- Vektorindex auf `mindnet_chunks` (HNSW) -- Payload‑Indizes: - - `type` (keyword), `visibility` (keyword), `created/modified` (datetime), `note_id` (keyword) - - optional: **Full‑Text** auf `text` (Qdrant payload text index) für Hybrid‑Suche - -### 2.3 Retrieval & Scoring -**Hybrid‑Pipeline (empfohlen):** -1. **Lexikalische Kandidaten** (FTS auf `text`, `title`, `aliases`) – k_l. -2. **Vektor‑Kandidaten** (ANN auf Embeddings) – k_v. -3. **Graph‑Expansion**: 1–2 Hops über `mindnet_edges` mit relation‑spezifischen Boosts. -4. **Re‑Ranking** (finale Score): - - `S = α·sim + β·kw + γ·G + δ·time_decay + ε·type_weight + ζ·recency_boost_overrides` - - `type_weight` = `retriever_weight` aus **types.yaml** oder Note‑Override. - - `time_decay` = `exp(−λ·age_days)`; λ global/konfigurierbar; Override per Frontmatter möglich. - - **Erklärbarkeit:** Speichere die Summanden + Top‑Edges als **evidence** im Query‑Log. - -**Relation‑Gewichte (Defaultvorschlag):** -```yaml -relation_weights: - references: 1.0 - related_to: 0.6 - depends_on: 1.2 - belongs_to: 0.8 - derived_from: 1.1 - inspired_by: 0.8 - supports: 1.0 - contradicts: -0.9 -``` - -### 2.4 Konfiguration (types.yaml + config.yaml) -```yaml -version: 1.0 -types: - concept: { chunk_profile: medium, edge_defaults: [references, related_to], retriever_weight: 0.33 } - task: { chunk_profile: short, edge_defaults: [depends_on, belongs_to], retriever_weight: 0.80 } - experience: { chunk_profile: medium, edge_defaults: [derived_from, inspired_by],retriever_weight: 0.90 } - project: { chunk_profile: long, edge_defaults: [references, depends_on], retriever_weight: 0.95 } -retrieval: - alpha: 0.45 # sim - beta: 0.25 # keyword - gamma: 0.20 # graph - delta: 0.07 # time decay - epsilon: 0.03 # type weight -time_decay: - lambda: 0.0025 # ~ Halbwertzeit ≈ 277 Tage -visibility_defaults: public -``` - -### 2.5 Determinismus, Versionierung, Idempotenz -- **IDs:** UUIDv5 über `{collection_prefix}:{path}:{chunk_idx}` (Chunks) bzw. `{collection_prefix}:{path}` (Notes). -- **content_hash:** SHA256 über normalisierte Inhalte (Unicode NFC, Whitespace‑Policy, Überschriften‑Normalisierung). -- **Import‑Lauf (`mindnet_runs`)**: `{run_id, started_at, finished_at, config_hash, files_seen, files_changed, errors}`. -- **Idempotenz:** nur `changed` bei `content_hash`‑Abweichung. - -### 2.6 Erklärbarkeit (Audit Trail) -- Jede Antwort liefert: Top‑Chunks, Summanden des Scores, **angetretene Kanten** inkl. Relation & Gewicht, Zeitfaktor. -- Persistiere Query‑Logs (optional): `{query, filters, topk, decision_trace, timestamp}`. - -### 2.7 Sicherheit & Datenschutz -- **visibility**: `public|internal|sensitive`, Query‑Filter standardmäßig `internal`. -- Optionale **ACL**‑Schicht: Nutzer/Gruppen, Freigabe je Note/Chunk/Edge. -- **Redaktions‑Flags**: `needs_review`, `draft`, `private_journal` (werden standardmäßig ausgeschlossen). - -### 2.8 Skalierung & Betrieb -- **Shards/Replicas** (Qdrant): 1/1 lokal; 2/2 in DC/Cloud. -- **Snapshots/Backups**: Qdrant Snapshot + Vault‑Repo Snapshot (rsync/borg). -- **Observability**: Prometheus‑Exporter (API Latenz, Import‑Dauer, Chunks/Edges pro Lauf), strukturierte Logs (JSON). -- **Tests**: deterministische Golden‑Files für Parser/Chunker; Contract‑Tests für API; Offline‑Eval für Retrieval. +- **Edge** + Eine gerichtete Kante, die eine Beziehung zwischen: + - zwei Chunks, + - Chunk ↔ Note, + - oder (konzeptionell) Note ↔ Note beschreibt. + Gespeichert in `_edges`. --- -## 3. Implementierungsleitfaden (konkret) -### 3.1 Collections anlegen (Qdrant) -- `mindnet_chunks(dim=384, distance=cosine)` -- `mindnet_notes(dim=384, distance=cosine)` (optional, falls Note‑Level‑Suche gewünscht) -- `mindnet_edges(dim=0)` (reiner Payload‑Store; kein Vektor) -- Payload‑Indizes: siehe 2.2 +### 2.2 Qdrant Collections (Stand V2) -### 3.2 Import‑Pipeline (CLI) -1. **Scan** Vault → valide Dateien. -2. **Make Note Payload** (inkl. Typ‑Regeln; `retriever_weight`/`chunk_profile` aus **types.yaml**, Override per Frontmatter). -3. **Chunken** gemäß Profil + `chunk_idx`. -4. **Edges bauen**: declared + rule‑inferred (duplizierte Kanten per ID‑Determinismus verhindern). -5. **Upsert**: Notes → Chunks → Edges (in dieser Reihenfolge). -6. **Run‑Record** und **Config‑Schnappschuss** persistieren. +**Namenskonvention:** +Prefix aus ENV (`COLLECTION_PREFIX`, z. B. `mindnet`): -### 3.3 Query‑API (FastAPI) -- `POST /query`: text, filters({visibility, types, date_range}), topk, graph_hops, `explain=true`. -- `GET /subgraph/{id}`: Nachbarschaft, Relation‑Filter, Depth. -- `POST /admin/reindex` / `POST /admin/import`. +- `mindnet_notes` +- `mindnet_chunks` +- `mindnet_edges` -### 3.4 Konfigurierbarkeit -- `types.yaml` heiß ladbar (mit Versionsstempel); bei Änderung → `config_hash` & reimport optional. -- `retrieval.yaml` für Gewichte, Relation‑Boosts, Zeitdecay. +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). --- -## 4. Migrations‑ und Qualitätsregeln -- **Keine Workarounds**, die Semantik zerstören (z. B. „Dot‑Edge“ ohne Relation). -- **Explizite Kantenpersistenz**: *jede* Regelkante wird als `source='rule'` gespeichert. -- **Chunk‑Zahl**: Profiltreue sicherstellen; Regressionstest (z. B. 171 ➝ 59 = **Fehler** oder Profiländerung dokumentieren). -- **Provenance Pflicht**: `import_run_id` in Chunks/Edges setzen. +### 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. --- -## 5. Offene Erweiterungen (vordenken) -- **ML‑Edge‑Inference** (NER, Coref, TEI‑Reranking). -- **Reasoning‑Graph** (Motivationsketten: Kindheitserlebnis → Prinzip → Entscheidung). -- **Citations‑Fabric** (autom. Quellenkonsolidierung). -- **Temporale Graphabfragen** (Edges mit Zeitintervallen). +### 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. --- -## Anhang A: Originalinhalt (unverändert, falls vorhanden) -Der vollständige Originaltext aus `knowledge_design.md` bleibt hier als Referenz erhalten, damit keine implizite Aussage verloren geht. +### 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::` + 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. --- -# mindnet – Knowledge Design -**Version:** 1.4.0 (aktualisiert 2025-10-06) +## 3. Typ-Konfiguration (`config/types.yaml`) -## 1. Gesamtziel und Systemverständnis +### 3.1 Struktur des Files -**mindnet** ist ein lokales Wissensnetzwerk, das alle meine Notizen, Projekte, Gedanken, Erfahrungen und Pläne als Markdown-Dateien speichert, automatisch importiert, semantisch in **Chunks** zerlegt und als **Graph-Struktur in Qdrant** ablegt. -Es ist die Grundlage für ein agentenfähiges, lokal betriebenes LLM-System, das meine Daten verknüpft, abfragt und erweitert. +`types.yaml` (vereinfachtes Beispiel): -**Zentrale Ziele** -- Vollständige Repräsentation persönlichen Wissens in strukturierter, maschinenlesbarer Form -- Reproduzierbare Verarbeitung: Import → Chunking → Embedding → Edges → Graph -- Verlustfreie Roundtrip-Fähigkeit (Import ⇄ Export) -- Erweiterbarkeit für Agenten, RAG-Retriever und semantische Suche + ```yaml + version: 1.0 + default: + retriever_weight: 1.0 + chunk_profile: default -**Prinzipien** -- **Deterministisch:** IDs, Hashes und Edges wiederholbar -- **Portabel:** Markdown + YAML -- **Robust:** fehlertoleranter Parser -- **Idempotent:** keine Duplikate bei Re-Import -- **Erweiterbar:** neue Typen und Edge-Arten ohne Migration + 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..retriever_weight` (falls vorhanden) + - `chunk_profile` ← `types..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**. --- -## 2. YAML-Frontmatter-Schema +## 4. Import-Pipeline (Markdown → Qdrant) -| Feld | Typ | Beschreibung | -|-------|-----|--------------| -| `title` | string | Menschlich lesbarer Titel | -| `id` | string | Eindeutige Note-ID (Slug) | -| `type` | enum | `concept`, `thought`, `experience`, `task`, `project`, `journal`, `person`, `meeting`, `milestone` | -| `status` | enum | `draft`, `active`, `done`, `archived` | -| `created` | date | ISO-Datum | -| `updated` | date | Letzte Änderung | -| `area` | string | Themen- oder Lebensbereich | -| `project` | string | Zugehöriges Projekt | -| `tags` | list | Strukturierte Tags (`area/…`, `topic/…`) | -| `depends_on` | list | IDs anderer Notizen | -| `assigned_to` | list | Beteiligte Personen | -| `embedding_exclude` | bool | Falls `true`, keine Embeddings | -| `hash_mode` | enum | Hash-Modus beim Import (`body`, `frontmatter`, `full`) | -| `priority` | enum/int | 1–5 oder `low` / `med` / `high` | -| `effort_min`, `due` | int/date | Aufwand / Termin | -| `aliases` | list | Alternative IDs | -| `lang` | string | ISO-Sprachcode | -| `source` | string | Herkunft / Referenzquelle | +### 4.1 Runbook -**Pflichtfelder:** `title`, `id`, `type`, `status`, `created` -**Optionale Felder:** alle übrigen +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" + ``` --- -## 3. Dateinamen und Ordner +### 4.2 Verarbeitungsschritte -- Format: `YYYY-MM-DD_title.md` oder `slug.md` -- Strukturierte Ordner: - - `10_thoughts/`, `20_concepts/`, `30_projects/`, `40_experiences/`, … -- Alle Pfade relativ; Unterstriche statt Leerzeichen +**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. --- -## 4. Link- und Edge-Design +## 5. Edges im Detail – Semantik & Regeln -**Verlinkungen** -- Wikilinks: `[[note-id]]` oder `[[Titel|note-id]]` -- Automatische Edges aus Text und Frontmatter +### 5.1 Struktur-Kanten -**Edge-Typen in Qdrant** +Erzeugt für **jede Note** mit >0 Chunks: -| Typ | Bedeutung | Quelle | Scope | -|------|------------|--------|--------| -| `belongs_to` | Chunk → Note | intern | chunk | -| `prev` / `next` | Reihenfolge | intern | chunk | -| `references` | explizite Wikilinks | Text | chunk | -| `backlink` | Gegenkante zu `references` | abgeleitet | note | -| `depends_on` | YAML-Abhängigkeiten | YAML | note | -| `assigned_to` | YAML-Zuweisung | YAML | note | -| `unresolved` | Ziel fehlt (Stub) | abgeleitet | chunk | +- **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` -**Edge-Schlüssel** -- `source_id`, `target_id`, `kind`, `scope`, `note_id` -- Dedup-Schlüssel: Kombination aus `(kind, source_id, target_id, scope)` +Tests (`tests.test_edges_smoke` / `tests.test_edges_all`) stellen sicher: + +- `belongs_to` == `#chunks` +- `next` == `prev` == `#chunks - 1` +- keine Duplikate der Strukturkanten. --- -## 5. Chunking-Modell +### 5.2 Explizite Wiki-Links (`[[Target Title]]`) -**Chunk-Datenstruktur** -| Feld | Beschreibung | -|-------|---------------| -| `note_id` | Zugehörige Note | -| `chunk_index` | Laufende Nummer | -| `text` | Originalabschnitt | -| `window` | Text + Overlap links/rechts | -| `overlap_left` | Länge des linken Overlaps | -| `overlap_right` | Länge des rechten Overlaps | -| `embedding` | 384-dimensionaler Vektor | -| `tokens` | Tokenanzahl (optional) | +- 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 = ""` (z. B. `"Vector DB Basics"`) + - `rule_id = "explicit:wikilink"` + - `confidence = 1.0` -**Chunking-Regeln (aus `chunking_strategy.md`)** -- Trennung nach Absätzen, Überschriften, Listen, Tabellen -- Token-Zielgröße 350–500 (max 600) -- Overlap: 30–40 % für semantische Kohärenz -- Overlap wird in `window` integriert, sodass `text ≠ window` +Diese Kanten bilden das **explizite Referenznetz** des Nutzers ab (Obsidian-typische Wikilinks). --- -## 6. Hash-Strategie und Änderungsverfolgung +### 5.3 Inline-Relationen (`[[rel:… …]]`) -**Zweck:** Nur bei realer Inhaltsänderung re-indexieren +Aktuell **implementiert und funktional** ist folgende Form: -| Umgebungsvariable | Beschreibung | -|--------------------|--------------| -| `MINDNET_HASH_COMPARE` | Vergleichsquelle (`Body`, `Full`, `Frontmatter`) | -| `MINDNET_HASH_SOURCE` | Rohtext vs. geparst (`raw` / `parsed`) | -| `MINDNET_HASH_NORMALIZE` | Normalisierung (`canonical` / `none`) | +- **Format (aktuell unterstützt):** -**Basismodi** -- `body` → Nur Textkörper -- `frontmatter` → Nur Metadaten -- `full` → Kombination -- Hash = SHA-256(canonicalized_input) + ```md + [[rel:depends_on Embeddings 101]] + [[rel:similar_to Vector DB Basics]] + [[rel:related_to Qdrant Vektordatenbank]] + ``` -Bei Änderung: Re-Import, neue Embeddings, Edges werden aktualisiert. +- 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 = ""` + - `scope = "chunk"` + - `source_id = chunk_id` + - `target_id = ""` + - `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**. --- -## 7. Qdrant-Speicherstruktur +### 5.4 Typ-basierte Default-Edges (`edge_defaults`) -| Collection | Inhalt | Primärfelder | Embedding | -|-------------|---------|---------------|------------| -| `mindnet_notes` | Notes mit Fulltext & Hash | `note_id` | optional | -| `mindnet_chunks` | Chunks mit Text, Window, Embedding | `chunk_id`, `note_id` | 384 d | -| `mindnet_edges` | Graphkanten | `edge_id`, `source_id`, `target_id` | — | +Zweck: -**Upsert-Strategie** -- Idempotente UUIDv5-IDs -- Keine Duplikate durch deterministische Signaturen -- `purge-before-upsert` zur Bereinigung +- 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. -**Indizes** -- notes: `note_id`, `hash_signature` -- chunks: `note_id`, `chunk_index` -- edges: `(kind, source_id, target_id, scope)` +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)`. --- -## 8. Fehler-Toleranz und Parser-Robustheit +### 5.5 `rule_id` & `confidence` – Bedeutung -- UTF-8 mit Fallback: ersetzt ungültige Bytes (`errors="replace"`) -- Ignoriert Nullbytes, BOM, exotische Zeilenenden -- Erkennt defekte oder leere YAML-Header und überspringt sie -- Logt Problemfälle (`read_markdown failed`, `make_note_payload returned non-dict`) -- Bei Fehlern: Import → warn → continue +**`rule_id`** kodiert **Herkunft** und **Regel**, z. B.: + +- `structure:belongs_to`, `structure:next`, `structure:prev` +- `explicit:wikilink` +- `inline:rel` +- `edge_defaults::` + +**`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. --- -## 9. Tests & Validierung +## 6. Qdrant-Schema & Indizes -**Integrationstests** -- Roundtrip: Import → Export → Compare (`compare_vaults.py`) -- Chunk-Integrität: `verify_chunks_integrity.py` -- Hash-Audit: `hash_reporter.py` -- Window-Vergleich: `check_chunks_window_vs_text.py` +### 6.1 Erstellung & Indizes -**Erwartete Ergebnisse** -- Alle Notes werden erkannt -- `window ≠ text` bei > 60 % der Chunks -- Roundtrip-Vergleich liefert „OK“ -- Edge-Anzahl entspricht Modell (belongs_to = #Chunks, next/prev ≈ #Chunks – 1) +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.) --- -## 10. Export & Roundtrip-Verhalten +## 7. Nutzung im Retrieval (Ausblick) -- `export_markdown.py` erzeugt identische Markdown-Dateien -- Schreibt YAML + Body in korrekter Reihenfolge -- Roundtrip-Prüfung (`compare_vaults.py`) validiert: - - vollständigen Body - - unveränderte Hash-Signatur - - gleiche Chunkzahl +### 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. --- -## 11. Graph-Schicht und WP-04-Integration +## 8. Offene Punkte / Known Limitations -- Qdrant fungiert als Graph-Backend -- `graph/service.py` (WP-04): stellt API für Mehrhop-Abfragen bereit -- Unterstützt: - - `expand(note_id, hops=2)` - - `resolve_unresolved()` - - `neighbors(kind=…)` -- Grundlage für spätere **Retriever-Funktionen** (LLM / Agenten) +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)`). --- -## 12. Agenten- und LLM-Integration +## 9. Zusammenfassung -**Retriever-Pipeline** -1. Suche top-k Chunks (cosine distance) -2. Erweiterung um Edges (`references`, `backlinks`) -3. Kontext-Assembling via `window`-Text -4. Prompt-Generierung +- Die Mindnet-V2-Architektur implementiert einen **dreistufigen Speicher**: + - Notes (`mindnet_notes`), + - Chunks (`mindnet_chunks`), + - Edges (`mindnet_edges`). -**Agent-Use-Cases** -- Automatisches Tagging (`type`, `status`) -- Vorschläge für neue Edges -- Auflösen von `unresolved`-Referenzen -- Auto-Import externer Quellen (MediaWiki, PDFs, Webseiten) +- **`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. -## 13. Erweiterbarkeit +- 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`). -- Neue Edge-Typen: `inspired_by`, `relates_to`, `contradicts` -- Neue Note-Typen: `decision`, `resource`, `recommendation` -- Embedding-Engines austauschbar (MiniLM → LaBSE → E5) -- Versionierte Chunking-Strategien (`chunk_config.py`) +- `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. -## 14. Qualitätssicherung und Best Practices +Dieses Dokument bildet damit das aktuelle technische Gesamtbild von Mindnet V2 ab, inkl. aller relevanten Mechanismen rund um Typen, Chunking, Edges und Qdrant-Schema. -- Validierung vor Upsert -- Keine Leer-Chunks -- Vollständige Metadaten in Notes -- Tests in `tests/` automatisch ausführbar (`run_e2e_roundtrip.sh`) -- Ergebnis: „Roundtrip OK“ und `verify_chunks_integrity` = OK - ---- - -## 15. Änderungsverlauf - -**v1.4.0 (2025-10-06)** -- Neu: `window` vs `text` Feld in `mindnet_chunks` -- Neu: Hash-Modi (`body`, `frontmatter`, `full`) mit Env-Steuerung -- Neu: Baseline-Modus für Vergleich -- Parser → fehlertolerant (UTF-8 replace) -- Roundtrip-Test vollständig integriert -- Graph-Service und Mehrhop-Abfragen (WP-04-Vorbereitung) -- `purge-before-upsert` und `--note-scope-refs` Parameter ergänzt - -**v1.3.0 (2025-09-09)** -- Hash-Normalisierung und Edge-Scope -- Roundtrip-Tests, Agenten-Integration - -**v1.2.0 (2025-09-02)** -- Grundstruktur des Knowledge-Designs - ----