Files
Orchestrator/bahn/teamlandkarte-mcp/.kiro/specs/llm-competence-inference/design.md
T
ankn a5f8fb49ab Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
2026-06-30 20:39:52 +02:00

12 KiB
Raw Blame History

LLM-Kompetenz-Inferenz Bugfix Design

Übersicht

Die Kompetenz-Inferenz in validate_task_requirements, extract_requirements und find_matching_tasks (Score-Modus) ist defekt, weil der alte Embedding-Ansatz entfernt wurde, ohne einen Ersatz zu implementieren. Die Variable inferred_comps ist stets eine leere Liste [].

Der Fix implementiert eine neue Methode infer_competences in der bestehenden VocabularyCache-Klasse, analog zum bereits funktionierenden infer_primary_role. Diese Methode nutzt den AzureOpenAIClient.chat_completion-Aufruf, um aus der vollständigen Kompetenzliste (get_all_competence_names()) bis zu 10 passende Kompetenzen mit Konfidenzwerten auszuwählen.

Glossar

  • Bug_Condition (C): Der Zustand, in dem ein Task-Text vorhanden ist UND Kompetenzen in der DB existieren, aber inferred_comps trotzdem [] zurückgibt
  • Property (P): Das gewünschte Verhalten eine nicht-leere Liste von bis zu 10 (competence_name, confidence)-Tupeln, wobei jeder Name in get_all_competence_names() enthalten ist
  • Preservation: Die bestehende Rollen-Inferenz (infer_primary_role), DB-Skills-Anzeige und LLM-Fulltext-Matching bleiben unverändert
  • VocabularyCache: Klasse in src/teamlandkarte_mcp/matching/vocabulary.py, die LLM-basierte Inferenz kapselt
  • AzureOpenAIClient: Client in src/teamlandkarte_mcp/azure/openai_client.py mit chat_completion(system, user) -> str
  • inferred_comps: Die lokale Variable in den betroffenen Tools, die aktuell immer [] ist

Bug-Details

Fault Condition

Der Bug manifestiert sich, wenn ein Tool (validate_task_requirements, extract_requirements, find_matching_tasks im Score-Modus) einen nicht-leeren Task-Text verarbeitet und Kompetenzen in der DB vorhanden sind. Die Kompetenz-Inferenz liefert stets eine leere Liste, weil kein LLM-Aufruf stattfindet.

Formale Spezifikation:

FUNCTION isBugCondition(input)
  INPUT: input of type {task_text: str, competence_names: list[str]}
  OUTPUT: boolean
  
  RETURN input.task_text.strip() != ""
         AND len(input.competence_names) > 0
         AND infer_competences(input.task_text) == []
END FUNCTION

Beispiele

  • validate_task_requirements("task-123") mit Task-Titel "Python Backend Entwicklung" und 50 Kompetenzen in der DB → Erwartung: bis zu 10 Kompetenzen mit Konfidenz; Aktuell: leere Tabelle
  • extract_requirements("Wir brauchen einen React-Entwickler mit TypeScript-Erfahrung") mit Kompetenzen ["React", "TypeScript", "Angular", ...] in der DB → Erwartung: ["React", "TypeScript", ...] mit Konfidenz; Aktuell: inferred_competences = []
  • find_matching_tasks(capacity_id=1) im Score-Modus → Erwartung: inferred_comp_names enthält LLM-inferierte Kompetenzen für das Scoring; Aktuell: nur DB-Skills werden verwendet
  • Leerer Task-Text → Erwartung: leere Liste (kein Fehler) dieses Verhalten ist korrekt und bleibt erhalten

Erwartetes Verhalten

Preservation Requirements

Unverändertes Verhalten:

  • infer_primary_role muss weiterhin genau eine Rolle mit Konfidenz zurückgeben
  • DB-Skills eines Tasks müssen weiterhin korrekt in der Ausgabe angezeigt werden
  • LLM-Fulltext-Matching (find_matching_capacities / find_matching_tasks im llm_fulltext-Modus) bleibt unverändert
  • Fehlerbehandlung bei LLM-Aufruf-Fehlern für Rollen-Inferenz bleibt unverändert (None zurückgeben)
  • Die AzureOpenAIClient-Schnittstelle wird nicht verändert

Scope: Alle Eingaben, die KEINEN nicht-leeren Task-Text mit vorhandenen Kompetenzen in der DB kombinieren, sind vom Fix nicht betroffen:

  • Leerer Task-Text → weiterhin leere Liste
  • Keine Kompetenzen in der DB → weiterhin leere Liste
  • Mausklick-/UI-Interaktionen → nicht betroffen (MCP-Server)
  • LLM-Fulltext-Modus → verwendet eigene Logik, nicht betroffen

Hypothesierte Ursache

Basierend auf der Bug-Analyse sind die Ursachen klar identifiziert:

  1. Fehlende Implementierung: Die alte infer_competences-Methode (Embedding-basiert) wurde entfernt. An den drei Stellen im Code steht nur noch inferred_comps: list[tuple[str, float]] = [] bzw. inferred_competences: list[tuple[str, float]] = [] ohne jeglichen LLM-Aufruf.

  2. Kein System-Prompt für Kompetenz-Inferenz: Im Gegensatz zu _ROLE_INFERENCE_SYSTEM_PROMPT existiert kein entsprechender Prompt für Kompetenz-Inferenz.

  3. Keine Methode in VocabularyCache: Die Klasse hat nur infer_primary_role, aber keine infer_competences-Methode.

  4. Keine Integration in die Tools: Selbst wenn eine Methode existieren würde, fehlt der await-Aufruf an den drei betroffenen Stellen.

Correctness Properties

Property 1: Fault Condition - Kompetenz-Inferenz liefert Ergebnisse

For any input where der Task-Text nicht leer ist UND mindestens eine Kompetenz in der DB existiert (isBugCondition returns true), SHALL die fixierte infer_competences-Methode eine nicht-leere Liste von bis zu 10 Tupeln (competence_name, confidence) zurückgeben, wobei jeder competence_name in get_all_competence_names() enthalten ist und confidence im Bereich [0.0, 1.0] liegt.

Validates: Requirements 2.1, 2.2, 2.3

Property 2: Preservation - Rollen-Inferenz und DB-Skills unverändert

For any input (unabhängig davon ob die Bug-Condition gilt oder nicht), SHALL die fixierte Codebasis das gleiche Ergebnis für infer_primary_role und die DB-Skills-Anzeige produzieren wie der originale Code, und das LLM-Fulltext-Matching bleibt unverändert.

Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5

Fix-Implementierung

Erforderliche Änderungen

Datei: src/teamlandkarte_mcp/matching/vocabulary.py

Änderung 1: System-Prompt hinzufügen

  • Neuer Modul-Level-Konstante _COMPETENCE_INFERENCE_SYSTEM_PROMPT analog zu _ROLE_INFERENCE_SYSTEM_PROMPT
  • Prompt instruiert das LLM, aus einer gegebenen Kompetenzliste bis zu 10 passende Kompetenzen für einen Task-Text auszuwählen
  • Antwortformat: {"competences": [{"name": "<name>", "confidence": <float>}]}

Änderung 2: Neue Methode infer_competences in VocabularyCache

  • Signatur: async def infer_competences(self, *, task_text: str, max_competences: int = 10) -> list[tuple[str, float]]
  • Holt Kompetenzliste via self._db.get_all_competence_names()
  • Bei leerer Liste oder leerem Text: [] zurückgeben
  • LLM-Aufruf via self._client.chat_completion(system, user)
  • JSON-Parsing der Antwort, Validierung gegen DB-Kompetenzliste
  • Bei Fehler: leere Liste zurückgeben + Warning loggen

Datei: src/teamlandkarte_mcp/mcp_server.py

Änderung 3: validate_task_requirements LLM-Aufruf integrieren

  • Ersetze inferred_comps: list[tuple[str, float]] = [] durch:
    inferred_comps = await vocab_cache.infer_competences(task_text=task_text)
    

Änderung 4: extract_requirements LLM-Aufruf integrieren

  • Ersetze inferred_competences: list[tuple[str, float]] = [] durch:
    inferred_competences = await vocab_cache.infer_competences(task_text=desc)
    

Änderung 5: find_matching_tasks (Score-Modus) LLM-Aufruf integrieren

  • Ersetze den Block inferred_comp_names = [str(x) for x in (getattr(t, "skills", None) or []) if x] durch:
    inferred_comp_tuples = await vocab_cache.infer_competences(task_text=full_text)
    inferred_comp_names = [name for name, _conf in inferred_comp_tuples]
    if not inferred_comp_names:
        inferred_comp_names = [str(x) for x in (getattr(t, "skills", None) or []) if x]
    

Testing-Strategie

Validierungsansatz

Die Testing-Strategie folgt einem zweiphasigen Ansatz: Zuerst Counterexamples auf dem unfixierten Code aufdecken, dann den Fix verifizieren und Preservation sicherstellen.

Exploratory Fault Condition Checking

Ziel: Counterexamples aufdecken, die den Bug VOR der Implementierung des Fixes demonstrieren. Root-Cause-Analyse bestätigen oder widerlegen.

Testplan: Tests schreiben, die infer_competences (bzw. die betroffenen Tools) mit nicht-leerem Task-Text und vorhandenen Kompetenzen aufrufen. Auf dem unfixierten Code beobachten, dass stets [] zurückkommt.

Testfälle:

  1. validate_task_requirements mit gültigem Task: Aufruf mit Task-ID, der einen beschriebenen Task hat (wird auf unfixiertem Code leere Kompetenz-Tabelle liefern)
  2. extract_requirements mit Freitext: Aufruf mit beschreibendem Text (wird auf unfixiertem Code inferred_competences = [] liefern)
  3. find_matching_tasks im Score-Modus: Aufruf mit Capacity-ID (wird auf unfixiertem Code nur DB-Skills verwenden, keine LLM-Inferenz)
  4. Leerer Task-Text: Aufruf mit leerem Text (soll auch nach Fix [] liefern Baseline)

Erwartete Counterexamples:

  • inferred_comps ist immer [], unabhängig vom Task-Text
  • Ursache: Kein LLM-Aufruf, keine infer_competences-Methode vorhanden

Fix Checking

Ziel: Verifizieren, dass für alle Eingaben, bei denen die Bug-Condition gilt, die fixierte Funktion das erwartete Verhalten produziert.

Pseudocode:

FOR ALL input WHERE isBugCondition(input) DO
  result := infer_competences_fixed(input.task_text)
  ASSERT len(result) > 0
  ASSERT len(result) <= 10
  FOR EACH (name, confidence) IN result DO
    ASSERT name IN get_all_competence_names()
    ASSERT 0.0 <= confidence <= 1.0
  END FOR
END FOR

Preservation Checking

Ziel: Verifizieren, dass für alle Eingaben, bei denen die Bug-Condition NICHT gilt, die fixierte Funktion das gleiche Ergebnis wie die originale Funktion produziert.

Pseudocode:

FOR ALL input WHERE NOT isBugCondition(input) DO
  ASSERT infer_competences_fixed(input.task_text) == []
END FOR

FOR ALL input DO
  ASSERT infer_primary_role_fixed(input) == infer_primary_role_original(input)
END FOR

Testing-Ansatz: Property-Based Testing wird für Preservation Checking empfohlen, weil:

  • Es automatisch viele Testfälle über den Eingabebereich generiert
  • Es Randfälle findet, die manuelle Unit-Tests übersehen könnten
  • Es starke Garantien bietet, dass Verhalten für alle nicht-buggy Eingaben unverändert bleibt

Testplan: Verhalten auf unfixiertem Code zuerst beobachten (leere Ergebnisse, funktionierende Rollen-Inferenz), dann Property-Based Tests schreiben, die dieses Verhalten nach dem Fix verifizieren.

Testfälle:

  1. Rollen-Inferenz Preservation: Verifizieren, dass infer_primary_role nach dem Fix identische Ergebnisse liefert
  2. DB-Skills Preservation: Verifizieren, dass DB-Skills weiterhin korrekt angezeigt werden
  3. Leerer Text Preservation: Verifizieren, dass leerer Task-Text weiterhin [] liefert
  4. LLM-Fulltext Preservation: Verifizieren, dass der LLM-Fulltext-Modus nicht beeinflusst wird

Unit Tests

  • Test infer_competences mit gemocktem LLM-Client: gültige JSON-Antwort → korrekte Tupel-Liste
  • Test infer_competences mit leerem Task-Text → []
  • Test infer_competences mit leerer Kompetenzliste in DB → []
  • Test infer_competences bei LLM-Fehler (Exception) → [] + Warning geloggt
  • Test infer_competences bei ungültiger JSON-Antwort → []
  • Test infer_competences bei Kompetenz-Namen die nicht in DB sind → werden herausgefiltert
  • Test validate_task_requirements liefert nicht-leere Kompetenz-Tabelle
  • Test extract_requirements liefert nicht-leere inferred_competences

Property-Based Tests

  • Generiere zufällige Task-Texte und Kompetenzlisten; verifiziere, dass Ergebnisse stets Subset der DB-Kompetenzen sind
  • Generiere zufällige Eingaben; verifiziere, dass Konfidenzwerte immer in [0.0, 1.0] liegen
  • Generiere zufällige Eingaben; verifiziere, dass maximal 10 Kompetenzen zurückgegeben werden
  • Generiere zufällige Eingaben; verifiziere, dass infer_primary_role unverändert funktioniert (Preservation)

Integration Tests

  • End-to-End Test: validate_task_requirements mit echtem (gemocktem) LLM-Client zeigt Kompetenzen
  • End-to-End Test: extract_requirementsfind_matching_capacities Pipeline mit inferierten Kompetenzen
  • End-to-End Test: find_matching_tasks im Score-Modus nutzt inferierte Kompetenzen für besseres Scoring