2025-12-28 10:40:28 +01:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
|
|
|
|
|
FILE: scripts/debug_edge_loss.py
|
|
|
|
|
|
VERSION: 2.1.0 (2025-12-15)
|
|
|
|
|
|
STATUS: Active (Debug-Tool)
|
|
|
|
|
|
COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b)
|
|
|
|
|
|
|
|
|
|
|
|
Zweck:
|
|
|
|
|
|
-------
|
|
|
|
|
|
Debug-Tool zur Analyse von Edge-Verlust-Problemen bei der Chunk-Verarbeitung.
|
|
|
|
|
|
Hilft zu verstehen, warum bestimmte Edges nicht erkannt oder verloren gehen.
|
|
|
|
|
|
|
|
|
|
|
|
Funktionsweise:
|
|
|
|
|
|
---------------
|
|
|
|
|
|
1. Analysiert eine einzelne Markdown-Datei
|
|
|
|
|
|
2. Führt zwei Analyseschritte durch:
|
|
|
|
|
|
a) Pre-Scan: Extrahiert alle Edge-Kandidaten aus dem rohen Text
|
|
|
|
|
|
- Wikilinks
|
|
|
|
|
|
- Typed Relations
|
|
|
|
|
|
- Callout Relations
|
|
|
|
|
|
b) Chunk-Analyse:
|
|
|
|
|
|
- Erstellt Chunks mit konfigurierbarem Profil
|
|
|
|
|
|
- Prüft, welche Edges in jedem Chunk gefunden werden
|
|
|
|
|
|
- Vergleicht mit Pre-Scan-Ergebnissen
|
|
|
|
|
|
3. Gibt detaillierte Analyse pro Chunk aus
|
|
|
|
|
|
|
|
|
|
|
|
Ergebnis-Interpretation:
|
|
|
|
|
|
------------------------
|
|
|
|
|
|
- [1] Globale Kandidaten: Alle Edge-Kandidaten, die im Text vorhanden sind
|
|
|
|
|
|
- [2] Chunk-Struktur: Für jeden Chunk:
|
|
|
|
|
|
* Section-Pfad
|
|
|
|
|
|
* Text-Snippet
|
|
|
|
|
|
* Gefundene explizite Kanten (callout:edge, inline:rel)
|
|
|
|
|
|
* Warnung, falls Callout im Text vorhanden, aber nicht erkannt
|
|
|
|
|
|
|
|
|
|
|
|
Verwendung:
|
|
|
|
|
|
-----------
|
|
|
|
|
|
- Debugging von Edge-Verlust bei Chunk-Grenzen
|
|
|
|
|
|
- Validierung der Edge-Extraktion in verschiedenen Chunk-Kontexten
|
|
|
|
|
|
- Analyse von Problemen mit Callout-Erkennung
|
|
|
|
|
|
|
|
|
|
|
|
Hinweise:
|
|
|
|
|
|
---------
|
|
|
|
|
|
- Asynchrones Skript (nutzt assemble_chunks async API)
|
|
|
|
|
|
- Konfigurierbares Chunking-Profil (Standard: sliding_window, 400/600 chars)
|
|
|
|
|
|
- Smart Edge Allocation ist standardmäßig deaktiviert für klare Analyse
|
|
|
|
|
|
|
|
|
|
|
|
Aufruf:
|
|
|
|
|
|
-------
|
|
|
|
|
|
python3 scripts/debug_edge_loss.py [PFAD_ZUR_DATEI.md]
|
|
|
|
|
|
|
|
|
|
|
|
Parameter:
|
|
|
|
|
|
----------
|
|
|
|
|
|
Keine CLI-Parameter. Dateipfad als erstes Argument oder Standard-Pfad.
|
|
|
|
|
|
|
|
|
|
|
|
Änderungen:
|
|
|
|
|
|
-----------
|
|
|
|
|
|
v2.1.0 (2025-12-15): Kompatibilität mit WP-14 Modularisierung
|
|
|
|
|
|
- Entfernt: _extract_all_edges_from_md (nicht mehr vorhanden)
|
|
|
|
|
|
- Ersetzt durch: extract_wikilinks, extract_typed_relations, extract_callout_relations
|
|
|
|
|
|
- Chunk-Payload-Struktur angepasst (window, type hinzugefügt)
|
|
|
|
|
|
v1.0.0: Erster Release
|
|
|
|
|
|
"""
|
2025-12-22 05:56:36 +01:00
|
|
|
|
import asyncio
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
# Pfad-Setup
|
|
|
|
|
|
sys.path.insert(0, os.path.abspath("."))
|
|
|
|
|
|
|
2025-12-28 10:40:28 +01:00
|
|
|
|
from app.core.chunking import assemble_chunks
|
2025-12-22 05:56:36 +01:00
|
|
|
|
from app.core.derive_edges import build_edges_for_note
|
2025-12-28 10:40:28 +01:00
|
|
|
|
from app.core.parser import extract_wikilinks
|
|
|
|
|
|
from app.core.graph.graph_extractors import extract_typed_relations, extract_callout_relations
|
2025-12-22 05:56:36 +01:00
|
|
|
|
|
|
|
|
|
|
# Mock für Settings, falls nötig
|
|
|
|
|
|
os.environ["MINDNET_LLM_MODEL"] = "phi3:mini"
|
|
|
|
|
|
|
|
|
|
|
|
async def analyze_file(file_path: str):
|
|
|
|
|
|
print(f"\n=== ANALYSE: {file_path} ===")
|
|
|
|
|
|
|
|
|
|
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
|
|
|
|
text = f.read()
|
|
|
|
|
|
|
|
|
|
|
|
# 1. Globale Kandidaten (Was sieht der Pre-Scan?)
|
2025-12-28 10:40:28 +01:00
|
|
|
|
# Wir extrahieren alle Edge-Kandidaten aus dem Text
|
2025-12-22 05:56:36 +01:00
|
|
|
|
note_id = Path(file_path).stem
|
2025-12-28 10:40:28 +01:00
|
|
|
|
candidates = []
|
|
|
|
|
|
|
|
|
|
|
|
# Wikilinks extrahieren
|
|
|
|
|
|
wikilinks = extract_wikilinks(text)
|
|
|
|
|
|
for wl in wikilinks:
|
|
|
|
|
|
candidates.append(f"wikilink:{wl}")
|
|
|
|
|
|
|
|
|
|
|
|
# Typed Relations extrahieren
|
|
|
|
|
|
typed_rels, _ = extract_typed_relations(text)
|
|
|
|
|
|
for kind, target in typed_rels:
|
|
|
|
|
|
candidates.append(f"typed:{kind}:{target}")
|
|
|
|
|
|
|
|
|
|
|
|
# Callout Relations extrahieren
|
|
|
|
|
|
callout_rels, _ = extract_callout_relations(text)
|
|
|
|
|
|
for kind, target in callout_rels:
|
|
|
|
|
|
candidates.append(f"callout:{kind}:{target}")
|
|
|
|
|
|
|
2025-12-22 05:56:36 +01:00
|
|
|
|
print(f"\n[1] Globale Kandidaten (Pre-Scan):")
|
|
|
|
|
|
for c in candidates:
|
|
|
|
|
|
print(f" - {c}")
|
|
|
|
|
|
|
|
|
|
|
|
# 2. Chunking (Ohne Smart Edges erstmal, um die physische Integrität zu prüfen)
|
|
|
|
|
|
# Wir nutzen ein Profil, das dem User-Setup entspricht
|
|
|
|
|
|
config = {
|
|
|
|
|
|
"strategy": "sliding_window",
|
|
|
|
|
|
"target": 400,
|
|
|
|
|
|
"max": 600,
|
|
|
|
|
|
"overlap": 50,
|
|
|
|
|
|
"enable_smart_edge_allocation": False # Erstmal aus
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
chunks = await assemble_chunks(note_id, text, "concept", config=config)
|
|
|
|
|
|
|
|
|
|
|
|
print(f"\n[2] Chunk-Struktur & Physische Kanten:")
|
|
|
|
|
|
for i, chunk in enumerate(chunks):
|
|
|
|
|
|
print(f"\n--- Chunk {i} (Section: {chunk.section_path}) ---")
|
|
|
|
|
|
print(f"Snippet: {chunk.text[:50]}...")
|
|
|
|
|
|
|
|
|
|
|
|
# Was findet derive_edges in diesem rohen Chunk?
|
|
|
|
|
|
# Wir simulieren das Payload-Dict, das derive_edges erwartet
|
2025-12-28 10:40:28 +01:00
|
|
|
|
chunk_pl = {
|
|
|
|
|
|
"text": chunk.text,
|
|
|
|
|
|
"window": chunk.window or chunk.text,
|
|
|
|
|
|
"chunk_id": chunk.id,
|
|
|
|
|
|
"type": "concept"
|
|
|
|
|
|
}
|
2025-12-22 05:56:36 +01:00
|
|
|
|
edges = build_edges_for_note(note_id, [chunk_pl])
|
|
|
|
|
|
|
|
|
|
|
|
found_explicitly = [f"{e['kind']}:{e.get('target_id')}" for e in edges if e['rule_id'] in ['callout:edge', 'inline:rel']]
|
|
|
|
|
|
|
|
|
|
|
|
if found_explicitly:
|
|
|
|
|
|
print(f" ✅ Gefundene Explizite Kanten: {found_explicitly}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f" ❌ Keine expliziten Kanten gefunden (trotz Callout im Text?)")
|
|
|
|
|
|
|
|
|
|
|
|
# Check auf Callout im Text
|
|
|
|
|
|
if "> [!edge]" in chunk.text:
|
|
|
|
|
|
print(" ℹ️ HINWEIS: '> [!edge]' String ist im Text vorhanden!")
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
# Pfad zu deiner problematischen Datei hier anpassen!
|
|
|
|
|
|
target_file = "./vault_master/Dein_Problematisches_File.md"
|
|
|
|
|
|
|
|
|
|
|
|
if len(sys.argv) > 1:
|
|
|
|
|
|
target_file = sys.argv[1]
|
|
|
|
|
|
|
|
|
|
|
|
asyncio.run(analyze_file(target_file))
|