Enrichir les traces : tags, contexte et retours
Une fois que vous avez instrumenté votre agent pour émettre des traces, vous pouvez enrichir ces traces avec des informations supplémentaires qui les rendent utiles pour la recherche, le debugging et le monitoring de la qualité :
- Tags et métadonnées — des paires clé-valeur permettant d'organiser, de filtrer et d'annoter les traces.
- Contexte : ID utilisateur, ID de session, environnement et version de l'agent pour l'analyse de cohorte et le debugging spécifique au déploiement.
- Retour d'information des utilisateurs finaux — notes et commentaires saisis sous forme d'évaluations sur les traces, vous fournissant un signal de qualité de référence issu de la production.
Le feedback des utilisateurs finaux constitue un modèle de production clé. Lorsqu'un utilisateur clique sur le pouce vers le haut ou vers le bas, ou laisse un commentaire dans votre application déployée, enregistrez-le en tant qu'évaluation sur la trace de cette interaction. Le fait de conserver le feedback rattaché à l'exécution d'origine le rend immédiatement utile en aval — pour le debugging de la requête spécifique et pour la création de datasets d'évaluation à partir de réussites et d'échecs réels.
Conditions requises
Choisissez le package approprié pour votre environnement :
- Production
- Development
pip install --upgrade mlflow-tracing
Le package mlflow-tracing comporte un nombre minimal de dépendances et est optimisé pour une utilisation en production.
pip install --upgrade "mlflow[databricks]>=3.1.0" openai "databricks-connect>=16.1"
Créez une expérimentation MLflow en suivant la section Configurer votre environnement.
Tags et métadonnées
Les tags sont des paires clé-valeur modifiables que vous pouvez définir, mettre à jour ou supprimer à tout moment, y compris après l’enregistrement de la trace. Utilisez des tags pour des information dynamiques : statut de révision, labels de qualité des données ou signaux de retour d’information utilisateur.
Metadata est immuable une fois que la trace est enregistrée. Utilisez les métadonnées pour les faits stables capturés au moment de l’exécution : version du modèle, environnement ou configuration.
API | Quand l'utiliser |
|---|---|
Définissez des tags ou des métadonnées sur une trace active pendant l’exécution. | |
Définir ou mettre à jour un tag sur une trace terminée | |
Remove a tag from a finished trace | |
Interface utilisateur MLflow | Définir ou mettre à jour des tags sur une trace terminée de manière interactive |
Définir les tags et les métadonnées pendant l’exécution
Appelez mlflow.update_current_trace dans une fonction tracée pour joindre des tags ou des métadonnées lorsque
la trace est active :
import mlflow
@mlflow.trace
def my_func(x):
mlflow.update_current_trace(
metadata={"model_version": "v1.2.3", "environment": "production"},
tags={"fruit": "apple"}
)
return x + 1
my_func(10)
update_current_trace ajoute une nouvelle clé ou remplace une clé existante pour les tags . Pour les métadonnées , toute tentative de mise à jour d’une clé existante est ignorée silencieusement : les métadonnées sont immuables une fois définies.
Set tags on a finished trace
Pour mettre à jour ou supprimer des tags après l’enregistrement d’une trace :
import mlflow
@mlflow.trace
def process_data(data):
return data.upper()
result = process_data("hello world")
trace_id = mlflow.get_last_active_trace_id()
mlflow.set_trace_tag(trace_id=trace_id, key="review_status", value="approved")
mlflow.set_trace_tag(trace_id=trace_id, key="data_quality", value="high")
mlflow.delete_trace_tag(trace_id=trace_id, key="data_quality")
Définir les tags dans l’interface utilisateur
Accédez à la trace, puis cliquez sur l'icône du crayon située à côté de n'importe quel tag pour le modifier ou le supprimer.

Ajouter du contexte aux traces
Le contexte associe les traces aux utilisateurs, aux sessions, aux déploiements et au code, ce qui permet le regroupement des conversations à plusieurs tours, l’analyse des cohortes d’utilisateurs et le debugging spécifique à l’environnement.
Appelez mlflow.update_current_trace dans la logique de votre agent tracé pour joindre le contexte :
import mlflow
mlflow.update_current_trace(
metadata={
"mlflow.trace.user": user_id,
"mlflow.trace.session": session_id,
},
tags={
"query_category": "chat",
},
)
Après la journalisation, accédez au contexte via mlflow.search_traces() (les colonnes metadata et tags dans le DataFrame renvoyé), ou directement sur les objets Trace via Trace.info.trace_metadata et Trace.info.tags.
Consultez Enrichir les traces : tags, contexte et feedback pour voir un exemple concret complet.
Champs contextuels standard
MLflow définit des champs de métadonnées standardisés pour les types de contextes les plus courants. Lorsque vous les utilisez, l'interface utilisateur active automatiquement le filtrage et le regroupement par ces champs.
Type de contexte | Champ MLflow | Cas d’utilisation |
|---|---|---|
ID utilisateur |
| Associer des traces à des utilisateurs spécifiques pour la personnalisation, l’analyse de cohorte et le debugging spécifique aux utilisateurs |
Identifiant de session |
| Regroupez les traces des conversations à plusieurs tours pour analyser le flux conversationnel complet. |
ID de requête client | Link les traces aux appels d'API amont pour un debugging de bout en bout | |
Environnement / version |
| Suivez le contexte de déploiement entre les environnements et les versions d’agent |
Champs personnalisés | (vos clés de métadonnées) | Tout contexte spécifique à l’agent : identifiant de déploiement, région, indicateurs de fonctionnalité |
Champs remplis automatiquement
MLflow définit automatiquement plusieurs champs de métadonnées issus de votre environnement d’exécution. Vous pouvez en remplacer l’un d’entre eux avec mlflow.update_current_trace lorsque la détection par default ne répond pas à vos besoins.
Champ de métadonnées | Description | Défini automatiquement à partir de |
|---|---|---|
| Point d'entrée ou nom du script | Nom du fichier Python ; nom du notebook Databricks |
| Hachage de commit Git | Dépôt git actuel |
| Nom de Branch Git | Dépôt git actuel |
| URL du référentiel Git | Dépôt git actuel |
| Environnement d’exécution |
|
| ID de l'exécution source | Exécution MLflow active |
| Identifiant MLflow LoggedModel |
|
Pour les métadonnées de déploiement telles que l'environnement et la version, extrayez les valeurs des variables d'environnement plutôt que de les coder en dur :
import mlflow
import os
mlflow.update_current_trace(
metadata={
"mlflow.source.type": os.getenv("APP_ENVIRONMENT", "development"),
}
)
Bonnes pratiques
- Formats d’ID cohérents — Utilisez des formats standardisés pour les ID utilisateur et de session dans tout votre agent.
- Limites de session — Définissez des règles claires concernant le start et la fin des sessions.
- Environment variables — Populate metadata from environment variables rather than hard-coding values.
- Combiner les types de contexte — Suivez conjointement le contexte de l’utilisateur, de la session et de l’environnement.
- Analyse régulière — Configurez des tableaux de bord pour surveiller le comportement des utilisateurs, les tendances des sessions et les performances des versions.
- Remplacer les valeurs par défaut de manière réfléchie — Ne remplacez les métadonnées remplies automatiquement que lorsque la valeur détectée automatiquement ne convient pas à votre déploiement.
Recueillir les commentaires des utilisateurs
Les commentaires des utilisateurs finaux vous offrent un signal de vérité terrain sur la qualité réelle de votre agent. MLflow recueille les retours sous forme d’ évaluations — une entité structurée rattachée de manière permanente à une trace —, de sorte que chaque note reste associée à l’interaction exacte qui l’a motivée.

Types de feedback
Feedback type | Description | Cas d'utilisation courants |
|---|---|---|
Binaire | Pouce levé/pouce vers le bas ou correct/incorrect | Signaux de satisfaction rapides |
Numérique | Ratings on a scale (for example, 1–5 stars) | Évaluation détaillée de la qualité |
Catégorique | Options à choix multiples | Classification de problèmes ou de types de réponses |
Texte | Commentaires libres | Explications utilisateur détaillées |
Modèle de données de feedback
Les commentaires des utilisateurs sont enregistrés sous la forme d’une entité Feedback (un type d’ Assessment) associée à une trace ou à une étendue. Chaque entité Feedback stocke :
- Value — le signal de feedback (booléen, numérique, texte ou données structurées)
- Source — un
AssessmentSourceidentifiant l'auteur du retour d'expérience (voir ci-dessous) - Justification — explications facultatives pour les commentaires
- Métadonnées — contexte supplémentaire tel que des Timestamp ou des attributs personnalisés
Champs d'AssessmentSource
The AssessmentSource object on every feedback assessment identifies the origin of the feedback:
source_type—"HUMAN"for end-user feedback,"LLM_JUDGE"for automated evaluationsource_id— l'utilisateur ou le système spécifique ayant fourni le retour (par exemple, une chaîne d'ID utilisateur ou un identifiant de juge)
Transmettez les deux champs lors de l'appel de mlflow.log_feedback:
from mlflow.entities import AssessmentSource
mlflow.log_feedback(
trace_id=trace_id,
name="user_feedback",
value=True,
source=AssessmentSource(source_type="HUMAN", source_id=user_id),
rationale="The answer was accurate and helpful.",
)
Link feedback aux traces
Pour enregistrer le feedback, vous devez associer la réponse de l’utilisateur à une trace spécifique. Deux approches :
Approche 1 — Utiliser l’ID de trace MLflow (plus simple) : récupérez l’ID de trace généré par MLflow pendant la requête et renvoyez-le au client. Le client le renvoie avec les commentaires.
Approche 2 — Utiliser l’identifiant de requête client (plus de contrôle) : générez votre propre identifiant unique par requête, associez-le en tant que tag de trace, puis recherchez la trace à l’aide de ce tag lorsque le feedback arrive. Utile si vous disposez déjà d’un système de suivi des requêtes.
Si vous déployez votre agent sur un endpoint Databricks Model Serving, définissez client_request_id comme un
tag (et non un attribut). L’utilisation de update_current_trace(client_request_id=...) comme attribut de métadonnées
interrompt l’exportation des traces dans les environnements de serving. Si vous devez utiliser Model Serving,
préférez l’approche 1 (ID de trace MLflow) ou définissez client_request_id via
update_current_trace(tags={"client_request_id": ...}).
- Approach 1: MLflow trace ID
- Approach 2: Client request ID
Back-end
import mlflow
from fastapi import FastAPI, Query
from mlflow.entities import AssessmentSource
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
class ChatRequest(BaseModel):
message: str
class ChatResponse(BaseModel):
response: str
trace_id: str # Return the trace ID so the client can reference it for feedback
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
response = process_message(request.message) # Your agent logic here
trace_id = mlflow.get_current_active_span().trace_id
return ChatResponse(response=response, trace_id=trace_id)
class FeedbackRequest(BaseModel):
is_correct: bool
comment: Optional[str] = None
@app.post("/feedback")
def submit_feedback(
trace_id: str = Query(..., description="Trace ID from the chat response"),
feedback: FeedbackRequest = ...,
user_id: Optional[str] = Query(None)
):
mlflow.log_feedback(
trace_id=trace_id,
name="user_feedback",
value=feedback.is_correct,
source=AssessmentSource(source_type="HUMAN", source_id=user_id),
rationale=feedback.comment
)
return {"status": "success", "trace_id": trace_id}
Frontend (React)
import React, { useState } from 'react';
function ChatWithFeedback() {
const [message, setMessage] = useState('');
const [response, setResponse] = useState('');
const [traceId, setTraceId] = useState(null);
const [feedbackSubmitted, setFeedbackSubmitted] = useState(false);
const sendMessage = async () => {
const res = await fetch('/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
const data = await res.json();
setResponse(data.response);
setTraceId(data.trace_id);
setFeedbackSubmitted(false);
};
const submitFeedback = async (isCorrect, comment = null) => {
if (!traceId || feedbackSubmitted) return;
const params = new URLSearchParams({ trace_id: traceId });
await fetch(`/feedback?${params}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ is_correct: isCorrect, comment }),
});
setFeedbackSubmitted(true);
};
return (
<div>
<input value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Ask a question..." />
<button onClick={sendMessage}>Send</button>
{response && (
<div>
<p>{response}</p>
<div>
<button onClick={() => submitFeedback(true)} disabled={feedbackSubmitted}>
👍
</button>
<button onClick={() => submitFeedback(false)} disabled={feedbackSubmitted}>
👎
</button>
</div>
{feedbackSubmitted && Thanks for your feedback!</span>}
</div>
)}
</div>
);
}
Attachez un ID personnalisé en tant que tag pendant la requête, puis recherchez la trace par ce tag lorsque les commentaires arrivent.
Back-end
import mlflow
from fastapi import FastAPI, Query, Request
from mlflow.client import MlflowClient
from mlflow.entities import AssessmentSource
from pydantic import BaseModel
from typing import Optional
import uuid
app = FastAPI()
class ChatRequest(BaseModel):
message: str
class ChatResponse(BaseModel):
response: str
client_request_id: str
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
client_request_id = f"req-{uuid.uuid4().hex[:8]}"
# Must be a tag, not an attribute — required for Model Serving compatibility
mlflow.update_current_trace(tags={"client_request_id": client_request_id})
response = process_message(request.message)
return ChatResponse(response=response, client_request_id=client_request_id)
class FeedbackRequest(BaseModel):
is_correct: bool
comment: Optional[str] = None
@app.post("/feedback")
def submit_feedback(
request: Request,
client_request_id: str = Query(..., description="Request ID from the original interaction"),
feedback: FeedbackRequest = ...
):
client = MlflowClient()
experiment = client.get_experiment_by_name("/Shared/production-app")
traces = client.search_traces(
experiment_ids=[experiment.experiment_id],
filter_string=f"tags.client_request_id = '{client_request_id}'",
max_results=1
)
if not traces:
return {"status": "error", "message": "Unexpected error: request not found"}, 500
mlflow.log_feedback(
trace_id=traces[0].info.trace_id,
name="user_feedback",
value=feedback.is_correct,
source=AssessmentSource(
source_type="HUMAN",
source_id=request.headers.get("X-User-ID")
),
rationale=feedback.comment
)
return {"status": "success", "trace_id": traces[0].info.trace_id}
The frontend mirrors Approach 1 — send the message, store the returned client_request_id per conversation turn, and pass it back with each feedback submission.
Feedback multidimensionnel
Log multiple named assessments on a single trace to capture separate quality dimensions:
from mlflow.entities import AssessmentSource
@app.post("/detailed-feedback")
def submit_detailed_feedback(
trace_id: str,
accuracy: int = Query(..., ge=1, le=5, description="Accuracy rating 1–5"),
helpfulness: int = Query(..., ge=1, le=5, description="Helpfulness rating 1–5"),
relevance: int = Query(..., ge=1, le=5, description="Relevance rating 1–5"),
user_id: str = Query(...),
comment: Optional[str] = None
):
dimensions = {"accuracy": accuracy, "helpfulness": helpfulness, "relevance": relevance}
for dimension, score in dimensions.items():
mlflow.log_feedback(
trace_id=trace_id,
name=f"user_{dimension}",
value=score / 5.0, # Normalize to 0–1 scale
source=AssessmentSource(source_type="HUMAN", source_id=user_id),
rationale=comment if dimension == "accuracy" else None
)
return {"status": "success", "trace_id": trace_id, "feedback_recorded": dimensions}
Réponses en streaming
Avec le streaming (SSE ou WebSockets), l’identifiant de trace n’est pas disponible tant que le stream n’est pas terminé. Renvoyez-le en tant qu’événement de stream final et désactivez les contrôles de feedback jusqu’à son arrivée.
Backend (FastAPI SSE)
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import mlflow, json, asyncio
from typing import AsyncGenerator
@app.post("/chat/stream")
async def chat_stream(request: ChatRequest):
async def generate() -> AsyncGenerator[str, None]:
try:
with mlflow.start_span(name="streaming_chat") as span:
full_response = ""
async for token in your_llm_stream_function(request.message):
full_response += token
yield f"data: {json.dumps({'type': 'token', 'content': token})}\n\n"
await asyncio.sleep(0.01) # Prevent overwhelming the client
span.set_attribute("response", full_response)
span.set_attribute("token_count", len(full_response.split()))
# Send trace ID as the final event
yield f"data: {json.dumps({'type': 'done', 'trace_id': span.trace_id})}\n\n"
except Exception as e:
yield f"data: {json.dumps({'type': 'error', 'error': str(e)})}\n\n"
return StreamingResponse(
generate(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no", # Disable proxy buffering
},
)
Sur le frontend, lisez le stream, accumulez les événements token dans le texte de réponse et capturez l’ID de trace à partir de l’événement done final. Utilisez traceId && !isStreaming comme condition pour activer les contrôles de feedback.
Remarques clés relatives à l’implémentation :
- L’ID de trace est uniquement disponible une fois le streaming terminé — concevez votre interface utilisateur de manière à désactiver les contrôles de commentaires jusqu’à son arrivée.
- Utilisez un format d’événement cohérent doté d’un champ
typepour distinguer les jetons de contenu, les événements de fin et les erreurs. - Définissez
X-Accel-Buffering: nopour désactiver la mise en mémoire tampon du proxy. - Implémentez la mise en mémoire tampon des lignes dans le front-end pour traiter les messages SSE partiels.
- Incluez les événements d’erreur dans le stream pour que les échecs soient enregistrés dans la trace et visibles par l’utilisateur.
Analyser les commentaires
View feedback in the MLflow UI by opening any trace — assessments appear alongside span data.


Query et agrégation du feedback par programmation :
from mlflow.client import MlflowClient
from datetime import datetime, timedelta
def analyze_user_feedback(experiment_name: str, hours: int = 24):
client = MlflowClient()
cutoff_ms = int((datetime.now() - timedelta(hours=hours)).timestamp() * 1000)
traces = client.search_traces(
experiment_names=[experiment_name],
filter_string=f"trace.timestamp_ms > {cutoff_ms}"
)
total = len(traces)
with_feedback = positive = negative = 0
for trace in traces:
detail = client.get_trace(trace.info.trace_id)
if detail.data.assessments:
with_feedback += 1
for a in detail.data.assessments:
if a.name == "user_feedback":
if a.value:
positive += 1
else:
negative += 1
feedback_rate = (with_feedback / total * 100) if total else 0
positive_rate = (positive / with_feedback * 100) if with_feedback else 0
print(f"Feedback rate: {feedback_rate:.1f}% Positive: {positive_rate:.1f}%")
print(f"Total feedback: {with_feedback} of {total} traces")
analyze_user_feedback("/Shared/production-genai-agent")
Le même schéma s’étend au feedback multidimensionnel: parcourez les évaluations de chaque trace et groupez a.value par a.name pour calculer la moyenne de chaque dimension d’évaluation séparément.
Ressources supplémentaires
- Enrichir les traces : tags, contexte et retours - Tutoriel complet : ajouter le contexte utilisateur, de session, d'environnement et de version aux traces
- Accès programmatique aux traces – Filtrer et rechercher des traces à l’aide de tags et de métadonnées
- Rechercher des problèmes dans les traces – Exemples d’analytique des traces
- Building MLflow evaluation datasets - Use collected feedback to build evaluation datasets
- Configurer le monitoring de production - Surveiller les indicateurs de qualité selon les retours
Étape suivante : Stocker les traces OpenTelemetry dans Unity Catalog