Antwort A: Anthropic Claude Fable 5
Abhängigkeitsbasierter Aufgabendisponent.
Erzeugt einen Ausführungsplan als Liste von Batches (Listen von Aufgaben-IDs),
bei denen jede Aufgabe in einem Batch parallel ausgeführt werden kann und Batches
sequenziell ausgeführt werden müssen. Verwendet Kahns Algorithmus (topologische Sortierung auf Ebene für Ebene).
Löst ValueError aus bei:
- doppelten Aufgaben-IDs,
- Abhängigkeiten, die auf unbekannte Aufgaben-IDs verweisen,
- zirkulären Abhängigkeiten (mit den betroffenen Aufgaben aufgeführt).
from collections import deque
from typing import Dict, List
class TaskScheduler:
"""Plant Aufgaben basier...
Vollstandige Antwort anzeigen ▼
Abhängigkeitsbasierter Aufgabendisponent.
Erzeugt einen Ausführungsplan als Liste von Batches (Listen von Aufgaben-IDs),
bei denen jede Aufgabe in einem Batch parallel ausgeführt werden kann und Batches
sequenziell ausgeführt werden müssen. Verwendet Kahns Algorithmus (topologische Sortierung auf Ebene für Ebene).
Löst ValueError aus bei:
- doppelten Aufgaben-IDs,
- Abhängigkeiten, die auf unbekannte Aufgaben-IDs verweisen,
- zirkulären Abhängigkeiten (mit den betroffenen Aufgaben aufgeführt).
from collections import deque
from typing import Dict, List
class TaskScheduler:
"""Plant Aufgaben basierend auf Abhängigkeiten in parallelisierbare Batches."""
def __init__(self, tasks: List[dict]):
self.tasks = tasks
self._validate_and_build_graph()
def _validate_and_build_graph(self) -> None:
# Sammelt alle Aufgaben-IDs und prüft auf Duplikate.
self.task_ids = set()
for task in self.tasks:
if 'id' not in task:
raise ValueError(f"Aufgabe hat den erforderlichen Schlüssel 'id' nicht: {task!r}")
task_id = task['id']
if task_id in self.task_ids:
raise ValueError(f"Doppelte Aufgaben-ID gefunden: '{task_id}'")
self.task_ids.add(task_id)
# Erstellt Adjazenzliste (Abhängigkeit -> Abhängige) und In-Degree-Map.
self.dependents: Dict[str, List[str]] = {tid: [] for tid in self.task_ids}
self.in_degree: Dict[str, int] = {tid: 0 for tid in self.task_ids}
for task in self.tasks:
task_id = task['id']
for dep in task.get('dependencies') or []:
if dep not in self.task_ids:
raise ValueError(
f"Aufgabe '{task_id}' hängt von unbekannter Aufgaben-ID '{dep}' ab"
)
if dep == task_id:
raise ValueError(
f"Aufgabe '{task_id}' hängt von sich selbst ab (zirkuläre Abhängigkeit)"
)
self.dependents[dep].append(task_id)
self.in_degree[task_id] += 1
def schedule(self) -> List[List[str]]:
"""
Gibt den Ausführungsplan als Liste von Batches zurück.
Jeder Batch ist eine Liste von Aufgaben-IDs, die parallel ausgeführt werden können.
Batches müssen in der zurückgegebenen Reihenfolge ausgeführt werden.
"""
in_degree = dict(self.in_degree) # Arbeitet mit einer Kopie
current_batch = deque(
tid for tid in in_degree if in_degree[tid] == 0
)
plan: List[List[str]] = []
scheduled_count = 0
while current_batch:
batch = sorted(current_batch) # deterministische Ausgabe (Reihenfolge ist beliebig)
plan.append(batch)
scheduled_count += len(batch)
next_batch = deque()
for task_id in batch:
for dependent in self.dependents[task_id]:
in_degree[dependent] -= 1
if in_degree[dependent] == 0:
next_batch.append(dependent)
current_batch = next_batch
if scheduled_count != len(self.task_ids):
unresolved = sorted(
tid for tid in in_degree if in_degree[tid] > 0
)
raise ValueError(
"Zirkuläre Abhängigkeit erkannt, die Aufgaben betrifft: "
+ ", ".join(f"'{t}'" for t in unresolved)
)
return plan
def schedule_tasks(tasks: List[dict]) -> List[List[str]]:
"""
Hilfsfunktion: Plant eine Liste von Aufgaben-Dictionaries.
Args:
tasks: Liste von Dictionaries, jedes mit den Schlüsseln 'id' (str),
optional 'name' (str) und 'dependencies' (Liste von Aufgaben-ID-Strings).
Returns:
Eine Liste von Listen von Aufgaben-IDs. Jede innere Liste ist ein Batch von Aufgaben,
die parallel ausgeführt werden können; Batches werden sequenziell in der Reihenfolge ausgeführt.
Raises:
ValueError: Wenn eine Abhängigkeit auf eine nicht existierende Aufgaben-ID verweist, wenn
eine zirkuläre Abhängigkeit besteht oder wenn Aufgaben-IDs dupliziert werden.
"""
return TaskScheduler(tasks).schedule()
if name == 'main':
# Beispiel 1
tasks = [
{'id': 'A', 'name': 'Datenerfassung', 'dependencies': []},
{'id': 'B', 'name': 'Datenbereinigung', 'dependencies': ['A']},
{'id': 'C', 'name': 'Feature Engineering', 'dependencies': ['A']},
{'id': 'D', 'name': 'Modelltraining', 'dependencies': ['B', 'C']},
{'id': 'E', 'name': 'Logging einrichten', 'dependencies': []},
{'id': 'F', 'name': 'Modellbereitstellung', 'dependencies': ['D']},
]
print(schedule_tasks(tasks))
# -> [['A', 'E'], ['B', 'C'], ['D'], ['F']]
# Beispiel 2
pipeline = [
{'id': 'checkout', 'dependencies': []},
{'id': 'build', 'dependencies': ['checkout']},
{'id': 'unit_test', 'dependencies': ['build']},
{'id': 'integration_test', 'dependencies': ['build']},
{'id': 'deploy', 'dependencies': ['unit_test', 'integration_test']},
]
print(schedule_tasks(pipeline))
# -> [['checkout'], ['build'], ['integration_test', 'unit_test'], ['deploy']]
# Beispiel 3: zirkuläre Abhängigkeit löst ValueError aus
cyclic = [
{'id': 'X', 'dependencies': ['Y']},
{'id': 'Y', 'dependencies': ['X']},
]
try:
schedule_tasks(cyclic)
except ValueError as e:
print(f"ValueError: {e}")
# Beispiel 4: unbekannte Abhängigkeit löst ValueError aus
broken = [
{'id': 'A', 'dependencies': ['missing_task']},
]
try:
schedule_tasks(broken)
except ValueError as e:
print(f"ValueError: {e}")
Ergebnis
Siegstimmen
2 / 3
Durchschnittsscore
Gesamtpunktzahl
Gesamtkommentar
Antwort A ist funktional korrekt und verwendet den Algorithmus von Kahn mit einer sauberen Level-by-Level (Batch)-Topologischen Sortierung. Sie validiert doppelte IDs, unbekannte Abhängigkeiten und Selbstschleifen und erkennt Zyklen durch die Überprüfung der geplanten Anzahl, wobei beschreibende ValueErrors ausgelöst werden, die sogar die betreffenden Aufgaben auflisten. Bemerkenswerte Stärken: Sie erstellt den Graphen zur Konstruktionszeit (sodass Fehler bei unbekannten Abhängigkeiten sofort auftreten), liefert deterministische Ausgaben durch Sortierung, enthält gründliche Docstrings, Typ-Annotationen und einen umfassenden Satz ausführbarer Beispiele, die sowohl gültige als auch ungültige Fälle (Zyklus und unbekannte Abhängigkeit) abdecken. Sie bietet sowohl eine Klassen-API als auch eine praktische Funktion, die der Flexibilität der Aufforderung entspricht. Kleinere Schwächen: Das Sortieren jeder Charge ist ein kleiner (und nicht angeforderter) Mehraufwand, und die sofortige Überprüfung von Selbstabhängigkeiten ist angesichts des Zykluserkenners leicht redundant, aber beides ist nicht schädlich.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Implementiert korrekt den Level-by-Level-Algorithmus von Kahn; gültige Zeitpläne entsprechen den erwarteten Ausgaben, Zyklen und unbekannte Abhängigkeiten lösen beide ValueError aus, und die Zykluserkennung über scheduled_count ist solide. Sortierte Batches garantieren korrekte, deterministische Ergebnisse.
Vollstandigkeit
Gewichtung 20%Behandelt Duplikate, unbekannte Abhängigkeiten, Selbstschleifen und Zyklen; bietet sowohl Klassen- als auch Funktions-APIs; und enthält vier ausführbare Beispiele, die gültige Zeitpläne sowie beide Fehlertypen abdecken und eine vollständige Abdeckung demonstrieren.
Codequalitat
Gewichtung 20%Saubere Klassenstruktur mit klarer Trennung von Validierung und Planung, gute Docstrings, Typ-Annotationen und informative Fehlermeldungen. Leichte Redundanz bei der expliziten Überprüfung von Selbstabhängigkeiten.
Praktischer Nutzen
Gewichtung 15%Determinierte Ausgabe und sofortige Validierung bei der Konstruktion machen sie zuverlässig und einfach zu integrieren; ausführbare Beispiele für alle Pfade erleichtern die praktische Übernahme.
Befolgung der Anweisungen
Gewichtung 10%Entspricht dem erforderlichen Ausgabeformat, löst ValueError für beide erforderlichen Fehlerfälle mit beschreibenden Meldungen aus und stellt wie gefordert eine Funktion bereit; entspricht vollständig der Aufforderung.
Gesamtpunktzahl
Gesamtkommentar
Antwort A ist eine starke, größtenteils korrekte Implementierung mit dem Level-by-Level Kahn-Algorithmus und gibt parallelisierbare Batches sauber zurück. Sie behandelt unbekannte Abhängigkeiten, doppelte IDs und Zyklen mit klaren Fehlermeldungen, und der Code ist lesbar und gut strukturiert. Ihre Hauptschwäche ist, dass sie weniger Eingabevalidierung durchführt als Antwort B und bei fehlerhaften Eingaben, die über die Kernanforderungen der Aufforderung hinausgehen, etwas weniger robust ist.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Implementiert die Batch-weise topologische Sortierung korrekt und erkennt unbekannte Abhängigkeiten und Zyklen. Sie erkennt auch explizit Selbstabhängigkeiten. Eine geringfügige Einschränkung besteht darin, dass fehlerhafte Datentypen im Abhängigkeitsfeld nicht validiert werden und zu unbeabsichtigtem Verhalten anstelle eines klaren, vertragstreuen Fehlers führen könnten.
Vollstandigkeit
Gewichtung 20%Deckt alle erforderlichen Verhaltensweisen ab und fügt sogar die Behandlung doppelter IDs und Beispiele hinzu. Sie ist jedoch weniger vollständig in Bezug auf die Validierung fehlerhafter Eingaben, wie z. B. nicht-listenförmige Abhängigkeiten oder nicht-zeichenkettenförmige IDs über das fehlende 'id' hinaus.
Codequalitat
Gewichtung 20%Gut organisiert mit klarem Klassendesign, Hilfsmethoden, Typ-Hints, Docstrings und deterministisch sortierten Batches. Die Struktur ist leicht zu verfolgen und zu warten.
Praktischer Nutzen
Gewichtung 15%Praktisch nutzbar, insbesondere mit deterministischer Batch-Sortierung und aussagekräftigen Fehlermeldungen. Sie ist etwas weniger defensiv gegenüber fehlerhaften Eingaben, was die Robustheit in Produktionsumgebungen verringert.
Befolgung der Anweisungen
Gewichtung 10%Folgt der Aufforderung genau: Akzeptiert Task-Dictionaries, gibt Listen-von-Listen-Batches zurück und löst ValueError für Zyklen und fehlende Abhängigkeiten aus. Die Implementierung und die Beispiele stimmen gut mit dem erforderlichen Antworttyp überein.
Gesamtpunktzahl
Gesamtkommentar
Antwort A bietet eine ausgezeichnete, professionelle Lösung. Sie verwendet ein gut strukturiertes klassenbasiertes Design, das den Aufbau und die Validierung des Graphen sauber von der Planungslogik trennt. Die Implementierung von Kahns Algorithmus ist korrekt und effizient. Die Fehlerbehandlung ist robust und deckt alle spezifizierten Fälle sowie zusätzliche wie doppelte Task-IDs ab. Der Code ist sauber, gut dokumentiert mit Typ-Annotationen und enthält einen umfassenden `if __name__ == '__main__'`-Block, der als eine Reihe von Mini-Tests dient und sowohl erfolgreiche Ausführung als auch Fehlerbehandlung demonstriert.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Die Implementierung von Kahns Algorithmus ist fehlerfrei. Sie identifiziert korrekt Batches von parallelisierbaren Tasks und respektiert alle Abhängigkeiten, was für die gegebenen Beispiele das korrekte Ergebnis liefert.
Vollstandigkeit
Gewichtung 20%Die Lösung behandelt alle spezifizierten Anforderungen korrekt, einschließlich der Erkennung von zirkulären Abhängigkeiten und nicht existierenden Abhängigkeiten. Sie fügt auch nützliche Prüfungen auf doppelte Task-IDs und Selbstabhängigkeiten hinzu.
Codequalitat
Gewichtung 20%Die Codequalität ist ausgezeichnet. Das klassenbasierte Design bietet eine großartige Struktur und Trennung der Zuständigkeiten. Der Code ist sauber, lesbar, gut dokumentiert und verwendet Typ-Annotationen effektiv. Der `if __name__ == '__main__'`-Block ist umfassend und demonstriert die volle Funktionalität des Codes.
Praktischer Nutzen
Gewichtung 15%Die Lösung ist sehr praktisch. Das klassenbasierte Design erleichtert die Wiederverwendung und Erweiterung. Eine Instanz des Planers könnte erstellt und ihr Graph inspiziert oder wiederverwendet werden, was in realen Anwendungen eine häufige Anforderung ist.
Befolgung der Anweisungen
Gewichtung 10%Die Antwort folgt perfekt allen Anweisungen, einschließlich des Eingabe-/Ausgabeformats, der Wahl einer Funktion oder Klasse und der spezifischen Anforderungen an die Fehlerbehandlung.