Omni Eval
Exécutez des evals sur les APIs de génération de requêtes IA d'Omni — soumettez des prompts de test, capturez le JSON de requête généré, comparez-le avec les résultats attendus et évaluez la précision selon plusieurs dimensions.
Conseil : Utilisez
omni-ai-optimizerpour améliorer les scores après avoir identifié des défaillances, etomni-model-explorerpour découvrir les topics et champs disponibles pour construire des cas d'eval.
Prérequis
# Vérifiez que l'Omni CLI est installé — sinon, demandez à l'utilisateur de l'installer
# Voir : https://github.com/exploreomni/cli#readme
command -v omni >/dev/null || echo "ERROR: Omni CLI is not installed."
# Affichage des profils disponibles et sélection du profil approprié
omni config show
# S'il existe plusieurs profils, demandez à l'utilisateur lequel utiliser, puis changez :
omni config use <profile-name>
Vous avez aussi besoin d'un ID de modèle et d'un ensemble d'eval — un fichier de cas de test avec des prompts et des structures de requête attendues. Consultez le Guide de Conception d'Eval pour les meilleures pratiques de construction d'ensembles d'eval.
Découverte des Commandes
omni ai --help # Opérations IA (generate-query, jobs, pick-topic)
Conseil : Utilisez
-o jsonpour forcer une sortie structurée pour l'analyse programmatique, ou-o humanpour des tableaux lisibles. Par défaut,auto(humain dans un TTY, JSON lors du pipe).
Format d'Entrée d'Eval
Chaque cas d'eval associe un prompt en langage naturel à la structure de requête attendue. JSONL (un objet JSON par ligne) fonctionne bien pour les exécutions en masse :
{"id": "rev-by-month", "prompt": "Show me revenue by month", "modelId": "your-model-id", "expected": {"topic": "order_items", "fields": ["order_items.created_at[month]", "order_items.total_revenue"], "filters": {}, "sorts": [{"column_name": "order_items.created_at[month]", "sort_descending": false}]}, "tags": ["time-series"]}
{"id": "top-customers", "prompt": "Top 10 customers by spend", "modelId": "your-model-id", "expected": {"topic": "order_items", "fields": ["users.name", "order_items.total_revenue"], "filters": {}, "sorts": [{"column_name": "order_items.total_revenue", "sort_descending": true}]}, "tags": ["top-n"]}
| Champ | Requis | Description |
|---|---|---|
id |
Oui | Identifiant unique du cas d'eval |
prompt |
Oui | Question en langage naturel à envoyer à l'IA |
modelId |
Oui | UUID du modèle cible |
expected |
Oui | Objet avec topic, fields, filters, sorts |
branchId |
Non | Branche à tester |
currentTopicName |
Non | Limiter à un topic spécifique |
tags |
Non | Tableau de tags pour filtrer/regrouper les résultats |
Note : JSONL est montré ici, mais n'importe quel format structuré fonctionne — CSV, tableaux JSON, YAML — tant que vous pouvez itérer sur les cas et extraire ces champs.
Exécution des Evals : Chemin Rapide (API Generate Query)
Le endpoint synchrone generate-query est le moyen le plus rapide d'évaluer la génération de requêtes. Passez --run-query false pour obtenir uniquement le JSON de requête généré sans l'exécuter contre la base de données.
Appel Eval Unique
omni ai generate-query your-model-id "Show me revenue by month" --run-query false
Structure de Réponse
{
"query": {
"fields": ["order_items.created_at[month]", "order_items.total_revenue"],
"table": "order_items",
"filters": {},
"sorts": [{"column_name": "order_items.created_at[month]", "sort_descending": false}],
"limit": 500
},
"topic": "order_items",
"error": null
}
Paramètres de Requête
| Arg/Flag | Requis | Description |
|---|---|---|
<model-id> |
Oui | UUID du modèle Omni (arg positionnel) |
<prompt> |
Oui | Question en langage naturel (arg positionnel) |
--run-query |
Non | Définir à false pour ignorer l'exécution de la requête (plus rapide, par défaut true) |
--branch-id |
Non | UUID de branche pour les tests spécifiques à une branche |
--current-topic-name |
Non | Limiter la sélection du topic à un topic spécifique |
Boucle Batch (bash)
while IFS= read -r line; do
id=$(echo "$line" | jq -r '.id')
prompt=$(echo "$line" | jq -r '.prompt')
model_id=$(echo "$line" | jq -r '.modelId')
branch_id=$(echo "$line" | jq -r '.branchId // empty')
branch_flag=""
if [ -n "$branch_id" ]; then
branch_flag="--branch-id $branch_id"
fi
result=$(omni ai generate-query "$model_id" "$prompt" --run-query false $branch_flag --compact)
echo "{\"id\": \"$id\", \"generated\": $result}" >> eval_results.jsonl
done < eval_cases.jsonl
Exécution des Evals : Chemin Agentic (API AI Jobs)
Utilisez l'API asynchrone AI Jobs lorsque vous voulez tester le workflow agentic complet — analyse multi-étapes, tool use et sélection de topic comme Blobby le ferait en production.
Soumettre un Job
omni ai job-submit your-model-id "Show me revenue by month"
Réponse :
{
"jobId": "job-uuid",
"conversationId": "conv-uuid",
"omniChatUrl": "https://yourorg.omniapp.co/chat/..."
}
Attendre la Complétion
omni ai job-status <jobId>
Progression du statut : QUEUED → EXECUTING → COMPLETE (ou FAILED). Interrogez avec backoff exponentiel (par exemple, 2s, 4s, 8s) jusqu'à ce que le state soit terminal.
Obtenir le Résultat
omni ai job-result <jobId>
Le résultat contient un tableau actions. Cherchez les actions avec type: "generate_query" pour extraire le JSON de requête :
{
"actions": [
{
"type": "generate_query",
"message": "Querying revenue by month...",
"result": {
"queryName": "Revenue by Month",
"query": { "fields": [...], "table": "...", "filters": {...} },
"status": "success",
"totalRowCount": 12
}
}
],
"topic": "order_items",
"resultSummary": "Here are the monthly revenue figures..."
}
Quand Utiliser Quel Chemin
| Critère | Generate Query (Rapide) | AI Jobs (Agentic) |
|---|---|---|
| Vitesse | Synchrone, rapide | Async, plus lent |
| Volume | Exécutions haute volume | Volume plus faible |
| Portée | Génération de requête seulement | Workflow agent complet |
| Cas d'usage | Précision des champs/filtres | Comportement bout en bout |
| Multi-étapes | Requête unique | Peut générer plusieurs requêtes |
Test de Sélection de Topic
Évaluez la sélection de topic indépendamment avec le endpoint pick-topic :
omni ai pick-topic your-model-id "How many users signed up last month?"
Réponse :
{
"topicId": "users"
}
Cela vous permet d'évaluer la précision de la sélection de topic comme dimension séparée — utile lorsque la sélection de topic est un point faible connu.
Scoring : Comparaison Structurelle de Requête
Comparez le JSON de requête généré contre la requête attendue selon quatre dimensions :
| Dimension | Méthode de Comparaison | Scoring |
|---|---|---|
topic |
Correspondance exacte de chaîne | réussi/échoué |
fields |
Comparaison d'ensemble (indépendante de l'ordre) | réussi/échoué + score de similarité |
filters |
Correspondance clé-valeur (clé présente + valeur correspond) | réussi/échoué par clé de filtre |
sorts |
Comparaison d'ordre de tableau | réussi/échoué |
Exemple de Logique de Comparaison (TypeScript)
function scoreEval(expected: any, generated: any) {
// Topic: exact match
const topicPass = generated.topic === expected.topic;
// Fields: set comparison (order-independent)
const expectedFields = new Set(expected.fields);
const generatedFields = new Set(generated.query.fields);
const missing = [...expectedFields].filter(f => !generatedFields.has(f));
const extra = [...generatedFields].filter(f => !expectedFields.has(f));
const fieldsPass = missing.length === 0 && extra.length === 0;
// Filters: key-value match
const expectedFilters = expected.filters || {};
const generatedFilters = generated.query.filters || {};
const missingKeys = Object.keys(expectedFilters).filter(k => !(k in generatedFilters));
const wrongValues = Object.keys(expectedFilters)
.filter(k => k in generatedFilters && generatedFilters[k] !== expectedFilters[k]);
const filtersPass = missingKeys.length === 0 && wrongValues.length === 0;
// Sorts: ordered comparison
const sortsPass = JSON.stringify(expected.sorts || []) ===
JSON.stringify(generated.query.sorts || []);
return {
topic: topicPass,
fields: { pass: fieldsPass, missing, extra },
filters: { pass: filtersPass, missingKeys, wrongValues },
sorts: sortsPass,
allPass: topicPass && fieldsPass && filtersPass && sortsPass,
};
}
Scoring Agrégé
Calculez les taux de réussite sur tous les cas d'eval :
Eval Results: 47/50 passed (94,0%)
Topic: 49/50 (98,0%)
Fields: 47/50 (94,0%)
Filters: 48/50 (96,0%)
Sorts: 50/50 (100,0%)
Les taux par dimension aident à identifier où la précision est la plus faible — si la précision du topic est élevée mais celle des filtres est basse, concentrez les améliorations de ai_context sur les orientations liées aux filtres.
Comparaison A/B
Exécutez la même suite d'eval avec une variable modifiée pour mesurer l'impact. C'est le workflow central pour comprendre si une modification améliore ou dégrade la précision de l'IA.
Variables Courantes à Comparer
- Branches de modèle — passez différentes valeurs
--branch-idpour tester les changements de contexte sur une branche avant de fusionner - Portée du topic —
--current-topic-name "orders"vs omis (auto-sélection) - Changements de contexte de modèle —
ai_context,sample_queries, descriptions de champs (appliquez viaomni-model-buildersur une branche, puis évaluez contre cette branche) - Formulation du prompt — même requête attendue, texte de prompt différent
- Configuration de l'IA — type de modèle, niveau de réflexion ou autres paramètres IA
Workflow
- Exécutez la suite d'eval avec configuration A → enregistrez sous
results_a.jsonl - Exécutez la suite d'eval avec configuration B → enregistrez sous
results_b.jsonl - Évaluez les deux ensembles de résultats
- Comparez côte à côte, en vérifiant les régressions
Exemple de Sortie de Comparaison
A/B Comparison: main vs branch/new-context
A (main) B (new-context) Delta
Overall pass rate: 88,0% 94,0% +6,0%
Topic accuracy: 96,0% 98,0% +2,0%
Field accuracy: 90,0% 94,0% +4,0%
Filter accuracy: 88,0% 96,0% +8,0%
Regressions (passed in A, failed in B):
- rev-by-quarter: fields missing order_items.total_revenue
Improvements (failed in A, passed in B):
- customer-count: topic now correctly selects users
- top-products: filters now include status=complete
Important : Vérifiez toujours les régressions, pas seulement l'amélioration globale. Une amélioration nette qui casse des cas précédemment corrects peut indiquer un conflit de
ai_context.
Snapshot de l'État du Modèle
Avant d'exécuter les evals, faites un snapshot de la définition du modèle pour que les résultats soient reproductibles :
# Enregistrer le modèle YAML
omni models yaml-get <modelId> --compact > model_snapshot_$(date +%Y%m%d).json
# Valider l'intégrité du modèle
omni models validate <modelId>
Versionnez votre ensemble d'eval à côté des snapshots de modèle pour pouvoir tracer quel état de modèle a produit quels scores.
Problèmes Connus & Pièges
- La comparaison de filtres peut être complexe — Omni supporte les expressions de filtre riches (
"last 7 days","between 10 and 100","not null"). La comparaison structurelle ci-dessus utilise la correspondance exacte de chaîne sur les valeurs de filtre. Si l'IA produit des expressions sémantiquement équivalentes mais syntaxiquement différentes, vous pouvez voir des faux négatifs. Envisagez de normaliser les motifs courants ou d'utiliser un seuil Jaccard. - AI Jobs sont asynchrones — interrogez avec backoff exponentiel. N'inondez pas le endpoint de statut.
- Rate limiting — pour les exécutions d'eval haute volume, ajoutez un petit délai entre les appels ou regroupez les requêtes.
- Le champ
limitpeut varier — l'IA peut choisir des limites différentes de celles attendues. Envisagez d'exclurelimitde la comparaison stricte si ce n'est pas critique pour votre eval. tablevstopic— la réponse generate-query retournetopiccomme champ de niveau supérieur ettableà l'intérieur de l'objet query. Ils correspondent généralement mais ne sont pas toujours identiques. Comparez contre letopicde niveau supérieur.
Référence Documentation
Skills Connexes
- omni-query — exécuter des requêtes golden pour valider les résultats attendus
- omni-model-explorer — découvrir les topics et champs pour construire des cas d'eval
- omni-ai-optimizer — améliorer la précision de l'IA selon les résultats d'eval
- omni-model-builder — appliquer les changements de contexte sur les branches avant les tests A/B