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.
385 lines
25 KiB
Markdown
385 lines
25 KiB
Markdown
# Implementation Plan: LLM-Volltext-Matching als zweites Verfahren
|
||
|
||
## Overview
|
||
|
||
Inkrementelle Einführung des neuen `llm_fulltext`-Matching-Verfahrens neben dem bestehenden `score`-Verfahren. Die Reihenfolge stellt sicher, dass keine Zwischenstände kaputt sind: zuerst Datenbankschicht (DBClient/TrinoClient) erweitern, dann Datenmodelle (`CapacityProfile`/`TaskProfile`) und der `LlmFulltextMatcher`, anschließend Konfiguration und MCP-Tool-Integration, am Ende Cache-/Tabellenausgabe sowie Anpassungen an `get_results_by_category`/`filter_search_results` und Dokumentation.
|
||
|
||
## Tasks
|
||
|
||
- [x] 1. Datenbankschicht für Capacity-Volltext-Felder erweitern
|
||
- [x] 1.1 Neue Methoden im `DBClient`-Protokoll deklarieren (`database/types.py`)
|
||
- `CapacityReferenceRow` als `TypedDict` mit Feldern `partner_name: str` und `projects: str` im Protokoll-Modul (bzw. unter `database/types.py`) deklarieren; `partner_name` darf leer sein (NULL `partner_id` oder Join-Mismatch, Anforderung 2.8)
|
||
- `get_capacity_description(capacity_id) -> str | None`
|
||
- `get_capacity_certificates(capacity_id) -> list[str]`
|
||
- `get_capacity_references(capacity_id) -> list[CapacityReferenceRow]`
|
||
- `batch_get_capacity_descriptions(capacity_ids) -> dict[str, str | None]`
|
||
- `batch_get_capacity_certificates(capacity_ids) -> dict[str, list[str]]`
|
||
- `batch_get_capacity_references(capacity_ids) -> dict[str, list[CapacityReferenceRow]]`
|
||
- Docstrings gemäß Design (Quelle, Join-Verhalten inkl. Partner-Join `partner_id = id`, Rückgabetyp bei leerer Eingabe, leerer `partner_name` bei NULL/Mismatch)
|
||
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.8_
|
||
|
||
- [x] 1.2 Einzel-Methoden im `TrinoClient` implementieren (`database/trino_client.py`)
|
||
- SELECT-Only-Statements mit `_ensure_select_only`, Connection-Pool und `_retry`
|
||
- `get_capacity_description`: NULL/leerer String → `None`
|
||
- `get_capacity_certificates`: Join über `capacity_id`, leere Strings filtern, Rückgabe `[]` wenn keine Treffer
|
||
- `get_capacity_references`: zusätzlich `LEFT JOIN teamlandkarte_v_partners_latest p ON r.partner_id = p.id` und `COALESCE(p.name, '') AS partner_name` in derselben Abfrage; Rückgabe als `CapacityReferenceRow`-Liste mit `partner_name` und `projects`
|
||
- NULL `partner_id` bzw. Join-Mismatch → leerer `partner_name`, Referenz bleibt mit `projects` erhalten (Anforderung 2.8)
|
||
- Parameter-Bindung gegen SQL-Injection
|
||
- _Requirements: 2.1, 2.2, 2.3, 2.5, 2.6, 2.7, 2.8, 2.9, 2.10_
|
||
|
||
- [x] 1.3 Batch-Methoden im `TrinoClient` implementieren
|
||
- Genau **eine** SQL-Abfrage pro Quelle (Anforderung 2.4); der Partner-LEFT-JOIN ist Bestandteil derselben Referenz-Abfrage und erzeugt keinen zusätzlichen Roundtrip
|
||
- `batch_get_capacity_references`: `SELECT r.capacity_id, r.projects, COALESCE(p.name, '') AS partner_name ... LEFT JOIN teamlandkarte_v_partners_latest p ON r.partner_id = p.id`
|
||
- Schlüssel der Rückgaben sind `str(capacity_id)` (deterministisch)
|
||
- Fehlende IDs als `None` (Beschreibungen) bzw. `[]` (Listen) vorbelegen
|
||
- Gruppierung n:1 via `defaultdict(list)`, leere `projects`-Strings filtern; leerer `partner_name` (NULL/Mismatch) führt nicht zum Verwerfen der Referenz
|
||
- Frühe Rückkehr bei leerer Eingabeliste (`{}`)
|
||
- _Requirements: 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 2.10_
|
||
|
||
- [x] 1.4 Unit-Tests für die neuen DB-Methoden mit Mock-Cursor*
|
||
- Genau eine SQL-Abfrage je Methode wird abgesetzt; LEFT JOIN auf `teamlandkarte_v_partners_latest` ist Bestandteil derselben Referenz-Abfrage (kein zusätzlicher Roundtrip)
|
||
- SELECT-Only-Guard wird angewendet
|
||
- Korrekte Gruppierung n:1 in den Batch-Varianten
|
||
- Mock-Cursor liefert für `references` drei Spalten (`capacity_id`, `projects`, `partner_name`); Test inkl. Fall NULL `partner_id` → leerer `partner_name`, Referenz bleibt erhalten
|
||
- Defaults für fehlende IDs (None / [])
|
||
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8_
|
||
|
||
- [x] 2. Datenmodelle `CapacityProfile` und `TaskProfile`
|
||
- [x] 2.1 Neue frozen Dataclasses anlegen (`matching/profiles.py`)
|
||
- `CapacityReferenceEntry` als `@dataclass(frozen=True)` mit Feldern `partner_name: str` und `projects: str` (`partner_name` darf leer sein, vgl. Anforderung 2.8 / 3.2)
|
||
- `CapacityProfile` mit Feldern `id`, `owner_name`, `role_name`, `competences`, `description`, `references: list[CapacityReferenceEntry]`, `certificates`
|
||
- `TaskProfile` mit Feldern `id`, `title`, `description`, `skills`
|
||
- Beide `@dataclass(frozen=True)` und nur primitive Felder bzw. `list[str]` / `list[CapacityReferenceEntry]`
|
||
- _Requirements: 3.1, 3.2, 4.1_
|
||
|
||
- [x] 2.2 Profil-Builder implementieren (`matching/profiles.py`)
|
||
- `build_capacity_profile(capacity, *, description, certificates, references) -> CapacityProfile`
|
||
- Konvertiere `CapacityReferenceRow`-Einträge aus dem DBClient in `CapacityReferenceEntry`-Instanzen; ein leerer `partner_name` führt nicht zum Verwerfen der Referenz (Anforderung 3.4)
|
||
- `build_task_profile(task) -> TaskProfile` (alternativ `build_task_profile_from_requirements(...)` für Suchrichtung Aufgabe→Kapazität)
|
||
- Leere/`None`-Felder → leerer String bzw. leere Liste, Profil wird nie verworfen
|
||
- Reihenfolge der Felder ist über alle Profile konstant
|
||
- _Requirements: 3.1, 3.2, 3.4, 4.1, 4.2_
|
||
|
||
- [x] 2.3 Deterministische Profil-Serialisierung implementieren
|
||
- `serialize_capacity_profile(profile) -> str` mit fixer Reihenfolge `Rolle:`, `Kompetenzen:`, `Beschreibung:`, `Referenzen:`, `Zertifikate:`
|
||
- `_format_reference(entry: CapacityReferenceEntry) -> str`: bei nicht-leerem `partner_name` → `Partner: <partner_name> – Projekte: <projects>`; bei leerem `partner_name` → `Projekte: <projects>` (kein Platzhalter, kein `Partner:`-Token)
|
||
- `serialize_task_profile(profile) -> str` mit fixer Reihenfolge `Titel:`, `Beschreibung:`, `Gesuchte Kompetenzen:`
|
||
- Leere Listen werden als `(keine)` ausgegeben, alle Feldüberschriften erscheinen immer; Reihenfolge der Felder und Referenzen bleibt deterministisch
|
||
- _Requirements: 3.3, 3.4, 3.6, 3.7, 4.3, 4.4_
|
||
|
||
- [x] 2.4 Property-Test für deterministische Serialisierung schreiben
|
||
- **Property 1: Profil-Serialisierung ist deterministisch und feldvollständig**
|
||
- Hypothesis mit `@settings(max_examples=100)`, `st.builds(...)` für Profile inkl. `CapacityReferenceEntry` mit/ohne `partner_name`
|
||
- Zwei Aufrufe liefern identischen String; alle Feldüberschriften enthalten; Partner-Name erscheint deterministisch in der serialisierten Ausgabe, sofern nicht leer
|
||
- **Validates: Requirements 3.3, 3.4, 3.6, 3.7, 4.3, 4.4**
|
||
|
||
- [x] 2.5 Property-Test für Profil-Builder mit leeren Feldern schreiben
|
||
- **Property 2: Leere/None-Felder verwerfen das Profil nicht**
|
||
- Capacity-/Task-Strategien mit `st.one_of(st.none(), st.text())`; Referenzen-Strategie mit gemischtem `partner_name` (`st.one_of(st.just(""), st.text(min_size=1))`)
|
||
- Profil enthält leere Strings/Listen; Serialisierung enthält weiterhin alle Überschriften; auch Referenzen mit leerem `partner_name` werden nicht verworfen
|
||
- **Validates: Requirements 3.2, 3.4, 4.2**
|
||
|
||
- [x] 2.6 Property-Test für Referenzen mit leerem Partner-Name schreiben
|
||
- **Property 2b: Referenzen mit leerem Partner_Name behalten projects, ohne Partner-Token**
|
||
- Hypothesis-Generator mischt leere und nicht-leere `partner_name`-Werte: `st.lists(st.builds(CapacityReferenceEntry, partner_name=st.one_of(st.just(""), st.text(min_size=1)), projects=st.text(min_size=1)))`
|
||
- Prüft (a) die Anzahl der Referenz-Zeilen entspricht der Anzahl der Einträge mit nicht-leerem `projects`, (b) jede Zeile zu einem Eintrag mit leerem `partner_name` beginnt mit `Projekte:` und enthält keinen `Partner:`-Token, (c) jede Zeile zu einem Eintrag mit nicht-leerem `partner_name` enthält sowohl `Partner: <name>` als auch `Projekte: <projects>`
|
||
- **Validates: Requirements 2.8, 3.4, 3.6**
|
||
|
||
- [x] 3. `LlmFulltextMatcher`-Komponente bauen
|
||
- [x] 3.1 Modul-Skelett `matching/llm_fulltext_matcher.py` anlegen
|
||
- Konstanten `_ALLOWED_CATEGORIES = ("Top", "Good", "Partial", "Low", "Irrelevant")` und `_ALIAS`
|
||
- Dataclasses `LlmFulltextItem`, `LlmFulltextError`, `LlmFulltextResult`
|
||
- Klasse `LlmFulltextMatcher` mit Konstruktor `(*, db, client, rationale_max_chars=280)`
|
||
- _Requirements: 5.1, 5.3, 6.1, 6.3, 7.4_
|
||
|
||
- [x] 3.2 Kategorie-Normalisierung implementieren
|
||
- `normalize_category(value) -> tuple[str, bool]`
|
||
- Trim + lowercase, Mapping über `_ALIAS`, ungültige Werte → `("Irrelevant", False)`
|
||
- Nicht-String-Eingaben → `("Irrelevant", False)`
|
||
- _Requirements: 5.3, 5.6, 6.3, 6.6_
|
||
|
||
- [x] 3.3 Property-Test für Kategorie-Normalisierung schreiben
|
||
- **Property 3: Kategorienormalisierung bildet auf erlaubte Menge ab**
|
||
- Hypothesis mit `st.text()` und Aliase via `st.sampled_from`
|
||
- Output immer in erlaubter Menge; `is_valid` korrekt
|
||
- **Validates: Requirements 5.3, 5.6, 6.3, 6.6**
|
||
|
||
- [x] 3.4 LLM-Aufruf und Antwort-Parsing implementieren
|
||
- System-Prompt gemäß Design (deutschsprachig, deterministisch, JSON-only)
|
||
- User-Prompt baut auf `serialize_task_profile`/`serialize_capacity_profile` auf
|
||
- `chat_completion(system, user, response_format=json_object)` aufrufen
|
||
- JSON parsen, Felder `category` und `rationale` extrahieren
|
||
- Bei `JSONDecodeError` → `LlmFulltextError` mit Meldung `"invalid JSON: <excerpt>"`
|
||
- Bei ungültiger Kategorie → Item nach `Irrelevant` mit Hinweis `[Hinweis: ungültige LLM-Kategorie: <wert>]` an Rationale anhängen
|
||
- Bei `AzureAPIError`/Timeout/sonstiger Exception → `LlmFulltextError` mit Klassenname + erstem Satz
|
||
- _Requirements: 5.4, 5.5, 5.6, 5.7, 6.4, 6.5, 6.6, 6.7_
|
||
|
||
- [x] 3.5 `match_capacities` implementieren
|
||
- Eingabe: `task_profile`, `capacities` (bereits vorgefiltert)
|
||
- Capacity-IDs sammeln, `batch_get_capacity_descriptions/certificates/references` aufrufen
|
||
- Pro Kapazität `CapacityProfile` bauen und LLM-Aufruf durchführen
|
||
- Erfolgreiche Items in `by_category` einsortieren, fehlerhafte in `errors`
|
||
- Innerhalb jeder Kategorie deterministisch nach `item_id` aufsteigend (lexikographisch) sortieren
|
||
- _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.7, 5.8_
|
||
|
||
- [x] 3.6 `match_tasks` implementieren
|
||
- Eingabe: `capacity_profile`, `tasks`
|
||
- Pro Aufgabe `TaskProfile` bauen und LLM-Aufruf durchführen
|
||
- Sortierung primär nach Kategorie, sekundär nach `task_id` aufsteigend
|
||
- _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.7, 6.8_
|
||
|
||
- [x] 3.7 Property-Test für ungültige LLM-Kategorie schreiben
|
||
- **Property 4: Ungültige LLM-Kategorie wird auf Irrelevant gemappt**
|
||
- Mock-LLM gibt zufällige ungültige Kategorie zurück
|
||
- Item landet in `Irrelevant`, Rationale enthält Originaltext + Hinweis
|
||
- **Validates: Requirements 5.6, 6.6**
|
||
|
||
- [x] 3.8 Property-Test für LLM-Fehler-Trennung schreiben
|
||
- **Property 5: LLM-Fehler erscheinen in der Fehlerliste, nicht als Ergebnis**
|
||
- Hypothesis-Strategy `st.lists(st.booleans())` als Fehlermaske
|
||
- Items aus Fehlermaske erscheinen ausschließlich in `result.errors`
|
||
- **Validates: Requirements 5.7, 6.7**
|
||
|
||
- [x] 3.9 Property-Test für deterministische Sortierung schreiben
|
||
- **Property 6: Ergebnisse sind innerhalb jeder Kategorie deterministisch sortiert**
|
||
- Permutationen der Eingabeliste erzeugen identische `by_category`-Reihenfolge
|
||
- **Validates: Requirements 5.8, 6.8**
|
||
|
||
- [x] 3.10 Unit-Tests für `LlmFulltextMatcher` mit gemocktem `AzureOpenAIClient`*
|
||
- Erfolgsfall (gültige Kategorie + Rationale)
|
||
- Ungültiges JSON
|
||
- Gültiges JSON mit unbekannter Kategorie
|
||
- `chat_completion` wirft `AzureAPIError`
|
||
- Vermischung mehrerer Items: Reihenfolge und Sortierung korrekt
|
||
- _Requirements: 5.4, 5.5, 5.6, 5.7, 5.8, 6.4, 6.5, 6.6, 6.7, 6.8_
|
||
|
||
- [x] 4. Checkpoint - Ensure all tests pass
|
||
- Ensure all tests pass, ask the user if questions arise.
|
||
|
||
- [x] 5. Konfiguration `matching.default_method` einführen
|
||
- [x] 5.1 `MatchingConfig` um Feld `default_method: str = "score"` erweitern (`config.py`)
|
||
- Validierung in `load_config`: Wert muss `"score"` oder `"llm_fulltext"` sein
|
||
- Bei ungültigem Wert `ConfigError` mit beschreibender Meldung werfen
|
||
- Fehlender Schlüssel → Default `"score"`
|
||
- _Requirements: 12.2, 12.3, 12.4_
|
||
|
||
- [x] 5.2 `config.toml` und `config.toml.example` aktualisieren
|
||
- Neuer Abschnitt `[matching].default_method = "score"` mit Kommentar zu erlaubten Werten
|
||
- _Requirements: 12.2, 12.3_
|
||
|
||
- [x] 5.3 Unit-Tests für Konfigurations-Validierung*
|
||
- `default_method` fehlt → Default `"score"`
|
||
- `default_method = "llm_fulltext"` → übernommen
|
||
- `default_method = "irgendwas"` → `ConfigError`
|
||
- _Requirements: 12.3, 12.4_
|
||
|
||
- [x] 6. MCP-Tool-Integration für `matching_method`
|
||
- [x] 6.1 Hilfsfunktion `_resolve_matching_method` im `mcp_server.py` implementieren
|
||
- `None` → `cfg.matching.default_method`
|
||
- Trim + lowercase, gegen `("score", "llm_fulltext")` validieren
|
||
- Bei ungültigem Wert `ValueError` mit beiden erlaubten Werten in der Meldung werfen
|
||
- _Requirements: 1.1, 1.2, 1.5, 12.3_
|
||
|
||
- [x] 6.2 `find_matching_capacities` um `matching_method` erweitern
|
||
- Neuer Parameter `matching_method: Optional[str] = None`
|
||
- Validierung **vor** DB-/LLM-Aufrufen, bei Fehler frühe Markdown-Fehlermeldung mit erlaubten Werten zurückgeben
|
||
- Routing: `"score"` → bestehender `Matcher`-Pfad; `"llm_fulltext"` → `LlmFulltextMatcher.match_capacities`
|
||
- Im LLM-Modus Vorfilter (z. B. Verfügbarkeit) identisch zum Score-Modus anwenden
|
||
- Task_Profile aus aktuell bestätigtem Anforderungs-Set bauen
|
||
- _Requirements: 1.1, 1.3, 1.4, 1.5, 5.1, 5.2_
|
||
|
||
- [x] 6.3 `find_matching_tasks` um `matching_method` erweitern
|
||
- Neuer Parameter `matching_method: Optional[str] = None`
|
||
- Routing analog 6.2
|
||
- Im LLM-Modus für die `capacity_id` Beschreibung, Zertifikate und Referenzen aus DB laden und ins `CapacityProfile` einbeziehen
|
||
- _Requirements: 1.1, 1.3, 1.4, 1.5, 6.1, 6.2_
|
||
|
||
- [x] 6.4 Docstrings für betroffene MCP-Tools aktualisieren
|
||
- `find_matching_capacities`, `find_matching_tasks`: Parameter `matching_method` mit erlaubten Werten dokumentieren
|
||
- Hinweis auf Unterschiede in Ausgabe-Schema (keine Score-Spalten im LLM-Modus, `Begründung`-Spalte)
|
||
- _Requirements: 12.1_
|
||
|
||
- [x] 6.5 Property-Test für `matching_method`-Validierung schreiben
|
||
- **Property 9: matching_method-Validierung lehnt unbekannte Werte ab**
|
||
- Hypothesis-Strategy `st.text()`
|
||
- Bei ungültiger Eingabe enthält die Fehlermeldung beide erlaubten Werte; DB- und LLM-Mocks werden nicht aufgerufen
|
||
- **Validates: Requirements 1.5**
|
||
|
||
- [x] 6.6 Unit-Tests für Routing in beiden MCP-Tools
|
||
- `matching_method=None` → Default greift, Schema entspricht Score-Modus
|
||
- `matching_method="llm_fulltext"` → `LlmFulltextMatcher` wird aufgerufen, Score-Pfad nicht
|
||
- `matching_method="bogus"` → Fehlermeldung, weder DB noch LLM werden aufgerufen
|
||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
|
||
|
||
- [x] 7. SearchCache-Payload und META-JSON
|
||
- [x] 7.1 Persistenz im SearchCache an LLM-Modus anpassen
|
||
- `matching_method` als Schlüssel im Payload aufnehmen (auch im Score-Modus)
|
||
- Im LLM-Modus pro Item Felder `category` und `rationale` (ungekürzt) speichern, **keine** `role_score`/`competence_score`/`overall_score`
|
||
- Optionale `errors`-Liste mit `{"item_id", "error"}` im LLM-Modus
|
||
- Im Score-Modus bisheriges Schema unverändert (keine `rationale`/`errors`)
|
||
- _Requirements: 7.1, 7.4, 8.6, 9.5_
|
||
|
||
- [x] 7.2 META-JSON in Tool-Antworten um `matching_method` erweitern
|
||
- In beiden Modi setzen, sodass Folgewerkzeuge das Schema korrekt interpretieren
|
||
- _Requirements: 1.6, 9.5_
|
||
|
||
- [x] 7.3 Property-Test für persistierte ungekürzte Rationale schreiben
|
||
- **Property 8: Ungekürzte Rationale wird persistiert**
|
||
- Hypothesis `st.text(min_size=300)`, Cache-Eintrag muss exakt der LLM-Antwort entsprechen
|
||
- **Validates: Requirements 8.6**
|
||
|
||
- [x] 7.4 Property-Test für META-`matching_method` schreiben
|
||
- **Property 11: META enthält das verwendete Verfahren**
|
||
- Aus Tool-Output META-JSON parsen und Wert prüfen
|
||
- **Validates: Requirements 1.6, 9.5**
|
||
|
||
- [x] 7.5 Property-Test für Score-Modus-Abwärtskompatibilität schreiben
|
||
- **Property 10: Score-Modus ist abwärtskompatibel**
|
||
- Tool-Aufruf ohne `matching_method` bzw. mit `"score"`: Persistiertes Payload-Schema entspricht weiter dem bisherigen (Felder `role_score`, `competence_score`, `overall_score`; kein `rationale`)
|
||
- **Validates: Requirements 1.2, 1.3, 7.3**
|
||
|
||
- [x] 8. Tabellenausgabe mit `Begründung`-Spalte und Rationale-Formatierung
|
||
- [x] 8.1 Hilfsfunktion `_format_rationale_for_table` implementieren
|
||
- Pipes (`|`) → `/`, Carriage Return / Linefeed → Leerzeichen
|
||
- Whitespace zusammenfalten
|
||
- Bei > 280 Zeichen kürzen und mit `…` abschließen
|
||
- _Requirements: 8.4, 8.5_
|
||
|
||
- [x] 8.2 Ausgabe-Tabellen für `find_matching_capacities` (LLM-Modus) anpassen
|
||
- Spalten: `ID | Owner | Role | Competences | Availability | Category | Begründung`
|
||
- **Keine** Spalten `Role Score`, `Competence Score`, `Overall Score`
|
||
- Pro Zeile gekürzte Rationale via `_format_rationale_for_table`
|
||
- Leere Ergebnistabelle weiterhin korrekt rendern (Header-Konsistenz)
|
||
- _Requirements: 7.1, 7.2, 8.1, 8.2, 8.3, 8.4, 8.5_
|
||
|
||
- [x] 8.3 Ausgabe-Tabellen für `find_matching_tasks` (LLM-Modus) anpassen
|
||
- Spalten: `task_id | Title | Required Competences | Availability | Category | Begründung`
|
||
- Verhalten analog 8.2
|
||
- _Requirements: 7.1, 7.2, 8.1, 8.2, 8.3, 8.4, 8.5_
|
||
|
||
- [x] 8.4 Summary-Tabelle und Errors-Block einbauen
|
||
- Summary bleibt in beiden Modi identisch (Counter je Kategorie)
|
||
- Wenn `errors` nicht leer: zusätzlicher Markdown-Block `## Errors` nach der Ergebnistabelle
|
||
- _Requirements: 5.7, 6.7, 7.5_
|
||
|
||
- [x] 8.5 Score-Modus-Tabelle unverändert lassen (Regression)
|
||
- Bestehende Spalten `Role Score`, `Competence Score`, `Overall Score`, `Category` bleiben
|
||
- Keine `Begründung`-Spalte
|
||
- _Requirements: 7.3_
|
||
|
||
- [x] 8.6 Property-Test für `_format_rationale_for_table` schreiben
|
||
- **Property 7: Tabellen-Rationale ist gültiges Markdown und längenbegrenzt**
|
||
- Hypothesis erzeugt Strings inkl. `|`, `\n`, sehr lang
|
||
- Output ≤ 280 Zeichen, weder `|` noch Zeilenumbrüche, `…`-Suffix genau dann, wenn normalisierte Eingabe > 280 Zeichen war
|
||
- **Validates: Requirements 8.4, 8.5**
|
||
|
||
- [x] 9. Checkpoint - Ensure all tests pass
|
||
- Ensure all tests pass, ask the user if questions arise.
|
||
|
||
- [x] 10. Anpassungen für `get_results_by_category`
|
||
- [x] 10.1 Hilfsfunktion `_format_results_table(items, *, search_type, matching_method, ref_start, ref_end)` einführen
|
||
- Routing nach `matching_method`: Score-Tabelle (bisher) oder LLM-Tabelle mit `Begründung`-Spalte
|
||
- Wiederverwendbar in `find_matching_capacities`, `find_matching_tasks` und `get_results_by_category`
|
||
- _Requirements: 7.1, 7.2, 8.1, 8.2, 9.2_
|
||
|
||
- [x] 10.2 `get_results_by_category` an LLM-Modus anpassen
|
||
- `matching_method` aus persistiertem SearchEntry lesen
|
||
- Im LLM-Modus Tabelle ohne Score-Spalten und mit `Begründung`-Spalte rendern
|
||
- Im Score-Modus unverändertes Verhalten
|
||
- META-JSON enthält `matching_method`
|
||
- _Requirements: 9.1, 9.2, 9.5_
|
||
|
||
- [x] 10.3 Docstring für `get_results_by_category` aktualisieren
|
||
- Hinweis auf modusabhängige Spalten (`Begründung` im LLM-Modus)
|
||
- _Requirements: 12.1_
|
||
|
||
- [x] 11. Anpassungen für `filter_search_results`
|
||
- [x] 11.1 Modus-Erkennung über persistiertes `matching_method`
|
||
- Im LLM-Modus bestehende Filter (Rollen-, Kompetenz-, Verfügbarkeits-, Aufgaben-Text-/Kompetenzfilter) weiterhin anwenden
|
||
- Score-bezogenen Filter `min_similarity` ignorieren und in `Applied Filters` einen Hinweis aufnehmen (`min_similarity (ignored: not applicable in llm_fulltext mode)`)
|
||
- _Requirements: 9.3, 9.4_
|
||
|
||
- [x] 11.2 Sortierung in `filter_search_results` modusabhängig
|
||
- Score-Modus: bestehende Sortierung nach `overall_score`
|
||
- LLM-Modus: stabile Sortierung nach `(category_rank, item_id)`
|
||
- _Requirements: 5.8, 6.8, 9.3_
|
||
|
||
- [x] 11.3 Tabellen-Rendering in `filter_search_results` an `_format_results_table` anbinden
|
||
- Verwendung der neuen Hilfsfunktion (siehe 10.1) für konsistente Spalten
|
||
- META-JSON enthält `matching_method`
|
||
- _Requirements: 9.2, 9.5_
|
||
|
||
- [x] 11.4 Docstring für `filter_search_results` aktualisieren
|
||
- Hinweis, dass `min_similarity` im LLM-Modus ignoriert wird
|
||
- _Requirements: 12.1_
|
||
|
||
- [x] 11.5 Unit-Tests für `get_results_by_category` und `filter_search_results` in beiden Modi
|
||
- LLM-Modus: `Begründung`-Spalte vorhanden, `min_similarity` ignoriert mit Hinweis
|
||
- Score-Modus: bestehendes Verhalten unverändert (Regression)
|
||
- _Requirements: 9.1, 9.2, 9.3, 9.4, 9.5_
|
||
|
||
- [x] 12. Server-Wiring und Integrationstests
|
||
- [x] 12.1 `LlmFulltextMatcher` in `build_server` instanziieren
|
||
- Konstruktor mit `db`, `client` (`AzureOpenAIClient`) verdrahten
|
||
- Komponente an MCP-Tool-Handler weiterreichen
|
||
- _Requirements: 1.4, 5.1, 6.1_
|
||
|
||
- [x] 12.2 End-to-End-Integrationstests mit gemocktem LLM-Client und gemocktem `DBClient`
|
||
- `find_matching_capacities` und `find_matching_tasks` mit `matching_method="llm_fulltext"`
|
||
- Verfügbarkeitsfilter im LLM-Modus identisch zum Score-Modus
|
||
- SearchCache enthält `matching_method`, ungekürzte `rationale`, `errors`-Liste
|
||
- _Requirements: 5.1, 5.2, 6.1, 6.2, 7.4, 8.6, 9.5_
|
||
|
||
- [x] 12.3 Tabellen-Snapshot-Test für Header in beiden Modi
|
||
- Score-Modus-Header unverändert
|
||
- LLM-Modus-Header endet auf `... | Category | Begründung`
|
||
- _Requirements: 7.1, 8.1, 8.2_
|
||
|
||
- [x] 13. Checkpoint - Ensure all tests pass
|
||
- Ensure all tests pass, ask the user if questions arise.
|
||
|
||
- [x] 14. Dokumentation aktualisieren
|
||
- [x] 14.1 `docs/architecture.md` erweitern
|
||
- `LLM_Fulltext_Matcher` als Komponente in der Business-Logic-Layer beschreiben (Eingaben, Ausgaben, externe Abhängigkeit Azure OpenAI Chat Completion)
|
||
- Zusätzliche Datenquellen (`teamlandkarte_v_capacities_latest.description`, `teamlandkarte_v_capacity_certificates_latest`, `teamlandkarte_v_capacity_references_latest`, `teamlandkarte_v_partners_latest.{id, name}`) im Schema-Verifikationsabschnitt aufführen
|
||
- Join `teamlandkarte_v_capacity_references_latest.partner_id = teamlandkarte_v_partners_latest.id` und Übernahme der Spalte `name` als Partner_Name dokumentieren
|
||
- Tool-Surface-Tabelle um Parameter `matching_method` (Wertebereich `score`/`llm_fulltext`) ergänzen
|
||
- Runtime-View für beide Suchrichtungen um den LLM-Volltext-Pfad erweitern
|
||
- _Requirements: 11.1, 11.2, 11.3, 11.4, 11.5_
|
||
|
||
- [x] 14.2 `README.md` erweitern
|
||
- Quick-Start- und Usage-Abschnitt: Auswahl zwischen `score` und `llm_fulltext` per Tool-Parameter
|
||
- Hinweis: Im LLM-Volltext-Modus keine numerischen Scores; stattdessen Spalte `Begründung`
|
||
- Zusätzliche Datenbank-Views auflisten, die der Server im LLM-Volltext-Modus liest, einschließlich `teamlandkarte_v_partners_latest` mit Hinweis auf den LEFT JOIN über `partner_id` in der Referenz-Abfrage
|
||
- Default-Konfiguration `[matching].default_method`
|
||
- _Requirements: 11.6, 11.7, 11.8_
|
||
|
||
- [x] 14.3 `.github/agents/teamlandkarte_agent.md` aktualisieren
|
||
- Beschreibung beider Verfahren `score` und `llm_fulltext` (Existenz und Zweck)
|
||
- Pflicht-Frage nach `matching_method` vor `find_matching_capacities`/`find_matching_tasks`, sofern nicht aus dem Verlauf bekannt
|
||
- Skills/Workflows: Aufruf der Tools mit zusätzlichem Parameter `matching_method`
|
||
- Rolle der Spalte `Begründung` und Hinweis, dass im LLM-Modus keine numerischen Scores erscheinen
|
||
- Bestehender Bestätigungs-Workflow (`show_pending_requirements`, `confirm_requirements`) bleibt für beide Verfahren gleich
|
||
- _Requirements: 10.1, 10.2, 10.3, 10.4, 10.5_
|
||
|
||
- [x] 14.4 `.kiro/agents/teamlandkarte.md` analog zu 14.3 aktualisieren
|
||
- Inhaltliche Gleichheit zum GitHub-Pendant sicherstellen
|
||
- _Requirements: 10.1, 10.2, 10.3, 10.4, 10.5_
|
||
|
||
- [x] 14.5 Beispiele und Mini-Walkthroughs in README/Architektur ergänzen
|
||
- Beispielausgabe einer LLM-Volltext-Tabelle inkl. Spalte `Begründung`
|
||
- Beispielhafte META-JSON-Ausgabe mit `matching_method`
|
||
- _Requirements: 11.5, 11.6_
|
||
|
||
- [x] 15. Final checkpoint - Ensure all tests pass
|
||
- Ensure all tests pass, ask the user if questions arise.
|
||
|
||
## Notes
|
||
|
||
- Tasks markiert mit `*` sind optional und können für einen schnelleren MVP übersprungen werden.
|
||
- Property-Based Tests verwenden Hypothesis mit `@settings(max_examples=100)` (mindestens 100 Iterationen pro Property) und werden als `# Feature: llm-fulltext-matching, Property {N}: {title}` getaggt.
|
||
- Unit- und Integrationstests verwenden `pytest` (Run-once, kein Watch-Modus); der `AzureOpenAIClient` wird stets gemockt.
|
||
- Tasks referenzieren explizit Anforderungen aus `requirements.md` zur lückenlosen Nachverfolgbarkeit.
|
||
- Die Implementierungssprache ist Python (bestehende Codebase).
|
||
- Score-Modus bleibt vollständig abwärtskompatibel; nur additive Erweiterungen am Payload und am META-JSON.
|