Créer des évaluations en ligne
Une évaluation en ligne note automatiquement soit chaque $ai_generation correspondante, soit la trace entière qui la contient, jusqu'à désactivation. Une bonne évaluation provient d'un vrai mode de défaillance trouvé dans le trafic de production, non d'une hypothèse ou d'une métrique générique comme « hallucination » ou « utilité ». Cette compétence démarre une fois ces modes de défaillance identifiés et les transforme en évaluations scoped et continuellement exécutées.
Une évaluation par mode de défaillance, et autant d'évaluations que les données le justifient. Le nombre à créer est un jugement que vous portez sur ce que les traces ont réellement montré — parfois un, souvent trois ou quatre. Ne supposez jamais que la réponse est un, et ne regroupez jamais plusieurs modes dans un seul évaluateur.
Proposez avant de créer. Présentez à l'utilisateur une courte liste d'évaluations candidates et laissez-le choisir lesquelles il souhaite (Phase 1.1). Créer des évaluations qu'il n'a pas demandées lui coûte de l'argent et du bruit.
D'abord, sachez ce que vous évaluez. Trouver et classer les modes de défaillance worth catching est un travail séparé. Si l'utilisateur ne précise pas ce qu'il veut évaluer, demandez-le. S'il est encore vague à ce sujet et ne se réfère pas à un mode de défaillance spécifique, exécutez exploring-ai-failures pour scoper un cas d'usage, trouver des traces défaillantes et produire une liste classée de modes de défaillance.
Pour la mécanique de rédaction et itération d'un évaluateur (source Hog vs prompt LLM-judge, dry-running, debug d'une évaluation en direct), reportez-vous à exploring-llm-evaluations.
Outils
| Outil | Objectif |
|---|---|
posthog:llma-evaluation-config-get |
Vérifier la clé du fournisseur actif utilisée par les judges non épinglés |
posthog:llma-provider-key-list |
Trouver une clé de fournisseur utilisable (état ok) à épingler |
posthog:llma-evaluation-judge-models |
Lister les combos fournisseur+modèle valides |
posthog:llma-evaluation-directory-list |
Lister les répertoires disponibles pour organiser l'évaluation |
posthog:llma-evaluation-directory-create |
Créer un répertoire quand l'utilisateur en demande un |
posthog:llma-evaluation-test-hog |
Dry-run source Hog contre les générations récentes avant création |
posthog:llma-evaluation-create |
Créer l'évaluation (toujours enabled: false d'abord) |
posthog:llma-evaluation-run |
Spot-run un eval brouillon contre une génération |
posthog:llma-evaluation-update |
Itérer la config, puis basculer enabled: true |
posthog:execute-sql |
Vérifier qu'une condition correspond aux événements et au volume attendus |
posthog:generate-app-url |
Construire un deep link qualifié par région et projet vers l'évaluation |
Le payload complet de création (tous les champs, les schémas de config, la forme exacte conditions) se trouve dans references/evaluation-payload.md.
Phase 1 — Décider ce que proposer, puis laisser l'utilisateur choisir
Partez de vraies défaillances observées, non de métriques choisies à l'avance. Si vous ne les avez pas déjà, exécutez exploring-ai-failures pour scoper un cas d'usage, trouver des traces défaillantes et produire une liste classée de modes de défaillance — puis revenir.
1.1 — Transformer les modes de défaillance en ensemble de candidats
Laissez les données décider du nombre. Un mode de défaillance = une évaluation, donc une liste classée de quatre modes distincts = un ensemble candidat de quatre évaluations. Ne les effondrez pas en un seul évaluateur qui essaie de tout attraper, et ne vous arrêtez pas au mode principal quand les traces ont clairement montré plus worth watching. Gardez un candidat quand :
- Ça fait mal. Fréquent ou douloureux. Une poignée de modes couvre généralement la majorité des défaillances.
- C'est vérifiable. Ça se réduit à un critère clair — « la réponse doit rester sur le sujet de l'utilisateur », « l'appel tool doit inclure un
order_id». Si vous ne pouvez pas l'énoncer en une ligne, ce n'est pas prêt à proposer. - C'est distinct. Deux candidats qui échouent sur les mêmes générations = une seule évaluation.
Classez par l'ampleur du problème et proposez environ les cinq premiers ; mentionnez en une ligne que vous avez mis de côté les plus faibles plutôt que de les laisser de côté silencieusement.
1.2 — Proposer l'ensemble en langage clair
Ne créez jamais d'évaluations que l'utilisateur n'a pas choisies. Présentez les candidats et demandez lesquels il souhaite.
Supposez qu'il n'a pas lu les traces avec vous et ne connaît pas le vocabulaire des évaluations. Gardez chacun à une ou deux lignes — un mur de texte par évaluation signifie qu'il ne peut pas les comparer — mais rendez évident ce qu'il regarde et ce qu'il verra quand il échoue. Pas de snippets Hog, filtres de propriété ou internals hog/llm_judge ici. Numérotez-les pour qu'il puisse répondre « 1 et 3 » :
Trouvé 3 motifs de défaillance worth watching. Lesquels dois-je configurer ?
1. Les réponses dérivent du sujet — vérifie que la réponse aborde ce que l'utilisateur a demandé. Attrape les réponses de support qui répondent avec confiance à une question différente. Vu ~40×/jour.
2. Les recherches de commandes manquent d'un order ID — vérifie que chaque appel
lookup_orderinclut unorder_id. Attrape les défaillances silencieuses d'outil qui font que l'agent invente un statut de commande. Vu ~15×/jour.3. Refuse les questions qu'il peut répondre — vérifie que la réponse n'est pas un « Je ne peux pas aider avec ça » inutile. Attrape les utilisateurs détournés de choses que la documentation couvre. Coûte un appel LLM par vérification.
Par évaluation : ce qu'elle vérifie, quelle mauvaise chose elle révèle, et volume approximatif si vous le connaissez (Phase 2.5 le vérifie correctement). Signalez ceux qui ont besoin d'un judge LLM, car ils coûtent par exécution — c'est la seule mécanique worth exposing d'avance. Gardez le classement implicite dans l'ordre ; ignorez les tableaux de score.
Recommandez un point de départ s'il semble indécis (généralement le premier ou les deux premiers), et traitez « tous » ou « allez-y » comme acceptant l'ensemble complet. Si un ajustement de prompt pourrait probablement corriger un mode, suggérez la correction aux côtés de l'évaluation plutôt qu'en remplacement — un taux de réussite croissant confirme que le fix a fonctionné.
1.3 — Quand rien ne s'est dégagé, proposer la sentiment
Si les traces ont été lues et qu'aucun mode de défaillance ne vaut une évaluation, n'inventez pas une défaillance et ne revenir pas les mains vides. Dites ce que vous avez regardé, puis proposez une évaluation sentiment comme baseline :
Aucun motif de défaillance clair les 7 derniers jours. Worth starting with :
1. Frustration utilisateur — étiquète chaque message utilisateur comme positif, neutre ou négatif. Montre quelles conversations vont mal, ce qui est généralement où se cachent les vraies défaillances. Pas de coût judge.
Ça n'a besoin de clé de fournisseur, c'est bon marché, et ça leur donne un signal pour revenir une fois qu'il y a assez de trafic pour repérer les motifs.
Pas de générations = pas d'évaluation d'aucune sorte. Les évaluations sentiment marquent seulement les événements $ai_generation correspondants, donc si le projet n'en a aucun, chaque évaluation que vous pourriez créer — sentiment inclus — resterait sans jamais s'exécuter. N'en proposez pas. Pointez-les vers instrument-llm-analytics pour que l'observabilité AI capture d'abord les générations, et revenez à cette compétence une fois qu'il y a du trafic à lire.
Phase 2 — Construire chaque évaluation acceptée
Exécutez 2.1 à 2.5 une fois par évaluation que l'utilisateur a choisie, pour que chacun se termine comme brouillon vérifié. Laissez chacun d'eux désactivé jusqu'à ce que l'ensemble complet soit vérifié — 2.6 est un passage unique sur l'ensemble fini à la fin, pas la dernière étape de chaque boucle. Activer eval 1 alors que eval 3 est encore en cours de rédaction met un ensemble partiellement en direct en production, ce qui est du bruit et (pour un judge) coûte à l'utilisateur ce qu'il n'a pas encore approuvé.
2.1 — Choisir le type d'évaluation
| Utiliser… | Quand le critère est… |
|---|---|
hog |
Structurel / basé sur règles (JSON parses, longueur, regex, forme appel tool). Bon marché, déterministe, pas de clé de fournisseur nécessaire. |
llm_judge |
Subjectif / flou (ton, factualité, sur le sujet). Coûte un appel LLM par exécution ; nécessite un fournisseur, modèle et clé de fournisseur utilisable. |
sentiment |
Vous voulez des étiquettes sentiment sur les messages utilisateur, pas un pass/fail (sauf si très spécifiquement demandé, généralement non pertinent pour cette compétence). |
Cherchez hog d'abord, escaladez à llm_judge s'il n'y a aucun moyen déterministe de vérifier ce que nous voulons vérifier.
2.2 — Choisir la cible
| Cible | Comportement |
|---|---|
generation |
S'exécute une fois pour chaque $ai_generation correspondante, immédiatement après ingestion. C'est le défaut. |
trace |
S'exécute une fois pour la trace entière après la première génération correspondante et une attente configurable pour que la trace se termine. |
session |
S'exécute une fois pour toute la session $ai_session_id, après que la session se stabilise. |
Pour une cible trace, envoyez "target": "trace" plus une config settle qui contrôle quand la trace est évaluée, discriminée sur strategy :
{ "strategy": "fixed_window", "window_seconds": 1800 }— évaluer une attente fixe après la première génération correspondante. Entre 10 secondes et 2 heures, défaut 30 minutes. Untarget_configsans cléstrategysignifie ceci.{ "strategy": "inactivity", "quiet_period_seconds": 300, "max_age_seconds": 7200 }— évaluer une fois que la trace n'a eu aucune activité nouvelle pendant la période quiet (10 secondes à 30 minutes, défaut 5 minutes).max_age_secondsplafonne l'attente totale depuis la première génération correspondante (1 minute à 2 heures, défaut 2 heures, doit être au moins la période quiet).
Une cible session prend la même config settle avec limites session-sized, et défauts à inactivity plutôt que fixed_window :
{ "strategy": "inactivity", "quiet_period_seconds": 3600, "max_age_seconds": 86400 }— évaluer une fois que la session n'a eu aucune activité nouvelle pendant la période quiet (10 secondes à 24 heures, défaut 1 heure).max_age_secondsplafonne l'attente totale depuis la première génération correspondante (1 minute à 7 jours, défaut 24 heures, doit être au moins la période quiet).{ "strategy": "fixed_window", "window_seconds": 1800 }— évaluer une attente fixe après la première génération correspondante (10 secondes à 7 jours).
Une évaluation session ne s'exécute que pour les événements portant $ai_session_id. Les producteurs le définissent soit sur chaque génération soit pas du tout, donc un SDK qui ne le définit pas ne déclenchera jamais une évaluation session. $ai_session_id n'est pas $session_id : le second est la session d'analyse de produit PostHog et n'a aucun lien.
Une évaluation session peut aussi revenir skipped plutôt que notée. L'événement $ai_evaluation émis porte alors $ai_evaluation_skipped: true et un $ai_evaluation_skip_reason, et son $ai_evaluation_result est false quand l'évaluation interdit N/A, donc toute analyse des taux de réussite doit exclure les exécutions skipped plutôt que de les compter comme défaillances. Les sessions sont skipped quand elles contiennent plus de 2500 événements (généralement un id de session partagé entre conversations), quand rien n'a été trouvé dans la fenêtre d'évaluation, et, pour un judge LLM, quand la transcription est trop longue pour envoyer en intégralité.
Une session est évaluée au maximum une fois par évaluation, aussi longtemps que l'exécution complétée reste dans la fenêtre de rétention Temporal. Une session qui reprend longtemps après avoir été évaluée peut être évaluée à nouveau, donc choisissez une période quiet assez longue pour que la session soit vraiment terminée. Une période quiet plus longue ne coûte que la latence.
Les conditions correspondent toujours à la génération qui déclenche l'exécution ; l'évaluateur lui-même reçoit la trace ou session complète. Les évaluations sentiment ne supportent que la cible generation.
La nouvelle source Hog devrait utiliser les globals partagés par toutes les cibles :
| Global | Signification |
|---|---|
evaluation_events |
Un événement génération pour une cible generation, ou chaque événement capturé pour une cible trace ou session. |
target |
Le type, id, total_cost_usd et total_latency_seconds de la cible. |
item.input_text / item.output_text |
Projections lisibles best-effort ; utilisez-les pour longueur, keyword et checks regex. |
item.input / item.output |
Valeurs sérialisées originales ; utilisez-les quand l'évaluateur a besoin de parser le JSON capturé lui-même. |
Pour une cible session, target.id est l'id de session, et target.total_cost_usd / target.total_latency_seconds sont sommés à travers les traces de la session. total_latency_seconds est le temps passé sur travail IA, non wall-clock session ; les deux peuvent différer de plusieurs ordres de grandeur. Session wall-clock est dérivable des timestamps evaluation_events.
Les évaluations generation exposent toujours les globals top-level input, output, properties et event. Les évaluations trace exposent toujours leurs globals originaux events et trace. Ces globals sont conservés pour compatibilité avec les évaluateurs sauvegardés. Les évaluations session ne les portent pas : la source Hog session ne reçoit que target et evaluation_events. N'utilisez pas les globals spécifiques aux cibles dans la nouvelle source qui a besoin de fonctionner à travers les cibles. Les projections texte reconnaissent les payloads de fournisseurs communs mais ne sont pas autoritaires ; utilisez item.input / item.output quand la structure exacte importe.
2.3 — Configurer le judge LLM
Une évaluation llm_judge nécessite un provider et un model valides. Elle a aussi besoin d'une clé de fournisseur utilisable quand elle s'exécute. provider_key_id contrôle si l'évaluation en épingle une spécifique :
- Définissez
provider_key_idà l'UUID d'une clé en étatokpour le même fournisseur pour l'épingler. - Définissez
provider_key_idànullpour utiliser la clé de fournisseur actif d'équipe. La clé actif doit être en étatoket utiliser le même fournisseur quemodel_configuration.provider.
Les évaluations Hog et sentiment passent cette étape.
posthog:llma-evaluation-config-get // vérifier active_provider_key pour un judge non épinglé
posthog:llma-provider-key-list // trouver une clé état ok à épingler
posthog:llma-evaluation-judge-models // {} → chaque fournisseur et ses modèles ; { "provider": "openai" } restreint
Confirmer le fournisseur et modèle avec llma-evaluation-judge-models.
L'appeler sans arguments pour voir le catalogue entier à la fois.
Les fournisseurs pour lesquels PostHog ne finance aucun modèle reviennent vides sauf si vous passez key_id pour une des clés d'équipe ; la liste providers de la réponse signale lesquels. Préférez épingler la clé choisie pour qu'un changement de clé active d'équipe ultérieur ne change pas le fonctionnement de l'évaluation. Laissez provider_key_id à null seulement après que llma-evaluation-config-get confirme que la clé active est utilisable et son fournisseur correspond.
S'il n'y a pas de clé utilisable, vous pouvez quand même créer un brouillon désactivé pour que l'utilisateur révise. Ne spot-run ou activez pas. Demandez à l'utilisateur d'ajouter ou valider une clé dans l'UI avant de continuer.
2.4 — Le créer désactivé
Créer avec enabled: false pour que rien ne s'exécute jusqu'à ce que le scope soit vérifié. Exemple hog minimal :
Les évaluations peuvent être créées au top-level ou dans un répertoire. Si l'utilisateur nomme un répertoire, appelez posthog:llma-evaluation-directory-list et passez son UUID comme directory_id. Créer un répertoire seulement quand l'utilisateur le demande. Omettez directory_id ou passez null pour le top-level. Les répertoires ne peuvent pas être imbriqués.
posthog:llma-evaluation-create
{
"name": "Output is not empty",
"description": "Fails when a generation has no readable output",
"evaluation_type": "hog",
"evaluation_config": { "source": "let count := 0\nfor (let i, item in evaluation_events) {\n if (item.event == '$ai_generation') {\n count := count + 1\n if (length(trim(item.output_text)) == 0) { return false }\n }\n}\nreturn count > 0" },
"output_type": "boolean",
"output_config": { "allows_na": false },
"target": "generation",
"target_config": {},
"conditions": [
{ "id": "default", "rollout_percentage": 100, "properties": [{ "key": "$ai_model", "type": "event", "operator": "icontains", "value": "gpt" }] }
],
"enabled": false
}
Pour llm_judge, échangez evaluation_config contre { "prompt": "…" } et ajoutez "model_configuration": { "provider": "openai", "model": "gpt-5-mini", "provider_key_id": "<uuid of an ok-state key from llma-provider-key-list>" }. Utilisez null seulement quand la clé d'équipe active est ok et utilise le même fournisseur. Référence de champ complète : references/evaluation-payload.md.
2.5 — Vérifier le scope avant d'activer
conditions est où les evals en ligne se trompent : trop larges et vous évaluez (et facturez) un firehose ; trop étroits et ça ne s'exécute jamais. Confirmer que le filtre correspond aux événements attendus, et à peu près combien par jour :
posthog:execute-sql
SELECT count() AS matched, count() / 7 AS per_day
FROM events
WHERE event = '$ai_generation'
AND properties.$ai_model ILIKE '%gpt%' -- mirror each condition property
AND timestamp >= now() - INTERVAL 7 DAY
Pour les cibles generation, count() est le volume d'exécution. Pour les cibles trace, comptez les valeurs $ai_trace_id non-vides distinctes car les générations correspondantes d'une même trace ne planifient qu'une exécution.
Si le volume est élevé, réglez rollout_percentage en dessous de 100 pour échantillonner. Spot-check l'évaluateur avec llma-evaluation-test-hog (hog) ou llma-evaluation-run contre une génération (llm_judge). Les deux outils utilisent actuellement des échantillons generation ; pour une cible trace ils peuvent vérifier le comportement source ou prompt partagé, mais ne reproduisent pas la trace complète stabilisée. Réviser les premiers résultats trace en direct avant d'augmenter rollout.
Attention : certaines orgs réutilisent un
$ai_trace_idunique à travers 100k+ événements. Scoper par préfixe trace-ID peut correspondre à bien plus que prévu — vérifiez le volume avec le SQL ci-dessus avant activation.
2.6 — Activer l'ensemble vérifié, puis fermer la boucle
Seulement une fois que chaque eval acceptée est un brouillon scope-vérifié, activez-les — un appel chacun :
posthog:llma-evaluation-update
{ "evaluationId": "<uuid>", "enabled": true }
Chacun s'exécute maintenant sur chaque nouvelle génération correspondante, ou une fois par trace correspondante pour une cible trace. Ceci n'est pas one-and-done : l'utilisateur devrait être conscient qu'il doit surveiller les résultats et itérer si le résultat n'est pas celui attendu. Pour câbler les résultats dans un flux Slack, voir feature-usage-feed.
Fermer la boucle à travers l'ensemble complet à la fois — une courte liste de ce qui est maintenant en direct avec un lien chacun, pas un play-by-play par eval. Mentionner tout candidat laissé désactivé (pas de clé de fournisseur utilisable, volume trop élevé pour activation encore) et ce qui le débloquerait.
Scoping avec conditions
conditions est une liste de condition sets — OR entre sets, AND dans les properties d'un set. Chaque set est { id, rollout_percentage, properties[] }. Il n'y a pas de fenêtre temps à l'intérieur de conditions ; l'échantillonnage est seulement rollout_percentage (0–100). Les filtres propriété utilisent la forme PostHog standard (key, type, operator, value). Pour les cibles trace, ces filtres sélectionnent toujours la génération qui déclenche l'évaluation trace complète éventuelle.
"conditions": [
{ "id": "openai", "rollout_percentage": 100, "properties": [{"key": "$ai_provider", "type": "event", "operator": "exact", "value": "openai"}] },
{ "id": "anthropic", "rollout_percentage": 25, "properties": [{"key": "$ai_provider", "type": "event", "operator": "exact", "value": "anthropic"}] }
]
Construire les liens UI
Construire les liens avec posthog:generate-app-url — jamais ne hand-write l'hôte ou le préfixe /project/<id>/. L'url doit être un template catalogue canonique ; passez les ids concrets via params, jamais inline dans le chemin.
- Liste évaluations :
generate-app-url {url: "/ai-evals/evaluations"} - Évaluation unique :
generate-app-url {url: "/ai-evals/evaluations/{id}", params: {id: "<evaluation_id>"}}
Ceux-ci se résolvent à l'hôte région correct et préfixe projet (par ex. https://us.posthog.com/project/<id>/ai-evals/evaluations/<evaluation_id>). Surface le lien après création pour que l'utilisateur puisse réviser et le basculer dans l'UI.
Conseils
- Les evals proviennent de vraies défaillances, non de métriques génériques. Partez d'une défaillance trouvée dans le trafic de ce produit (via
exploring-ai-failures), non de « mesurons l'hallucination ». Une métrique que personne n'a tracée jusqu'à une mauvaise sortie réelle est du bruit. - Une eval, un mode de défaillance — et autant d'evals que les données le justifient. Les modes de défaillance différents ont besoin d'evals différentes ; ne faites pas qu'une eval essaie d'attraper tout, et ne défautez pas sur créer exactement un quand les traces ont montré plusieurs modes worth watching.
- Proposez, puis créez ce qu'ils ont choisi. Montrez l'ensemble candidat en langage clair, une ou deux lignes chacun, et attendez que l'utilisateur choisisse. Les write-ups longues par eval sont survolées, pas lues.
- Rien trouvé a quand même une réponse. Traces lues mais aucun mode de défaillance worth eval signifie proposer une eval
sentiment, pas revenir les mains vides. Pas d'événements$ai_generationdu tout est l'exception — aucune eval ne peut s'exécuter, donc envoyez-les àinstrument-llm-analyticsà la place. - Suggérez les changements aux côtés de l'eval si possible. S'il est clair qu'un changement de prompt fixerait le problème, par instance, configurez l'eval mais suggérez aussi à l'utilisateur de changer le prompt : il devrait bientôt voir l'eval passer d'un taux de réussite bas à plus élevé.
hogd'abord. Pas de clé de fournisseur, pas d'approbation AI, déterministe. Escaladez àllm_judgeseulement quand le critère ne peut vraiment pas être codé.- Toujours créer désactivé, vérifier scope, puis activer. Une eval qui s'exécute sur les mauvais événements est pire qu'aucune — bruit, et (pour llm_judge) coût.
- Configurez les credentials llm_judge avant d'exécuter. Un judge a besoin d'un fournisseur et modèle valides plus une clé de fournisseur utilisable.
provider_key_idpeut êtrenullseulement quand la clé active d'équipe correspondante peut être utilisée. bytecodeest server-written pour les evals hog — ne le passez jamais ; envoyez seulementevaluation_config.source.- Pour les evals scoped cluster, identifier le cluster avec
exploring-llm-clusters, puis traduire son filtre événement dansconditions.
Compétences liées
exploring-ai-failures— trouver et classer les modes de défaillance worth évaluer — faites ceci en premierexploring-llm-evaluations— debug et gérer les évaluations qui existent déjàexploring-llm-clusters— identifier un cluster pour scoper les conditions eval scoped cluster