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

205 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WP-02 Chunking & Embedding-Strategie
**Projekt:** mindnet Wissensnetzwerk
**Bezug:** `knowledge_design.md` (IDs, Dateinamen, Tags, Edge-Typen, Abschnitts-Konventionen)
---
## 1) Ziel & Rahmen
Entwickle eine robuste, parser-freundliche **Chunking-Strategie** für Markdown-Notizen, die:
- die **Frontmatter** (YAML) als Meta nutzt, aber nicht (standardmäßig) embedden lässt,
- den **Body** in semantisch sinnvolle Chunks zerlegt (Überschriften/Absätze/Listen),
- **Edges** zwischen Chunks und Notizen/Links erzeugt (u. a. `belongs_to`, `references`, `backlink`).
---
## 2) Annahmen aus `knowledge_design.md`
- **IDs**: deterministische, stabile `id` pro Note (Slug / semantischer Identifier), ggf. `aliases`.
- **Dateinamen**: entweder `YYYY-MM-DD_title.md` oder `slug.md`; Pfade sind **relativ** zum Vault.
- **Frontmatter-Pflichtfelder**: `id`, `title`, `type`, `created`, `updated` (Details in `knowledge_design.md`).
- **Backlink-/Link-Regeln**: Wikilinks `[[id]]` im Body; am Ende optional redaktionelle Sektion „Mögliche Verbindungen“ (erzeugt **keine** Kanten).
---
## 3) Chunking-Prinzipien
- **Semantische Schnitte**: Chunks enden an Absätzen, Überschriften, Listenpunkten, Codeblöcken, Zitaten.
- **Frontmatter**: YAML **nicht** embedden; stattdessen komplett im Note-Payload speichern.
- **Verlustfreiheit**:
- Note-Payload: `fulltext` (gesamter Body).
- Chunk-Payload: `text` (Originalchunk).
Damit kann der Export den Body **verlustfrei** rekonstruieren (erst `fulltext`, sonst Chunks).
---
## 4) Optimale Chunkgrößen & Overlaps
- **Zielgröße**: 250400 Tokens (Richtwert, abhängig vom `type`).
- **Maxgröße**: 600800 Tokens (nur in Ausnahmen).
- **Mingröße**: 80120 Tokens (sonst mit Nachbar verschmelzen).
- **Overlap**: 4060 Tokens, um Kontextverlust zu vermeiden.
- **Kontextabhängig**:
- `concept`, `thought`, `experience`: 250350 Tokens
- `task`, `project`: eher 200300 Tokens (mehr Bullet-Struktur)
- `journal`: 300400 Tokens (tagesabschnittsweise)
---
## 4.1 Overlap-Regeln
- Overlap betrifft **nur den Body**; die YAML-Frontmatter bleibt **außerhalb**.
- Keine Overlaps, die Codeblöcke oder Tabellen „aufreißen“. Falls nötig: Chunk-Grenze an Blockgrenze ziehen.
- Bei reinen Listen: zusammenhängende Bullets gruppieren, ggf. kleinere Overlaps.
---
## 5) Semantische Regeln je Strukturelement
- **Überschriften**: vorzugsweise an H2/H3 trennen; H1 meist Dokumenttitel → Teil der Frontmatter.
- **Absätze**: primäre Trennkandidaten.
- **Listen**: zusammenhängende Gruppe als ein Chunk, wenn sie einen semantischen Block bilden.
- **Codeblöcke**: niemals splitten; Code bleibt in einem Chunk.
- **Zitate**: ab 2+ Zeilen als eigener Chunk.
- **Bilder/Anhänge**: Referenzen im Text belassen; Pfade relativ halten.
---
## 6) Normalisierung & Preprocessing
- **Zeilenenden**: `\r\n``\n`, trailing spaces trimmen (nur für Hashing, nicht für Anzeige).
- **Whitespace**: mehrfach-Blankzeilen reduzieren (optional, Hash-Modus beachten).
- **Links**: `[[Titel|id]]` zu `(display="Titel", target_id="id")` normalisieren.
- **Pfade**: `path` im Payload **zwingend relativ** (Backslashes → Slashes).
---
## 7) Edge-Modell (Graph)
- **Chunk → Note:** `belongs_to(note_id)` (obligatorisch; Edge-Payload enthält zusätzlich `note_id` als Owner-Note).
- **Chunk → Chunk (gleiche Note):** `prev`, `next` (symmetrisch für lineare Navigation; beide Richtungen werden erzeugt).
- **Chunk → Note:** `references(target_id)` aus `[[Wikilinks]]` oder Markdown-Links im Chunk-Text (Standard; *Chunk-Scope*).
- **Note → Note:** `backlink(target_id)` automatisch generiert, dedupliziert pro Note (*Note-Scope*).
- **Optional:** `references:note` (Note-Scope-References) per Flag/ENV aktivierbar; **Default = aus**, um Doppelzählungen zu vermeiden.
- **Unresolved:** Wenn `target_id` nicht existiert, wird eine Kante mit `status: "unresolved"` angelegt (später durch Resolver/Agent heilbar).
---
## 8) Datenmodell (Qdrant-tauglich)
**Point (Chunk)**
{
"id": "20250902-1830-thought-yaml-recall#c02",
"vector": [/* embedding */],
"payload": {
"note_id": "20250902-1830-thought-yaml-recall",
"note_title": "Gedanke: Einheitliche YAML-Standards steigern Recall",
"path": "10_thoughts/20250902-1830-thought-yaml-recall.md",
"type": "thought",
"area": "mindnet",
"project": "project-mindnet",
"tags": ["area/mindnet","type/thought","topic/yaml"],
"chunk_index": 2,
"char_start": 1024,
"char_end": 1789,
"section_title": "Begründung / Details",
"section_path": "Begründung / Details",
"neighbors": {"prev": "…#c01", "next": "…#c03"},
"wikilinks": ["concept-yaml-styleguide"],
"references": [{"kind":"wikilink", "target_id":"concept-yaml-styleguide"}],
"text": "… ursprünglicher Chunk-Text …"
}
}
**Point (Note)**
{
"id": "20250902-1830-thought-yaml-recall",
"vector": [/* optional Nullvektor oder Zentroid */],
"payload": {
"note_id": "20250902-1830-thought-yaml-recall",
"title": "Gedanke: Einheitliche YAML-Standards steigern Recall",
"type": "thought",
"status": "draft",
"created": "2025-09-02",
"updated": "2025-09-02",
"path": "10_thoughts/20250902-1830-thought-yaml-recall.md",
"tags": ["area/mindnet","type/thought","topic/yaml"],
"hash_fulltext": "sha256:…",
"fulltext": "… gesamter Body für verlustfreie Rekonstruktion …",
"references": ["concept-yaml-styleguide"] // Fallback für Note-Level-Backlinks
}
}
**Edges (Payload; neues Schema)**
{
"kind": "references" | "backlink" | "belongs_to" | "prev" | "next",
"source_id": "<chunk_id|note_id>",
"target_id": "<note_id|chunk_id>",
"scope": "chunk" | "note",
"note_id": "<owner-note-id>", // neu: für schnelles Filtern/Purge
"status": "ok" | "unresolved" // optional
}
**Performance-Hinweis (Qdrant):** Für schnelle Filter/Deletes Payload-Indizes anlegen: **edges**`kind`, `scope`, `source_id`, `target_id`, `note_id`; **chunks**`note_id`, `chunk_index`; **notes**`note_id`.
---
## 9) Algorithmus (Python-ready Outline)
def chunk_markdown_note(md_text: str, file_path: str) -> list[Chunk]:
fm, body = split_frontmatter(md_text) # YAML -> dict
meta = normalize_frontmatter(fm) # enforce schema defaults
ast = parse_markdown_to_ast(body) # any mdast-compatible lib
sections = split_by_headings(ast, levels=(2,3)) # H2/H3 primary
raw_chunks = []
for sec in sections:
blocks = group_blocks(sec, keep_code=True, keep_lists=True)
for group in soft_wrap(blocks, target_tokens=target_size(meta["type"]),
max_tokens=max_size(meta["type"])):
raw_chunks.append(render_md(group))
chunks = apply_overlap(raw_chunks, overlap_tokens=overlap(meta["type"]))
chunks = normalize_chunks(chunks) # trim, fix code fences, etc.
# IDs & Nachbarn
for i, ch in enumerate(chunks, start=1):
ch.id = f"{meta['id']}#c{i:02d}"
ch.index = i
ch.neighbors_prev = chunks[i-2].id if i > 1 else None
ch.neighbors_next = chunks[i].id if i < len(chunks) else None
# Wikilinks je Chunk
for ch in chunks:
ch.wikilinks = extract_wikilinks(ch.text)
return chunks
---
## 10) Einbettung (Embeddings)
- Modell: MiniLM/all-MiniLM-L6-v2 (384d) oder E5 (besseres Retrieval, ggf. multilingual).
- **Note-Embeddings** optional (Zentroid der Chunks).
- **Hashing für Change-Detection** (Importer):
- `MINDNET_HASH_MODE = body|frontmatter|body+frontmatter` (Default: `body`)
- `MINDNET_HASH_NORMALIZE = canonical|none` (Default: `canonical`)
---
## 11) Qualitätssicherung
- **Zähler**: `notes/chunks/edges` je Lauf prüfen.
- **Edge-Invarianten**:
- `belongs_to == #Chunks`
- `next == prev == Σ(max(chunks_in_note-1, 0))`
- `references (chunk) == Σ(Chunk-Wikilinks)`
- `backlink == Σ(eindeutige Wikilinks je Note)`
- **Unresolved**: sollten durch Resolver-Jobs über Zeit abnehmen.
- **Roundtrip-Test**: Import Export Diff; exakt rekonstruierbare Bodies.
---
## 12) Deliverables
- Code: Parser, Chunker, Importer, Exporter, Edges.
- Schemas: `note.schema.json`, Payload-Layouts.
- Tests: Mini-Vault (`scripts/make_test_vault.py`), Audit/Validate.
---
## 13) Prompt für neues Chatfenster (kopieren & einfügen)
*(Unverändert)*