Compare commits

..

No commits in common. "main" and "v3.2.0" have entirely different histories.
main ... v3.2.0

20 changed files with 37 additions and 2022 deletions

View File

@ -15,44 +15,13 @@ from dotenv import load_dotenv
# WP-20: Lade Umgebungsvariablen aus der .env Datei # WP-20: Lade Umgebungsvariablen aus der .env Datei
# override=True garantiert, dass Änderungen in der .env immer Vorrang haben. # override=True garantiert, dass Änderungen in der .env immer Vorrang haben.
# WP-24c v4.5.10: Expliziter Pfad für .env-Datei, um Probleme mit Arbeitsverzeichnis zu vermeiden load_dotenv(override=True)
# Suche .env im Projekt-Root (3 Ebenen über app/config.py: app/config.py -> app/ -> root/)
_project_root = Path(__file__).parent.parent.parent
_env_file = _project_root / ".env"
_env_loaded = False
# Versuche zuerst expliziten Pfad
if _env_file.exists():
_env_loaded = load_dotenv(_env_file, override=True)
if _env_loaded:
# Optional: Logging (nur wenn logging bereits initialisiert ist)
try:
import logging
_logger = logging.getLogger(__name__)
_logger.debug(f"✅ .env geladen von: {_env_file}")
except:
pass # Logging noch nicht initialisiert
# Fallback: Automatische Suche (für Dev/Test oder wenn .env an anderer Stelle liegt)
if not _env_loaded:
_env_loaded = load_dotenv(override=True)
if _env_loaded:
try:
import logging
_logger = logging.getLogger(__name__)
_logger.debug(f"✅ .env geladen via automatische Suche (cwd: {Path.cwd()})")
except:
pass
class Settings: class Settings:
# --- Qdrant Datenbank --- # --- Qdrant Datenbank ---
QDRANT_URL: str = os.getenv("QDRANT_URL", "http://127.0.0.1:6333") QDRANT_URL: str = os.getenv("QDRANT_URL", "http://127.0.0.1:6333")
QDRANT_API_KEY: str | None = os.getenv("QDRANT_API_KEY") QDRANT_API_KEY: str | None = os.getenv("QDRANT_API_KEY")
# WP-24c v4.5.10: Harmonisierung - Unterstützt beide Umgebungsvariablen für Abwärtskompatibilität COLLECTION_PREFIX: str = os.getenv("MINDNET_PREFIX", "mindnet_dev")
# COLLECTION_PREFIX hat Priorität, MINDNET_PREFIX als Fallback
# WP-24c v4.5.10-FIX: Default auf "mindnet" (Prod) statt "mindnet_dev" (Dev)
# Dev muss explizit COLLECTION_PREFIX=mindnet_dev in .env setzen
COLLECTION_PREFIX: str = os.getenv("COLLECTION_PREFIX") or os.getenv("MINDNET_PREFIX") or "mindnet"
# WP-22: Vektor-Dimension für das Embedding-Modell (nomic) # WP-22: Vektor-Dimension für das Embedding-Modell (nomic)
VECTOR_SIZE: int = int(os.getenv("VECTOR_DIM", "768")) VECTOR_SIZE: int = int(os.getenv("VECTOR_DIM", "768"))

View File

@ -44,9 +44,7 @@ class QdrantConfig:
port = os.getenv("QDRANT_PORT") port = os.getenv("QDRANT_PORT")
port = int(port) if port else None port = int(port) if port else None
api_key = os.getenv("QDRANT_API_KEY") or None api_key = os.getenv("QDRANT_API_KEY") or None
# WP-24c v4.5.10: Harmonisierung - Unterstützt beide Umgebungsvariablen für Abwärtskompatibilität prefix = os.getenv("COLLECTION_PREFIX") or "mindnet"
# COLLECTION_PREFIX hat Priorität, MINDNET_PREFIX als Fallback
prefix = os.getenv("COLLECTION_PREFIX") or os.getenv("MINDNET_PREFIX") or "mindnet"
dim = int(os.getenv("VECTOR_DIM") or 384) dim = int(os.getenv("VECTOR_DIM") or 384)
distance = os.getenv("DISTANCE", "Cosine") distance = os.getenv("DISTANCE", "Cosine")
on_disk_payload = (os.getenv("ON_DISK_PAYLOAD", "true").lower() == "true") on_disk_payload = (os.getenv("ON_DISK_PAYLOAD", "true").lower() == "true")

View File

@ -910,20 +910,9 @@ def build_edges_for_note(
# Schritt 3: ID-Zuweisung nach Scope-Entscheidung # Schritt 3: ID-Zuweisung nach Scope-Entscheidung
final_edges: List[dict] = [] final_edges: List[dict] = []
# WP-24c v4.5.10: Deterministische Sortierung der semantic_groups für konsistente Edge-Extraktion # WP-24c v4.5.9: Deterministische Sortierung der semantic_groups für konsistente Edge-Extraktion
# Verhindert Varianz zwischen Batches (33 vs 34 Kanten) # Verhindert Varianz zwischen Batches (33 vs 34 Kanten)
# WICHTIG: target_section kann None sein, daher benötigen wir eine benutzerdefinierte Sortierfunktion sorted_semantic_keys = sorted(semantic_groups.keys())
def sort_key_func(key_tuple):
"""
Sortierfunktion für semantic_keys, die None-Werte korrekt behandelt.
None wird als leere Zeichenkette behandelt, um Vergleichbarkeit zu gewährleisten.
"""
kind, semantic_source, target_id, target_section = key_tuple
# Konvertiere None zu leerem String für konsistente Sortierung
target_section_safe = target_section if target_section is not None else ""
return (kind, semantic_source, target_id, target_section_safe)
sorted_semantic_keys = sorted(semantic_groups.keys(), key=sort_key_func)
for semantic_key in sorted_semantic_keys: for semantic_key in sorted_semantic_keys:
group = semantic_groups[semantic_key] group = semantic_groups[semantic_key]

View File

@ -216,42 +216,17 @@ def _build_explanation(
direction = "in" if tgt == target_note_id else "out" direction = "in" if tgt == target_note_id else "out"
# WP-24c v4.5.10: Robuste EdgeDTO-Erstellung mit Fehlerbehandlung edge_obj = EdgeDTO(
# Falls Provenance-Wert nicht unterstützt wird, verwende Fallback id=f"{src}->{tgt}:{kind}",
try: kind=kind,
edge_obj = EdgeDTO( source=src,
id=f"{src}->{tgt}:{kind}", target=tgt,
kind=kind, weight=conf,
source=src, direction=direction,
target=tgt, provenance=prov,
weight=conf, confidence=conf
direction=direction, )
provenance=prov, edges_dto.append(edge_obj)
confidence=conf
)
edges_dto.append(edge_obj)
except Exception as e:
# WP-24c v4.5.10: Fallback bei Validierungsfehler (z.B. alte EdgeDTO-Version im Cache)
logger.warning(
f"⚠️ [EDGE-DTO] Provenance '{prov}' nicht unterstützt für Edge {src}->{tgt} ({kind}). "
f"Fehler: {e}. Verwende Fallback 'explicit'."
)
# Fallback: Verwende 'explicit' als sicheren Default
try:
edge_obj = EdgeDTO(
id=f"{src}->{tgt}:{kind}",
kind=kind,
source=src,
target=tgt,
weight=conf,
direction=direction,
provenance="explicit", # Fallback
confidence=conf
)
edges_dto.append(edge_obj)
except Exception as e2:
logger.error(f"❌ [EDGE-DTO] Auch Fallback fehlgeschlagen: {e2}. Überspringe Edge.")
# Überspringe diese Kante - besser als kompletter Fehler
# Die 3 wichtigsten Kanten als Begründung formulieren # Die 3 wichtigsten Kanten als Begründung formulieren
top_edges = sorted(edges_dto, key=lambda e: e.confidence, reverse=True) top_edges = sorted(edges_dto, key=lambda e: e.confidence, reverse=True)

View File

@ -109,23 +109,12 @@ def create_app() -> FastAPI:
@app.get("/healthz") @app.get("/healthz")
def healthz(): def healthz():
"""Bietet Statusinformationen über die Engine und Datenbank-Verbindung.""" """Bietet Statusinformationen über die Engine und Datenbank-Verbindung."""
# WP-24c v4.5.10: Prüfe EdgeDTO-Version zur Laufzeit
edge_dto_supports_callout = False
try:
from app.models.dto import EdgeDTO
import inspect
source = inspect.getsource(EdgeDTO)
edge_dto_supports_callout = "explicit:callout" in source
except Exception:
pass # Fehler beim Prüfen ist nicht kritisch
return { return {
"status": "ok", "status": "ok",
"version": "1.1.0", "version": "1.1.0",
"qdrant": s.QDRANT_URL, "qdrant": s.QDRANT_URL,
"prefix": s.COLLECTION_PREFIX, "prefix": s.COLLECTION_PREFIX,
"moe_enabled": True, "moe_enabled": True
"edge_dto_supports_callout": edge_dto_supports_callout # WP-24c v4.5.10: Diagnose-Hilfe
} }
# Inkludieren der Router (100% Kompatibilität erhalten) # Inkludieren der Router (100% Kompatibilität erhalten)

View File

@ -32,14 +32,9 @@ streams_library:
top_k: 5 top_k: 5
edge_boosts: edge_boosts:
guides: 3.0 guides: 3.0
depends_on: 2.5 enforced_by: 2.5
based_on: 2.0 based_on: 2.0
upholds: 2.5
violates: 2.5
aligned_with: 2.0
conflicts_with: 2.0
supports: 1.5
contradicts: 1.5
facts_stream: facts_stream:
name: "Operative Realität" name: "Operative Realität"
llm_profile: "synthesis_pro" llm_profile: "synthesis_pro"
@ -65,8 +60,6 @@ streams_library:
related_to: 1.5 related_to: 1.5
experienced_in: 2.0 experienced_in: 2.0
expert_for: 2.5 expert_for: 2.5
followed_by: 2.0
preceded_by: 2.0
risk_stream: risk_stream:
name: "Risiko-Radar" name: "Risiko-Radar"

View File

@ -94,7 +94,7 @@ llm_settings:
types: types:
experience: experience:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 1.10 retriever_weight: 1.10
detection_keywords: ["erleben", "reagieren", "handeln", "prägen", "reflektieren"] detection_keywords: ["erleben", "reagieren", "handeln", "prägen", "reflektieren"]
schema: schema:
@ -104,7 +104,7 @@ types:
- "Reflexion & Learning (Was lerne ich daraus?)" - "Reflexion & Learning (Was lerne ich daraus?)"
insight: insight:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 1.20 retriever_weight: 1.20
detection_keywords: ["beobachten", "erkennen", "verstehen", "analysieren", "schlussfolgern"] detection_keywords: ["beobachten", "erkennen", "verstehen", "analysieren", "schlussfolgern"]
schema: schema:
@ -114,7 +114,7 @@ types:
- "Handlungsempfehlung" - "Handlungsempfehlung"
project: project:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 0.97 retriever_weight: 0.97
detection_keywords: ["umsetzen", "planen", "starten", "bauen", "abschließen"] detection_keywords: ["umsetzen", "planen", "starten", "bauen", "abschließen"]
schema: schema:
@ -214,7 +214,7 @@ types:
- "Strategie" - "Strategie"
need: need:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 1.05 retriever_weight: 1.05
detection_keywords: ["bedürfnis", "brauchen", "mangel", "erfüllung"] detection_keywords: ["bedürfnis", "brauchen", "mangel", "erfüllung"]
schema: schema:
@ -223,7 +223,7 @@ types:
- "Bezug zu Werten" - "Bezug zu Werten"
motivation: motivation:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 0.95 retriever_weight: 0.95
detection_keywords: ["motivation", "antrieb", "warum", "energie"] detection_keywords: ["motivation", "antrieb", "warum", "energie"]
schema: schema:
@ -244,15 +244,14 @@ types:
schema: ["Aktueller Zustand", "Auslöser", "Auswirkung auf den Tag"] schema: ["Aktueller Zustand", "Auslöser", "Auswirkung auf den Tag"]
boundary: boundary:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 0.90 retriever_weight: 0.90
detection_keywords: ["grenze", "nein sagen", "limit", "schutz"] detection_keywords: ["grenze", "nein sagen", "limit", "schutz"]
schema: ["Die Grenze", "Warum sie wichtig ist", "Konsequenz bei Verletzung"] schema: ["Die Grenze", "Warum sie wichtig ist", "Konsequenz bei Verletzung"]
goal: goal:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 0.95 retriever_weight: 0.95
detection_keywords: ["ziel", "zielzustand", "kpi", "zeitrahmen", "deadline", "meilenstein"]
schema: ["Zielzustand", "Zeitrahmen & KPIs", "Motivation"] schema: ["Zielzustand", "Zeitrahmen & KPIs", "Motivation"]
risk: risk:
@ -262,49 +261,41 @@ types:
schema: ["Beschreibung des Risikos", "Auswirkungen", "Gegenmaßnahmen"] schema: ["Beschreibung des Risikos", "Auswirkungen", "Gegenmaßnahmen"]
concept: concept:
chunking_profile: structured_smart_edges chunking_profile: sliding_smart_edges
retriever_weight: 0.6 retriever_weight: 0.60
detection_keywords: ["definition", "konzept", "begriff", "modell", "rahmen", "theorie"]
schema: ["Definition", "Kontext", "Verwandte Konzepte"] schema: ["Definition", "Kontext", "Verwandte Konzepte"]
task: task:
chunking_profile: sliding_short chunking_profile: sliding_short
retriever_weight: 0.8 retriever_weight: 0.80
detection_keywords: ["aufgabe", "todo", "next_action", "erledigen", "definition_of_done", "checkliste"]
schema: ["Aufgabe", "Kontext", "Definition of Done"] schema: ["Aufgabe", "Kontext", "Definition of Done"]
journal: journal:
chunking_profile: sliding_standard chunking_profile: sliding_standard
retriever_weight: 0.8 retriever_weight: 0.80
detection_keywords: ["journal", "tagebuch", "log", "eintrag", "reflexion", "heute"]
schema: ["Log-Eintrag", "Gedanken"] schema: ["Log-Eintrag", "Gedanken"]
source: source:
chunking_profile: sliding_standard chunking_profile: sliding_standard
retriever_weight: 0.5 retriever_weight: 0.50
detection_keywords: ["quelle", "paper", "buch", "artikel", "link", "zitat", "studie"]
schema: ["Metadaten", "Zusammenfassung", "Zitate"] schema: ["Metadaten", "Zusammenfassung", "Zitate"]
glossary: glossary:
chunking_profile: sliding_short chunking_profile: sliding_short
retriever_weight: 0.4 retriever_weight: 0.40
detection_keywords: ["glossar", "begriff", "definition", "terminologie"]
schema: ["Begriff", "Definition"] schema: ["Begriff", "Definition"]
person: person:
chunking_profile: sliding_standard chunking_profile: sliding_standard
retriever_weight: 0.5 retriever_weight: 0.50
detection_keywords: ["person", "mensch", "kontakt", "name", "beziehung", "stakeholder"] schema: ["Rolle", "Beziehung", "Kontext"]
schema: ["Profile", "Beziehung", "Kontext"]
event: event:
chunking_profile: sliding_standard chunking_profile: sliding_standard
retriever_weight: 0.6 retriever_weight: 0.60
detection_keywords: ["ereignis", "termin", "datum", "ort", "teilnehmer", "meeting"]
schema: ["Datum & Ort", "Teilnehmer", "Ergebnisse"] schema: ["Datum & Ort", "Teilnehmer", "Ergebnisse"]
default: default:
chunking_profile: sliding_standard chunking_profile: sliding_standard
retriever_weight: 1.0 retriever_weight: 1.00
detection_keywords: []
schema: ["Inhalt"] schema: ["Inhalt"]

View File

@ -1 +0,0 @@
[0114/152756.633:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: Das System kann die angegebene Datei nicht finden. (0x2)

View File

@ -1,394 +0,0 @@
# Mindnet Causal Assistant Dokumentation der bisher erreichten Resultate (0.4.x) + Architektur, Konfiguration & Strategien
> Stand: basierend auf den beobachteten Chain-Inspector-Logs und den zuletzt beschriebenen Implementierungen in 0.4.6/0.4.x.
> Ziel dieser Doku: Eine **einheitliche, belastbare Basis**, damit Weiterentwicklung (0.5.x/0.6.x) nicht mehr “im Kreis” läuft.
---
## 1) Zweck & Gesamtziel von Mindnet
Mindnet soll in einem Obsidian Vault **kausale/argumentative Zusammenhänge** als Graph abbilden und daraus **nützliche Diagnosen** ableiten:
- **Graph-Aufbau:** Notes/Sections als Knoten, Links/Kanten als gerichtete Beziehungen (z.B. *wirkt_auf*, *resulted_in*, *depends_on* …).
- **Analyse aus einem Kontext:** Nutzer steht in einer Note an einer bestimmten Überschrift/Section → Mindnet analysiert lokale Nachbarschaft + Pfade im Graphen.
- **Template Matching:** Einordnen der gefundenen Knoten/Kanten in “Kettenmuster” (Chain Templates) wie z.B. *trigger → transformation → outcome* oder *loop_learning*.
- **Findings (Gap-Heuristiken):** Hinweise wie “fehlende Slots”, “fehlende Links”, “unmapped edge types”, “einseitige Konnektivität”, etc.
→ Ziel: **Nutzer konkret zum besseren Graphen führen**, ohne “Noisy” zu sein.
---
## 2) Begriffe & Datenmodell (so arbeitet der Chain Inspector)
### 2.1 Kontext (Context)
Der Chain Inspector läuft immer gegen einen **aktuellen Kontext**:
- `file`: aktuelle Note (z.B. `Tests/03_insight_transformation.md`)
- `heading`: aktuelle Section (z.B. `Kern`)
- `zoneKind`: i.d.R. `content`
Das ist wichtig, weil Kanten teils **section-spezifisch** sind und teils (geplant/teilweise offen) **note-weit** gelten könnten.
---
### 2.2 Knoten (Nodes)
Ein Knoten ist im Report meist referenziert als:
- `file + heading` (z.B. `Tests/01_experience_trigger.md:Kontext`)
- plus abgeleitete Metadaten wie `noteType` (z.B. experience, insight, decision, event)
**noteType** ist entscheidend fürs Template Matching (Slots).
---
### 2.3 Kanten (Edges)
Eine Kante hat typischerweise:
- `rawEdgeType`: Original-Typ aus Markdown/Notation (z.B. `wirkt_auf`, `resulted_in`, `depends_on`, `derived_from`, …)
- `from`: Quelle (file:heading)
- `to`: Ziel (file:heading)
- `scope`: Gültigkeit / Herkunft
- `section`: Edge ist “voll gültig” für die Section
- `candidate`: Edge ist nur Kandidat/unsicher, wird optional zugelassen
- (geplant/offen) `note`: Edge gilt note-weit (unabhängig von Section)
- `evidence`: Fundstelle (file, sectionHeading, lineRange)
---
## 3) Erreichte Resultate in 0.4.x (verifiziert)
### 3.1 includeCandidates Kandidatenkanten funktionieren wie erwartet
**Ergebnis (bereits mehrfach in Logs verifiziert):**
- Wenn `includeCandidates=false`, werden Kanten mit `scope: candidate` **im effektiven Graphen ausgefiltert**.
- Wenn `includeCandidates=true`, werden Kandidatenkanten **als incoming/outgoing** berücksichtigt und tauchen in `neighbors`/`paths` auf.
**Implikation:**
- Das System kann “unsichere” oder “LLM-vorschlagene” Verbindungen existieren lassen, ohne in jedem Lauf die Analyse zu verfälschen.
- In “Discovery” kann man Kandidaten zulassen (mehr Explorationspower).
- In “Decisioning” kann man Kandidaten typischerweise sperren (mehr Verlässlichkeit).
---
### 3.2 required_links: Strict vs Soft Mode missing_link_constraints Unterdrückung ist umgesetzt
**Problem (historisch):**
- `missing_link_constraints` wurde teilweise auch dann ausgegeben, wenn `required_links=false` (Soft Mode) aktiv war → unnötig “noisy”.
**Fix (laut Cursor-Report umgesetzt + Tests):**
- `missing_link_constraints` wird nur erzeugt, wenn `effectiveRequiredLinks === true`.
- Es gibt eine definierte Auflösungsreihenfolge für `required_links`:
**Resolution Order (effective required_links):**
1. `template.matching?.required_links`
2. `profile.required_links`
3. `defaults.matching?.required_links`
4. Fallback: `false`
**Transparenz bleibt erhalten:**
- `satisfiedLinks` und `requiredLinks` werden weiterhin im Report angezeigt.
- `linksComplete` bleibt als technischer Wert im Report bestehen.
- **Nur** das Finding `missing_link_constraints` wird unterdrückt, nicht die Fakten.
**Implikation:**
- Soft Mode (= required_links=false) ist jetzt ruhig genug, um “Entdeckung” zu unterstützen.
- Strict Mode (= required_links=true) eignet sich für harte Qualitätskontrolle.
---
### 3.3 “Healthy graph” → Findings leer ([]), Template “confirmed”
Wenn Slots **und** geforderte Links erfüllt sind (z.B. `trigger_transformation_outcome` mit 2/2 Links),
dann ist `findings: []` das erwartete Ergebnis.
**Implikation:**
- Das ist das zentrale “Green Path”-Signal: Graph ist konsistent für das gewählte Template/Profil.
---
### 3.4 Unmapped edges werden erkannt (Diagnose)
Wenn ein `rawEdgeType` nicht in die kanonischen Rollen/Edge-Rollen abgebildet werden kann, tauchen typischerweise Diagnosen auf:
- `edgesUnmapped > 0`
- Findings wie `no_causal_roles` oder link constraints bleiben unerfüllt
**Implikation:**
- Das Rollen-Mapping (chain_roles.yaml) ist “critical path”: wenn ein Edge-Typ nicht gemappt wird, bricht oft der kausale Interpretationspfad.
---
## 4) Was ist (noch) offen echtes nächstes Verifikationsziel
### 4.1 Note-Level Edges / Note-Scope (“für jede Sektion gültig”)
Es existiert ein Konzept/Einbau: In `02_event_trigger_detail` gibt es einen Bereich, der Kanten **auf Note-Ebene** definieren soll (unabhängig von aktueller Section).
**Offen ist die robuste Verifikation (oder Implementierung), dass:**
- diese Edges auch dann gelten, wenn der Cursor in einer anderen Section derselben Note steht,
- idealerweise mit klar erkennbarer Kennzeichnung wie `scope: note` im Report.
**Warum ist das wichtig?**
- Das ermöglicht “globaler Kontext” pro Note, ohne alles in jede Section duplizieren zu müssen.
- Es ist eine UX-Optimierung: Nutzer kann “Meta-Verbindungen” an einer Stelle pflegen.
---
## 5) Konfigurationsdateien Rolle & Interpretation (Mindnet-Strategie)
> Die folgenden Bereiche beschreiben eine **saubere, konsistente Interpretation**, wie Mindnet die Configs verwenden sollte.
> Konkrete Keys, die in Logs sichtbar waren (z.B. required_links, min_slots_filled_for_gap_findings, min_score_for_gap_findings) sind hier berücksichtigt.
### 5.1 chain_templates.yaml “Welche Ketten gibt es, wie werden sie gematcht?”
**Zweck:**
- Definiert Templates (Muster), z.B.:
- `trigger_transformation_outcome`
- `loop_learning`
- ggf. weitere (constraint_to_adaptation usw.)
**Template enthält typischerweise:**
- Slots (Rollen für Knoten): z.B. `trigger`, `transformation`, `outcome`, `experience`, `learning`, `behavior`, `feedback`
- Required Link Constraints (welche Slot-zu-Slot Verbindungen zwingend sind)
- Scoring/Matching-Parameter (ggf. weights, thresholds)
- Optional: template-level override für `required_links`
**Matching-Profile (wie in Logs sichtbar):**
- z.B. Profile: `discovery`, `decisioning`
- Parameter im Profil (sichtbar in Logs):
- `required_links` (strict vs soft)
- `min_slots_filled_for_gap_findings`
- `min_score_for_gap_findings`
- ggf. `maxTemplateMatches`
**Interpretation:**
- Templates liefern die “Soll-Struktur”
- Profile bestimmen “Wie streng” wir die Soll-Struktur im jeweiligen Workflow bewerten
---
### 5.2 chain_roles.yaml “Welche rawEdgeTypes zählen als welche Rollen?”
**Zweck:**
- Mappt `rawEdgeType` → kanonische Rollen/EdgeRoles (z.B. `causal`, `influences`, `enables_constraints`, `provenance`).
- Diese Rollen sind Grundlage für:
- `no_causal_roles` Finding
- Link-Constraint-Satisfaction (Template erwartet “causal” zwischen Slots)
- Matching Score (welche Edges zählen für welches Template)
**Interpretation:**
- Wenn ein Edge-Typ nicht gemappt ist:
- Edge kann trotzdem im Graph auftauchen,
- aber Template/Constraint-Logik kann ihn nicht “verstehen” → führt zu Findings.
---
### 5.3 analysis_policies.yaml “Wie noisy dürfen Findings sein?”
**Zweck:**
- Zentrale Policies für Findings:
- welche Finding-Codes existieren
- Default-Severity (info/warn/error)
- Profilabhängige Overrides
- Unterdrückungsregeln (z.B. suppress in soft mode, suppress wenn confirmed, suppress wenn Score hoch…)
**Interpretation:**
- Policies sind “produktseitige UX-Regeln”:
- Discovery: eher informativ, weniger warn
- Decisioning: klare Warnungen, wenn Qualität fehlt
- Der bereits umgesetzte Fix (`missing_link_constraints` nur in strict) ist exakt so eine Policy-Entscheidung (auch wenn technisch im Inspector gelöst).
---
## 6) Ablauf des Chain Inspectors (Vorgehensweise in Mindnet)
Hier ist ein konsistenter “Pipeline”-Ablauf, der zu den Logs passt:
### Schritt 1: Kontext bestimmen
- Aktuelle Datei + aktuelle Section/Heading
### Schritt 2: Edges aus aktueller Note laden
- Outgoing aus der aktuellen Section extrahieren (oder aus einem definierten Block)
- (optional/offen) Note-Level Edges ebenfalls laden und für jede Section gültig machen
### Schritt 3: Nachbarn laden
- Backlinks (Notes, die auf die aktuelle Note verlinken) → incoming Kandidatenquellen
- Outgoing Neighbor Notes (Notes, auf die aktuelle Note verweist) → Nachbarschaft erweitern
### Schritt 4: Edges aus Neighbor Notes laden
- Aus den verlinkenden Notes die Edges extrahieren, die auf die aktuelle Note/Section zielen
- Canonicalization: rawEdgeTypes via chain_roles.yaml in Rollen überführen
### Schritt 5: Kandidatenfilter / Scopefilter anwenden
- Wenn `includeCandidates=false`:
- `scope: candidate` aus effective graph entfernen
- Optional weitere Filter:
- includeNoteLinks / includeSectionLinks
- direction (forward/backward/both)
- maxDepth (Traversal)
### Schritt 6: Pfade berechnen (Paths)
- Forward/Backward (oder both)
- BFS/DFS bis `maxDepth`
- Resultat: Pfadlisten mit nodes + edges
### Schritt 7: Template Matching
- Kandidatenknoten für Slots finden (via noteType + Nähe + Pfade)
- Links/Constraints prüfen (erwartete slot→slot Beziehungen)
- Score berechnen (z.B. per:
- Slots erfüllt
- Link constraints erfüllt
- “RoleEvidence” passend)
### Schritt 8: Findings berechnen (Gap-Heuristics)
Beispiele:
- `missing_slot_*` wenn wichtige Slots fehlen (abhängig von Profil-Thresholds)
- `one_sided_connectivity` wenn nur incoming oder nur outgoing
- `no_causal_roles` wenn Edges da, aber keine causal Rollen im effektiven Graph
- `missing_link_constraints` nur wenn effectiveRequiredLinks=true und slotsComplete=true, requiredLinks>0, linksComplete=false
### Schritt 9: Report ausgeben
- context, settings, neighbors, paths, findings, analysisMeta, templateMatches
- Transparenz: satisfiedLinks/requiredLinks/linksComplete bleiben sichtbar
---
## 7) Strategien, die Mindnet verfolgen kann (Produkt-/UX-Strategie)
### Strategie A: Discovery (Exploration)
**Ziel:** Möglichst schnell “wo könnte eine sinnvolle Kette entstehen?” finden.
- required_links = false (Soft Mode)
- includeCandidates = true (optional)
- findings eher informativ (info), weniger warn
- Templates mehr als Vorschläge (“plausible/weak”), nicht als harte Bewertung
**Vorteil:** Nutzer bekommt schnell Hypothesen.
**Risiko:** Mehr Noise, mehr falsche Kandidaten muss per Policy gedämpft werden.
---
### Strategie B: Decisioning (Qualitätskontrolle)
**Ziel:** Prüfung, ob eine Kette “wirklich steht” und als belastbar gelten kann.
- required_links = true (Strict Mode)
- includeCandidates = false
- findings: warn, wenn Slots/Links fehlen
- “confirmed” nur wenn link constraints komplett
**Vorteil:** Qualitätssicherung & Verlässlichkeit.
**Risiko:** Nutzer fühlt sich “blockiert”, wenn Graph noch im Aufbau ist.
---
### Strategie C: Progressive Disclosure (hybrid)
**Ziel:** Nutzer nicht überfordern, aber zielgerichtet verbessern.
- Soft Mode für Einstieg
- Button/Toggle: “Strict prüfen”
- Candidate Edges als Vorschlag-Klasse (UI: “proposed edges”)
- Findings priorisieren: erst fehlende Slots, dann fehlende Links, dann Detail-Qualität
---
## 8) Wie ein “kausaler Retriever” funktionieren könnte (Causal Retriever)
Ein kausaler Retriever ist die Komponente, die aus dem Vault/Graphen **relevante Kausalkontexte** für den aktuellen Abschnitt liefert idealerweise deterministisch, skalierbar und template-aware.
### 8.1 Retrieval-Ziele
- Finde Knoten/Edges, die **kausal relevant** sind zum aktuellen Kontext:
- Ursachen (backward)
- Wirkungen/Entscheidungen (forward)
- Bedingungen/Constraints (seitlich)
- Gib nicht nur Knoten zurück, sondern:
- Pfade (explainable)
- Evidence (wo steht das)
- Role-Interpretation (warum ist das causal/influences/etc.)
### 8.2 Retrieval-Inputs
- startNode = current section
- direction = forward/backward/both
- maxDepth
- roleFilter (optional): nur causal/influences/enables_constraints
- scopeFilter: includeCandidates, includeNoteLevel
- templateBias: bevorzugte Pfadformen (z.B. “experience→insight→decision”)
### 8.3 Retrieval-Algorithmus (praktisch)
**Variante 1: BFS mit Rolle-Gewichtung**
- BFS über Kanten
- Priorität/Score pro Frontier:
- causal > influences > provenance
- section-scope > note-scope > candidate (wenn candidates eingeschaltet, sonst candidate=∞)
- Stop, wenn:
- maxDepth erreicht
- genug Top-N Pfade gesammelt (z.B. topNUsed)
**Variante 2: Template-driven Retrieval**
- Wenn ein Template im Fokus ist:
- suche explizit nach Slot-Knoten (noteType matching)
- suche dann die minimalen Verbindungen, die Constraints erfüllen
- Gute Option für “Decisioning”: deterministisch prüfen.
**Variante 3: Two-phase Retrieval**
1) Kandidaten finden (Slots)
2) Verbindungen prüfen (Constraints)
→ Liefert sehr gut “warum fehlt Link X?” Diagnosen.
### 8.4 Output-Format
- `neighbors` (incoming/outgoing, mit evidence)
- `paths` (forward/backward, nodes+edges)
- plus “slot candidates” optional (für UI)
---
## 9) Empfehlungen für robuste Tests (damit ihr nicht wieder im Kreis lauft)
### Was ist bereits ausreichend getestet (nicht wiederholen)
- includeCandidates Filterverhalten ✅
- missing_link_constraints Unterdrückung bei required_links=false ✅
- strict/soft required_links via profile/template override ✅
- “healthy graph” ergibt findings: [] ✅
- unmapped edge type triggert Diagnose ✅
### Was als einziges “neues” Testziel für Abschluss 0.4.x/Start 0.5.x taugt
- **Note-level edges / note-scope**: gelten Kanten “global” pro Note oder nicht?
**Minimal-Testdefinition (einmalig, reproduzierbar):**
1) In `02_event_trigger_detail.md` einen klaren Note-Level Block definieren (z.B. “## Note-Verbindungen”).
2) Edge dort definieren, die auf eine andere Note/Section zeigt.
3) Cursor in einer anderen Section derselben Note platzieren (z.B. “## Detail” oder “## Extra”).
4) Chain Inspector laufen lassen.
5) Erwartung:
- Edge erscheint trotzdem als outgoing/incoming
- evidence zeigt auf den Note-Level Block
- ideal: `scope: note`
Wenn das FAIL ist → klarer 0.5.0 Task.
---
## 10) Implikationen für 0.5.x / 0.6.x (wohin sinnvoll weiter)
### 0.5.x (Stabilisierung)
- Note-level edge scope finalisieren (inkl. Report-Transparenz)
- policies (analysis_policies) als zentrale Noise-Steuerung weiter ausbauen
- Debug/Explainability weiter verbessern (effectiveRequiredLinks pro Match explizit ausgeben)
### 0.6.x (UX & Workflows)
- Actionable Findings: “Was genau soll ich ändern?” inkl. Vorschlagtext oder Snippet
- UI-Toggles: Strict/Soft, Candidates on/off
- Template Authoring Tools: Linter, “Warum kein Match?”
---
## 11) Kurzes “Was heißt das für Mindnet im Alltag?”
- Im Discovery-Modus: Mindnet ist ein **Explorationswerkzeug** (Hypothesen + Hinweise, wenig Warnungen).
- Im Decisioning-Modus: Mindnet ist ein **Qualitätsprüfer** (strict, wenige false positives).
- Der nächste große Hebel ist Note-scope: Damit wird Pflege einfacher und Ketten werden “wartbarer”.
---
## 12) Appendix: Beispielhafte Report-Signale (Interpretationshilfe)
- `findings: []` + `confidence: confirmed`
→ Template passt sauber (Slots + Links vollständig im gewählten Modus).
- `linksComplete=false` aber `required_links=false` und **kein** `missing_link_constraints`
→ Soft Mode: bewusst kein “Warn-Noise”, aber Transparenz bleibt.
- `no_causal_roles`
→ Edges existieren, aber keine davon wird als “causal” interpretiert (Mapping oder rawEdgeType Problem).
- `edgesUnmapped > 0`
→ chain_roles unvollständig oder Edge-Typ ist neu/fehlerhaft geschrieben.
- `effectiveIncoming=0` bei includeCandidates=false, aber incoming candidate-edge existiert
→ Filter funktioniert wie geplant.
---
ENDE

View File

@ -1,223 +0,0 @@
<!-- FILE: konzept_zielbild_kausales_retrieval_mindnet.md -->
---
id: konzept_zielbild_kausales_retrieval_mindnet
title: Konzept & Zielbild Kausalketten-Prüfung und kausales Retrieval für Mindnet (Qdrant)
type: concept
status: draft
created: 2026-01-13
lang: de
tags:
- mindnet
- obsidian
- knowledge_graph
- causal_chains
- retrieval
---
# Konzept & Zielbild Kausalketten-Prüfung und kausales Retrieval für Mindnet (Qdrant)
## Ziel
Mindnet soll zu beliebigen Fragestellungen **die richtigen Notizen** nicht nur über semantische Nähe (Embeddings), sondern über **kausale Relevanz** finden.
Parallel soll ein Authoring-Assistent helfen, Obsidian-Notizen so anzulegen, dass Kausalketten **formal konsistent** und **traversierbar** sind.
---
## Ausgangslage / Problem
- Der Wissensgraph wird in Qdrant gepflegt; aktuelles Retrieval basiert primär auf **Gewichtung + semantischer Nähe**.
- Ergebnis: thematisch nahe Treffer, aber oft **nicht antwortrelevant** (fehlende Ursachen-/Folgenbezüge).
- Obsidian-Notizen enthalten Edges (Vorwärts/Rückwärts); Qualität hängt von:
- korrekter Relation (Kausalität vs Chronologie),
- konsistenten Node-Namen,
- Inversen (gegenläufigen Beziehungen),
- sauberer Typisierung ab.
---
## Grundannahmen
- Viele Antworten benötigen einen **Erklärungspfad** statt eines Einzel-Treffers:
- Ursache → Mechanismus/Transformation → Entscheidung → Wirkung → Rückkopplung
- Kausalität ist im Graph als gerichtete Kanten modelliert und über inverse Typen **bidirektional navigierbar**:
- `resulted_in``caused_by`
- `followed_by``preceeded_by`
- `derived_from``source_of`
- `impacts``impacted_by`
---
## System-Zielbild (2 Hauptkomponenten)
### 1) Authoring-Assistent (Obsidian Graph Linter + Chain Explorer)
Zweck: Qualitätssicherung beim Erstellen/Ändern von Notizen.
**Kernfunktionen**
- **Formale Prüfungen**
- Canonical Edge vs Alias (Normalisierung nach `edge_vocabulary`)
- Zielnoten existieren / leere Links als `open_question` oder TODO markieren
- Tippfehler/Node-Splitting erkennen (mehrere Schreibweisen desselben Knoten)
- Edge-Typ zulässig für Note-Typ (z.B. keine Kausal-Edges aus `open_question`)
- **Semantische Plausibilität (regelbasiert)**
- Chronologie (`followed_by`) ≠ Kausalität (`resulted_in`)
- Hub-/Index-Noten nutzen primär `related_to/consists_of` statt Kausalität
- Prinzipien bevorzugt `derived_from/based_on` statt pauschal `caused_by`
- **Ketten-Integrität**
- „Gap“-Warnungen (Sprünge ohne Zwischennoten)
- Zyklen ohne Sinn (A caused_by B und B caused_by A)
- Mehrfachursachen transparent markieren
**Outputs**
- Lint-Report pro Note (Fehler/Warnung/Empfehlung)
- Chain-Preview (24 Schritte vorwärts/rückwärts)
- Optional: Auto-Fix-Vorschläge (Alias→Canonical, Link-Normalisierung, Inversen ergänzen)
---
### 2) Mindnet Retrieval: Hybrid aus Embeddings + Graph Traversal + Reranking
Zweck: Aus einer Frage automatisch eine **kleine, kausal zusammenhängende** Menge von Notizen auswählen.
**Pipeline**
1. **Seed Retrieval (Qdrant Embeddings)**
- Top-K Kandidaten (z.B. 30) als Startpunkte
- Optional: Filter nach Node-Typ (z.B. bei „Welche Entscheidungen…“)
2. **Intent-Klassifikation (Frage → Richtung & Kettenform)**
- Regelbasiert (Start) oder später ML-Classifier
- Output: `{direction, preferred_edges, target_types, max_hops, need_explanation_chain}`
3. **Graph Expansion (Multi-Source Multi-Hop Traversal)**
- Expandiert von Seeds 13 Hops (typisch 24)
- Richtungslogik:
- „Warum/Ursache“ → rückwärts (`caused_by`, `preceeded_by`, `derived_from`)
- „Folgen/Ergebnis“ → vorwärts (`resulted_in`, `followed_by`, `impacts`)
- „Entwicklung/Veränderung“ → beides (forward + backward)
- Ergebnis: Pfad-Kandidaten (nicht nur Nodes)
4. **Reranking (Antwortrelevanz)**
- Score = Semantik + Pfadqualität + Antwortform-Passung
5. **Antwort-Bausteine (Minimal Explanation Subgraph)**
- Merged Top-Pfade zu einem kleinen Subgraph (z.B. 812 Nodes)
- Pruning nach zentralen Knoten und erklärender Kettenform
---
## Spezifikation: Intent → Traversal Mode (Heuristik)
### Intent-Struktur
- `direction`: `backward | forward | both`
- `preferred_edges`: Menge Edge-Typen
- `target_types`: Menge Node-Typen
- `max_hops`: int
- `need_explanation_chain`: bool
### Heuristik (Deutsch)
- **Warum / Ursache / Auslöser / wodurch / wie kam es dazu**
- direction: backward
- preferred_edges: `{caused_by, preceeded_by, derived_from}`
- target_types: `{experience, decision, strategy, state}`
- max_hops: 24
- **Was führte zu / Folgen / Auswirkungen / resultierte in**
- direction: forward
- preferred_edges: `{resulted_in, followed_by, impacts}`
- target_types: `{decision, strategy, state, principle}`
- max_hops: 24
- **Entwicklung / Veränderung / Weltbild / Glaubenssatz / Charakter**
- direction: both
- preferred_edges backward: `{caused_by, derived_from}`
- preferred_edges forward: `{resulted_in, impacts}`
- target_types: `{principle, state, strategy, decision}`
- max_hops: 24
- need_explanation_chain: true
---
## Spezifikation: Traversal (Weighted Multi-Hop)
### Gewichte (Startwerte)
**Edge Weights**
- `resulted_in`: 1.00
- `caused_by`: 1.00
- `derived_from`: 0.90
- `source_of`: 0.90
- `impacts`: 0.70
- `impacted_by`: 0.70
- `followed_by`: 0.50
- `preceeded_by`: 0.50
- `related_to`: 0.25
- `part_of/consists_of`: 0.25
**Node-Type Weights**
- `experience`: 1.00
- `decision`: 1.00
- `strategy`: 0.90
- `state`: 0.85
- `principle`: 0.85
- `insight(hub)`: 0.35
- `open_question/hypothesis/white_spot`: 0.00 (Filter)
**Hop Decay**
- `hop_decay(h) = 0.75^h`
### Traversal-Logik (pseudocode-nah)
- Multi-Source-Expansion ab Seeds
- Pfade priorisiert nach kumuliertem Pfadscore
- `visited` verhindert endlose Wiederholungen
---
## Spezifikation: Reranking (Semantik + Kausalität + Antwortform)
### Final Score
- `final_score(path) = alpha*semantic + beta*coherence + gamma*shape_match`
Startwerte:
- `alpha = 0.55` (Semantik)
- `beta = 0.30` (Kausal-Kohärenz)
- `gamma = 0.15` (Passung zur Frageform)
**Causal Coherence**
- Bonus, wenn Pfad Kausal-Edges enthält (`resulted_in/caused_by/derived_from`)
- Malus, wenn nur Navigation/Chronologie enthalten ist
- Bonus für Kernform: `experience → decision → (state|strategy|principle)`
---
## Output: Minimal Explanation Subgraph (MES)
Ziel: nicht eine Liste, sondern ein erklärendes Subgraph-Set.
**Regeln**
- Top-Pfade (z.B. 35) mergen
- max_nodes: 812
- Pruning:
- Hubs raus, wenn sie nur Navigation sind
- Decision/Principle/State bevorzugen (Antwortanker)
- Bridge-Nodes behalten (in mehreren Pfaden vorkommend)
---
## Authoring-Regeln (Graph-Hygiene) harte Leitplanken
1. Kausalität nur auf atomaren Noten (`experience/decision/state/strategy/principle`)
2. Hubs/Indexnoten: primär `related_to/consists_of` (keine „Hub verursacht X“-Kausalität)
3. Inverse Edges müssen erzeugbar sein (oder Build-Step erzeugt sie deterministisch)
4. Chronologie strikt trennen (`followed_by` ≠ `resulted_in`)
5. Prinzipien: `derived_from/based_on` für Herkunft (statt pauschal `caused_by`)
6. Leere Links als `open_question` oder TODO ohne Kausal-Edge
7. Kanonische Dateinamen: Node-Splitting verhindern
---
## Nutzen / Erfolgskriterien
- **Bessere Answer Relevance**: Mindnet liefert Knoten mit erklärender Kausalstruktur statt nur thematischer Nähe
- **Erklärbarkeit**: Antwort kann mit Pfad(en) begründet werden
- **Debuggability**: Fehlantworten lassen sich auf falsche/fehlende Kanten zurückführen
- **Authoring-Effizienz**: Assistent verhindert typische Edge-Fehler früh
---
## Offene Punkte (für nächste Iteration)
- Intent-Taxonomie (812 Frageklassen) finalisieren und evaluieren
- Welche Edges werden als „kausal“ im engeren Sinne akzeptiert?
- Welche Node-Typen sind Pflichtmetadaten für Mindnet?
- Evaluation: Retrieval-Qualität mit/ohne Traversal (A/B)

View File

@ -1,105 +0,0 @@
# Debug: .env-Lade-Problem in Prod
**Datum**: 2026-01-12
**Version**: v4.5.10
**Status**: 🔴 Kritisch
## Problem
Möglicherweise wird die `.env`-Datei in Prod nicht korrekt geladen, was zu:
- Falschen Log-Levels (DEBUG=true wird ignoriert)
- Falschen Collection-Präfixen
- Falschen Konfigurationen
führen kann.
## Diagnose
### Schritt 1: Prüfe, ob .env-Datei existiert
```bash
# In Prod
cd ~/mindnet
ls -la .env
cat .env | head -20
```
### Schritt 2: Prüfe Arbeitsverzeichnis beim Start
```bash
# In Prod - prüfe, von wo uvicorn gestartet wird
ps aux | grep uvicorn
# Oder in systemd service:
cat /etc/systemd/system/mindnet.service | grep WorkingDirectory
```
### Schritt 3: Verifikations-Script ausführen
```bash
# In Prod
cd ~/mindnet
source .venv/bin/activate
python3 scripts/verify_env_loading.py
```
**Erwartete Ausgabe**:
```
✅ .env geladen von: /path/to/mindnet/.env
✅ COLLECTION_PREFIX = mindnet
✅ DEBUG = true
```
### Schritt 4: Manuelle Verifikation
```python
# In Python-REPL in Prod
import os
from pathlib import Path
from dotenv import load_dotenv
# Prüfe aktuelles Verzeichnis
print(f"CWD: {Path.cwd()}")
print(f"Projekt-Root: {Path(__file__).parent.parent.parent}")
# Lade .env
env_file = Path(".env")
if env_file.exists():
load_dotenv(env_file, override=True)
print(f"✅ .env geladen: {env_file.absolute()}")
else:
print(f"❌ .env nicht gefunden in: {env_file.absolute()}")
# Prüfe kritische Variablen
print(f"DEBUG: {os.getenv('DEBUG', 'NICHT GESETZT')}")
print(f"COLLECTION_PREFIX: {os.getenv('COLLECTION_PREFIX', 'NICHT GESETZT')}")
```
## Mögliche Ursachen
### 1. Arbeitsverzeichnis-Problem
- **Problem**: uvicorn wird aus einem anderen Verzeichnis gestartet
- **Lösung**: Expliziter Pfad in `config.py` (bereits implementiert)
### 2. .env-Datei nicht im Projekt-Root
- **Problem**: .env liegt in `config/prod.env` statt `.env`
- **Lösung**: Symlink erstellen oder Pfad anpassen
### 3. Systemd-Service ohne WorkingDirectory
- **Problem**: Service startet ohne korrektes Arbeitsverzeichnis
- **Lösung**: `WorkingDirectory=/path/to/mindnet` in systemd service
### 4. Mehrere .env-Dateien
- **Problem**: Es gibt `.env`, `prod.env`, `config/prod.env` - welche wird geladen?
- **Lösung**: Expliziter Pfad oder Umgebungsvariable `DOTENV_PATH`
## Fix-Implementierung
Der Code in `app/config.py` wurde erweitert:
- ✅ Expliziter Pfad für `.env` im Projekt-Root
- ✅ Fallback auf automatische Suche
- ✅ Debug-Logging (wenn verfügbar)
## Verifikation nach Fix
1. **Log prüfen**: Sollte `✅ .env geladen von: ...` zeigen
2. **Umgebungsvariablen prüfen**: `echo $DEBUG`, `echo $COLLECTION_PREFIX`
3. **Settings prüfen**: `python3 -c "from app.config import get_settings; s = get_settings(); print(f'DEBUG: {s.DEBUG}, PREFIX: {s.COLLECTION_PREFIX}')"`

View File

@ -1,134 +0,0 @@
# Deployment-Checkliste: Prod vs. Dev Retrieval-Problem
**Datum**: 2026-01-12
**Version**: v4.5.10
**Status**: 🔴 Kritisch
## Problem
Prod-System findet keine Suchergebnisse, während Dev-System korrekt funktioniert. Identischer Code, identische Daten.
## Identifizierte Ursachen
### 1. 🔴 **KRITISCH: Alte EdgeDTO-Version in Prod**
**Symptom**:
```
ERROR: 1 validation error for EdgeDTO
provenance
Input should be 'explicit', 'rule', 'smart' or 'structure'
[type=literal_error, input_value='explicit:callout', input_type=str]
```
**Ursache**:
- Prod verwendet eine **alte Version** des `EdgeDTO`-Modells aus `app/models/dto.py`
- Die alte Version unterstützt nur: `"explicit", "rule", "smart", "structure"`
- Die neue Version (v4.5.3+) unterstützt: `"explicit:callout", "explicit:wikilink", "explicit:note_zone", ...`
**Lösung**:
- ✅ Code in `dto.py` ist bereits korrekt (Zeile 51-56)
- ⚠️ **Prod muss neu gestartet werden**, um die neue Version zu laden
- ⚠️ **Python-Modul-Cache leeren** falls nötig: `find . -type d -name __pycache__ -exec rm -r {} +`
### 2. ✅ Collection-Präfix korrekt
- Prod: `COLLECTION_PREFIX=mindnet``mindnet_chunks`
- Dev: `COLLECTION_PREFIX=mindnet_dev``mindnet_dev_chunks`
- **Kein Problem hier**
## Sofortmaßnahmen
### Schritt 1: Code-Verifikation in Prod
```bash
# In Prod-System
cd /path/to/mindnet
grep -A 10 "provenance.*Literal" app/models/dto.py
```
**Erwartete Ausgabe**:
```python
provenance: Optional[Literal[
"explicit", "rule", "smart", "structure",
"explicit:callout", "explicit:wikilink", "explicit:note_zone", ...
]] = "explicit"
```
**Falls nicht vorhanden**: Code ist nicht aktualisiert → Deployment erforderlich
### Schritt 2: Python-Cache leeren
```bash
# In Prod-System
find . -type d -name __pycache__ -exec rm -r {} +
find . -name "*.pyc" -delete
```
### Schritt 3: Service neu starten
```bash
# FastAPI/uvicorn neu starten
# Oder Docker-Container neu starten
```
### Schritt 4: Verifikation
1. **Test-Query ausführen**:
```bash
curl -X POST http://localhost:8001/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "Was für einen Status hat das Projekt mindnet?"}'
```
2. **Log prüfen**:
- ✅ Keine `validation error for EdgeDTO` mehr
- ✅ `✨ [SUCCESS] Stream 'facts_stream' lieferte X Treffer.`
- ✅ Ergebnisse werden zurückgegeben
## Code-Vergleich
### Aktuelle Version (sollte in Prod sein):
```python
# app/models/dto.py (Zeile 51-56)
provenance: Optional[Literal[
"explicit", "rule", "smart", "structure",
"explicit:callout", "explicit:wikilink", "explicit:note_zone", "explicit:note_scope",
"inline:rel", "callout:edge", "semantic_ai", "structure:belongs_to", "structure:order",
"derived:backlink", "edge_defaults", "global_pool"
]] = "explicit"
```
### Alte Version (verursacht Fehler):
```python
# Alte Version (nur 4 Werte)
provenance: Optional[Literal[
"explicit", "rule", "smart", "structure"
]] = "explicit"
```
## Weitere mögliche Ursachen (wenn Fix nicht hilft)
### 1. Unterschiedliche Python-Versionen
- Prüfen: `python --version` in Dev vs. Prod
- Pydantic-Verhalten kann zwischen Versionen variieren
### 2. Unterschiedliche Pydantic-Versionen
- Prüfen: `pip list | grep pydantic` in Dev vs. Prod
- `requirements.txt` sollte identisch sein
### 3. Unterschiedliche Embedding-Modelle
- Prüfen: `MINDNET_EMBEDDING_MODEL` in beiden Systemen
- **Beide verwenden**: `nomic-embed-text`
### 4. Unterschiedliche Vektor-Dimensionen
- Prüfen: `VECTOR_DIM` in beiden Systemen
- **Beide verwenden**: `768`
## Erwartetes Ergebnis nach Fix
- ✅ Keine Pydantic-Validierungsfehler mehr
- ✅ Alle Streams liefern Ergebnisse
- ✅ Retrieval funktioniert identisch in Dev und Prod
- ✅ `explicit:callout` Provenance wird korrekt akzeptiert

View File

@ -1,134 +0,0 @@
# Fix: Python-Modul-Cache-Problem in Prod
**Datum**: 2026-01-12
**Version**: v4.5.10
**Status**: 🔴 Kritisch
## Problem
Code in `app/models/dto.py` ist korrekt (enthält `explicit:callout`), aber Prod verwendet trotzdem eine alte Version.
**Symptom**:
```
ERROR: 1 validation error for EdgeDTO
provenance
Input should be 'explicit', 'rule', 'smart' or 'structure'
[type=literal_error, input_value='explicit:callout', input_type=str]
```
## Ursache
**Python-Modul-Cache**: Python speichert kompilierte `.pyc` Dateien in `__pycache__` Verzeichnissen. Wenn der Code aktualisiert wird, aber der Service nicht neu gestartet wird, lädt Python die alte gecachte Version.
## Sofortmaßnahmen
### Schritt 1: Python-Cache leeren
```bash
# In Prod-System
cd ~/mindnet
# Finde und lösche alle __pycache__ Verzeichnisse
find . -type d -name __pycache__ -exec rm -r {} + 2>/dev/null || true
# Finde und lösche alle .pyc Dateien
find . -name "*.pyc" -delete
# Speziell für dto.py
rm -rf app/models/__pycache__
rm -rf app/__pycache__
rm -rf __pycache__
```
### Schritt 2: Verifikation des Codes
```bash
# Prüfe, ob der Code korrekt ist
grep -A 10 "provenance.*Literal" app/models/dto.py | grep "explicit:callout"
```
**Erwartete Ausgabe**: Sollte `explicit:callout` enthalten
### Schritt 3: Service neu starten
**Option A: FastAPI/uvicorn direkt**:
```bash
# Service stoppen (Ctrl+C oder kill)
# Dann neu starten
source .venv/bin/activate
uvicorn app.main:app --host 0.0.0.0 --port 8001 --reload
```
**Option B: Systemd-Service**:
```bash
sudo systemctl restart mindnet-prod
# oder
sudo systemctl restart mindnet
```
**Option C: Docker-Container**:
```bash
docker-compose restart mindnet
# oder
docker restart mindnet-container
```
### Schritt 4: Verifikation zur Laufzeit
**Test-Script ausführen** (wenn verfügbar):
```bash
python3 scripts/verify_dto_import.py
```
**Erwartete Ausgabe**:
```
✅ EdgeDTO unterstützt 'explicit:callout'
✅ 'explicit:callout' ist in der Literal-Liste enthalten
✅ EdgeDTO mit 'explicit:callout' erfolgreich erstellt!
```
**Oder manuell testen**:
```python
python3 -c "
from app.models.dto import EdgeDTO
test = EdgeDTO(
id='test', kind='test', source='test', target='test',
weight=1.0, provenance='explicit:callout'
)
print('✅ EdgeDTO mit explicit:callout funktioniert!')
"
```
## Code-Fix (Fallback-Mechanismus)
Ein Fallback-Mechanismus wurde in `retriever.py` implementiert:
- Wenn `EdgeDTO` mit `explicit:callout` fehlschlägt, wird automatisch `explicit` als Fallback verwendet
- Dies verhindert, dass der gesamte Retrieval-Prozess fehlschlägt
- **WICHTIG**: Dies ist nur eine temporäre Lösung - der Cache muss trotzdem geleert werden!
## Verifikation nach Fix
1. **Test-Query ausführen**:
```bash
curl -X POST http://localhost:8001/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "Was für einen Status hat das Projekt mindnet?"}'
```
2. **Log prüfen**:
- ✅ Keine `validation error for EdgeDTO` mehr
- ✅ Keine `⚠️ [EDGE-DTO] Provenance 'explicit:callout' nicht unterstützt` Warnungen
- ✅ `✨ [SUCCESS] Stream 'facts_stream' lieferte X Treffer.`
- ✅ Ergebnisse werden zurückgegeben
## Warum passiert das?
1. **Code wurde aktualisiert**, aber Service läuft noch mit alter Version im Speicher
2. **Python lädt Module nur einmal** - nach dem ersten Import wird die gecachte Version verwendet
3. **__pycache__ Verzeichnisse** enthalten kompilierte Bytecode-Versionen der alten Dateien
## Prävention
- **Immer Service neu starten** nach Code-Änderungen
- **Cache regelmäßig leeren** bei Deployment
- **Verwende `--reload` Flag** bei uvicorn für automatisches Neuladen (nur für Dev!)

View File

@ -1,163 +0,0 @@
# Analyse: Retrieval-Unterschiede zwischen Dev und Prod
**Datum**: 2026-01-12
**Version**: v4.5.10
**Status**: 🔴 Kritisch
## Problemstellung
Bei identischer Codebasis und identischen Daten liefert das Dev-System Suchergebnisse, während das Prod-System keine Ergebnisse findet.
## Identifizierte Ursachen
### 1. 🔴 **KRITISCH: Inkonsistente Collection-Präfix-Konfiguration**
**Problem**: Zwei verschiedene Umgebungsvariablen werden für den Collection-Präfix verwendet:
1. **`app/config.py` (Zeile 24)**:
```python
COLLECTION_PREFIX: str = os.getenv("MINDNET_PREFIX", "mindnet_dev")
```
- Verwendet `MINDNET_PREFIX` als Umgebungsvariable
- Default: `"mindnet_dev"`
2. **`app/core/database/qdrant.py` (Zeile 47)**:
```python
prefix = os.getenv("COLLECTION_PREFIX") or "mindnet"
```
- Verwendet `COLLECTION_PREFIX` als Umgebungsvariable
- Default: `"mindnet"`
**Auswirkung**:
- **Retriever verwendet `QdrantConfig.from_env()`**, das `COLLECTION_PREFIX` liest
- **Ingestion verwendet `Settings.COLLECTION_PREFIX`**, das `MINDNET_PREFIX` liest
- **Resultat**: Daten werden in verschiedene Collections geschrieben/gesucht:
- Dev: `mindnet_dev_chunks`, `mindnet_dev_notes`, `mindnet_dev_edges`
- Prod: `mindnet_chunks`, `mindnet_notes`, `mindnet_edges`
### 2. ⚠️ **Mögliche weitere Ursachen**
#### 2.1 Unterschiedliche Embedding-Modelle
- **Prüfen**: `MINDNET_EMBEDDING_MODEL` in Dev vs. Prod
- **Auswirkung**: Unterschiedliche Vektoren → unterschiedliche Similarity-Scores
#### 2.2 Unterschiedliche Vektor-Dimensionen
- **Prüfen**: `VECTOR_DIM` in Dev vs. Prod
- **Auswirkung**: Dimension-Mismatch → Suche schlägt fehl
#### 2.3 Unterschiedliche Qdrant-Instanzen
- **Prüfen**: `QDRANT_URL` / `QDRANT_HOST` in Dev vs. Prod
- **Auswirkung**: Daten liegen in verschiedenen Datenbanken
#### 2.4 Unterschiedliche Score-Thresholds
- **Prüfen**: Filter-Logik oder Mindest-Scores
- **Auswirkung**: Ergebnisse werden gefiltert, bevor sie zurückgegeben werden
## Diagnose-Checkliste
### ✅ Sofort prüfen:
1. **Collection-Präfix-Verifikation**:
```bash
# Dev
echo $COLLECTION_PREFIX
echo $MINDNET_PREFIX
# Prod
echo $COLLECTION_PREFIX
echo $MINDNET_PREFIX
```
2. **Qdrant Collections prüfen**:
```python
# In beiden Systemen ausführen
from app.core.database.qdrant import get_client, QdrantConfig
cfg = QdrantConfig.from_env()
client = get_client(cfg)
print(f"Prefix: {cfg.prefix}")
print(f"Collections: {client.get_collections().collections}")
```
3. **Embedding-Modell prüfen**:
```bash
# Dev
echo $MINDNET_EMBEDDING_MODEL
echo $VECTOR_DIM
# Prod
echo $MINDNET_EMBEDDING_MODEL
echo $VECTOR_DIM
```
4. **Qdrant-Verbindung prüfen**:
```bash
# Dev
echo $QDRANT_URL
echo $QDRANT_HOST
echo $QDRANT_PORT
# Prod
echo $QDRANT_URL
echo $QDRANT_HOST
echo $QDRANT_PORT
```
## Lösungsvorschläge
### Option 1: Harmonisierung der Umgebungsvariablen (Empfohlen)
**Ziel**: Eine einzige Umgebungsvariable für den Collection-Präfix verwenden.
**Änderungen**:
1. **`app/core/database/qdrant.py`**:
```python
prefix = os.getenv("COLLECTION_PREFIX") or os.getenv("MINDNET_PREFIX") or "mindnet"
```
- Unterstützt beide Variablen (Abwärtskompatibilität)
- `COLLECTION_PREFIX` hat Priorität
2. **`app/config.py`**:
```python
COLLECTION_PREFIX: str = os.getenv("COLLECTION_PREFIX") or os.getenv("MINDNET_PREFIX") or "mindnet_dev"
```
- Unterstützt beide Variablen
- `COLLECTION_PREFIX` hat Priorität
3. **Dokumentation**: Klarstellen, dass `COLLECTION_PREFIX` die primäre Variable ist
### Option 2: Explizite Konfiguration in .env
**Ziel**: Beide Systeme verwenden explizit gesetzte `COLLECTION_PREFIX` Werte.
**Dev `.env`**:
```env
COLLECTION_PREFIX=mindnet_dev
```
**Prod `.env`**:
```env
COLLECTION_PREFIX=mindnet
```
### Option 3: Daten-Migration
**Ziel**: Daten von einer Collection in die andere migrieren.
**Vorgehen**:
1. Identifizieren, welche Collection die "richtigen" Daten enthält
2. Daten von Dev nach Prod migrieren (oder umgekehrt)
3. Collection-Präfix harmonisieren
## Sofortmaßnahmen
1. ✅ **Prüfen**: Welche Collections existieren in beiden Systemen?
2. ✅ **Prüfen**: Welche Umgebungsvariablen sind gesetzt?
3. ✅ **Prüfen**: Welche Collection enthält die Daten?
4. ✅ **Fix**: Collection-Präfix-Konfiguration harmonisieren
5. ✅ **Test**: Retrieval in beiden Systemen verifizieren
## Erwartetes Ergebnis nach Fix
- ✅ Beide Systeme verwenden dieselbe Collection-Präfix-Logik
- ✅ Retrieval findet Daten in beiden Systemen
- ✅ Konsistente Konfiguration zwischen Ingestion und Retrieval

View File

@ -1,250 +0,0 @@
<!-- DOCUMENT 1: pflichtenheft_obsidian_plugin_mindnet_assistant.md -->
---
id: pflichtenheft_obsidian_plugin_mindnet_assistant
title: Pflichtenheft Obsidian Plugin „Mindnet Causal Assistant“
type: specification
status: draft
created: 2026-01-13
lang: de
---
# Pflichtenheft Obsidian Plugin „Mindnet Causal Assistant“
## 1. Zielsetzung
Das Plugin unterstützt den Nutzer beim Erstellen und Pflegen eines narrativ-kausalen Wissensgraphen in Obsidian (Mindnet).
Es bietet:
- **Aktive Authoring-Unterstützung** (Guided Note Creation / Interview Flow)
- **Kausalketten-Prüfung** (Chain Explorer, Vorwärts/Rückwärts)
- **Linting + Auto-Fixes** (Graph-Hygiene, konsistente Kanten, Namensnormalisierung)
- **Export/Integration** der strukturierten Graph-Daten für ein Retrieval-System (Qdrant + Graph-Index)
## 2. Kontext & Randbedingungen
- Obsidian Vault enthält Notes als Markdown, jede Entität eine Datei.
- Notes enthalten Frontmatter (`id,title,type,status,...`) und Edges in Callout-Blöcken.
- Edge-Vokabular inklusive Aliasse & Inversen ist verfügbar (z.B. `edge_vocabulary.md`).
- Der Graph soll später von Mindnet traversierbar sein (Vorwärts/Rückwärts über inverse Relationen).
- Plugin muss offline nutzbar sein; optionale Backend/LLM-Integration ist konfigurierbar.
## 3. Begriffsdefinitionen
- **Node**: eine Obsidian Markdown-Datei (Entität).
- **Edge**: gerichtete Beziehung zwischen zwei Nodes (canonical edge type).
- **Alias**: alternative Edge-Bezeichnung in Notes, die auf canonical mapped wird.
- **Inverse**: Gegenkante zu einer Edge, laut Vokabular (z.B. `resulted_in``caused_by`).
- **Hub/Index**: Note, die primär navigiert (typisch `type: insight`), keine Kausalursache.
## 4. Scope
### In Scope (MVP → V2)
**MVP**
- Parser für Frontmatter + Edge-Callouts
- Normalizer: Alias → Canonical; optional Inversen-Erkennung
- Linter: Regelset (Error/Warn/Info) + Report
- Chain Explorer: forward/backward traversal (14 hops) ab aktueller Note
- Quickfixes: Edge-Typ ersetzen, Links normalisieren, Missing Note Stub erzeugen
**V2**
- Guided Authoring (Interview Flow) für `experience/decision/principle/state/strategy`
- Refactor Mode: Text → Node/Edge-Kandidaten + Review UI
- Export/Sync: JSON Graph Export + optional Qdrant Sync (separater Service)
### Out of Scope (initial)
- Vollautomatische Kausalitäts-Interpretation ohne User-Bestätigung
- Vollständige UI-Graph-Visualisierung (Obsidian Graph View bleibt nutzbar)
- Direkte medizinische/psychologische Beratung
## 5. Nutzerrollen & Use Cases
### Rolle: Nutzer (Author)
- UC1: „Ich schreibe eine Note und will Kanten prüfen“
- UC2: „Ich will von einem Ereignis aus die Kausalkette sehen“
- UC3: „Ich will eine neue Experience/Decision Note sauber anlegen“
- UC4: „Ich habe Text und will daraus Kandidaten extrahieren“
- UC5: „Ich will leere Links als open_question sauber erzeugen“
### Rolle: System/Indexer (Mindnet)
- UC6: „Ich brauche exportierbare adjacency lists + canonical edges“
## 6. Funktionale Anforderungen (FR)
### FR1: Vault Parsing
- FR1.1 Parse Frontmatter (YAML): `id,title,type,status,date,tags,...`
- FR1.2 Parse Edge-Callouts im Format:
- `> [!abstract]- 🕸️ Semantic Mapping`
- `>> [!edge] <relation>`
- `>> [[target]]`
- FR1.3 Extrahiere alle WikiLinks `[[...]]` aus Edge-Blocks
- FR1.4 Erkenne Datei-Pfade, Dateinamen und canonical node identifiers (Dateiname ohne `.md`)
**Output (intern)**
```ts
type Node = { id?: string; title?: string; type?: string; status?: string; path: string; slug: string };
type Edge = { srcSlug: string; dstSlug: string; rawType: string; canonicalType?: string; line?: number; blockId?: string };
```
### FR2: Edge Normalization
- FR2.1 Map rawType/Alias auf canonical edge type via Edge Vocabulary
- FR2.2 Speichere Mapping-Entscheidungen (raw → canonical) pro Edge
- FR2.3 Liefere inverse edge type (`inverseType`) pro canonical edge type (sofern definiert)
### FR3: Linting
- FR3.1 Führe Checkliste von Regeln aus (siehe separates Dokument)
- FR3.2 Liefere LintReport mit Severity (ERROR/WARN/INFO), Location (file, line), Fix-Vorschlägen
- FR3.3 Quickfix: wende Fix auf Note an (Text edit), mit Preview/Diff
### FR4: Chain Explorer (Traversal)
- FR4.1 Startpunkt: aktuelle Note im Editor
- FR4.2 Forward traversal: `resulted_in`, `followed_by`, `impacts`, `source_of`, optional `related_to`
- FR4.3 Backward traversal: `caused_by`, `preceeded_by`, `derived_from`, `impacted_by`
- FR4.4 Konfigurierbare maxHops (Default 3)
- FR4.5 Ergebnis als Liste von Pfaden + kompaktes Subgraph-Summary (Nodes/Edges)
### FR5: Missing Notes / Stubs
- FR5.1 Erkenne, wenn Edge-Target nicht existiert
- FR5.2 Biete „Create Stub Note“ an:
- Template basierend auf Typ (default `open_question` wenn unbekannt)
- Einhaltung Naming-Rules: `a-z0-9_`
- FR5.3 Optional: convert TODO-Link → open_question note
### FR6: Guided Authoring (V2)
- FR6.1 Wizard für neue Notes: Auswahl Typ → Fragen (eine nach der anderen)
- FR6.2 Wizard erstellt Datei mit Template + initialen Edge-Blocks
- FR6.3 Jede automatische Edge-Vermutung ist „review-required“
### FR7: Refactor Mode (V2)
- FR7.1 Extrahiere aus aktuellem Note-Text Kandidaten:
- Event-Kandidaten (Datum/Ort/Verben)
- Decision-Kandidaten („entschied“, „nahm an“, „wechselte“)
- Principle-Kandidaten („ich glaube“, „ich habe gelernt“)
- Relation-Kandidaten („dadurch“, „führte zu“, „weil“)
- FR7.2 UI-Review: Checkbox-Liste zum Erstellen/Verwerfen
- FR7.3 Generiere Notes + Edges nur nach Bestätigung
### FR8: Export (MVP optional / V2 empfohlen)
- FR8.1 Export JSON: Nodes + canonical edges + inverses
- FR8.2 Export adjacency list pro node slug
- FR8.3 Optional: webhook/CLI hook für Indexer (Qdrant)
## 7. Nicht-funktionale Anforderungen (NFR)
- NFR1: Performant bei 10k Notes (incremental parse, caching)
- NFR2: Offline-first; LLM/Backend optional
- NFR3: Deterministische Normalisierung (gleiches Input → gleiches Output)
- NFR4: Kein Erfinden von Fakten: Auto-Edge nur als Vorschlag
- NFR5: Sicheres Editieren (Diff/Undo via Obsidian APIs)
- NFR6: Konfigurierbarkeit (YAML/Settings Tab): maxHops, allowed edges, strict mode
## 8. Technisches Lösungsdesign
### 8.1 Obsidian Plugin Struktur (TypeScript)
- `main.ts` Plugin lifecycle, commands, views
- `settings.ts` Einstellungen
- `parser/` Markdown + callout parser
- `graph/` Node/Edge model, normalization, traversal
- `lint/` Rules engine, reports, quickfixes
- `ui/` Sidebar view, modals, diff preview
### 8.2 Speicherung / Cache
- In-memory cache: map `path → parsed Node + edges + hash`
- Incremental update: on file change events re-parse only changed file
- Optional persisted cache in `.obsidian/plugins/.../cache.json`
### 8.3 Edge Vocabulary Integration
- Input: `edge_vocabulary.md` im Vault ODER eingebettete JSON Ressource
- Parsing:
- canonical edge types
- alias list
- inverse mapping
- Fallback: minimal builtin vocabulary, wenn Datei fehlt
### 8.4 Traversal Engine
- Graph Index: adjacency lists aus canonical edges
- Traversal:
- BFS mit hop limit
- optional weighted expansion (für Chain Explorer „relevant paths first“)
### 8.5 Quickfix Engine
- Applies patches auf Markdown:
- Replace edge type token im Callout (`>> [!edge] ...`)
- Rename link targets (replace `[[old]]``[[new]]`)
- Insert stub note file from template
- Safety:
- Show diff modal
- Use Obsidian editor transactions / file API
### 8.6 Optional Backend / LLM (V2)
- Backend (local node service) für:
- text extraction (Refactor Mode)
- suggestion generation
- Communication:
- HTTP local (`127.0.0.1`) oder WebSocket
- API key storage via Obsidian settings (encrypted if possible)
- Claude-code/Cursor: nutzt Code-Agent für Implementierung, nicht zur Runtime.
## 9. UI/UX Anforderungen
### 9.1 Sidebar View „Mindnet Assistant“
Tabs:
- **Validate**: Lint Report + Fix Buttons
- **Chains**: Forward/Backward Chain Explorer + Copy as text
- **Create** (V2): Wizard new note
- **Refactor** (V2): Extract candidates
### 9.2 Commands (Command Palette)
- `Mindnet: Validate current note`
- `Mindnet: Validate vault (selected folders)`
- `Mindnet: Show chains from current note`
- `Mindnet: Normalize edges in current note`
- `Mindnet: Create stub for missing links`
- (V2) `Mindnet: Start guided authoring`
- (V2) `Mindnet: Refactor current note to graph`
## 10. Akzeptanzkriterien
- AK1: Plugin erkennt Edge-Callouts und normalisiert Aliasse deterministisch
- AK2: Linter findet Node-Splitting (mindestens Levenshtein/ähnliche Slugs) und Missing Notes
- AK3: Chain Explorer liefert identische Pfade vorwärts/rückwärts bei inversen Edge-Paaren
- AK4: Quickfix ersetzt Edge-Typen ohne Markdown zu zerstören; Undo funktioniert
- AK5: Export JSON enthält canonical edges + inverse types
## 11. Deliverables
- Obsidian Plugin (TS) mit MVP Features
- Dokumentation:
- Install/Build
- Settings
- Rule reference
- Example vault sample
- Optional: JSON Export Format Spec (nodes/edges)
---
## 12. Prompts für Code-Agenten (Cursor / Claude-code)
### Prompt A (Repo Scaffold + MVP)
> Du bist ein Senior TypeScript Engineer. Implementiere ein Obsidian Plugin „Mindnet Causal Assistant“.
> Ziele MVP:
> 1) Parse Frontmatter (YAML) und Edge-Callouts im Format:
> `> [!abstract]- 🕸️ Semantic Mapping``>> [!edge] <relation>``>> [[target]]`.
> 2) Normalisiere Edge Aliasse auf canonical edge types (Vokabular als JSON im Code; später ersetzbar).
> 3) Baue eine Sidebar View mit Tabs „Validate“ und „Chains“.
> 4) Implementiere Lint Regeln: missing target note, alias-not-normalized, hub-has-causal-edge, chronology-vs-causality warning.
> 5) Implementiere Chain Explorer (forward/backward, maxHops=3).
> 6) Implementiere Quickfix: replace edge type token, create stub note.
> Nutze Obsidian APIs, schreibe sauberen TS Code, mit Tests für Parser/Normalizer.
### Prompt B (Parser Unit Tests)
> Schreibe Unit Tests für den Markdown Parser:
> - erkennt mehrere Edge-Blocks pro Datei
> - erkennt mehrere Targets pro Edge
> - liefert line numbers
> - ignoriert WikiLinks außerhalb der Semantic Mapping Callouts
> Nutze vitest/jest. Erzeuge fixtures.
### Prompt C (Lint Engine + Quickfix)
> Implementiere eine Lint Engine als Rule-Pipeline.
> Jede Regel: id, severity, detect(node, graph) -> findings, fix(finding)->patch.
> Baue eine Diff Preview Modal und applyPatch über Obsidian file API.
> Implementiere zunächst 6 Regeln aus der Checkliste.
### Prompt D (Vocabulary Loader)
> Implementiere einen VocabularyLoader:
> - lädt entweder eingebettetes JSON oder eine Vault-Datei `edge_vocabulary.md`
> - parst canonical types, aliases, inverse
> - bietet getCanonical(raw) und getInverse(canonical)
> Fallback auf builtin vocabulary wenn parsing fehlschlägt.

View File

@ -1,136 +0,0 @@
<!-- DOCUMENT 2: checklist_lint_regeln_mindnet_assistant.md -->
---
id: checklist_lint_regeln_mindnet_assistant
title: Checkliste Lint-Regeln für Mindnet Causal Assistant
type: specification
status: draft
created: 2026-01-13
lang: de
---
# Checkliste Lint-Regeln für Mindnet Causal Assistant
## Severity Levels
- **ERROR**: bricht Traversal/Indexer oder erzeugt falsche Nodes
- **WARN**: wahrscheinlich falsche Semantik / schlechter Retrieval-Impact
- **INFO**: Optimierung / Empfehlung
---
## A. Graph-Integrität & Naming
### L1 (ERROR) Missing Target Note
**Wenn:** Edge target `[[X]]` existiert nicht als Datei im Vault
**Dann:** Finding `missing_target`
**Fix:** „Create Stub Note“ (default `type: open_question`) oder remove edge
### L2 (ERROR) Node Splitting durch Schreibvarianten
**Wenn:** mehrere Targets im Vault sind ähnlich (slug distance), oder in Edges mehrere Varianten vorkommen
**Dann:** Finding `node_split_candidate`
**Fix:** Vorschlag canonical slug + bulk replace links
### L3 (ERROR) Invalid Filename Policy
**Wenn:** Dateiname enthält Zeichen außerhalb `[a-z0-9_]`
**Dann:** Finding `invalid_filename`
**Fix:** Rename file + update all backlinks
---
## B. Edge-Formale Regeln
### L4 (ERROR) Unknown Edge Type / Unmapped Alias
**Wenn:** raw edge type nicht im Vokabular (alias→canonical)
**Dann:** Finding `unknown_edge_type`
**Fix:** Edge type ersetzen durch best guess oder user selection
### L5 (WARN) Alias not normalized
**Wenn:** raw edge type ist Alias, canonical bekannt, aber Note enthält Alias
**Dann:** Finding `alias_not_normalized`
**Fix:** Replace raw with canonical (optional config)
### L6 (WARN) Missing Inverse Edge (optional strict mode)
**Wenn:** Edge A->B existiert, inverse nach Vokabular fehlt in B
**Dann:** Finding `missing_inverse`
**Fix:** Add inverse edge to target note (review + diff)
---
## C. Semantik: Kausalität vs Chronologie
### L7 (WARN) Chronology used as Causality
**Wenn:** Knoten/Target wirkt wie Prozessschritt („warten“, „gehen“, „ankommen“) und Edge type ist `resulted_in`
**Dann:** Finding `chronology_as_causality`
**Fix:** Vorschlag `followed_by` (inverse `preceeded_by`)
### L8 (INFO) Kausalität ohne Brücken (Gap)
**Wenn:** Pfad springt von Ereignis direkt zu Entscheidung, aber intermediäre Knoten fehlen (heuristisch)
**Dann:** Finding `missing_bridge_node`
**Fix:** Vorschlag „Create open_question bridge“ (z.B. „Was war der konkrete Auslöser…?“)
---
## D. Node-Type Regeln
### L9 (ERROR) Causal edges from open_question/hypothesis/white_spot
**Wenn:** node.type in `{open_question, hypothesis, white_spot}` und Edge type in `{caused_by, resulted_in, impacts, derived_from}`
**Dann:** Finding `invalid_causal_edge_from_uncertain`
**Fix:** Replace with `related_to` oder remove edge
### L10 (WARN) Hub/Insight Note trägt Kausalität
**Wenn:** node.type == `insight` (oder Hub-Pattern) und hat `caused_by/resulted_in`
**Dann:** Finding `hub_has_causality`
**Fix:** Replace edges with `related_to` und verschiebe Kausalität in atomare Notes
### L11 (WARN) Principle uses caused_by for origin
**Wenn:** node.type == `principle` und enthält `caused_by` zu Erlebnissen
**Dann:** Finding `principle_origin_edge`
**Fix:** Vorschlag `derived_from` oder `based_on`
### L12 (INFO) Decision without caused_by
**Wenn:** node.type == `decision` und hat keine `caused_by`-Kanten
**Dann:** Finding `decision_without_causes`
**Fix:** Wizard: „Was war der Auslöser?“ → create open_question or add edges
---
## E. Redundanz & Zyklen
### L13 (WARN) Duplicate Edges
**Wenn:** identische canonical edge mehrfach gesetzt (src,type,dst)
**Dann:** Finding `duplicate_edge`
**Fix:** remove duplicates
### L14 (WARN) Cycles in pure causality subgraph
**Wenn:** Zyklus ausschließlich über `{caused_by,resulted_in,derived_from,source_of}`
**Dann:** Finding `causal_cycle`
**Fix:** Markiere zur Review; oft ist eine Kante falsch gerichtet
---
## F. Traversal-Optimierung (Retrieval-Wirkung)
### L15 (INFO) Overuse of related_to
**Wenn:** Note enthält nur `related_to` und keine spezifischeren Kanten
**Dann:** Finding `weak_semantics`
**Fix:** Vorschlag: präzisere Beziehungstypen setzen
### L16 (INFO) Missing type metadata
**Wenn:** Frontmatter `type` fehlt
**Dann:** Finding `missing_type`
**Fix:** Prompt user to choose type; set via template
### L17 (INFO) Missing date on experience
**Wenn:** node.type == `experience` und kein Datum/Zeitraum vorhanden
**Dann:** Finding `missing_date_experience`
**Fix:** Prompt: „Wann ungefähr?“ → set `date` oder `time_range`
---
## Suggested Implementation Notes
- Jede Regel liefert:
- `ruleId`, `severity`, `message`, `location`, `evidence`, `quickFixes[]`
- QuickFixes sind Patch-Operationen:
- replaceText(range, text)
- insertBlock(atLine, block)
- createFile(path, content)
- renameFile(old, new) + updateLinks(glob)

6
package-lock.json generated
View File

@ -1,6 +0,0 @@
{
"name": "mindnet",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

View File

@ -1,69 +0,0 @@
#!/usr/bin/env python3
"""
Script zur Verifikation der EdgeDTO-Import-Version in Prod.
Prüft, ob die korrekte Version des EdgeDTO-Modells geladen wird.
"""
import sys
import os
# Stelle sicher, dass der Projekt-Pfad im Python-Path ist
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try:
from app.models.dto import EdgeDTO
import inspect
# Extrahiere die Literal-Definition aus dem Source-Code
source = inspect.getsource(EdgeDTO)
# Prüfe, ob explicit:callout in der Literal-Liste ist
if "explicit:callout" in source:
print("✅ EdgeDTO unterstützt 'explicit:callout'")
print(f" -> Modul-Pfad: {EdgeDTO.__module__}")
print(f" -> Datei: {inspect.getfile(EdgeDTO)}")
# Zeige die Provenance-Definition
import re
match = re.search(r'provenance.*?Literal\[(.*?)\]', source, re.DOTALL)
if match:
literal_values = match.group(1)
if "explicit:callout" in literal_values:
print("'explicit:callout' ist in der Literal-Liste enthalten")
print(f"\n Literal-Werte (erste 200 Zeichen):\n {literal_values[:200]}...")
else:
print("'explicit:callout' ist NICHT in der Literal-Liste!")
print(f"\n Gefundene Literal-Werte:\n {literal_values}")
else:
print("⚠️ Konnte Literal-Definition nicht finden")
else:
print("❌ EdgeDTO unterstützt NICHT 'explicit:callout'")
print(f" -> Modul-Pfad: {EdgeDTO.__module__}")
print(f" -> Datei: {inspect.getfile(EdgeDTO)}")
print("\n Source-Code (erste 500 Zeichen):")
print(f" {source[:500]}...")
# Test: Versuche ein EdgeDTO mit explicit:callout zu erstellen
print("\n🧪 Test: Erstelle EdgeDTO mit provenance='explicit:callout'...")
try:
test_edge = EdgeDTO(
id="test",
kind="test",
source="test",
target="test",
weight=1.0,
provenance="explicit:callout"
)
print("✅ EdgeDTO mit 'explicit:callout' erfolgreich erstellt!")
print(f" -> Provenance: {test_edge.provenance}")
except Exception as e:
print(f"❌ Fehler beim Erstellen: {e}")
print(f" -> Typ: {type(e).__name__}")
except ImportError as e:
print(f"❌ Import-Fehler: {e}")
sys.exit(1)
except Exception as e:
print(f"❌ Unerwarteter Fehler: {e}")
import traceback
traceback.print_exc()
sys.exit(1)

View File

@ -1,114 +0,0 @@
#!/usr/bin/env python3
"""
Script zur Verifikation des .env-Ladens in Prod.
Prüft, ob die .env-Datei korrekt geladen wird und welche Werte tatsächlich verwendet werden.
"""
import os
import sys
from pathlib import Path
# Stelle sicher, dass der Projekt-Pfad im Python-Path ist
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
print("=" * 80)
print("🔍 .env-Lade-Verifikation")
print("=" * 80)
# 1. Prüfe, ob .env-Datei existiert
env_files = [
project_root / ".env",
project_root / "prod.env",
project_root / "config" / "prod.env",
Path.cwd() / ".env",
Path.cwd() / "prod.env",
]
print("\n1. Suche nach .env-Dateien:")
found_env = None
for env_file in env_files:
if env_file.exists():
print(f" ✅ Gefunden: {env_file}")
if found_env is None:
found_env = env_file
else:
print(f" ❌ Nicht gefunden: {env_file}")
if not found_env:
print("\n ⚠️ WARNUNG: Keine .env-Datei gefunden!")
print(" -> load_dotenv() wird Standard-Werte verwenden")
# 2. Lade .env manuell
print("\n2. Lade .env-Datei:")
from dotenv import load_dotenv
if found_env:
result = load_dotenv(found_env, override=True)
print(f" ✅ load_dotenv('{found_env}') = {result}")
else:
result = load_dotenv(override=True)
print(f" ⚠️ load_dotenv() ohne expliziten Pfad = {result}")
print(" -> Sucht automatisch nach .env im aktuellen Verzeichnis")
# 3. Prüfe kritische Umgebungsvariablen
print("\n3. Kritische Umgebungsvariablen:")
critical_vars = [
"COLLECTION_PREFIX",
"MINDNET_PREFIX",
"DEBUG",
"VECTOR_DIM",
"MINDNET_EMBEDDING_MODEL",
"QDRANT_URL",
]
for var in critical_vars:
value = os.getenv(var, "NICHT GESETZT")
source = "Umgebung" if var in os.environ else "Default/Code"
print(f" {var:30} = {value:40} ({source})")
# 4. Prüfe, welche .env-Datei tatsächlich geladen wurde
print("\n4. Verifikation der geladenen Werte:")
print(f" Arbeitsverzeichnis: {Path.cwd()}")
print(f" Projekt-Root: {project_root}")
print(f" Python-Pfad[0]: {sys.path[0] if sys.path else 'N/A'}")
# 5. Test: Importiere Settings
print("\n5. Test: Importiere Settings aus app.config:")
try:
from app.config import get_settings
settings = get_settings()
print(f" ✅ Settings erfolgreich geladen")
print(f" -> COLLECTION_PREFIX: {settings.COLLECTION_PREFIX}")
print(f" -> VECTOR_SIZE: {settings.VECTOR_SIZE}")
print(f" -> EMBEDDING_MODEL: {settings.EMBEDDING_MODEL}")
except Exception as e:
print(f" ❌ Fehler beim Laden der Settings: {e}")
import traceback
traceback.print_exc()
# 6. Test: Prüfe EdgeDTO
print("\n6. Test: Prüfe EdgeDTO-Import:")
try:
from app.models.dto import EdgeDTO
import inspect
source = inspect.getsource(EdgeDTO)
if "explicit:callout" in source:
print(" ✅ EdgeDTO unterstützt 'explicit:callout'")
print(f" -> Modul-Pfad: {EdgeDTO.__module__}")
print(f" -> Datei: {inspect.getfile(EdgeDTO)}")
else:
print(" ❌ EdgeDTO unterstützt NICHT 'explicit:callout'")
# Test-Erstellung
test_edge = EdgeDTO(
id="test", kind="test", source="test", target="test",
weight=1.0, provenance="explicit:callout"
)
print(" ✅ EdgeDTO mit 'explicit:callout' erfolgreich erstellt!")
except Exception as e:
print(f" ❌ Fehler: {e}")
import traceback
traceback.print_exc()
print("\n" + "=" * 80)

View File

@ -1,160 +0,0 @@
#!/usr/bin/env python3
"""
Script zur Laufzeit-Verifikation des laufenden Mindnet-Services.
Prüft, ob der Service die korrekte EdgeDTO-Version verwendet und ob die .env korrekt geladen wurde.
"""
import sys
import requests
import json
from pathlib import Path
# Stelle sicher, dass der Projekt-Pfad im Python-Path ist
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
print("=" * 80)
print("🔍 Laufzeit-Verifikation des Mindnet-Services")
print("=" * 80)
# 1. Prüfe Health-Endpoint
print("\n1. Prüfe Service-Status (Health-Check):")
try:
response = requests.get("http://localhost:8001/healthz", timeout=5)
if response.status_code == 200:
health_data = response.json()
print(f" ✅ Service läuft")
print(f" -> Status: {health_data.get('status')}")
print(f" -> Version: {health_data.get('version')}")
print(f" -> Prefix: {health_data.get('prefix')}")
print(f" -> Qdrant: {health_data.get('qdrant')}")
# WP-24c v4.5.10: Prüfe EdgeDTO-Version im laufenden Service
edge_dto_supports = health_data.get('edge_dto_supports_callout')
if edge_dto_supports is not None:
if edge_dto_supports:
print(f" ✅ Service unterstützt 'explicit:callout' (zur Laufzeit verifiziert)")
else:
print(f" ❌ Service unterstützt NICHT 'explicit:callout' (alte Version im Speicher!)")
print(f" -> Aktion erforderlich: Cache leeren und Service neu starten")
else:
print(f" ⚠️ Health-Check meldet keine EdgeDTO-Version (alte API-Version?)")
else:
print(f" ⚠️ Service antwortet mit Status {response.status_code}")
print(f" -> Response: {response.text[:200]}")
except requests.exceptions.ConnectionError:
print(" ❌ Service nicht erreichbar (läuft er auf Port 8001?)")
print(" -> Tipp: sudo systemctl status mindnet-prod")
sys.exit(1)
except Exception as e:
print(f" ❌ Fehler beim Health-Check: {e}")
sys.exit(1)
# 2. Test: Versuche eine Test-Query mit explicit:callout Edge
print("\n2. Test: Retrieval mit explicit:callout Edge:")
print(" -> Sende Test-Query an /chat/...")
print(" -> Hinweis: Timeout nach 30s ist möglich bei LLM-Calls")
try:
test_query = {
"message": "Test query für EdgeDTO-Verifikation",
"explain": False
}
# WP-24c: Router ist mit prefix="/chat" eingebunden, Endpoint ist "/"
# Erhöhter Timeout für LLM-Calls (können länger dauern)
response = requests.post(
"http://localhost:8001/chat/",
json=test_query,
timeout=60 # Erhöht von 30 auf 60 Sekunden
)
if response.status_code == 200:
result = response.json()
print(f" ✅ Query erfolgreich verarbeitet")
print(f" -> Antwort-Länge: {len(result.get('response', ''))} Zeichen")
# Prüfe Logs auf EdgeDTO-Fehler (wenn verfügbar)
if 'warnings' in result or 'errors' in result:
warnings = result.get('warnings', [])
errors = result.get('errors', [])
if warnings:
print(f" ⚠️ Warnings: {warnings}")
if errors:
print(f" ❌ Errors: {errors}")
else:
print(f" ⚠️ Query fehlgeschlagen mit Status {response.status_code}")
print(f" -> Response: {response.text[:500]}")
except requests.exceptions.ReadTimeout:
print(f" ⚠️ Query-Timeout (Service antwortet nicht innerhalb von 60s)")
print(f" -> Mögliche Ursachen:")
print(f" - LLM-Call dauert länger als erwartet")
print(f" - Service hängt bei der Verarbeitung")
print(f" -> Tipp: Prüfe Service-Logs mit: sudo journalctl -u mindnet-prod -n 50")
print(f" -> WICHTIG: EdgeDTO-Problem ist gelöst (siehe Punkt 1)")
except Exception as e:
print(f" ⚠️ Fehler bei Test-Query: {e}")
print(f" -> WICHTIG: EdgeDTO-Problem ist gelöst (siehe Punkt 1)")
# Kein vollständiger Traceback für Timeouts, da diese erwartbar sind
# 3. Direkte Code-Verifikation (falls Service-Code zugänglich)
print("\n3. Code-Verifikation (lokale Dateien):")
try:
from app.models.dto import EdgeDTO
import inspect
source = inspect.getsource(EdgeDTO)
if "explicit:callout" in source:
print(" ✅ Lokaler Code unterstützt 'explicit:callout'")
print(f" -> Datei: {inspect.getfile(EdgeDTO)}")
# Prüfe, ob die Datei aktuell ist
dto_file = Path(inspect.getfile(EdgeDTO))
if dto_file.exists():
import time
mtime = dto_file.stat().st_mtime
mtime_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mtime))
print(f" -> Letzte Änderung: {mtime_str}")
else:
print(" ❌ Lokaler Code unterstützt NICHT 'explicit:callout'")
# Test-Erstellung
test_edge = EdgeDTO(
id="test", kind="test", source="test", target="test",
weight=1.0, provenance="explicit:callout"
)
print(" ✅ EdgeDTO mit 'explicit:callout' erfolgreich erstellt!")
except Exception as e:
print(f" ❌ Fehler bei Code-Verifikation: {e}")
import traceback
traceback.print_exc()
# 4. Prüfe Python-Cache
print("\n4. Prüfe Python-Cache:")
try:
dto_cache_dir = project_root / "app" / "models" / "__pycache__"
if dto_cache_dir.exists():
cache_files = list(dto_cache_dir.glob("dto*.pyc"))
if cache_files:
print(f" ⚠️ Gefunden: {len(cache_files)} Cache-Datei(en) in app/models/__pycache__")
print(f" -> Tipp: Cache leeren mit: find . -type d -name __pycache__ -exec rm -r {{}} +")
else:
print(" ✅ Keine Cache-Dateien gefunden")
else:
print(" ✅ Kein Cache-Verzeichnis vorhanden")
except Exception as e:
print(f" ⚠️ Fehler bei Cache-Prüfung: {e}")
# 5. Empfehlungen
print("\n5. Empfehlungen:")
print(" 📋 Wenn der Service noch eine alte Version verwendet:")
print(" 1. Python-Cache leeren:")
print(" find . -type d -name __pycache__ -exec rm -r {} + 2>/dev/null || true")
print(" find . -name '*.pyc' -delete")
print(" 2. Service neu starten:")
print(" sudo systemctl restart mindnet-prod")
print(" 3. Status prüfen:")
print(" sudo systemctl status mindnet-prod")
print(" 4. Logs prüfen:")
print(" sudo journalctl -u mindnet-prod -f")
print("\n" + "=" * 80)