mindnet_obsidian/docs/02_concepts/03_chain_identification_and_matching.md

422 lines
13 KiB
Markdown
Raw Permalink Normal View History

# Chain-Identifikation und Template Matching
> **Stand:** Januar 2026
> **Siehe auch:** [modules/analysis](../modules/analysis.md), [06_Konfigurationsdateien_Referenz](../06_Konfigurationsdateien_Referenz.md)
---
## Übersicht
Das System identifiziert und füllt Chains durch einen mehrstufigen Prozess:
1. **Graph-Indexierung**: Erfassung aller Edges (Kanten) im lokalen Subgraph
2. **Candidate-Node-Sammlung**: Identifikation potenzieller Knoten für Template-Slots
3. **Template Matching**: Backtracking-Algorithmus zur optimalen Slot-Zuordnung
4. **Link-Validierung**: Prüfung, ob Edges zwischen zugewiesenen Slots existieren
5. **Scoring & Confidence**: Bewertung der Match-Qualität
---
## 1. Graph-Indexierung
### Edge-Erfassung
Der **Chain Inspector** (`src/analysis/chainInspector.ts`) erfasst alle Edges im lokalen Subgraph:
- **Current Section**: Die aktuelle Section (file + heading)
- **Incoming Edges**: Edges, die zur aktuellen Section zeigen
- **Outgoing Edges**: Edges, die von der aktuellen Section ausgehen
- **Neighbor Notes**: Verbundene Notes werden geladen, um den Subgraph zu erweitern
### Edge-Scopes
Edges können drei Scopes haben:
- **`section`**: Section-spezifisch (`[[Note#Heading]]`)
- **`note`**: Note-weit (`[[Note]]` ohne Heading)
- **`candidate`**: In "## Kandidaten" Zone
### Filterung
Edges werden basierend auf `InspectorOptions` gefiltert:
- `includeNoteLinks`: Ob Note-Scope Edges einbezogen werden
- `includeCandidates`: Ob Candidate Edges einbezogen werden
- `maxDepth`: Maximale Tiefe für Path-Traversal
---
## 2. Candidate-Node-Sammlung
### Prozess (`buildCandidateNodes`)
Für jeden Edge im Subgraph werden **Candidate Nodes** erstellt:
1. **Source Nodes**: Alle Knoten, von denen Edges ausgehen
2. **Target Nodes**: Alle Knoten, zu denen Edges zeigen
3. **Note-Type-Extraktion**: Frontmatter `type:` wird aus jeder Note extrahiert
4. **Deduplizierung**: Gleiche Knoten (file + heading) werden nur einmal erfasst
### Node-Repräsentation
```typescript
interface CandidateNode {
nodeKey: {
file: string;
heading: string | null;
};
noteType: string; // z.B. "insight", "decision", "experience"
}
```
### Maximale Anzahl
Standardmäßig werden maximal **30 Candidate Nodes** gesammelt (konfigurierbar).
---
## 3. Template Matching
### Template-Definition (`chain_templates.yaml`)
Jedes Template definiert:
- **Slots**: Positionen in der Chain mit erlaubten Note-Types
- **Links**: Erwartete Verbindungen zwischen Slots mit erlaubten Edge-Roles
**Beispiel: `loop_learning`**
```yaml
slots:
- id: experience
allowed_node_types: [experience, journal, event]
- id: learning
allowed_node_types: [insight, principle, value, belief, skill, trait]
- id: behavior
allowed_node_types: [habit, decision, task]
- id: feedback
allowed_node_types: [experience, journal, event, state]
links:
- from: experience
to: learning
allowed_edge_roles: [causal, provenance, influences]
- from: learning
to: behavior
allowed_edge_roles: [influences, enables_constraints, causal]
- from: behavior
to: feedback
allowed_edge_roles: [causal, influences]
```
### Backtracking-Algorithmus (`findBestAssignment`)
Der Algorithmus findet die **beste Slot-Zuordnung** durch systematisches Ausprobieren:
1. **Slot-Filterung**: Für jeden Slot werden nur Candidate Nodes gefiltert, die den `allowed_node_types` entsprechen
2. **Backtracking**: Rekursives Durchprobieren aller möglichen Zuordnungen
3. **Distinct Nodes**: Jeder Knoten kann nur einmal zugeordnet werden (wenn `distinct_nodes: true`)
4. **Scoring**: Jede vollständige Zuordnung wird bewertet
5. **Best Match**: Die Zuordnung mit dem höchsten Score wird zurückgegeben
### Slot-Zuordnung
```typescript
function backtrack(assignment: Map<string, CandidateNode>, slotIndex: number) {
// Wenn alle Slots zugeordnet sind:
if (slotIndex >= slots.length) {
const result = scoreAssignment(...); // Bewerte Zuordnung
if (result.score > bestScore) {
bestMatch = result; // Speichere besten Match
}
return;
}
// Probiere jeden passenden Candidate für diesen Slot
for (const candidate of slotCandidates.get(slot.id)) {
if (!alreadyAssigned(candidate)) {
assignment.set(slot.id, candidate);
backtrack(assignment, slotIndex + 1); // Rekursiv weiter
assignment.delete(slot.id); // Backtrack
}
}
// Auch: Slot leer lassen (für unvollständige Chains)
backtrack(assignment, slotIndex + 1);
}
```
---
## 4. Link-Validierung
### Edge-Suche (`findEdgeBetween`)
Für jedes Template-Link wird geprüft, ob ein Edge zwischen den zugewiesenen Slots existiert:
1. **Node-Keys**: `fromKey = "file:heading"`, `toKey = "file:heading"`
2. **Edge-Suche**: Durchsuche `allEdges` nach passendem Edge
3. **Canonicalisierung**: Edge-Typ wird auf Canonical gemappt (via `edge_vocabulary.md`)
4. **Role-Mapping**: Canonical Edge-Typ wird auf Role gemappt (via `chain_roles.yaml`)
5. **Role-Validierung**: Prüfe, ob Edge-Role in `allowed_edge_roles` enthalten ist
### Role-Evidence
Wenn ein Link erfüllt ist, wird **Role Evidence** gespeichert:
```typescript
roleEvidence.push({
from: "learning", // Slot-ID (nicht Dateipfad!)
to: "behavior", // Slot-ID
edgeRole: "causal", // Role aus chain_roles.yaml
rawEdgeType: "resulted_in" // Original Edge-Typ im Vault
});
```
**Wichtig**: `roleEvidence` verwendet **Slot-IDs**, nicht Dateipfade!
---
## 5. Scoring & Confidence
### Scoring (`scoreAssignment`)
**Implementierung:** `src/analysis/templateMatching.ts`
| Komponente | Punkte |
|------------|--------|
| Jeder zugewiesene Slot | +2 |
| Intra-Note-Bonus (alle Slots in derselben Note) | +5 |
| Erfüllter Link (mit erlaubter Edge-Role) | +10 |
| Fehlender Link (wenn `required_links: true`) | -5 |
| Falsche Role (wenn `required_links: true`) | -5 |
**Heading-Matching:** Vergleiche via `headingsMatch` (normalisiert `^BlockID`; siehe [12_Heading_Block_Link_Recommendation](../../12_Heading_Block_Link_Recommendation.md)).
### Confidence-Berechnung
Die **Confidence** wird nach dem Matching berechnet:
1. **`weak`**: Wenn `slotsComplete === false` (fehlende Slots)
2. **`confirmed`**: Wenn `slotsComplete === true` UND `linksComplete === true` UND mindestens eine causal-ish Role Evidence
3. **`plausible`**: Sonst (Slots vollständig, aber Links unvollständig oder keine causal Roles)
**Causal-ish Roles**: `["causal", "influences", "enables_constraints"]` (konfigurierbar in `chain_templates.yaml`)
### Completeness
- **`slotsComplete`**: `missingSlots.length === 0`
- **`linksComplete`**: `satisfiedLinks === requiredLinks`
---
## 6. Template-Matching-Profile
### Profile-Definition
Profile steuern die Matching-Strenge:
**`discovery`** (schreibfreundlich):
```yaml
required_links: false # Links sind optional
min_slots_filled_for_gap_findings: 2
min_score_for_gap_findings: 8
```
**`decisioning`** (strikt):
```yaml
required_links: true # Links sind Pflicht
min_slots_filled_for_gap_findings: 3
min_score_for_gap_findings: 18
```
**Profil-Auswahl:** Plugin-Setting `templateMatchingProfile` (`discovery` | `decisioning`). Profil-Definitionen in `chain_templates.yaml` unter `defaults.profiles`.
### Profile-Auflösung
1. **Settings**: Plugin-Einstellung `templateMatchingProfile`
2. **Template**: Template-spezifische `matching.required_links`
3. **Defaults**: `defaults.matching.required_links`
---
## 7. Beispiel: Loop Learning Match
### Input
- **Current Section**: `Tests/03_insight_transformation.md#Kern`
- **Edges im Subgraph**: 8 Edges (1 current, 7 neighbors)
- **Candidate Nodes**: 4 Nodes (experience, learning, behavior, feedback)
### Template
```yaml
name: loop_learning
slots: [experience, learning, behavior, feedback]
links:
- experience → learning
- learning → behavior
- behavior → feedback
```
### Matching-Prozess
1. **Slot-Zuordnung** (Backtracking):
- `experience` → `Tests/02_event_trigger_detail.md#Detail` (noteType: `event`)
- `learning` → `Tests/03_insight_transformation.md#Kern` (noteType: `insight`)
- `behavior` → `Tests/04_decision_outcome.md#Entscheidung` (noteType: `decision`)
- `feedback` → `Tests/01_experience_trigger.md#Kontext` (noteType: `experience`)
2. **Link-Validierung**:
- `experience → learning`: ❌ Kein Edge gefunden
- `learning → behavior`: ✅ Edge `resulted_in` mit Role `causal` gefunden
- `behavior → feedback`: ❌ Kein Edge gefunden
3. **Scoring**:
- Slots: 4 × 2 = **8 Punkte**
- Links: 1 × 10 = **10 Punkte**
- **Gesamt: 18 Punkte**
4. **Resultat**:
```json
{
"templateName": "loop_learning",
"score": 18,
"slotsComplete": true,
"linksComplete": false,
"satisfiedLinks": 1,
"requiredLinks": 3,
"confidence": "plausible",
"roleEvidence": [
{
"from": "learning",
"to": "behavior",
"edgeRole": "causal",
"rawEdgeType": "resulted_in"
}
]
}
```
---
## 8. Wichtige Konzepte
### Slot-IDs vs. Dateipfade
- **Slot-IDs**: Template-interne Bezeichner (`"learning"`, `"behavior"`)
- **Dateipfade**: Vault-Pfade (`"Tests/03_insight_transformation.md"`)
- **roleEvidence** verwendet Slot-IDs, nicht Dateipfade!
### Canonicalisierung
- **Intern**: Edge-Typen werden auf Canonical gemappt (für Analyse)
- **Vault**: Original Edge-Typen (Aliase) bleiben unverändert
- **Schreiben**: Plugin schreibt keine Canonicals, nur User-gewählte Typen
### Distinct Nodes
Wenn `distinct_nodes: true`:
- Jeder Knoten kann nur **einmal** pro Template zugeordnet werden
- Verhindert zirkuläre Zuordnungen
### Edge-Target-Resolution
Edges können verschiedene Pfad-Formate verwenden:
- Vollständig: `Tests/03_insight_transformation.md`
- Basename: `03_insight_transformation`
- Wikilink: `[[03_insight_transformation]]`
Das System normalisiert alle Formate für konsistente Matching.
---
## 9. Integration mit Chain Workbench
Der **Chain Workbench** nutzt die Template Matches, um:
1. **Todos zu generieren**:
- `missing_slot`: Fehlende Slot-Zuordnungen
- `missing_link`: Fehlende Links zwischen Slots
- `weak_roles`: Links mit schwachen (nicht-causalen) Roles
2. **Status zu berechnen**:
- `complete`: Slots + Links vollständig
- `near_complete`: 1-2 Links fehlen oder 1 Slot fehlt
- `partial`: Mehrere Lücken
- `weak`: Nur structural/temporal Roles, keine causal Roles
3. **Actions anzubieten**:
- `insert_edge_forward`: Edge einfügen (von Slot A nach Slot B)
- `link_existing`: Bestehenden Knoten verlinken
- `create_note_via_interview`: Neue Note für Slot erstellen
---
## 10. Konfigurationsdateien
### `chain_templates.yaml`
Definiert Template-Strukturen:
- Slots mit erlaubten Note-Types
- Links mit erlaubten Edge-Roles
- Profile (discovery, decisioning)
### `chain_roles.yaml`
Mappt Edge-Typen auf Roles:
- `causal`: Direkte Kausalität
- `influences`: Indirekte Einflussnahme
- `enables_constraints`: Ermöglicht/Einschränkt
- `structural`: Strukturelle Beziehung
- `temporal`: Zeitliche Beziehung
### `edge_vocabulary.md`
Definiert Canonical Edge-Typen und Aliase:
- Canonical: `resulted_in`
- Aliase: `führt_zu`, `resultiert_in`, etc.
### `graph_schema.md`
Definiert Note-Type-Kompatibilität:
- Welche Edge-Typen sind typisch für `insight → decision`?
- Welche Edge-Typen sind verboten?
### `analysis_policies.yaml`
Definiert Severity-Policies pro Profil (discovery, decisioning) für Findings:
- `missing_link_constraints`, `weak_chain_roles`, `unmapped_edge_types`, `missing_slot_*`
---
## 11. Matching-Parameter (defaults.matching)
| Parameter | Standard | Beschreibung |
|-----------|----------|--------------|
| `distinct_nodes` | true | Jeder Knoten nur einmal pro Template |
| `required_links` | true | Links erforderlich (profil-überschreibbar) |
| `max_candidate_nodes` | 30 | Max. Candidate Nodes pro Template |
| `max_matches_per_template` | 2 | Max. unterschiedliche Zuordnungen (z. B. intra-note + cross-note) |
| `max_assignments_collected` | 1000 | Schleifenschutz beim Backtracking |
---
## Zusammenfassung
Das System identifiziert Chains durch:
1. **Graph-Indexierung**: Erfassung aller Edges im lokalen Subgraph
2. **Candidate-Sammlung**: Identifikation potenzieller Knoten
3. **Backtracking-Matching**: Optimale Slot-Zuordnung
4. **Link-Validierung**: Prüfung vorhandener Edges
5. **Scoring**: Bewertung der Match-Qualität
Das Ergebnis sind **Template Matches** mit:
- Slot-Zuordnungen (welche Knoten in welchen Slots)
- Link-Status (welche Links erfüllt/fehlend)
- Confidence (confirmed/plausible/weak)
- Role Evidence (welche Edges welche Roles haben)
Diese Matches werden dann im **Chain Workbench** verwendet, um konkrete Todos und Actions zu generieren.