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.
12 KiB
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_compstrotzdem[]zurückgibt - Property (P): Das gewünschte Verhalten – eine nicht-leere Liste von bis zu 10
(competence_name, confidence)-Tupeln, wobei jeder Name inget_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.pymitchat_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 Tabelleextract_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_namesenthä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_rolemuss 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_tasksimllm_fulltext-Modus) bleibt unverändert - Fehlerbehandlung bei LLM-Aufruf-Fehlern für Rollen-Inferenz bleibt unverändert (
Nonezurü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:
-
Fehlende Implementierung: Die alte
infer_competences-Methode (Embedding-basiert) wurde entfernt. An den drei Stellen im Code steht nur nochinferred_comps: list[tuple[str, float]] = []bzw.inferred_competences: list[tuple[str, float]] = []ohne jeglichen LLM-Aufruf. -
Kein System-Prompt für Kompetenz-Inferenz: Im Gegensatz zu
_ROLE_INFERENCE_SYSTEM_PROMPTexistiert kein entsprechender Prompt für Kompetenz-Inferenz. -
Keine Methode in VocabularyCache: Die Klasse hat nur
infer_primary_role, aber keineinfer_competences-Methode. -
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_PROMPTanalog 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:
- validate_task_requirements mit gültigem Task: Aufruf mit Task-ID, der einen beschriebenen Task hat (wird auf unfixiertem Code leere Kompetenz-Tabelle liefern)
- extract_requirements mit Freitext: Aufruf mit beschreibendem Text (wird auf unfixiertem Code
inferred_competences = []liefern) - find_matching_tasks im Score-Modus: Aufruf mit Capacity-ID (wird auf unfixiertem Code nur DB-Skills verwenden, keine LLM-Inferenz)
- Leerer Task-Text: Aufruf mit leerem Text (soll auch nach Fix
[]liefern – Baseline)
Erwartete Counterexamples:
inferred_compsist 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:
- Rollen-Inferenz Preservation: Verifizieren, dass
infer_primary_rolenach dem Fix identische Ergebnisse liefert - DB-Skills Preservation: Verifizieren, dass DB-Skills weiterhin korrekt angezeigt werden
- Leerer Text Preservation: Verifizieren, dass leerer Task-Text weiterhin
[]liefert - LLM-Fulltext Preservation: Verifizieren, dass der LLM-Fulltext-Modus nicht beeinflusst wird
Unit Tests
- Test
infer_competencesmit gemocktem LLM-Client: gültige JSON-Antwort → korrekte Tupel-Liste - Test
infer_competencesmit leerem Task-Text →[] - Test
infer_competencesmit leerer Kompetenzliste in DB →[] - Test
infer_competencesbei LLM-Fehler (Exception) →[]+ Warning geloggt - Test
infer_competencesbei ungültiger JSON-Antwort →[] - Test
infer_competencesbei Kompetenz-Namen die nicht in DB sind → werden herausgefiltert - Test
validate_task_requirementsliefert nicht-leere Kompetenz-Tabelle - Test
extract_requirementsliefert nicht-leereinferred_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_roleunverändert funktioniert (Preservation)
Integration Tests
- End-to-End Test:
validate_task_requirementsmit echtem (gemocktem) LLM-Client zeigt Kompetenzen - End-to-End Test:
extract_requirements→find_matching_capacitiesPipeline mit inferierten Kompetenzen - End-to-End Test:
find_matching_tasksim Score-Modus nutzt inferierte Kompetenzen für besseres Scoring