diff --git a/projekt-matching/projektmatch/llm.py b/projekt-matching/projektmatch/llm.py index 1657d9f..210af4c 100644 --- a/projekt-matching/projektmatch/llm.py +++ b/projekt-matching/projektmatch/llm.py @@ -1,7 +1,12 @@ """vLLM (OpenAI-compatible) calls with response_format json_schema + German prompts. -The model is a REASONING model: max_tokens stays UNSET so long thinking -chains cannot truncate the final answer (65k context window). +max_tokens stays UNSET so nothing can truncate the final answer (65k +context window). Thinking is DISABLED via chat_template_kwargs: with the +vLLM qwen3 reasoning parser active, json_schema guided decoding plus +thinking degenerates — the run burns the entire context window +(finish_reason "length", ~63k completion tokens) and returns empty or +truncated content (verified live in Task 13). Without thinking the same +extract call answers in ~35 s with valid schema-conformant JSON. """ from __future__ import annotations @@ -86,7 +91,12 @@ Kategorie Misc. "plus"). Misc = Rahmenbedingungen und alles, was weder Muss noch \ Wunsch-Qualifikation ist. Explizite Abschnittsüberschriften haben Vorrang \ vor Signalwörtern; "idealerweise" INNERHALB einer Must-Zeile lässt die \ -Zeile Must bleiben. +Zeile Must bleiben. Nice-Signalwörter gelten NUR für die Zeile, in der \ +sie selbst stehen — sie färben NIE auf folgende Zeilen ab: Jede Zeile \ +eines Anforderungs-Abschnitts OHNE eigenes Nice-Signalwort ist Must, \ +auch wenn direkt davor Nice-Zeilen stehen. Beispiel: Auf "Idealerweise \ +zusätzlich Erfahrung mit X" (Nice) folgt "Erfahrung mit Y" ohne \ +Signalwort → Y ist Must. - miscType nur für Misc-Zeilen relevant (sonst "other"): start = \ Projektstart/Verfügbarkeit (startDate als ISO-Datum YYYY-MM-DD, wenn ein \ konkretes Datum genannt ist, sonst null); workload = Auslastung \ @@ -117,7 +127,9 @@ Proof-of-Concept-Erfahrung deckt "produktiven Betrieb" NICHT ab; \ "mehrjährig" wörtlich nehmen; ein Produktname (z. B. "Azure DevOps \ Server") belegt KEINE Cloud-Plattform-Erfahrung. - "unknown" NUR bei echter Teilevidenz, wenn die Entscheidung von Wissen \ -abhängt, das nur der Kandidat selbst hat. +abhängt, das nur der Kandidat selbst hat. Zusammengesetzte Anforderungen \ +("X sowie Y", "X und Y"): klare Evidenz für einen Teil und keine \ +Gegen-Evidenz für den anderen → "unknown", nicht "no". - "wie z. B."-Aufzählungen: gleichwertige Alternativen zählen als Abdeckung \ (Beispiel: Ollama/llama.cpp/Transformers decken "LLM-Inference-Stacks wie \ z. B. vLLM, TGI, Triton" ab). @@ -126,6 +138,7 @@ z. B. vLLM, TGI, Triton" ab). def _post(base, model, messages, schema): body = {"model": model, "messages": messages, "temperature": 0.1, + "chat_template_kwargs": {"enable_thinking": False}, "response_format": {"type": "json_schema", "json_schema": { "name": "out", "schema": schema}}} return requests.post(f"{base.rstrip('/')}/chat/completions", json=body,