arize-prompt-optimization

Par github · awesome-copilot

Invoquez cette skill lors de l'optimisation, l'amélioration ou le débogage de prompts LLM à l'aide de données de traces de production, d'évaluations et d'annotations. Couvre l'extraction de prompts depuis des spans, la collecte de signaux de performance, et l'exécution d'une boucle d'optimisation pilotée par les données via le CLI `ax`.

npx skills add https://github.com/github/awesome-copilot --skill arize-prompt-optimization

Compétence d'Optimisation de Prompts Arize

Concepts

Où vivent les prompts dans les données de trace

Les applications LLM émettent des spans en suivant les conventions sémantiques OpenInference. Les prompts sont stockés dans différents attributs de span selon le type de span et l'instrumentation :

Colonne Contenu Quand l'utiliser
attributes.llm.input_messages Messages de chat structurés (system, user, assistant, tool) au format basé sur les rôles Source principale pour les prompts LLM basés sur le chat
attributes.llm.input_messages.roles Tableau de rôles : system, user, assistant, tool Extraire les rôles des messages individuels
attributes.llm.input_messages.contents Tableau des chaînes de contenu des messages Extraire le texte des messages
attributes.input.value Prompt sérialisé ou question utilisateur (générique, tous les types de span) Secours quand les messages structurés ne sont pas disponibles
attributes.llm.prompt_template.template Modèle avec des placeholders {variable} (ex : "Answer {question} using {context}") Quand l'app utilise des modèles de prompts
attributes.llm.prompt_template.variables Valeurs des variables du modèle (objet JSON) Voir quelles valeurs ont été substituées dans le modèle
attributes.output.value Texte de la réponse du modèle Voir ce que le LLM a produit
attributes.llm.output_messages Sortie structurée du modèle (incluant les appels d'outils) Inspecter les réponses avec appels d'outils

Trouver les prompts par type de span

  • Span LLM (attributes.openinference.span.kind = 'LLM') : Vérifier attributes.llm.input_messages pour les messages de chat structurés, OU attributes.input.value pour un prompt sérialisé. Vérifier attributes.llm.prompt_template.template pour le modèle.
  • Span Chain/Agent : attributes.input.value contient la question utilisateur. Le prompt LLM réel se trouve sur les spans LLM enfants -- naviguer dans l'arborescence de la trace.
  • Span Tool : attributes.input.value a l'entrée de l'outil, attributes.output.value a le résultat de l'outil. Pas typiquement là où vivent les prompts.

Colonnes de signal de performance

Ces colonnes contiennent les données de feedback utilisées pour l'optimisation :

Motif de colonne Source Ce qu'il indique
annotation.<name>.label Évaluateurs humains Grade catégorique (ex : correct, incorrect, partial)
annotation.<name>.score Évaluateurs humains Score de qualité numérique (ex : 0.0 - 1.0)
annotation.<name>.text Évaluateurs humains Explication en forme libre de la note
eval.<name>.label Évaluations LLM-as-judge Évaluation catégorique automatisée
eval.<name>.score Évaluations LLM-as-judge Score numérique automatisé
eval.<name>.explanation Évaluations LLM-as-judge Pourquoi l'éval a donné ce score -- plus précieux pour l'optimisation
attributes.input.value Données de trace Ce qui est allé dans le LLM
attributes.output.value Données de trace Ce que le LLM a produit
{experiment_name}.output Exécutions d'expérience Sortie d'une expérience spécifique

Prérequis

Procédez directement à la tâche -- exécutez la commande ax dont vous avez besoin. NE vérifiez PAS les versions, les variables d'env ou les profils au préalable.

Si une commande ax échoue, dépannez en fonction de l'erreur :

  • command not found ou erreur de version → voir references/ax-setup.md
  • 401 Unauthorized / clé API manquante → exécutez ax profiles show pour inspecter le profil actuel. Si le profil est manquant ou la clé API est mauvaise : vérifiez .env pour ARIZE_API_KEY et l'utiliser pour créer/mettre à jour le profil via references/ax-profiles.md. Si .env n'a pas de clé non plus, demandez la clé API Arize de l'utilisateur (https://app.arize.com/admin > API Keys)
  • Space ID inconnu → vérifier .env pour ARIZE_SPACE_ID, ou exécuter ax spaces list -o json, ou demander à l'utilisateur
  • Projet flou → vérifier .env pour ARIZE_DEFAULT_PROJECT, ou demander, ou exécuter ax projects list -o json --limit 100 et présenter comme options sélectionnables
  • L'appel du fournisseur LLM échoue (OPENAI_API_KEY / ANTHROPIC_API_KEY manquante) → vérifier .env, charger si présent, sinon demander à l'utilisateur

Phase 1 : Extraire le prompt actuel

Trouver les spans LLM contenant des prompts

# Lister les spans LLM (où vivent les prompts)
ax spans list PROJECT_ID --filter "attributes.openinference.span.kind = 'LLM'" --limit 10

# Filtrer par modèle
ax spans list PROJECT_ID --filter "attributes.llm.model_name = 'gpt-4o'" --limit 10

# Filtrer par nom de span (ex : un appel LLM spécifique)
ax spans list PROJECT_ID --filter "name = 'ChatCompletion'" --limit 10

Exporter une trace pour inspecter la structure du prompt

# Exporter tous les spans dans une trace
ax spans export --trace-id TRACE_ID --project PROJECT_ID

# Exporter un seul span
ax spans export --span-id SPAN_ID --project PROJECT_ID

Extraire les prompts du JSON exporté

# Extraire les messages de chat structurés (system + user + assistant)
jq '.[0] | {
  messages: .attributes.llm.input_messages,
  model: .attributes.llm.model_name
}' trace_*/spans.json

# Extraire le prompt système spécifiquement
jq '[.[] | select(.attributes.llm.input_messages.roles[]? == "system")] | .[0].attributes.llm.input_messages' trace_*/spans.json

# Extraire le modèle de prompt et les variables
jq '.[0].attributes.llm.prompt_template' trace_*/spans.json

# Extraire depuis input.value (secours pour les prompts non-structurés)
jq '.[0].attributes.input.value' trace_*/spans.json

Reconstruire le prompt en tant que messages

Une fois que vous avez les données du span, reconstruisez le prompt en tant que tableau de messages :

[
  {"role": "system", "content": "You are a helpful assistant that..."},
  {"role": "user", "content": "Given {input}, answer the question: {question}"}
]

Si le span a attributes.llm.prompt_template.template, le prompt utilise des variables. Conservez ces placeholders ({variable} ou {{variable}}) -- ils sont substitués à l'exécution.

Phase 2 : Recueillir les données de performance

À partir des traces (feedback de production)

# Trouver les spans d'erreur -- ils indiquent les défaillances du prompt
ax spans list PROJECT_ID \
  --filter "status_code = 'ERROR' AND attributes.openinference.span.kind = 'LLM'" \
  --limit 20

# Trouver les spans avec des scores d'éval bas
ax spans list PROJECT_ID \
  --filter "annotation.correctness.label = 'incorrect'" \
  --limit 20

# Trouver les spans avec une latence élevée (peut indiquer des prompts trop complexes)
ax spans list PROJECT_ID \
  --filter "attributes.openinference.span.kind = 'LLM' AND latency_ms > 10000" \
  --limit 20

# Exporter les traces d'erreur pour inspection détaillée
ax spans export --trace-id TRACE_ID --project PROJECT_ID

À partir des datasets et expériences

# Exporter un dataset (exemples de vérité terrain)
ax datasets export DATASET_ID
# -> dataset_*/examples.json

# Exporter les résultats d'une expérience (ce que le LLM a produit)
ax experiments export EXPERIMENT_ID
# -> experiment_*/runs.json

Fusionner dataset + expérience pour l'analyse

Joindre les deux fichiers par example_id pour voir les entrées à côté des sorties et évaluations :

# Compter les exemples et les exécutions
jq 'length' dataset_*/examples.json
jq 'length' experiment_*/runs.json

# Voir un enregistrement fusionné unique
jq -s '
  .[0] as $dataset |
  .[1][0] as $run |
  ($dataset[] | select(.id == $run.example_id)) as $example |
  {
    input: $example,
    output: $run.output,
    evaluations: $run.evaluations
  }
' dataset_*/examples.json experiment_*/runs.json

# Trouver les exemples échoués (où eval score < seuil)
jq '[.[] | select(.evaluations.correctness.score < 0.5)]' experiment_*/runs.json

Identifier ce qu'il faut optimiser

Recherchez les motifs sur les défaillances :

  1. Comparer les sorties à la vérité terrain : Où la sortie du LLM diffère-t-elle de ce qui est attendu ?
  2. Lire les explications d'éval : eval.*.explanation indique POURQUOI quelque chose a échoué
  3. Vérifier le texte d'annotation : Le feedback humain décrit les problèmes spécifiques
  4. Chercher les décalages de verbosité : Si les sorties sont trop longues/courtes vs la vérité terrain
  5. Vérifier la conformité du format : Les sorties sont-elles au format attendu ?

Phase 3 : Optimiser le prompt

Le meta-prompt d'optimisation

Utilisez ce modèle pour générer une version améliorée du prompt. Remplissez les trois placeholders et envoyez-le à votre LLM (GPT-4o, Claude, etc.) :

You are an expert in prompt optimization. Given the original baseline prompt
and the associated performance data (inputs, outputs, evaluation labels, and
explanations), generate a revised version that improves results.

ORIGINAL BASELINE PROMPT
========================

{PASTE_ORIGINAL_PROMPT_HERE}

========================

PERFORMANCE DATA
================

The following records show how the current prompt performed. Each record
includes the input, the LLM output, and evaluation feedback:

{PASTE_RECORDS_HERE}

================

HOW TO USE THIS DATA

1. Compare outputs: Look at what the LLM generated vs what was expected
2. Review eval scores: Check which examples scored poorly and why
3. Examine annotations: Human feedback shows what worked and what didn't
4. Identify patterns: Look for common issues across multiple examples
5. Focus on failures: The rows where the output DIFFERS from the expected
   value are the ones that need fixing

ALIGNMENT STRATEGY

- If outputs have extra text or reasoning not present in the ground truth,
  remove instructions that encourage explanation or verbose reasoning
- If outputs are missing information, add instructions to include it
- If outputs are in the wrong format, add explicit format instructions
- Focus on the rows where the output differs from the target -- these are
  the failures to fix

RULES

Maintain Structure:
- Use the same template variables as the current prompt ({var} or {{var}})
- Don't change sections that are already working
- Preserve the exact return format instructions from the original prompt

Avoid Overfitting:
- DO NOT copy examples verbatim into the prompt
- DO NOT quote specific test data outputs exactly
- INSTEAD: Extract the ESSENCE of what makes good vs bad outputs
- INSTEAD: Add general guidelines and principles
- INSTEAD: If adding few-shot examples, create SYNTHETIC examples that
  demonstrate the principle, not real data from above

Goal: Create a prompt that generalizes well to new inputs, not one that
memorizes the test data.

OUTPUT FORMAT

Return the revised prompt as a JSON array of messages:

[
  {"role": "system", "content": "..."},
  {"role": "user", "content": "..."}
]

Also provide a brief reasoning section (bulleted list) explaining:
- What problems you found
- How the revised prompt addresses each one

Préparer les données de performance

Formatez les enregistrements en tant que tableau JSON avant de les coller dans le modèle :

# À partir de dataset + expérience : joindre et sélectionner les colonnes pertinentes
jq -s '
  .[0] as $ds |
  [.[1][] | . as $run |
    ($ds[] | select(.id == $run.example_id)) as $ex |
    {
      input: $ex.input,
      expected: $ex.expected_output,
      actual_output: $run.output,
      eval_score: $run.evaluations.correctness.score,
      eval_label: $run.evaluations.correctness.label,
      eval_explanation: $run.evaluations.correctness.explanation
    }
  ]
' dataset_*/examples.json experiment_*/runs.json

# À partir des spans exportés : extraire les paires entrée/sortie avec annotations
jq '[.[] | select(.attributes.openinference.span.kind == "LLM") | {
  input: .attributes.input.value,
  output: .attributes.output.value,
  status: .status_code,
  model: .attributes.llm.model_name
}]' trace_*/spans.json

Appliquer le prompt révisé

Après que le LLM retourne le tableau de messages révisé :

  1. Comparer les prompts original et révisé côte à côte
  2. Vérifier que toutes les variables de modèle sont préservées
  3. Vérifier que les instructions de format sont intactes
  4. Tester sur quelques exemples avant le déploiement complet

Phase 4 : Itérer

La boucle d'optimisation

1. Extraire le prompt    -> Phase 1 (une fois)
2. Exécuter l'expérience    -> ax experiments create ...
3. Exporter les résultats    -> ax experiments export EXPERIMENT_ID
4. Analyser les défaillances  -> jq pour trouver les scores bas
5. Exécuter le meta-prompt   -> Phase 3 avec les nouvelles données de défaillance
6. Appliquer le prompt révisé
7. Répéter à partir de l'étape 2

Mesurer l'amélioration

# Comparer les scores entre les expériences
# Expérience A (baseline)
jq '[.[] | .evaluations.correctness.score] | add / length' experiment_a/runs.json

# Expérience B (optimisée)
jq '[.[] | .evaluations.correctness.score] | add / length' experiment_b/runs.json

# Trouver les exemples qui sont passés de échoué à réussi
jq -s '
  [.[0][] | select(.evaluations.correctness.label == "incorrect")] as $fails |
  [.[1][] | select(.evaluations.correctness.label == "correct") |
    select(.example_id as $id | $fails | any(.example_id == $id))
  ] | length
' experiment_a/runs.json experiment_b/runs.json

Comparer A/B deux prompts

  1. Créer deux expériences contre le même dataset, chacune utilisant une version différente du prompt
  2. Exporter les deux : ax experiments export EXP_A et ax experiments export EXP_B
  3. Comparer les scores moyens, les taux de défaillance et les basculements d'exemples spécifiques
  4. Vérifier les régressions -- exemples qui ont réussi avec le prompt A mais échouent avec le prompt B

Meilleures pratiques d'ingénierie des prompts

Appliquez ces pratiques lors de la rédaction ou de la révision des prompts :

Technique Quand l'appliquer Exemple
Instructions claires et détaillées La sortie est vague ou hors sujet "Classifier le sentiment comme exactement l'un de : positif, négatif, neutre"
Instructions au début Le modèle ignore les instructions ultérieures Mettre la description de tâche avant les exemples
Décompositions étape par étape Processus multi-étapes complexes "D'abord extraire les entités, puis classifier chacune, puis résumer"
Personas spécifiques Besoin de style/ton cohérent "Vous êtes un analyste financier senior écrivant pour des investisseurs institutionnels"
Tokens délimiteurs Les sections se chevauchent Utiliser ---, ### ou les balises XML pour séparer l'entrée des instructions
Exemples few-shot Le format de sortie a besoin de clarification Montrer 2-3 paires d'entrée/sortie synthétiques
Spécifications de longueur de sortie Les réponses sont trop longues ou trop courtes "Répondre en exactement 2-3 phrases"
Instructions de raisonnement La précision est critique "Penser étape par étape avant de répondre"
Directives "Je ne sais pas" L'hallucination est un risque "Si la réponse n'est pas dans le contexte fourni, dites 'Je n'ai pas assez d'informations'"

Préservation des variables

Lors de l'optimisation des prompts qui utilisent des variables de modèle :

  • Accolades simples ({variable}) : Style f-string Python / Jinja. Plus courant dans Arize.
  • Accolades doubles ({{variable}}) : Style Mustache. Utilisé quand le framework l'exige.
  • Ne jamais ajouter ou supprimer les placeholders de variables pendant l'optimisation
  • Ne jamais renommer les variables -- la substitution à l'exécution dépend des noms exacts
  • Si vous ajoutez des exemples few-shot, utiliser des valeurs littérales, pas des placeholders de variables

Workflows

Optimiser un prompt à partir d'une trace défaillante

  1. Trouver les traces défaillantes :
    ax traces list PROJECT_ID --filter "status_code = 'ERROR'" --limit 5
  2. Exporter la trace :
    ax spans export --trace-id TRACE_ID --project PROJECT_ID
  3. Extraire le prompt du span LLM :
    jq '[.[] | select(.attributes.openinference.span.kind == "LLM")][0] | {
      messages: .attributes.llm.input_messages,
      template: .attributes.llm.prompt_template,
      output: .attributes.output.value,
      error: .attributes.exception.message
    }' trace_*/spans.json
  4. Identifier ce qui a échoué à partir du message d'erreur ou de la sortie
  5. Remplir le meta-prompt d'optimisation (Phase 3) avec le prompt et le contexte d'erreur
  6. Appliquer le prompt révisé

Optimiser en utilisant un dataset et une expérience

  1. Trouver le dataset et l'expérience :
    ax datasets list
    ax experiments list --dataset-id DATASET_ID
  2. Exporter les deux :
    ax datasets export DATASET_ID
    ax experiments export EXPERIMENT_ID
  3. Préparer les données jointes pour le meta-prompt
  4. Exécuter le meta-prompt d'optimisation
  5. Créer une nouvelle expérience avec le prompt révisé pour mesurer l'amélioration

Déboguer un prompt qui produit le mauvais format

  1. Exporter les spans où le format de sortie est incorrect :
    ax spans list PROJECT_ID \
      --filter "attributes.openinference.span.kind = 'LLM' AND annotation.format.label = 'incorrect'" \
      --limit 10 -o json > bad_format.json
  2. Regarder ce que le LLM produit vs ce qui était attendu
  3. Ajouter des instructions de format explicites au prompt (schéma JSON, exemples, délimiteurs)
  4. Correction courante : ajouter un exemple few-shot montrant le format de sortie exact souhaité

Réduire l'hallucination dans un prompt RAG

  1. Trouver les traces où le modèle a hallucyé :
    ax spans list PROJECT_ID \
      --filter "annotation.faithfulness.label = 'unfaithful'" \
      --limit 20
  2. Exporter et inspecter les spans du récupérateur et du LLM ensemble :
    ax spans export --trace-id TRACE_ID --project PROJECT_ID
    jq '[.[] | {kind: .attributes.openinference.span.kind, name, input: .attributes.input.value, output: .attributes.output.value}]' trace_*/spans.json
  3. Vérifier si le contexte récupéré contenait réellement la réponse
  4. Ajouter des instructions d'ancrage au prompt système : "Utiliser uniquement les informations du contexte fourni. Si la réponse n'est pas dans le contexte, dites-le."

Dépannage

Problème Solution
ax: command not found Voir references/ax-setup.md
No profile found Aucun profil n'est configuré. Voir references/ax-profiles.md pour en créer un.
Pas d'input_messages sur le span Vérifier le type de span -- les spans Chain/Agent stockent les prompts sur les spans LLM enfants, pas sur eux-mêmes
Le modèle de prompt est null Toutes les instrumentations n'émettent pas prompt_template. Utiliser plutôt input_messages ou input.value
Variables perdues après optimisation Vérifier que le prompt révisé préserve tous les placeholders {var} de l'original
L'optimisation empirait les choses Vérifier le surapprentissage -- le meta-prompt peut avoir mémorisé les données de test. Assurer que les exemples few-shot sont synthétiques
Pas de colonnes eval/annotation Exécuter d'abord les évaluations (via l'interface utilisateur Arize ou SDK), puis ré-exporter
Colonne de sortie d'expérience non trouvée Le nom de colonne est {experiment_name}.output -- vérifier le nom exact via ax experiments get
Erreurs jq sur le JSON du span Assurer que vous ciblez le bon chemin de fichier (ex : trace_*/spans.json)

Skills similaires