Aller au contenu principal

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 :

Bash
pip install --upgrade mlflow-tracing

Le package mlflow-tracing comporte un nombre minimal de dépendances et est optimisé pour une utilisation en production.

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

mlflow.update_current_trace

Définissez des tags ou des métadonnées sur une trace active pendant l’exécution.

mlflow.set_trace_tag

Définir ou mettre à jour un tag sur une trace terminée

mlflow.delete_trace_tag

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

API

Quand l'utiliser

mlflow.update_current_trace

Définissez des tags ou des métadonnées sur une trace active pendant l’exécution.

mlflow.set_trace_tag

Définir ou mettre à jour un tag sur une trace terminée

mlflow.delete_trace_tag

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 :

Python
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)
remarque

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 :

Python
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.

Mise à jour des tags de traces


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 :

Python
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

mlflow.trace.user

Associer des traces à des utilisateurs spécifiques pour la personnalisation, l’analyse de cohorte et le debugging spécifique aux utilisateurs

Identifiant de session

mlflow.trace.session

Regroupez les traces des conversations à plusieurs tours pour analyser le flux conversationnel complet.

ID de requête client

client_request_id le TraceInfo

Link les traces aux appels d'API amont pour un debugging de bout en bout

Environnement / version

mlflow.source.type + métadonnées personnalisées

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é

Type de contexte

Champ MLflow

Cas d’utilisation

ID utilisateur

mlflow.trace.user

Associer des traces à des utilisateurs spécifiques pour la personnalisation, l’analyse de cohorte et le debugging spécifique aux utilisateurs

Identifiant de session

mlflow.trace.session

Regroupez les traces des conversations à plusieurs tours pour analyser le flux conversationnel complet.

ID de requête client

client_request_id le TraceInfo

Link les traces aux appels d'API amont pour un debugging de bout en bout

Environnement / version

mlflow.source.type + métadonnées personnalisées

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

mlflow.source.name

Point d'entrée ou nom du script

Nom du fichier Python ; nom du notebook Databricks

mlflow.source.git.commit

Hachage de commit Git

Dépôt git actuel

mlflow.source.git.branch

Nom de Branch Git

Dépôt git actuel

mlflow.source.git.repoURL

URL du référentiel Git

Dépôt git actuel

mlflow.source.type

Environnement d’exécution

NOTEBOOK (Jupyter/Databricks), LOCAL (script Python), UNKNOWN dans le cas contraire

mlflow.sourceRun

ID de l'exécution source

Exécution MLflow active

metadata.mlflow.modelId

Identifiant MLflow LoggedModel

MLFLOW_ACTIVE_MODEL_ID variable d’environnement ou mlflow.set_active_model()

Champ de métadonnées

Description

Défini automatiquement à partir de

mlflow.source.name

Point d'entrée ou nom du script

Nom du fichier Python ; nom du notebook Databricks

mlflow.source.git.commit

Hachage de commit Git

Dépôt git actuel

mlflow.source.git.branch

Nom de Branch Git

Dépôt git actuel

mlflow.source.git.repoURL

URL du référentiel Git

Dépôt git actuel

mlflow.source.type

Environnement d’exécution

NOTEBOOK (Jupyter/Databricks), LOCAL (script Python), UNKNOWN dans le cas contraire

mlflow.sourceRun

ID de l'exécution source

Exécution MLflow active

metadata.mlflow.modelId

Identifiant MLflow LoggedModel

MLFLOW_ACTIVE_MODEL_ID variable d’environnement ou mlflow.set_active_model()

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 :

Python
import mlflow
import os

mlflow.update_current_trace(
metadata={
"mlflow.source.type": os.getenv("APP_ENVIRONMENT", "development"),
}
)

Bonnes pratiques

  1. Formats d’ID cohérents — Utilisez des formats standardisés pour les ID utilisateur et de session dans tout votre agent.
  2. Limites de session — Définissez des règles claires concernant le start et la fin des sessions.
  3. Environment variables — Populate metadata from environment variables rather than hard-coding values.
  4. Combiner les types de contexte — Suivez conjointement le contexte de l’utilisateur, de la session et de l’environnement.
  5. Analyse régulière — Configurez des tableaux de bord pour surveiller le comportement des utilisateurs, les tendances des sessions et les performances des versions.
  6. 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.

Évaluations des traces

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

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 AssessmentSource identifiant 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 evaluation
  • source_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:

Python
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.",
)

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.

attention

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": ...}).

Back-end

Python
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)

JavaScript
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) =&gt; setMessage(e.target.value)} placeholder="Ask a question..." />
<button onClick={sendMessage}>Send</button>
{response && (
<div>
<p>{response}</p>
<div>
<button onClick={() =&gt; submitFeedback(true)} disabled={feedbackSubmitted}>
👍
</button>
<button onClick={() =&gt; submitFeedback(false)} disabled={feedbackSubmitted}>
👎
</button>
</div>
{feedbackSubmitted && Thanks for your feedback!</span>}
</div>
)}
</div>
);
}

Feedback multidimensionnel

Log multiple named assessments on a single trace to capture separate quality dimensions:

Python
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)

Python
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={
&quot;Cache-Control&quot;: &quot;no-cache&quot;,
&quot;Connection&quot;: &quot;keep-alive&quot;,
&quot;X-Accel-Buffering&quot;: &quot;no&quot;, # 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 type pour distinguer les jetons de contenu, les événements de fin et les erreurs.
  • Définissez X-Accel-Buffering: no pour 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.

Interface utilisateur des évaluations de trace

Suivre les retours d’utilisateurs

Query et agrégation du feedback par programmation :

Python
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

Étape suivante : Stocker les traces OpenTelemetry dans Unity Catalog