Reponse A: Anthropic Claude Fable 5
Planificateur de tâches basé sur les dépendances.
Produit un plan d'exécution sous forme de liste de lots (listes d'identifiants de tâches), où chaque tâche d'un lot peut s'exécuter simultanément, et les lots doivent s'exécuter séquentiellement. Utilise l'algorithme de Kahn (tri topologique niveau par niveau).
Lève une ValueError en cas de :
- identifiants de tâches dupliqués,
- dépendances faisant référence à des identifiants de tâches inconnus,
- dépendances circulaires (avec les tâches fautives listées).
from collections import deque
from typing import Dict, List
class TaskScheduler:
"""Planifie...
Afficher la reponse complete ▼
Planificateur de tâches basé sur les dépendances.
Produit un plan d'exécution sous forme de liste de lots (listes d'identifiants de tâches), où chaque tâche d'un lot peut s'exécuter simultanément, et les lots doivent s'exécuter séquentiellement. Utilise l'algorithme de Kahn (tri topologique niveau par niveau).
Lève une ValueError en cas de :
- identifiants de tâches dupliqués,
- dépendances faisant référence à des identifiants de tâches inconnus,
- dépendances circulaires (avec les tâches fautives listées).
from collections import deque
from typing import Dict, List
class TaskScheduler:
"""Planifie les tâches en lots parallélisables en fonction des dépendances."""
def __init__(self, tasks: List[dict]):
self.tasks = tasks
self._validate_and_build_graph()
def _validate_and_build_graph(self) -> None:
# Collecte tous les identifiants de tâches et vérifie les doublons.
self.task_ids = set()
for task in self.tasks:
if 'id' not in task:
raise ValueError(f"La tâche est manquante de la clé 'id' requise : {task!r}")
task_id = task['id']
if task_id in self.task_ids:
raise ValueError(f"Identifiant de tâche dupliqué trouvé : '{task_id}'")
self.task_ids.add(task_id)
# Construit la liste d'adjacence (dépendance -> dépendants) et la carte des degrés entrants.
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"La tâche '{task_id}' dépend de l'identifiant de tâche inconnu '{dep}'"
)
if dep == task_id:
raise ValueError(
f"La tâche '{task_id}' dépend d'elle-même (dépendance circulaire)"
)
self.dependents[dep].append(task_id)
self.in_degree[task_id] += 1
def schedule(self) -> List[List[str]]:
"""
Retourne le plan d'exécution sous forme de liste de lots.
Chaque lot est une liste d'identifiants de tâches qui peuvent être exécutés simultanément.
Les lots doivent être exécutés dans l'ordre retourné.
"""
in_degree = dict(self.in_degree) # travaille sur une copie
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) # sortie déterministe (l'ordre est arbitraire)
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(
"Dépendance circulaire détectée impliquant les tâches : "
+ ", ".join(f"'{t}'" for t in unresolved)
)
return plan
def schedule_tasks(tasks: List[dict]) -> List[List[str]]:
"""
Fonction de commodité : planifie une liste de dictionnaires de tâches.
Args:
tasks: Liste de dictionnaires, chacun avec les clés 'id' (str), éventuellement 'name'
(str), et 'dependencies' (liste de chaînes d'identifiants de tâches).
Returns:
Une liste de listes d'identifiants de tâches. Chaque liste interne est un lot de tâches
qui peuvent s'exécuter en parallèle ; les lots s'exécutent séquentiellement dans l'ordre.
Raises:
ValueError: Si une dépendance fait référence à un identifiant de tâche inexistant, si une
dépendance circulaire existe, ou si les identifiants de tâches sont dupliqués.
"""
return TaskScheduler(tasks).schedule()
if name == 'main':
# Exemple 1
tasks = [
{'id': 'A', 'name': 'Ingestion de données', 'dependencies': []},
{'id': 'B', 'name': 'Nettoyage de données', 'dependencies': ['A']},
{'id': 'C', 'name': 'Ingénierie des caractéristiques', 'dependencies': ['A']},
{'id': 'D', 'name': 'Entraînement du modèle', 'dependencies': ['B', 'C']},
{'id': 'E', 'name': 'Configuration de la journalisation', 'dependencies': []},
{'id': 'F', 'name': 'Déploiement du modèle', 'dependencies': ['D']},
]
print(schedule_tasks(tasks))
# -> [['A', 'E'], ['B', 'C'], ['D'], ['F']]
# Exemple 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']]
# Exemple 3 : la dépendance circulaire lève une ValueError
cyclic = [
{'id': 'X', 'dependencies': ['Y']},
{'id': 'Y', 'dependencies': ['X']},
]
try:
schedule_tasks(cyclic)
except ValueError as e:
print(f"ValueError: {e}")
# Exemple 4 : la dépendance inconnue lève une ValueError
broken = [
{'id': 'A', 'dependencies': ['missing_task']},
]
try:
schedule_tasks(broken)
except ValueError as e:
print(f"ValueError: {e}")
Resultat
Votes gagnants
2 / 3
Score moyen
Score total
Commentaire global
La réponse A est fonctionnellement correcte, utilisant l'algorithme de Kahn avec un tri topologique propre niveau par niveau (par lots). Elle valide les identifiants dupliqués, les dépendances inconnues et les auto-boucles, et détecte les cycles via la vérification du nombre d'éléments planifiés, levant des ValueErrors descriptives qui listent même les tâches incriminées. Points forts notables : elle construit le graphe au moment de la construction (donc les erreurs de dépendance inconnue apparaissent rapidement), produit une sortie déterministe via le tri, inclut des docstrings complètes, des indications de type et un ensemble complet d'exemples exécutables couvrant les cas valides et invalides (cycle et dépendance inconnue). Elle offre également à la fois une API de classe et une fonction de commodité, correspondant à la flexibilité de l'invite. Points faibles mineurs : le tri de chaque lot représente une petite surcharge (et non demandée), et la vérification explicite de l'auto-dépendance est légèrement redondante étant donné le détecteur de cycle, mais rien de nuisible.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%Implémente correctement l'algorithme de Kahn niveau par niveau ; les planifications valides correspondent aux sorties attendues, les cycles et les dépendances inconnues lèvent tous deux une ValueError, et la vérification des cycles via scheduled_count est solide. Les lots triés garantissent des résultats corrects et déterministes.
Completude
Poids 20%Gère les doublons, les dépendances inconnues, les auto-boucles et les cycles ; fournit des API de classe et de fonction ; et inclut quatre exemples exécutables couvrant les planifications valides plus les deux types d'erreurs, démontrant une couverture complète.
Qualite du code
Poids 20%Structure de classe propre avec une séparation claire de la validation et de la planification, de bonnes docstrings, des indications de type et des messages d'erreur informatifs. Légère redondance dans la vérification explicite de l'auto-dépendance.
Valeur pratique
Poids 15%La sortie déterministe et la validation rapide à la construction la rendent fiable et facile à intégrer ; les exemples exécutables pour tous les chemins facilitent l'adoption pratique.
Respect des consignes
Poids 10%Correspond au format de sortie requis, lève une ValueError pour les deux cas d'erreur requis avec des messages descriptifs, et fournit une fonction comme demandé ; s'aligne entièrement sur l'invite.
Score total
Commentaire global
La réponse A est une implémentation solide et majoritairement correcte utilisant l'algorithme de Kahn niveau par niveau, et elle retourne proprement des lots parallélisables. Elle gère les dépendances inconnues, les ID dupliqués et les cycles avec des erreurs claires, et le code est lisible et bien structuré. Sa principale faiblesse est qu'elle effectue moins de validation d'entrée que la réponse B et est légèrement moins robuste pour les entrées malformées au-delà des exigences principales de l'invite.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%Implémente correctement le tri topologique par lots et détecte les dépendances inconnues et les cycles. Elle détecte également explicitement l'auto-dépendance. Une limitation mineure est que les types de champs de dépendance malformés ne sont pas validés et pourraient entraîner un comportement involontaire plutôt qu'une erreur claire préservant le contrat.
Completude
Poids 20%Couvre tous les comportements requis et ajoute même la gestion des ID dupliqués et des exemples. Cependant, elle est moins complète en ce qui concerne la validation des entrées malformées telles que les dépendances non listées ou les ID non textuels au-delà de l'absence d''id'.
Qualite du code
Poids 20%Bien organisé avec une conception de classe claire, une méthode d'aide, des indications de type, des docstrings et des lots triés déterministes. La structure est facile à suivre et à maintenir.
Valeur pratique
Poids 15%Utile en pratique, notamment avec un ordre de lots déterministe et des erreurs descriptives. Elle est quelque peu moins défensive contre les entrées malformées, ce qui réduit la robustesse dans les contextes de production.
Respect des consignes
Poids 10%Suit de près l'invite : accepte les dictionnaires de tâches, retourne des lots de listes de listes et lève une ValueError pour les cycles et les dépendances manquantes. L'implémentation et les exemples correspondent bien au type de réponse requis.
Score total
Commentaire global
La réponse A fournit une solution d'excellente qualité professionnelle. Elle utilise une conception basée sur des classes bien structurée qui sépare proprement la construction et la validation du graphe de la logique de planification. L'implémentation de l'algorithme de Kahn est correcte et efficace. La gestion des erreurs est robuste, couvrant tous les cas spécifiés ainsi que des cas supplémentaires tels que les identifiants de tâches en double. Le code est propre, bien documenté avec des indications de type, et inclut un bloc `if __name__ == '__main__'` complet qui sert d'ensemble de mini-tests, démontrant à la fois l'exécution réussie et la gestion des erreurs.
Afficher le detail de l evaluation ▼
Exactitude
Poids 35%L'implémentation de l'algorithme de Kahn est sans faille. Elle identifie correctement les lots de tâches parallélisables et respecte toutes les dépendances, produisant le résultat correct pour les exemples donnés.
Completude
Poids 20%La solution gère correctement toutes les exigences spécifiées, y compris la détection des dépendances circulaires et des dépendances inexistantes. Elle ajoute également des vérifications utiles pour les identifiants de tâches en double et les auto-dépendances.
Qualite du code
Poids 20%La qualité du code est excellente. La conception basée sur des classes offre une grande structure et une bonne séparation des préoccupations. Le code est propre, lisible, bien documenté et utilise efficacement les indications de type. Le bloc `if __name__ == '__main__'` est complet et démontre la pleine fonctionnalité du code.
Valeur pratique
Poids 15%La solution est très pratique. La conception basée sur des classes la rend facile à réutiliser et à étendre. Une instance du planificateur pourrait être créée et son graphe inspecté ou réutilisé, ce qui est une exigence courante dans les applications du monde réel.
Respect des consignes
Poids 10%La réponse suit parfaitement toutes les instructions, y compris le format d'entrée/sortie, le choix d'une fonction ou d'une classe, et les exigences spécifiques de gestion des erreurs.