2026-04-25 20:55:16 +02:00
|
|
|
"""LlmBewerter — Port für den LLM-Call in der Antragsbewertung.
|
|
|
|
|
|
|
|
|
|
Trennt die *Rohantwort* des LLMs (JSON-String) vom umgebenden
|
|
|
|
|
Application-Flow (Retry, Prompt-Composition, Citation-Binding). Die
|
|
|
|
|
Retry-Logik samt Temperatur-Escalation bleibt Adapter-Detail — ein
|
|
|
|
|
zweiter Adapter (Claude, OpenAI-kompatible Proxies) kann eine ganz
|
|
|
|
|
andere Strategie wählen.
|
|
|
|
|
|
|
|
|
|
Ein späterer Tag-Schritt (Kapitel 10.5 der DDD-Bewertung) kapselt
|
|
|
|
|
zusätzlich die JSON-Parse-Kaskade hinter dem Port; heute bekommt der
|
|
|
|
|
Caller noch einen JSON-String zurück.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from typing import Protocol, runtime_checkable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
class LlmRequest:
|
|
|
|
|
"""Alles, was der Adapter zum Generieren der Bewertung braucht —
|
|
|
|
|
inkl. Retry-Verhalten auf der Adapter-Seite."""
|
|
|
|
|
|
|
|
|
|
system_prompt: str
|
|
|
|
|
user_prompt: str
|
|
|
|
|
model: str = "qwen-plus"
|
|
|
|
|
max_retries: int = 3
|
|
|
|
|
max_tokens: int = 4000
|
|
|
|
|
base_temperature: float = 0.3
|
2026-05-06 01:53:29 +02:00
|
|
|
# Wenn True, wird der DashScope-API ``response_format={"type":"json_object"}``
|
|
|
|
|
# gesendet. Verhindert unescaped-Newlines-Bugs im LLM-Output. Bisher
|
|
|
|
|
# nur fuer den Pressemitteilungs-Generator (#170 Phase 4) benutzt;
|
|
|
|
|
# der Bewertungs-Pfad in analyzer.py laesst das auf False um die
|
|
|
|
|
# bewaehrte Retry-Semantik nicht zu aendern.
|
|
|
|
|
json_object_mode: bool = False
|
2026-04-25 20:55:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@runtime_checkable
|
|
|
|
|
class LlmBewerter(Protocol):
|
|
|
|
|
"""Port: wandelt einen Prompt in einen JSON-String (LLM-Rohantwort).
|
|
|
|
|
|
|
|
|
|
Der Adapter kümmert sich um:
|
|
|
|
|
|
|
|
|
|
- Markdown-Fence-Entfernung,
|
|
|
|
|
- JSON-Parse-Retry mit steigender Temperatur,
|
|
|
|
|
- Content-Fingerprint-Logging zur Forensik.
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
json.JSONDecodeError: wenn alle Retries scheitern. Höhere Schichten
|
|
|
|
|
behandeln das als Fehlschlag der Analyse.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
async def bewerte(self, request: LlmRequest) -> dict: ...
|