diff --git a/app/core/chunking/chunking_models.py b/app/core/chunking/chunking_models.py index d64c4e7..20c6cad 100644 --- a/app/core/chunking/chunking_models.py +++ b/app/core/chunking/chunking_models.py @@ -13,6 +13,7 @@ class RawBlock: level: Optional[int] section_path: str section_title: Optional[str] + exclude_from_chunking: bool = False # WP-24c v4.2.0: Flag für Edge-Zonen, die nicht gechunkt werden sollen @dataclass class Chunk: diff --git a/app/core/chunking/chunking_parser.py b/app/core/chunking/chunking_parser.py index e36ff0e..8cfc2c3 100644 --- a/app/core/chunking/chunking_parser.py +++ b/app/core/chunking/chunking_parser.py @@ -3,8 +3,10 @@ FILE: app/core/chunking/chunking_parser.py DESCRIPTION: Zerlegt Markdown in logische Einheiten (RawBlocks). Hält alle Überschriftenebenen (H1-H6) im Stream. Stellt die Funktion parse_edges_robust zur Verfügung. + WP-24c v4.2.0: Identifiziert Edge-Zonen und markiert sie für Chunking-Ausschluss. """ import re +import os from typing import List, Tuple, Set from .chunking_models import RawBlock from .chunking_utils import extract_frontmatter_from_text @@ -20,7 +22,10 @@ def split_sentences(text: str) -> list[str]: return [p.strip() for p in _SENT_SPLIT.split(text) if p.strip()] def parse_blocks(md_text: str) -> Tuple[List[RawBlock], str]: - """Zerlegt Text in logische Einheiten (RawBlocks), inklusive H1-H6.""" + """ + Zerlegt Text in logische Einheiten (RawBlocks), inklusive H1-H6. + WP-24c v4.2.0: Identifiziert Edge-Zonen (LLM-Validierung & Note-Scope) und markiert sie für Chunking-Ausschluss. + """ blocks = [] h1_title = "Dokument" section_path = "/" @@ -29,6 +34,31 @@ def parse_blocks(md_text: str) -> Tuple[List[RawBlock], str]: # Frontmatter entfernen fm, text_without_fm = extract_frontmatter_from_text(md_text) + # WP-24c v4.2.0: Konfigurierbare Header-Namen und -Ebenen + llm_validation_headers = os.getenv( + "MINDNET_LLM_VALIDATION_HEADERS", + "Unzugeordnete Kanten,Edge Pool,Candidates" + ) + llm_validation_header_list = [h.strip() for h in llm_validation_headers.split(",") if h.strip()] + if not llm_validation_header_list: + llm_validation_header_list = ["Unzugeordnete Kanten", "Edge Pool", "Candidates"] + + note_scope_headers = os.getenv( + "MINDNET_NOTE_SCOPE_ZONE_HEADERS", + "Smart Edges,Relationen,Global Links,Note-Level Relations,Globale Verbindungen" + ) + note_scope_header_list = [h.strip() for h in note_scope_headers.split(",") if h.strip()] + if not note_scope_header_list: + note_scope_header_list = ["Smart Edges", "Relationen", "Global Links", "Note-Level Relations", "Globale Verbindungen"] + + # Header-Ebenen konfigurierbar (Default: LLM=3, Note-Scope=2) + llm_validation_level = int(os.getenv("MINDNET_LLM_VALIDATION_HEADER_LEVEL", "3")) + note_scope_level = int(os.getenv("MINDNET_NOTE_SCOPE_HEADER_LEVEL", "2")) + + # Status-Tracking für Edge-Zonen + in_exclusion_zone = False + exclusion_zone_type = None # "llm_validation" oder "note_scope" + # H1 für Note-Titel extrahieren (Metadaten-Zweck) h1_match = re.search(r'^#\s+(.*)', text_without_fm, re.MULTILINE) if h1_match: @@ -47,20 +77,47 @@ def parse_blocks(md_text: str) -> Tuple[List[RawBlock], str]: if buffer: content = "\n".join(buffer).strip() if content: - blocks.append(RawBlock("paragraph", content, None, section_path, current_section_title)) + blocks.append(RawBlock( + "paragraph", content, None, section_path, current_section_title, + exclude_from_chunking=in_exclusion_zone + )) buffer = [] level = len(heading_match.group(1)) title = heading_match.group(2).strip() + # WP-24c v4.2.0: Prüfe, ob dieser Header eine Edge-Zone startet + is_llm_validation_zone = ( + level == llm_validation_level and + any(title.lower() == h.lower() for h in llm_validation_header_list) + ) + is_note_scope_zone = ( + level == note_scope_level and + any(title.lower() == h.lower() for h in note_scope_header_list) + ) + + if is_llm_validation_zone: + in_exclusion_zone = True + exclusion_zone_type = "llm_validation" + elif is_note_scope_zone: + in_exclusion_zone = True + exclusion_zone_type = "note_scope" + elif in_exclusion_zone: + # Neuer Header gefunden, der keine Edge-Zone ist -> Zone beendet + in_exclusion_zone = False + exclusion_zone_type = None + # Pfad- und Titel-Update für die Metadaten der folgenden Blöcke if level == 1: current_section_title = title; section_path = "/" elif level == 2: current_section_title = title; section_path = f"/{current_section_title}" - # Die Überschrift selbst als regulären Block hinzufügen - blocks.append(RawBlock("heading", stripped, level, section_path, current_section_title)) + # Die Überschrift selbst als regulären Block hinzufügen (auch markiert, wenn in Zone) + blocks.append(RawBlock( + "heading", stripped, level, section_path, current_section_title, + exclude_from_chunking=in_exclusion_zone + )) continue # Trenner (---) oder Leerzeilen beenden Blöcke, außer innerhalb von Callouts @@ -68,17 +125,26 @@ def parse_blocks(md_text: str) -> Tuple[List[RawBlock], str]: if buffer: content = "\n".join(buffer).strip() if content: - blocks.append(RawBlock("paragraph", content, None, section_path, current_section_title)) + blocks.append(RawBlock( + "paragraph", content, None, section_path, current_section_title, + exclude_from_chunking=in_exclusion_zone + )) buffer = [] if stripped == "---": - blocks.append(RawBlock("separator", "---", None, section_path, current_section_title)) + blocks.append(RawBlock( + "separator", "---", None, section_path, current_section_title, + exclude_from_chunking=in_exclusion_zone + )) else: buffer.append(line) if buffer: content = "\n".join(buffer).strip() if content: - blocks.append(RawBlock("paragraph", content, None, section_path, current_section_title)) + blocks.append(RawBlock( + "paragraph", content, None, section_path, current_section_title, + exclude_from_chunking=in_exclusion_zone + )) return blocks, h1_title diff --git a/app/core/chunking/chunking_processor.py b/app/core/chunking/chunking_processor.py index 26c2b68..358318b 100644 --- a/app/core/chunking/chunking_processor.py +++ b/app/core/chunking/chunking_processor.py @@ -6,9 +6,11 @@ DESCRIPTION: Der zentrale Orchestrator für das Chunking-System. - Integriert physikalische Kanten-Injektion (Propagierung). - Stellt H1-Kontext-Fenster sicher. - Baut den Candidate-Pool für die WP-15b Ingestion auf. + WP-24c v4.2.0: Konfigurierbare Header-Namen für LLM-Validierung. """ import asyncio import re +import os import logging from typing import List, Dict, Optional from .chunking_models import Chunk @@ -31,6 +33,10 @@ async def assemble_chunks(note_id: str, md_text: str, note_type: str, config: Op fm, body_text = extract_frontmatter_from_text(md_text) blocks, doc_title = parse_blocks(md_text) + # WP-24c v4.2.0: Filtere Blöcke aus Edge-Zonen (LLM-Validierung & Note-Scope) + # Diese Bereiche sollen nicht als Chunks angelegt werden, sondern nur die Kanten extrahiert werden + blocks_for_chunking = [b for b in blocks if not getattr(b, 'exclude_from_chunking', False)] + # Vorbereitung des H1-Präfix für die Embedding-Fenster (Breadcrumbs) h1_prefix = f"# {doc_title}" if doc_title else "" @@ -38,11 +44,11 @@ async def assemble_chunks(note_id: str, md_text: str, note_type: str, config: Op # Alle Strategien nutzen nun einheitlich context_prefix für die Window-Bildung. if config.get("strategy") == "by_heading": chunks = await asyncio.to_thread( - strategy_by_heading, blocks, config, note_id, context_prefix=h1_prefix + strategy_by_heading, blocks_for_chunking, config, note_id, context_prefix=h1_prefix ) else: chunks = await asyncio.to_thread( - strategy_sliding_window, blocks, config, note_id, context_prefix=h1_prefix + strategy_sliding_window, blocks_for_chunking, config, note_id, context_prefix=h1_prefix ) if not chunks: @@ -63,14 +69,29 @@ async def assemble_chunks(note_id: str, md_text: str, note_type: str, config: Op k, t = parts ch.candidate_pool.append({"kind": k, "to": t, "provenance": "explicit"}) - # 5. Global Pool (Unzugeordnete Kanten aus dem Dokument-Ende) - # Sucht nach dem Edge-Pool Block im Original-Markdown. - pool_match = re.search( - r'###?\s*(?:Unzugeordnete Kanten|Edge Pool|Candidates)\s*\n(.*?)(?:\n#|$)', - body_text, - re.DOTALL | re.IGNORECASE + # 5. Global Pool (Unzugeordnete Kanten - kann mitten im Dokument oder am Ende stehen) + # WP-24c v4.2.0: Konfigurierbare Header-Namen und -Ebene via .env + # Sucht nach ALLEN Edge-Pool Blöcken im Original-Markdown (nicht nur am Ende). + llm_validation_headers = os.getenv( + "MINDNET_LLM_VALIDATION_HEADERS", + "Unzugeordnete Kanten,Edge Pool,Candidates" ) - if pool_match: + header_list = [h.strip() for h in llm_validation_headers.split(",") if h.strip()] + # Fallback auf Defaults, falls leer + if not header_list: + header_list = ["Unzugeordnete Kanten", "Edge Pool", "Candidates"] + + # Header-Ebene konfigurierbar (Default: 3 für ###) + llm_validation_level = int(os.getenv("MINDNET_LLM_VALIDATION_HEADER_LEVEL", "3")) + header_level_pattern = "#" * llm_validation_level + + # Regex-Pattern mit konfigurierbaren Headern und Ebene + # WP-24c v4.2.0: finditer statt search, um ALLE Zonen zu finden (auch mitten im Dokument) + # Zone endet bei einem neuen Header (jeder Ebene) oder am Dokument-Ende + header_pattern = "|".join(re.escape(h) for h in header_list) + zone_pattern = rf'^{re.escape(header_level_pattern)}\s*(?:{header_pattern})\s*\n(.*?)(?=\n#|$)' + + for pool_match in re.finditer(zone_pattern, body_text, re.DOTALL | re.IGNORECASE | re.MULTILINE): global_edges = parse_edges_robust(pool_match.group(1)) for e_str in global_edges: parts = e_str.split(':', 1) diff --git a/app/core/graph/graph_derive_edges.py b/app/core/graph/graph_derive_edges.py index 3e85658..8d52b63 100644 --- a/app/core/graph/graph_derive_edges.py +++ b/app/core/graph/graph_derive_edges.py @@ -23,19 +23,34 @@ from .graph_extractors import ( ) # WP-24c v4.2.0: Header-basierte Identifikation von Note-Scope Zonen -NOTE_SCOPE_ZONE_HEADERS = [ - "Smart Edges", - "Relationen", - "Global Links", - "Note-Level Relations", - "Globale Verbindungen" -] +# Konfigurierbar via MINDNET_NOTE_SCOPE_ZONE_HEADERS (komma-separiert) +def get_note_scope_zone_headers() -> List[str]: + """ + Lädt die konfigurierten Header-Namen für Note-Scope Zonen. + Fallback auf Defaults, falls nicht konfiguriert. + """ + import os + headers_env = os.getenv( + "MINDNET_NOTE_SCOPE_ZONE_HEADERS", + "Smart Edges,Relationen,Global Links,Note-Level Relations,Globale Verbindungen" + ) + header_list = [h.strip() for h in headers_env.split(",") if h.strip()] + # Fallback auf Defaults, falls leer + if not header_list: + header_list = [ + "Smart Edges", + "Relationen", + "Global Links", + "Note-Level Relations", + "Globale Verbindungen" + ] + return header_list def extract_note_scope_zones(markdown_body: str) -> List[Tuple[str, str]]: """ WP-24c v4.2.0: Extrahiert Note-Scope Zonen aus Markdown. - Identifiziert Sektionen mit spezifischen Headern (z.B. "## Smart Edges") + Identifiziert Sektionen mit spezifischen Headern (konfigurierbar via .env) und extrahiert alle darin enthaltenen Links. Returns: @@ -46,8 +61,14 @@ def extract_note_scope_zones(markdown_body: str) -> List[Tuple[str, str]]: edges: List[Tuple[str, str]] = [] - # Regex für Header-Erkennung (## oder ###) - header_pattern = r'^#{2,3}\s+(.+?)$' + # WP-24c v4.2.0: Konfigurierbare Header-Ebene + import os + import re + note_scope_level = int(os.getenv("MINDNET_NOTE_SCOPE_HEADER_LEVEL", "2")) + header_level_pattern = "#" * note_scope_level + + # Regex für Header-Erkennung (konfigurierbare Ebene) + header_pattern = rf'^{re.escape(header_level_pattern)}\s+(.+?)$' lines = markdown_body.split('\n') in_zone = False @@ -60,9 +81,11 @@ def extract_note_scope_zones(markdown_body: str) -> List[Tuple[str, str]]: header_text = header_match.group(1).strip() # Prüfe, ob dieser Header eine Note-Scope Zone ist + # WP-24c v4.2.0: Dynamisches Laden der konfigurierten Header + zone_headers = get_note_scope_zone_headers() is_zone_header = any( header_text.lower() == zone_header.lower() - for zone_header in NOTE_SCOPE_ZONE_HEADERS + for zone_header in zone_headers ) if is_zone_header: diff --git a/config/prod.env b/config/prod.env index ae3f569..8b928c6 100644 --- a/config/prod.env +++ b/config/prod.env @@ -45,4 +45,19 @@ MINDNET_VAULT_ROOT=./vault_prod MINDNET_VOCAB_PATH=/mindnet/vault/mindnet/_system/dictionary/edge_vocabulary.md # Change Detection für effiziente Re-Imports -MINDNET_CHANGE_DETECTION_MODE=full \ No newline at end of file +MINDNET_CHANGE_DETECTION_MODE=full + +# --- WP-24c v4.2.0: Konfigurierbare Markdown-Header für Edge-Zonen --- +# Komma-separierte Liste von Headern für LLM-Validierung +# Format: Header1,Header2,Header3 +MINDNET_LLM_VALIDATION_HEADERS=Unzugeordnete Kanten,Edge Pool,Candidates + +# Header-Ebene für LLM-Validierung (1-6, Default: 3 für ###) +MINDNET_LLM_VALIDATION_HEADER_LEVEL=3 + +# Komma-separierte Liste von Headern für Note-Scope Zonen +# Format: Header1,Header2,Header3 +MINDNET_NOTE_SCOPE_ZONE_HEADERS=Smart Edges,Relationen,Global Links,Note-Level Relations,Globale Verbindungen + +# Header-Ebene für Note-Scope Zonen (1-6, Default: 2 für ##) +MINDNET_NOTE_SCOPE_HEADER_LEVEL=2 \ No newline at end of file diff --git a/docs/03_Technical_References/03_tech_configuration.md b/docs/03_Technical_References/03_tech_configuration.md index 1f0b2d7..f2be011 100644 --- a/docs/03_Technical_References/03_tech_configuration.md +++ b/docs/03_Technical_References/03_tech_configuration.md @@ -50,6 +50,11 @@ Diese Variablen steuern die Infrastruktur, Pfade und globale Timeouts. Seit der | `MINDNET_LL_BACKGROUND_LIMIT`| `2` | **Traffic Control:** Max. parallele Hintergrund-Tasks (Semaphore). | | `MINDNET_CHANGE_DETECTION_MODE` | `full` | `full` (Text + Meta) oder `body` (nur Text). | | `MINDNET_DEFAULT_RETRIEVER_WEIGHT` | `1.0` | **Neu (WP-22):** Systemweiter Standard für das Retriever-Gewicht einer Notiz. | +| `MINDNET_LLM_VALIDATION_HEADERS` | `Unzugeordnete Kanten,Edge Pool,Candidates` | **Neu (v4.2.0):** Komma-separierte Header-Namen für LLM-Validierung. | +| `MINDNET_LLM_VALIDATION_HEADER_LEVEL` | `3` | **Neu (v4.2.0):** Header-Ebene für LLM-Validierung (1-6, Default: 3 für ###). | +| `MINDNET_NOTE_SCOPE_ZONE_HEADERS` | `Smart Edges,Relationen,Global Links,Note-Level Relations,Globale Verbindungen` | **Neu (v4.2.0):** Komma-separierte Header-Namen für Note-Scope Zonen. | +| `MINDNET_NOTE_SCOPE_HEADER_LEVEL` | `2` | **Neu (v4.2.0):** Header-Ebene für Note-Scope Zonen (1-6, Default: 2 für ##). | +| `MINDNET_IGNORE_FOLDERS` | *(leer)* | **Neu (v4.1.0):** Komma-separierte Liste von Ordnernamen, die beim Import ignoriert werden. | --- diff --git a/docs/03_Technical_References/KONFIGURATION_EDGE_ZONEN.md b/docs/03_Technical_References/KONFIGURATION_EDGE_ZONEN.md new file mode 100644 index 0000000..c6bfc8b --- /dev/null +++ b/docs/03_Technical_References/KONFIGURATION_EDGE_ZONEN.md @@ -0,0 +1,242 @@ +# Konfiguration von Edge-Zonen Headern (v4.2.0) + +**Version:** v4.2.0 +**Status:** Aktiv + +## Übersicht + +Das Mindnet-System unterstützt zwei Arten von speziellen Markdown-Sektionen für Kanten: + +1. **LLM-Validierung Zonen** - Links, die vom LLM validiert werden +2. **Note-Scope Zonen** - Links, die der gesamten Note zugeordnet werden + +Die Header-Namen für beide Zonen-Typen sind über Umgebungsvariablen konfigurierbar. + +## Konfiguration via .env + +### LLM-Validierung Header + +**Umgebungsvariablen:** +- `MINDNET_LLM_VALIDATION_HEADERS` - Komma-separierte Liste von Header-Namen +- `MINDNET_LLM_VALIDATION_HEADER_LEVEL` - Header-Ebene (1-6, Default: 3 für `###`) + +**Format:** Komma-separierte Liste von Header-Namen + +**Default:** +``` +MINDNET_LLM_VALIDATION_HEADERS=Unzugeordnete Kanten,Edge Pool,Candidates +MINDNET_LLM_VALIDATION_HEADER_LEVEL=3 +``` + +**Beispiel:** +```env +MINDNET_LLM_VALIDATION_HEADERS=Unzugeordnete Kanten,Edge Pool,Candidates,Zu prüfende Links +MINDNET_LLM_VALIDATION_HEADER_LEVEL=3 +``` + +**Verwendung in Markdown:** +```markdown +### Unzugeordnete Kanten + +related_to:Ziel-Notiz +depends_on:Andere Notiz +``` + +**Wichtig:** Diese Bereiche werden **nicht als Chunks angelegt**, sondern nur die Kanten extrahiert. + +### Note-Scope Zone Header + +**Umgebungsvariablen:** +- `MINDNET_NOTE_SCOPE_ZONE_HEADERS` - Komma-separierte Liste von Header-Namen +- `MINDNET_NOTE_SCOPE_HEADER_LEVEL` - Header-Ebene (1-6, Default: 2 für `##`) + +**Format:** Komma-separierte Liste von Header-Namen + +**Default:** +``` +MINDNET_NOTE_SCOPE_ZONE_HEADERS=Smart Edges,Relationen,Global Links,Note-Level Relations,Globale Verbindungen +MINDNET_NOTE_SCOPE_HEADER_LEVEL=2 +``` + +**Beispiel:** +```env +MINDNET_NOTE_SCOPE_ZONE_HEADERS=Smart Edges,Relationen,Globale Verbindungen,Note-Level Links +MINDNET_NOTE_SCOPE_HEADER_LEVEL=2 +``` + +**Verwendung in Markdown:** +```markdown +## Smart Edges + +[[rel:depends_on|Globale Notiz]] +[[rel:part_of|System-Übersicht]] +``` + +**Wichtig:** Diese Bereiche werden **nicht als Chunks angelegt**, sondern nur die Kanten extrahiert. + +## Konfiguration in prod.env + +Fügen Sie die folgenden Zeilen zu Ihrer `.env` oder `config/prod.env` hinzu: + +```env +# --- WP-24c v4.2.0: Konfigurierbare Markdown-Header für Edge-Zonen --- +# Komma-separierte Liste von Headern für LLM-Validierung +MINDNET_LLM_VALIDATION_HEADERS=Unzugeordnete Kanten,Edge Pool,Candidates + +# Header-Ebene für LLM-Validierung (1-6, Default: 3 für ###) +MINDNET_LLM_VALIDATION_HEADER_LEVEL=3 + +# Komma-separierte Liste von Headern für Note-Scope Zonen +MINDNET_NOTE_SCOPE_ZONE_HEADERS=Smart Edges,Relationen,Global Links,Note-Level Relations,Globale Verbindungen + +# Header-Ebene für Note-Scope Zonen (1-6, Default: 2 für ##) +MINDNET_NOTE_SCOPE_HEADER_LEVEL=2 +``` + +**Wichtig:** Beide Zonen-Typen werden **nicht als Chunks angelegt**. Nur die Kanten werden extrahiert, der Text selbst wird vom Chunking ausgeschlossen. + +## Unterschiede + +### LLM-Validierung Zonen + +- **Header-Ebene:** Konfigurierbar via `MINDNET_LLM_VALIDATION_HEADER_LEVEL` (Default: 3 = `###`) +- **Zweck:** Links werden vom LLM validiert +- **Provenance:** `global_pool` +- **Scope:** `chunk` (wird Chunks zugeordnet) +- **Aktivierung:** Nur wenn `enable_smart_edge_allocation: true` +- **Chunking:** ❌ **Diese Bereiche werden NICHT als Chunks angelegt** - nur Kanten werden extrahiert + +**Beispiel:** +```markdown +### Unzugeordnete Kanten + +related_to:Mögliche Verbindung +depends_on:Unsichere Notiz +``` + +### Note-Scope Zonen + +- **Header-Ebene:** Konfigurierbar via `MINDNET_NOTE_SCOPE_HEADER_LEVEL` (Default: 2 = `##`) +- **Zweck:** Links werden der gesamten Note zugeordnet +- **Provenance:** `explicit:note_zone` +- **Scope:** `note` (Note-weite Verbindung) +- **Aktivierung:** Immer aktiv +- **Chunking:** ❌ **Diese Bereiche werden NICHT als Chunks angelegt** - nur Kanten werden extrahiert + +**Beispiel:** +```markdown +## Smart Edges + +[[rel:depends_on|Globale Notiz]] +[[rel:part_of|System-Übersicht]] +``` + +## Best Practices + +### ✅ Empfohlen + +1. **Konsistente Header-Namen:** + - Nutzen Sie aussagekräftige Namen + - Dokumentieren Sie die verwendeten Header in Ihrem Team + +2. **Minimale Konfiguration:** + - Nutzen Sie die Defaults, wenn möglich + - Nur bei Bedarf anpassen + +3. **Dokumentation:** + - Dokumentieren Sie benutzerdefinierte Header in Ihrer Projekt-Dokumentation + +### ❌ Vermeiden + +1. **Zu viele Header:** + - Zu viele Optionen können verwirrend sein + - Beschränken Sie sich auf 3-5 Header pro Typ + +2. **Ähnliche Namen:** + - Vermeiden Sie Header, die sich zu ähnlich sind + - Klare Unterscheidung zwischen LLM-Validierung und Note-Scope + +## Technische Details + +### Code-Referenzen + +- **LLM-Validierung:** `app/core/chunking/chunking_processor.py` (Zeile 66-72) +- **Note-Scope Zonen:** `app/core/graph/graph_derive_edges.py` → `get_note_scope_zone_headers()` + +### Fallback-Verhalten + +- Wenn die Umgebungsvariable nicht gesetzt ist, werden die Defaults verwendet +- Wenn die Variable leer ist, werden ebenfalls die Defaults verwendet +- Header-Namen werden case-insensitive verglichen + +### Regex-Escape + +- Header-Namen werden automatisch für Regex escaped +- Sonderzeichen in Header-Namen sind sicher + +## Beispiel-Konfiguration + +```env +# Eigene Header-Namen für LLM-Validierung (H3) +MINDNET_LLM_VALIDATION_HEADERS=Zu prüfende Links,Kandidaten,Edge Pool +MINDNET_LLM_VALIDATION_HEADER_LEVEL=3 + +# Eigene Header-Namen für Note-Scope Zonen (H2) +MINDNET_NOTE_SCOPE_ZONE_HEADERS=Globale Relationen,Note-Verbindungen,Smart Links +MINDNET_NOTE_SCOPE_HEADER_LEVEL=2 +``` + +**Alternative:** Beide auf H2 setzen: +```env +MINDNET_LLM_VALIDATION_HEADER_LEVEL=2 +MINDNET_NOTE_SCOPE_HEADER_LEVEL=2 +``` + +**Verwendung:** +```markdown +--- +type: decision +title: Meine Notiz +--- + +# Inhalt + +## Globale Relationen + +[[rel:depends_on|System-Architektur]] + +### Zu prüfende Links + +related_to:Mögliche Verbindung +``` + +## FAQ + +**Q: Kann ich beide Zonen-Typen in einer Notiz verwenden?** +A: Ja, beide können gleichzeitig verwendet werden. + +**Q: Was passiert, wenn ein Header in beiden Listen steht?** +A: Die Note-Scope Zone hat Vorrang (wird als Note-Scope behandelt). + +**Q: Können Header-Namen Leerzeichen enthalten?** +A: Ja, Leerzeichen werden beibehalten. + +**Q: Werden Header-Namen case-sensitive verglichen?** +A: Nein, der Vergleich ist case-insensitive. + +**Q: Kann ich Header-Namen mit Sonderzeichen verwenden?** +A: Ja, Sonderzeichen werden automatisch für Regex escaped. + +## Zusammenfassung + +- ✅ **LLM-Validierung:** + - `MINDNET_LLM_VALIDATION_HEADERS` (Header-Namen, komma-separiert) + - `MINDNET_LLM_VALIDATION_HEADER_LEVEL` (Header-Ebene 1-6, Default: 3) + - ❌ **Nicht als Chunks angelegt** - nur Kanten werden extrahiert +- ✅ **Note-Scope Zonen:** + - `MINDNET_NOTE_SCOPE_ZONE_HEADERS` (Header-Namen, komma-separiert) + - `MINDNET_NOTE_SCOPE_HEADER_LEVEL` (Header-Ebene 1-6, Default: 2) + - ❌ **Nicht als Chunks angelegt** - nur Kanten werden extrahiert +- ✅ **Format:** Komma-separierte Liste für Header-Namen +- ✅ **Fallback:** Defaults werden verwendet, falls nicht konfiguriert +- ✅ **Case-insensitive:** Header-Namen werden case-insensitive verglichen