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érifierattributes.llm.input_messagespour les messages de chat structurés, OUattributes.input.valuepour un prompt sérialisé. Vérifierattributes.llm.prompt_template.templatepour le modèle. - Span Chain/Agent :
attributes.input.valuecontient 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.valuea l'entrée de l'outil,attributes.output.valuea 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 foundou erreur de version → voir references/ax-setup.md401 Unauthorized/ clé API manquante → exécutezax profiles showpour inspecter le profil actuel. Si le profil est manquant ou la clé API est mauvaise : vérifiez.envpourARIZE_API_KEYet l'utiliser pour créer/mettre à jour le profil via references/ax-profiles.md. Si.envn'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
.envpourARIZE_SPACE_ID, ou exécuterax spaces list -o json, ou demander à l'utilisateur - Projet flou → vérifier
.envpourARIZE_DEFAULT_PROJECT, ou demander, ou exécuterax projects list -o json --limit 100et 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 :
- Comparer les sorties à la vérité terrain : Où la sortie du LLM diffère-t-elle de ce qui est attendu ?
- Lire les explications d'éval :
eval.*.explanationindique POURQUOI quelque chose a échoué - Vérifier le texte d'annotation : Le feedback humain décrit les problèmes spécifiques
- Chercher les décalages de verbosité : Si les sorties sont trop longues/courtes vs la vérité terrain
- 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é :
- Comparer les prompts original et révisé côte à côte
- Vérifier que toutes les variables de modèle sont préservées
- Vérifier que les instructions de format sont intactes
- 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
- Créer deux expériences contre le même dataset, chacune utilisant une version différente du prompt
- Exporter les deux :
ax experiments export EXP_Aetax experiments export EXP_B - Comparer les scores moyens, les taux de défaillance et les basculements d'exemples spécifiques
- 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
- Trouver les traces défaillantes :
ax traces list PROJECT_ID --filter "status_code = 'ERROR'" --limit 5 - Exporter la trace :
ax spans export --trace-id TRACE_ID --project PROJECT_ID - 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 - Identifier ce qui a échoué à partir du message d'erreur ou de la sortie
- Remplir le meta-prompt d'optimisation (Phase 3) avec le prompt et le contexte d'erreur
- Appliquer le prompt révisé
Optimiser en utilisant un dataset et une expérience
- Trouver le dataset et l'expérience :
ax datasets list ax experiments list --dataset-id DATASET_ID - Exporter les deux :
ax datasets export DATASET_ID ax experiments export EXPERIMENT_ID - Préparer les données jointes pour le meta-prompt
- Exécuter le meta-prompt d'optimisation
- 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
- 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 - Regarder ce que le LLM produit vs ce qui était attendu
- Ajouter des instructions de format explicites au prompt (schéma JSON, exemples, délimiteurs)
- Correction courante : ajouter un exemple few-shot montrant le format de sortie exact souhaité
Réduire l'hallucination dans un prompt RAG
- Trouver les traces où le modèle a hallucyé :
ax spans list PROJECT_ID \ --filter "annotation.faithfulness.label = 'unfaithful'" \ --limit 20 - 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 - Vérifier si le contexte récupéré contenait réellement la réponse
- 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) |