Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d61a9e191 | |||
| 55d1a7e290 | |||
| 4537e65428 | |||
| 43327c1f6d | |||
| 39a6998123 | |||
| 273c4c6919 | |||
| 2ed4488cf6 | |||
| 36490425c5 | |||
| b8cb8bb89b | |||
| 6d268d9dfb | |||
| df5f9b3fe4 | |||
| 5e67cd470c | |||
| 0b2a1f1a63 |
|
|
@ -15,13 +15,44 @@ 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.
|
||||||
load_dotenv(override=True)
|
# WP-24c v4.5.10: Expliziter Pfad für .env-Datei, um Probleme mit Arbeitsverzeichnis zu vermeiden
|
||||||
|
# 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")
|
||||||
COLLECTION_PREFIX: str = os.getenv("MINDNET_PREFIX", "mindnet_dev")
|
# WP-24c v4.5.10: Harmonisierung - Unterstützt beide Umgebungsvariablen für Abwärtskompatibilität
|
||||||
|
# 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"))
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@ 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
|
||||||
prefix = os.getenv("COLLECTION_PREFIX") or "mindnet"
|
# WP-24c v4.5.10: Harmonisierung - Unterstützt beide Umgebungsvariablen für Abwärtskompatibilität
|
||||||
|
# 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")
|
||||||
|
|
|
||||||
|
|
@ -910,9 +910,20 @@ 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.9: Deterministische Sortierung der semantic_groups für konsistente Edge-Extraktion
|
# WP-24c v4.5.10: 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)
|
||||||
sorted_semantic_keys = sorted(semantic_groups.keys())
|
# WICHTIG: target_section kann None sein, daher benötigen wir eine benutzerdefinierte Sortierfunktion
|
||||||
|
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]
|
||||||
|
|
|
||||||
|
|
@ -216,17 +216,42 @@ def _build_explanation(
|
||||||
|
|
||||||
direction = "in" if tgt == target_note_id else "out"
|
direction = "in" if tgt == target_note_id else "out"
|
||||||
|
|
||||||
edge_obj = EdgeDTO(
|
# WP-24c v4.5.10: Robuste EdgeDTO-Erstellung mit Fehlerbehandlung
|
||||||
id=f"{src}->{tgt}:{kind}",
|
# Falls Provenance-Wert nicht unterstützt wird, verwende Fallback
|
||||||
kind=kind,
|
try:
|
||||||
source=src,
|
edge_obj = EdgeDTO(
|
||||||
target=tgt,
|
id=f"{src}->{tgt}:{kind}",
|
||||||
weight=conf,
|
kind=kind,
|
||||||
direction=direction,
|
source=src,
|
||||||
provenance=prov,
|
target=tgt,
|
||||||
confidence=conf
|
weight=conf,
|
||||||
)
|
direction=direction,
|
||||||
edges_dto.append(edge_obj)
|
provenance=prov,
|
||||||
|
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)
|
||||||
|
|
|
||||||
13
app/main.py
13
app/main.py
|
|
@ -109,12 +109,23 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,14 @@ streams_library:
|
||||||
top_k: 5
|
top_k: 5
|
||||||
edge_boosts:
|
edge_boosts:
|
||||||
guides: 3.0
|
guides: 3.0
|
||||||
enforced_by: 2.5
|
depends_on: 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"
|
||||||
|
|
@ -60,6 +65,8 @@ 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"
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ llm_settings:
|
||||||
types:
|
types:
|
||||||
|
|
||||||
experience:
|
experience:
|
||||||
chunking_profile: sliding_smart_edges
|
chunking_profile: structured_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: sliding_smart_edges
|
chunking_profile: structured_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: sliding_smart_edges
|
chunking_profile: structured_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: sliding_smart_edges
|
chunking_profile: structured_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: sliding_smart_edges
|
chunking_profile: structured_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,14 +244,15 @@ types:
|
||||||
schema: ["Aktueller Zustand", "Auslöser", "Auswirkung auf den Tag"]
|
schema: ["Aktueller Zustand", "Auslöser", "Auswirkung auf den Tag"]
|
||||||
|
|
||||||
boundary:
|
boundary:
|
||||||
chunking_profile: sliding_smart_edges
|
chunking_profile: structured_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: sliding_smart_edges
|
chunking_profile: structured_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:
|
||||||
|
|
@ -261,41 +262,49 @@ types:
|
||||||
schema: ["Beschreibung des Risikos", "Auswirkungen", "Gegenmaßnahmen"]
|
schema: ["Beschreibung des Risikos", "Auswirkungen", "Gegenmaßnahmen"]
|
||||||
|
|
||||||
concept:
|
concept:
|
||||||
chunking_profile: sliding_smart_edges
|
chunking_profile: structured_smart_edges
|
||||||
retriever_weight: 0.60
|
retriever_weight: 0.6
|
||||||
|
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.80
|
retriever_weight: 0.8
|
||||||
|
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.80
|
retriever_weight: 0.8
|
||||||
|
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.50
|
retriever_weight: 0.5
|
||||||
|
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.40
|
retriever_weight: 0.4
|
||||||
|
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.50
|
retriever_weight: 0.5
|
||||||
schema: ["Rolle", "Beziehung", "Kontext"]
|
detection_keywords: ["person", "mensch", "kontakt", "name", "beziehung", "stakeholder"]
|
||||||
|
schema: ["Profile", "Beziehung", "Kontext"]
|
||||||
|
|
||||||
event:
|
event:
|
||||||
chunking_profile: sliding_standard
|
chunking_profile: sliding_standard
|
||||||
retriever_weight: 0.60
|
retriever_weight: 0.6
|
||||||
|
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.00
|
retriever_weight: 1.0
|
||||||
|
detection_keywords: []
|
||||||
schema: ["Inhalt"]
|
schema: ["Inhalt"]
|
||||||
1
debug.log
Normal file
1
debug.log
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[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)
|
||||||
394
docs/02_concepts/02_causal_chain_retrieving.md
Normal file
394
docs/02_concepts/02_causal_chain_retrieving.md
Normal file
|
|
@ -0,0 +1,394 @@
|
||||||
|
# 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
|
||||||
223
docs/02_concepts/02_concept_kausales_retrieval.md
Normal file
223
docs/02_concepts/02_concept_kausales_retrieval.md
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
<!-- 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 (2–4 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 1–3 Hops (typisch 2–4)
|
||||||
|
- 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. 8–12 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: 2–4
|
||||||
|
- **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: 2–4
|
||||||
|
- **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: 2–4
|
||||||
|
- 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. 3–5) mergen
|
||||||
|
- max_nodes: 8–12
|
||||||
|
- 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 (8–12 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)
|
||||||
|
|
||||||
105
docs/03_Technical_References/ENV_LOADING_DEBUG.md
Normal file
105
docs/03_Technical_References/ENV_LOADING_DEBUG.md
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
# 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}')"`
|
||||||
134
docs/03_Technical_References/PROD_DEPLOYMENT_CHECKLIST.md
Normal file
134
docs/03_Technical_References/PROD_DEPLOYMENT_CHECKLIST.md
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
# 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
|
||||||
134
docs/03_Technical_References/PROD_PYTHON_CACHE_FIX.md
Normal file
134
docs/03_Technical_References/PROD_PYTHON_CACHE_FIX.md
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
# 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!)
|
||||||
163
docs/03_Technical_References/RETRIEVAL_DEV_PROD_DIFF_ANALYSIS.md
Normal file
163
docs/03_Technical_References/RETRIEVAL_DEV_PROD_DIFF_ANALYSIS.md
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
# 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
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
<!-- 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 (1–4 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.
|
||||||
136
docs/05_Development/Obsidian/lint_regeln_Kausalität.md
Normal file
136
docs/05_Development/Obsidian/lint_regeln_Kausalität.md
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
<!-- 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
Normal file
6
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "mindnet",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
69
scripts/verify_dto_import.py
Normal file
69
scripts/verify_dto_import.py
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/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)
|
||||||
114
scripts/verify_env_loading.py
Normal file
114
scripts/verify_env_loading.py
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
#!/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)
|
||||||
160
scripts/verify_runtime_service.py
Normal file
160
scripts/verify_runtime_service.py
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
#!/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)
|
||||||
Loading…
Reference in New Issue
Block a user