Boucler la boucle : renvoyer aux humains les erreurs des agents et les désaccords du juge
Le temps de relecture humaine est la ressource la plus rare dans l'évaluation des agents. Potato 2.6 associe une file de triage fondée sur des signaux à un alignement juge-humain, pour que les pires traces parviennent d'abord aux humains et que votre juge LLM ne cesse de s'améliorer.
Dès que vous évaluez des agents à une échelle quelconque, la contrainte cesse d'être « peut-on étiqueter cela » pour devenir « à qui consacrer notre attention, et sur quoi ». Vous avez des milliers de traces de production et une poignée de relecteurs. Un juge LLM peut tout présélectionner, mais il est imparfait, et les cas où il se trompe sont exactement ceux qui méritent le temps d'un humain.
Deux fonctionnalités de Potato 2.6 collaborent pour gérer cette rareté. Une file de triage fondée sur des signaux décide ce que les humains voient en premier. L'alignement juge-humain mesure jusqu'où vous pouvez vous appuyer sur le juge, et l'améliore. Exécutez-les ensemble et vous obtenez une boucle d'évaluation active : le juge absorbe le volume facile, les cas suspects sautent la file vers les humains, et les désaccords nourrissent un meilleur juge.
Cet article couvre les deux moitiés et la façon dont elles se relient.
Le badge de la file de triage dans Potato
La moitié triage : le pire d'abord, et non premier entré premier sorti
Par défaut, une file d'annotation est FIFO : les éléments sont servis dans l'ordre où ils ont été chargés. C'est le mauvais ordre quand le temps de relecture est rare. Une trace propre et une trace où l'agent a levé une erreur méritent des quantités d'attention humaine très différentes, et FIFO les traite de la même façon.
La file de triage réordonne la file selon un signal de qualité par élément. Le signal peut être une erreur d'agent, un pouce vers le bas en production, un score automatique faible, ou n'importe quel champ de vos données :
triage:
enabled: true
order: desc # high priority first (default)
show_badge: true # banner during annotation explaining the priority
rules: # evaluated in order; highest matching priority wins
- name: "Agent errored"
priority: 100
when:
field: status
equals: error
- name: "Negative feedback"
priority: 80
when:
field: feedback
in: [thumbs_down, negative]
- name: "Low quality score"
priority: 60
when:
field: score
lt: 0.5
assignment_strategy: priorityLes règles sont évaluées de haut en bas et la priorité correspondante la plus élevée l'emporte, de sorte qu'une trace en erreur qui comporte aussi un retour négatif aboutit quand même à 100. Si vous omettez entièrement rules, Potato se rabat sur un jeu par défaut judicieux (statut d'erreur à 100, retour négatif à 80, score inférieur à 0.5 à 60), si bien que le comportement clé en main est raisonnable avant tout réglage.
Les opérateurs de condition couvrent les comparaisons dont vous avez réellement besoin :
| Operator | Signification |
|---|---|
equals | correspondance exacte (les chaînes sont insensibles à la casse) |
in | la valeur fait partie d'une liste |
contains | la liste contient, ou correspondance de sous-chaîne |
lt / lte / gt / gte | comparaison numérique |
exists | le champ est présent ou absent |
Lorsque le signal est déjà un nombre, vous pouvez le lire directement depuis le champ au lieu d'écrire des règles :
triage:
enabled: true
signal_field: quality_score
invert_signal: true # lower score => higher priorityCela fonctionne aussi sur le trafic en direct
Le score de priorité est calculé une seule fois quand un élément est chargé ou ingéré, puis stocké sur l'élément, de sorte que l'affectation reste peu coûteuse. Cette même conception fait que l'ingestion à l'exécution fonctionne d'emblée : une trace poussée en cours de session via le point de terminaison webhook ou un poller Langfuse est notée à son arrivée et s'insère dans l'ordre de priorité. Une trace à faible score ou en erreur qui arrive à 14 h passe devant les traces propres encore en attente depuis ce matin. Définir assignment_strategy: priority est ce qui fait que la file sert réellement dans cet ordre ; show_badge est indépendant, si bien que la bannière « pourquoi ceci a-t-il été signalé » s'affiche même si vous conservez une autre stratégie.
La moitié alignement : jusqu'où faire confiance au juge
Le triage décide ce que les humains voient. L'alignement décide quelle part du reste vous pouvez confier au juge sans supervision, et il resserre le juge au fil du temps.
Judge Alignment exécute un juge LLM configurable sur des instances que vos annotateurs ont déjà étiquetées, puis rapporte le κ de Cohen (kappa de Cohen, une mesure d'accord), une matrice de confusion et une liste de désaccords face au gold humain. La pratique standard (aligner un juge sur environ 100 à 200 étiquettes gold, inspecter là où il diverge, réécrire la rubric, puis relancer) est la boucle autour de laquelle ceci est conçu.
ai_support:
enabled: true
endpoint_type: "ollama"
ai_config:
model: "llama3.2"
temperature: 0.0
judge_alignment:
enabled: true
schemas:
correctness:
rubric: >
Label 'correct' only if the agent's answer is factually right and fully
satisfies the request; otherwise 'incorrect'.
inline:
enabled: true # show the judge verdict beside the human label
schemas: [correctness]Vous exécutez le juge depuis l'API d'administration, et les prédictions sont mises en cache par version de prompt, de sorte que les relances sont peu coûteuses :
curl -X POST localhost:8000/admin/api/judge-alignment/run \
-H "X-API-Key: <admin-key>" \
-H "Content-Type: application/json" \
-d '{"max_per_schema": 200}'Lorsque vous voulez calibrer, transmettez une rubric modifiée. Cela crée une nouvelle version de prompt, vous pouvez ainsi comparer κ d'un cycle à l'autre et voir réellement si votre réécriture a aidé :
curl -X POST localhost:8000/admin/api/judge-alignment/run \
-H "X-API-Key: <admin-key>" -H "Content-Type: application/json" \
-d '{"rubrics": {"correctness": "Stricter rubric text..."}}'Le rapport, disponible en JSON ou en page rendue sur /admin/judge-alignment, affiche κ avec une interprétation Landis–Koch, la matrice de confusion, une table de désaccords avec le raisonnement du juge, et un historique des versions de prompt, de sorte que la progression de la calibration soit visible d'un cycle à l'autre.
Le mode en ligne le place devant l'annotateur
Avec inline.enabled, chaque page d'annotation affiche le verdict mis en cache du juge à côté de l'étiquette humaine (son choix, sa confiance et un raisonnement dépliable) aux côtés d'un κ en cours pour la tâche. « Accepter » remplit le choix correspondant. Chaque enregistrement humain consigne une comparaison humain↔juge qui alimente l'accord en cours, si bien que le κ vers lequel vous réglez se met à jour à mesure que les gens travaillent.
Réunir les deux
Les fonctionnalités sont conçues pour se composer en une seule boucle :
La boucle d'évaluation active : triage, relecture humaine, alignement du juge, affinage de la rubric
- Le triage pousse les traces en erreur et à faible confiance en tête de la file humaine.
- Les humains relisent ces éléments à forte valeur, produisant des étiquettes gold fraîches exactement là où le système est le moins sûr.
- L'alignement note le juge face à ce gold, et la liste de désaccords montre précisément là où le juge et les humains divergent.
- Vous affinez la rubric, relancez, observez κ bouger, puis laissez le juge mieux calibré absorber davantage de volume facile, pour que le temps humain continue d'affluer vers les cas difficiles.
Chaque tour de boucle dépense l'attention humaine là où elle vaut le plus et la convertit en un juge auquel vous pouvez faire confiance un cran plus loin. C'est tout l'enjeu : non pas retirer les humains de l'évaluation des agents, mais les orienter.
Les deux fonctionnalités sont livrées dans Potato 2.6. Consultez la documentation de la file de triage et la documentation de l'alignement du juge pour la référence complète, ainsi que l'affichage eval_trace pour lire rapidement les traces priorisées.