scriptAudit #11
|
|
@ -1,11 +1,10 @@
|
|||
"""
|
||||
FILE: app/core/ingestion.py
|
||||
DESCRIPTION: Haupt-Ingestion-Logik. Liest Markdown, prüft Hashes (Change Detection), zerlegt in Chunks und schreibt in Qdrant.
|
||||
VERSION: 2.5.2
|
||||
VERSION: 2.5.3 (Fix: Hash-Mode Full for Metadata Detection)
|
||||
STATUS: Active
|
||||
DEPENDENCIES: app.core.parser, app.core.note_payload, app.core.chunker, app.core.derive_edges, app.core.qdrant*, app.services.embeddings_client
|
||||
EXTERNAL_CONFIG: config/types.yaml
|
||||
LAST_ANALYSIS: 2025-12-15
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
|
@ -94,7 +93,8 @@ class IngestionService:
|
|||
apply: bool = False,
|
||||
purge_before: bool = False,
|
||||
note_scope_refs: bool = False,
|
||||
hash_mode: str = "body",
|
||||
# FIX: Default auf "full", damit Metadata-Änderungen erkannt werden
|
||||
hash_mode: str = "full",
|
||||
hash_source: str = "parsed",
|
||||
hash_normalize: str = "canonical"
|
||||
) -> Dict[str, Any]:
|
||||
|
|
@ -150,17 +150,27 @@ class IngestionService:
|
|||
logger.error(f"Payload build failed: {e}")
|
||||
return {**result, "error": f"Payload build failed: {str(e)}"}
|
||||
|
||||
# 4. Change Detection (Das fehlende Stück!)
|
||||
# 4. Change Detection (Updated Logic)
|
||||
old_payload = None
|
||||
if not force_replace:
|
||||
old_payload = self._fetch_note_payload(note_id)
|
||||
|
||||
has_old = old_payload is not None
|
||||
key_current = f"{hash_mode}:{hash_source}:{hash_normalize}"
|
||||
old_hash = (old_payload or {}).get("hashes", {}).get(key_current)
|
||||
|
||||
# Robustere Abfrage: Falls 'hashes' im Payload fehlt, None zurückgeben
|
||||
old_hashes = (old_payload or {}).get("hashes")
|
||||
if isinstance(old_hashes, dict):
|
||||
old_hash = old_hashes.get(key_current)
|
||||
else:
|
||||
# Fallback für Legacy Payloads ohne Hash-Dict
|
||||
old_hash = None
|
||||
|
||||
new_hash = note_pl.get("hashes", {}).get(key_current)
|
||||
|
||||
# Wenn wir keinen alten Hash haben (z.B. neues Hash-Schema "full"), erzwingen wir Update
|
||||
hash_changed = (old_hash != new_hash)
|
||||
|
||||
chunks_missing, edges_missing = self._artifacts_missing(note_id)
|
||||
|
||||
should_write = force_replace or (not has_old) or hash_changed or chunks_missing or edges_missing
|
||||
|
|
@ -177,7 +187,6 @@ class IngestionService:
|
|||
|
||||
# --- Config Loading (Clean) ---
|
||||
chunk_config = get_chunk_config(note_type)
|
||||
# Hier greift die Logik aus types.yaml (smart=True/False)
|
||||
|
||||
chunks = await assemble_chunks(fm["id"], body_text, fm["type"], config=chunk_config)
|
||||
chunk_pls = make_chunk_payloads(fm, note_pl["path"], chunks, note_text=body_text)
|
||||
|
|
@ -240,7 +249,7 @@ class IngestionService:
|
|||
logger.error(f"Upsert failed: {e}", exc_info=True)
|
||||
return {**result, "error": f"DB Upsert failed: {e}"}
|
||||
|
||||
# --- Qdrant Helper (Restored) ---
|
||||
# --- Qdrant Helper ---
|
||||
|
||||
def _fetch_note_payload(self, note_id: str) -> Optional[dict]:
|
||||
from qdrant_client.http import models as rest
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
doc_type: glossary
|
||||
audience: all
|
||||
status: active
|
||||
version: 2.6
|
||||
version: 2.6.0
|
||||
context: "Definitionen zentraler Begriffe und Entitäten im Mindnet-System."
|
||||
---
|
||||
|
||||
|
|
@ -13,24 +13,25 @@ context: "Definitionen zentraler Begriffe und Entitäten im Mindnet-System."
|
|||
## Kern-Entitäten
|
||||
|
||||
* **Note:** Repräsentiert eine Markdown-Datei. Die fachliche Haupteinheit.
|
||||
* **Chunk:** Ein Textabschnitt einer Note (meist 512 Tokens). Die technische Sucheinheit (Vektor).
|
||||
* **Chunk:** Ein Textabschnitt einer Note. Die technische Sucheinheit (Vektor). Durch neue Strategien kann dies ein Fließtext-Abschnitt oder ein logisches Kapitel (Heading) sein.
|
||||
* **Edge:** Eine gerichtete Verbindung zwischen zwei Knoten (Chunks oder Notes).
|
||||
* **Vault:** Der lokale Ordner mit den Markdown-Dateien (Source of Truth).
|
||||
* **Frontmatter:** Der YAML-Header am Anfang einer Notiz (enthält `id`, `type`, `title`).
|
||||
|
||||
## Komponenten
|
||||
|
||||
* **Importer:** Das Python-Skript (`ingestion.py`), das Markdown liest und in Qdrant schreibt.
|
||||
* **Importer:** Das Python-Skript (`import_markdown.py`), das Markdown liest und in Qdrant schreibt.
|
||||
* **Retriever:** Die Komponente, die sucht. Nutzt hybrides Scoring (Semantik + Graph).
|
||||
* **Decision Engine:** Teil des Routers, der entscheidet, wie auf eine Anfrage reagiert wird (z.B. Strategie wählen).
|
||||
* **Hybrid Router v5:** Die Logik, die erkennt, ob der User eine Frage stellt (`RAG`) oder einen Befehl gibt (`INTERVIEW`).
|
||||
* **Draft Editor:** Die Web-UI-Komponente, in der generierte Notizen bearbeitet werden.
|
||||
* **Traffic Control:** Ein Mechanismus im `LLMService`, der Chat-Anfragen priorisiert und Hintergrund-Jobs (wie Import) drosselt.
|
||||
* **Traffic Control (WP15):** Ein Mechanismus im `LLMService`, der Prioritäten verwaltet (`realtime` für Chat vs. `background` für Import) und Hintergrund-Tasks mittels Semaphoren drosselt.
|
||||
|
||||
## Konzepte & Features
|
||||
|
||||
* **Active Intelligence:** Feature im Web-Editor, das während des Schreibens automatisch Links vorschlägt.
|
||||
* **Smart Edge Allocation (WP15):** Ein KI-Verfahren, das prüft, ob ein Link in einer Notiz für einen spezifischen Textabschnitt relevant ist.
|
||||
* **Smart Edge Allocation (WP15):** Ein KI-Verfahren, das prüft, ob ein Link in einer Notiz für einen spezifischen Textabschnitt relevant ist, statt ihn blind allen Chunks zuzuordnen.
|
||||
* **Strict Heading Split:** Chunking-Strategie, bei der Überschriften (z.B. H2) als harte Grenzen dienen. Verhindert das Vermischen von Themen (z.B. zwei unterschiedliche Rollen in einem Chunk).
|
||||
* **Healing Parser:** UI-Funktion, die fehlerhaften Output des LLMs (z.B. defektes YAML) automatisch repariert.
|
||||
* **Explanation Layer:** Die Schicht, die dem Nutzer erklärt, *warum* ein Suchergebnis gefunden wurde (z.B. "Weil Projekt X davon abhängt").
|
||||
* **Provenance:** Die Herkunft einer Kante.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ doc_type: technical_reference
|
|||
audience: developer, admin
|
||||
scope: configuration, env
|
||||
status: active
|
||||
version: 2.6
|
||||
version: 2.6.0
|
||||
context: "Referenztabellen für Umgebungsvariablen und YAML-Konfigurationen."
|
||||
---
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ Diese Variablen steuern die Infrastruktur, Timeouts und Feature-Flags.
|
|||
| `MINDNET_OLLAMA_URL` | `http://127.0.0.1:11434`| URL zum LLM-Server. |
|
||||
| `MINDNET_LLM_TIMEOUT` | `300.0` | Timeout in Sekunden (Erhöht für CPU Cold-Starts). |
|
||||
| `MINDNET_API_TIMEOUT` | `300.0` | Frontend Timeout (Erhöht für Smart Edge Wartezeiten). |
|
||||
| `MINDNET_LLM_BACKGROUND_LIMIT`| `2` | **Traffic Control:** Max. parallele Import-Tasks. |
|
||||
| `MINDNET_LLM_BACKGROUND_LIMIT`| `2` | **Traffic Control (Neu):** Max. parallele Import-Tasks (Semaphore). |
|
||||
| `MINDNET_VAULT_ROOT` | `./vault` | Pfad für Write-Back Operationen (Drafts). |
|
||||
| `MINDNET_HASH_COMPARE` | `Body` | Import-Strategie: `Body`, `Frontmatter` oder `Full`. |
|
||||
| `MINDNET_HASH_SOURCE` | `parsed` | Hash-Quelle: `parsed`, `raw` oder `file`. |
|
||||
|
|
@ -47,16 +47,18 @@ Steuert das Import-Verhalten, Chunking und die Kanten-Logik pro Typ.
|
|||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **concept** | `sliding_smart_edges` | 0.60 | Ja | Abstrakte Begriffe. |
|
||||
| **project** | `sliding_smart_edges` | 0.97 | Ja | Aktive Vorhaben. |
|
||||
| **decision** | `structured_smart_edges` | 1.00 | Ja | Entscheidungen (ADRs). |
|
||||
| **decision** | `structured_smart_edges_strict` | 1.00 | Ja | Entscheidungen (ADRs). Atomar. |
|
||||
| **experience** | `sliding_smart_edges` | 0.90 | Ja | Persönliche Learnings. |
|
||||
| **journal** | `sliding_standard` | 0.80 | Nein | Logs / Dailies. |
|
||||
| **value** | `structured_smart_edges` | 1.00 | Ja | Werte/Prinzipien. |
|
||||
| **value** | `structured_smart_edges_strict` | 1.00 | Ja | Werte/Prinzipien. Atomar. |
|
||||
| **risk** | `sliding_short` | 0.90 | Nein | Risiken. |
|
||||
| **person** | `sliding_standard` | 0.50 | Nein | Profile. |
|
||||
| **source** | `sliding_standard` | 0.50 | Nein | Externe Quellen. |
|
||||
| **event** | `sliding_standard` | 0.60 | Nein | Meetings. |
|
||||
| **goal** | `sliding_standard` | 0.95 | Nein | Strategische Ziele. |
|
||||
| **goal** | `sliding_smart_edges` | 0.95 | Nein | Strategische Ziele. |
|
||||
| **belief** | `sliding_short` | 0.90 | Nein | Glaubenssätze. |
|
||||
| **profile** | `structured_smart_edges_strict` | 0.70 | Nein | Rollenprofile. Strict Split. |
|
||||
| **principle** | `structured_smart_edges_strict_L3`| 0.95 | Nein | Prinzipien. Tiefer Split (H3). |
|
||||
| **default** | `sliding_standard` | 1.00 | Nein | Fallback. |
|
||||
|
||||
*Hinweis: `Smart Edges?` entspricht dem YAML-Key `enable_smart_edge_allocation: true`.*
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ doc_type: technical_reference
|
|||
audience: developer, devops
|
||||
scope: backend, ingestion, smart_edges
|
||||
status: active
|
||||
version: 2.6
|
||||
context: "Detaillierte technische Beschreibung der Import-Pipeline, Quality Gates und CLI-Befehle."
|
||||
version: 2.6.0
|
||||
context: "Detaillierte technische Beschreibung der Import-Pipeline, Chunking-Strategien und CLI-Befehle."
|
||||
---
|
||||
|
||||
# Ingestion Pipeline & Smart Processing
|
||||
|
|
@ -21,16 +21,16 @@ Der Prozess ist **asynchron** und **idempotent**.
|
|||
2. **Frontmatter extrahieren:** Validierung von Pflichtfeldern (`id`, `type`, `title`).
|
||||
3. **Typauflösung:** Bestimmung des `type` via `types.yaml`.
|
||||
4. **Note-Payload generieren:** Erstellen des JSON-Objekts für `mindnet_notes`.
|
||||
5. **Chunking anwenden:** Zerlegung des Textes basierend auf dem `chunk_profile` (siehe unten).
|
||||
5. **Chunking anwenden:** Zerlegung des Textes basierend auf dem `chunk_profile` (siehe Kap. 3).
|
||||
6. **Smart Edge Allocation (WP15):**
|
||||
* Wenn `enable_smart_edge_allocation: true`: Der `SemanticAnalyzer` sendet Chunks an das LLM.
|
||||
* **Traffic Control:** Request nutzt `priority="background"`. Semaphore (Limit: 2) drosselt die Last.
|
||||
* **Traffic Control:** Request nutzt `priority="background"`. Semaphore (Limit via `.env`) drosselt die Last.
|
||||
* **Resilienz:** Bei Timeout (Ollama) greift ein Fallback (Broadcasting an alle Chunks).
|
||||
7. **Inline-Kanten finden:** Parsing von `[[rel:...]]`.
|
||||
8. **Callout-Kanten finden:** Parsing von `> [!edge]`.
|
||||
9. **Default-Edges erzeugen:** Anwendung der `edge_defaults` aus Registry.
|
||||
10. **Strukturkanten erzeugen:** `belongs_to`, `next`, `prev`.
|
||||
11. **Embedding (Async):** Generierung via `nomic-embed-text` (768d).
|
||||
11. **Embedding (Async):** Generierung via `nomic-embed-text` (768 Dim).
|
||||
12. **Strict Mode:** Abbruch bei leeren Embeddings oder Dimension 0.
|
||||
13. **Diagnose:** Integritäts-Check nach dem Lauf.
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ export COLLECTION_PREFIX="mindnet"
|
|||
> Das Flag `--purge-before-upsert` ist kritisch. Es löscht vor dem Schreiben einer Note ihre alten Chunks/Edges. Ohne dieses Flag entstehen **"Geister-Chunks"** (alte Textabschnitte, die im Markdown gelöscht wurden, aber im Index verbleiben).
|
||||
|
||||
### 2.2 Full Rebuild (Clean Slate)
|
||||
Notwendig bei Änderungen an `types.yaml` oder Modell-Wechsel.
|
||||
Notwendig bei Änderungen an `types.yaml` (z.B. neue Chunking-Profile) oder Modell-Wechsel.
|
||||
|
||||
```bash
|
||||
# 0. Modell sicherstellen
|
||||
|
|
@ -75,18 +75,35 @@ python3 -m scripts.import_markdown --vault ./vault --prefix "mindnet" --apply --
|
|||
|
||||
## 3. Chunking & Payload
|
||||
|
||||
Das Chunking ist profilbasiert.
|
||||
Das Chunking ist profilbasiert und in `types.yaml` konfiguriert. Seit v2.6 unterscheiden wir zwischen **Sliding Window** und **Heading Split**.
|
||||
|
||||
| Profil | Max Token | Overlap | Einsatz |
|
||||
### 3.1 Profile und Strategien
|
||||
|
||||
| Profil | Strategie | Parameter | Einsatzgebiet |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `sliding_short` | 128 | 20 | Logs, Chats. |
|
||||
| `sliding_standard` | 512 | 50 | Massendaten. |
|
||||
| `sliding_smart_edges`| 512 | 50 | Wichtige Inhalte (Experience, Project). |
|
||||
| `structured_smart` | n/a | n/a | Trennt strikt an Headings (für ADRs). |
|
||||
| `sliding_short` | `sliding_window` | Max: 350, Target: 200 | Kurze Logs, Chats, Risiken. |
|
||||
| `sliding_standard` | `sliding_window` | Max: 650, Target: 450 | Massendaten (Journal, Quellen). |
|
||||
| `sliding_smart_edges`| `sliding_window` | Max: 600, Target: 400 | Fließtexte mit hohem Wert (Projekte, Erfahrungen). |
|
||||
| `structured_smart_edges` | `by_heading` | `strict: false` (Soft) | Strukturierte Texte, wo kleine Abschnitte gemergt werden dürfen. |
|
||||
| `structured_smart_edges_strict` | `by_heading` | `strict: true` (Hard) | **Atomare Einheiten**: Entscheidungen, Werte, Profile. |
|
||||
|
||||
**Payload-Felder:**
|
||||
* `text`: Der reine Inhalt (Anzeige).
|
||||
* `window`: Inhalt plus Overlap (für Embedding).
|
||||
### 3.2 Die `by_heading` Logik (Neu in v2.6)
|
||||
|
||||
Die Strategie `by_heading` zerlegt Texte anhand ihrer Struktur (Überschriften).
|
||||
|
||||
* **Split Level:** Definiert die Tiefe (z.B. `2` = H1 & H2 triggern Split).
|
||||
* **Modus "Strict" (`strict_heading_split: true`):**
|
||||
* Jede Überschrift (`<= split_level`) erzwingt einen neuen Chunk.
|
||||
* *Ausnahme:* Wenn der vorherige Chunk leer war (nur Überschriften), wird gemergt (Context-Aware Merge).
|
||||
* *Safety:* Wird ein Abschnitt zu lang (> `max`), wird trotzdem getrennt (Hybrid-Fallback).
|
||||
* **Modus "Soft" (`strict_heading_split: false`):**
|
||||
* Überschriften auf dem Split-Level (z.B. H2) lösen nur dann einen neuen Chunk aus, wenn der aktuelle Chunk die `target`-Größe erreicht hat.
|
||||
* Überschriften *oberhalb* (z.B. H1) erzwingen immer einen Split (Hierarchie-Reset).
|
||||
|
||||
### 3.3 Payload-Felder (Qdrant)
|
||||
|
||||
* `text`: Der reine Inhalt (Anzeige im UI).
|
||||
* `window`: Inhalt plus Overlap (für Embedding). Bei `by_heading` enthält dies oft den Kontext der Eltern-Überschrift.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user