Recueillir les commentaires des utilisateurs
La collecte et l'enregistrement des commentaires des utilisateurs sont essentiels pour comprendre la qualité réelle de votre application GenAI. MLflow offre un moyen structuré de recueillir les commentaires sous forme d'évaluations sur les traces, ce qui vous permet de suivre la qualité au fil du temps, d'identifier les points à améliorer et de créer des datasets d'évaluation à partir des données de production.

Prérequis
Choisissez la méthode d'installation appropriée en fonction de votre environnement :
- Production
- Development
Pour les déploiements de production, installez le package mlflow-tracing :
pip install --upgrade mlflow-tracing
Le package mlflow-tracing est optimisé pour une utilisation en production avec des dépendances minimales et de meilleures caractéristiques de performance.
Pour les environnements de développement, installez le package MLflow complet avec les extras Databricks :
pip install --upgrade "mlflow[databricks]>=3.1"
Le package mlflow[databricks] complet inclut toutes les fonctionnalités nécessaires au développement local et à l'expérimentation sur Databricks.
L’API log_feedback est disponible dans les deux packages. Vous pouvez donc recueillir les commentaires des utilisateurs, quelle que soit la méthode d’installation que vous choisissez.
MLflow 3 est requis pour la collecte des commentaires des utilisateurs. MLflow 2.x n'est pas pris en charge en raison de limitations de performances et de fonctionnalités manquantes essentielles pour une utilisation en production.
Pourquoi collecter les retours des utilisateurs ?
Les commentaires des utilisateurs fournissent la vérité terrain sur les performances de votre application :
- **Signaux de qualité réels** — Comprenez comment les utilisateurs réels perçoivent les sorties de votre application
- Amélioration continue – Identifiez les modèles de commentaires négatifs pour orienter le développement.
- Création de données d'entraînement : utilisez les commentaires pour créer des datasets d'évaluation de haute qualité
- Monitoring de la qualité : Suivez les indicateurs de satisfaction dans le temps et à travers les différents segments d’utilisateurs.
- Affinement de modèle — Exploitez les données de feedback pour améliorer vos modèles sous-jacents.
Types de commentaires
MLflow prend en charge divers types de commentaires par l'intermédiaire de son système d'évaluation :
Type de commentaires | Description | Cas d'utilisation courants |
|---|---|---|
Retour binaire | Simple pouce levé/baissé ou correct/incorrect | Signaux rapides de satisfaction des utilisateurs |
Scores numériques | Évaluations sur une échelle (par exemple, 1-5 étoiles) | Évaluation détaillée de la qualité |
Commentaires catégoriels | Options à choix multiples | Classification des problèmes ou des types de réponse |
Commentaires textuels | Commentaires libres | Explications détaillées pour l'utilisateur |
Comprendre le modèle de données de Feedback
Dans MLflow, les retours d'utilisateurs sont capturés à l'aide de l'entité Feedback , qui est un type d'Évaluation qui peut être attachée à des traces ou des spans spécifiques. L'entité Feedback offre un moyen structuré de stocker :
- Valeur : Le retour d'information réel (booléen, numérique, texte ou données structurées)
- Source : Informations sur qui ou quoi a fourni le feedback (utilisateur humain, juge LLM ou code)
- Justification : explication facultative des commentaires.
- Métadonnées : Contexte supplémentaire comme les Timestamp ou les attributs personnalisés
La compréhension de ce modèle de données vous aide à concevoir des systèmes efficaces de collecte de commentaires qui s'intègrent parfaitement aux capacités d'évaluation et de monitoring de MLflow. Pour des informations détaillées sur le schéma d'entité de retour d'information et tous les champs disponibles, consultez la section Feedback dans les concepts Span.
Collecte de commentaires des utilisateurs finaux
Lors de l'implémentation de la collecte de feedback en production, vous devez Link les retours des utilisateurs à des traces spécifiques. Il existe deux approches que vous pouvez utiliser :
- Utilisation des ID de requête client – Générez vos propres ID uniques lors du traitement des requêtes et référencez-les ultérieurement pour les retours.
- Utilisation des ID de trace MLflow — Utilisez l'ID de trace généré automatiquement par MLflow
Comprendre le flux de collecte des commentaires
Les deux approches suivent un modèle similaire :
- Pendant la requête initiale : votre application génère un ID de requête client unique ou récupère l'ID de trace généré par MLflow
- Après avoir reçu la réponse : l’utilisateur peut fournir des commentaires en référençant l’un ou l’autre des ID
- Les commentaires sont enregistrés : l'API
log_feedbackde MLflow crée une évaluation associée à la trace originale - Analyse et monitoring : Vous pouvez query et analyser les retours d’information sur toutes les traces.
Mise en œuvre de la collecte de commentaires
- Approach 1: Using MLflow trace IDs
- Approach 2: Using client request IDs
L'approche la plus simple consiste à utiliser l'ID de trace que MLflow génère automatiquement pour chaque trace. Vous pouvez récupérer cet ID pendant le traitement de la requête et le renvoyer au client :
Mise en œuvre du back-end
import mlflow
from fastapi import FastAPI, Query
from mlflow.client import MlflowClient
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 # Include the trace ID in the response
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
"""
Process a chat request and return the trace ID for feedback collection.
"""
# Your GenAI application logic here
response = process_message(request.message) # Replace with your actual processing logic
# Get the current trace ID
trace_id = mlflow.get_current_active_span().trace_id
return ChatResponse(
response=response,
trace_id=trace_id
)
class FeedbackRequest(BaseModel):
is_correct: bool # True for thumbs up, False for thumbs down
comment: Optional[str] = None
@app.post("/feedback")
def submit_feedback(
trace_id: str = Query(..., description="The trace ID from the chat response"),
feedback: FeedbackRequest = ...,
user_id: Optional[str] = Query(None, description="User identifier")
):
"""
Collect user feedback using the MLflow trace ID.
"""
# Log the feedback directly using the trace ID
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,
}
Exemple de mise en œuvre frontale
Vous trouverez ci-dessous un exemple de l'implémentation front-end pour une application basée sur React :
// React example for chat with feedback
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 () => {
try {
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);
} catch (error) {
console.error('Chat error:', error);
}
};
const submitFeedback = async (isCorrect, comment = null) => {
if (!traceId || feedbackSubmitted) return;
try {
const params = new URLSearchParams({
trace_id: traceId,
...(userId && { user_id: userId }),
});
const res = await fetch(`/feedback?${params}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
is_correct: isCorrect,
comment: comment,
}),
});
if (res.ok) {
setFeedbackSubmitted(true);
// Optionally show success message
}
} catch (error) {
console.error('Feedback submission error:', error);
}
};
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 className="feedback-buttons">
<button onClick={() => submitFeedback(true)} disabled={feedbackSubmitted}>
👍
</button>
<button onClick={() => submitFeedback(false)} disabled={feedbackSubmitted}>
👎
</button>
</div>
{feedbackSubmitted && Thanks for your feedback!</span>}
</div>
)}
</div>
);
}
Pour un meilleur contrôle du suivi des requêtes, vous pouvez utiliser vos propres identifiants de requête client uniques. Cette approche est utile lorsque vous devez maintenir votre propre système de suivi des requêtes ou vous intégrer à l'infrastructure existante :
Cette approche exige que vous implémentiez le suivi des requêtes où chaque trace a un tag client_request_id. Pour plus d'informations sur la façon d'attacher les ID de requête client à vos traces pendant la requête initiale, consultez Ajouter du contexte aux traces.
Si vous déployez votre modèle sur un endpoint de Model Serving Databricks, vous devez définir client_request_id comme balise (et non comme attribut). Le définir comme attribut avec update_current_trace(client_request_id=...) compromet l'exportation des traces dans les environnements de service. Vous pouvez également utiliser Approche 1 : Utilisation des ID de trace MLflow.
Mise en œuvre du 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 # Include the client request ID in the response
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
"""
Process a chat request and set a client request ID for later feedback collection.
"""
# Sample: Generate a unique client request ID
# Normally, this ID would be your app's backend existing ID for this interaction
client_request_id = f"req-{uuid.uuid4().hex[:8]}"
# Attach the client request ID to the current trace as a tag.
mlflow.update_current_trace(tags={"client_request_id": client_request_id})
# Your GenAI application logic here
response = process_message(request.message) # Replace with your actual processing logic
return ChatResponse(
response=response,
client_request_id=client_request_id
)
class FeedbackRequest(BaseModel):
is_correct: bool # True for thumbs up, False for thumbs down
comment: Optional[str] = None
@app.post("/feedback")
def submit_feedback(
request: Request,
client_request_id: str = Query(..., description="The request ID from the original interaction"),
feedback: FeedbackRequest = ...
):
"""
Collect user feedback for a specific interaction.
This endpoint:
1. Finds the trace using the client request ID
2. Logs the feedback as an MLflow assessment
3. Adds tags for easier querying and filtering
"""
client = MlflowClient()
# Find the trace using the client request ID tag
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
# Log the feedback as an assessment
# Assessments are the structured way to attach feedback to traces
mlflow.log_feedback(
trace_id=traces[0].info.trace_id,
name="user_feedback",
value=feedback.is_correct,
source=AssessmentSource(
source_type="HUMAN", # Indicates this is human feedback
source_id=request.headers.get("X-User-ID") # Link feedback to the user who provided it
),
rationale=feedback.comment # Optional explanation from the user
)
return {
"status": "success",
"trace_id": traces[0].info.trace_id,
}
Exemple de mise en œuvre frontale
Vous trouverez ci-dessous un exemple d'implémentation front-end pour une application basée sur React. Lorsque vous utilisez des ID de requête client, votre frontend doit stocker et gérer ces ID :
// React example with session-based request tracking
import React, { useState, useEffect } from 'react';
function ChatWithRequestTracking() {
const [message, setMessage] = useState('');
const [conversations, setConversations] = useState([]);
const [sessionId] = useState(() => `session-${Date.now()}`);
const sendMessage = async () => {
try {
const res = await fetch('/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Session-ID': sessionId,
},
body: JSON.stringify({ message }),
});
const data = await res.json();
// Store conversation with request ID
setConversations((prev) => [
...prev,
{
id: data.client_request_id,
message: message,
response: data.response,
timestamp: new Date(),
feedbackSubmitted: false,
},
]);
setMessage('');
} catch (error) {
console.error('Chat error:', error);
}
};
const submitFeedback = async (requestId, isCorrect, comment = null) => {
try {
const params = new URLSearchParams({
client_request_id: requestId,
});
const res = await fetch(`/feedback?${params}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-User-ID': getUserId(), // Your user identification method
},
body: JSON.stringify({
is_correct: isCorrect,
comment: comment,
}),
});
if (res.ok) {
// Mark feedback as submitted
setConversations((prev) =>
prev.map((conv) => (conv.id === requestId ? { ...conv, feedbackSubmitted: true } : conv)),
);
}
} catch (error) {
console.error('Feedback submission error:', error);
}
};
return (
<div>
<div className="chat-history">
{conversations.map((conv) => (
<div key={conv.id} className="conversation">
<div className="user-message">{conv.message}</div>
<div className="bot-response">{conv.response}</div>
<div className="feedback-section">
<button onClick={() => submitFeedback(conv.id, true)} disabled={conv.feedbackSubmitted}>
👍
</button>
<button onClick={() => submitFeedback(conv.id, false)} disabled={conv.feedbackSubmitted}>
👎
</button>
{conv.feedbackSubmitted && ✓ Feedback received</span>}
</div>
</div>
))}
</div>
<div className="chat-input">
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Type your message..."
/>
<button onClick={sendMessage}>Send</button>
</div>
</div>
);
}
Détails clés de l'implémentation
**AssessmentSource** : AssessmentSource L'objet identifie qui ou quoi a fourni le feedback :
source_type: Peut être "HUMAN" pour les commentaires des utilisateurs ou "LLM_JUDGE" pour l'évaluation automatiséesource_id: identifie l'utilisateur ou le système spécifique fournissant des retours d'expérience
**Stockage des commentaires** : les commentaires sont stockés sous forme d’évaluations sur la trace, ce qui signifie :
- Il est associé de manière permanente à l'interaction spécifique
- Il peut être interrogé avec les données de trace
- C'est visible dans l'interface utilisateur MLflow lors de la consultation de la trace.
Gestion des différents types de feedback
Vous pouvez étendre l'une ou l'autre approche pour prendre en charge des retours d'information plus complexes. Voici un exemple d’utilisation d’ID de trace :
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 from 1-5"),
helpfulness: int = Query(..., ge=1, le=5, description="Helpfulness rating from 1-5"),
relevance: int = Query(..., ge=1, le=5, description="Relevance rating from 1-5"),
user_id: str = Query(..., description="User identifier"),
comment: Optional[str] = None
):
"""
Collect multi-dimensional feedback with separate ratings for different aspects.
Each aspect is logged as a separate assessment for granular analysis.
"""
# Log each dimension as a separate assessment
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
}
Gestion des commentaires avec des réponses en streaming
Lorsque vous utilisez des réponses de streaming (Server-Sent Events ou WebSockets), l'ID de trace n'est pas disponible tant que le stream n'est pas terminé. Cela représente un défi unique pour la collecte de feedback qui nécessite une approche différente.
Pourquoi le streaming est différent
Dans les modèles traditionnels de requête-réponse, vous recevez la réponse complète et l'ID de trace ensemble. Avec le streaming :
- Les jetons arrivent de manière incrémentielle : La réponse est construite au fil du temps à mesure que les jetons sont Stream depuis le LLM
- Achèvement de la trace différé : L’ID de trace n’est généré qu’une fois le Stream entier terminé.
- L'interface utilisateur de feedback doit attendre : les utilisateurs ne peuvent pas fournir de feedback tant qu'ils n'ont pas à la fois la réponse complète et l'ID de trace
Implémentation backend avec SSE
Voici comment implémenter le streaming avec la livraison de l’ID de trace à la fin du stream :
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import mlflow
import json
import asyncio
from typing import AsyncGenerator
@app.post("/chat/stream")
async def chat_stream(request: ChatRequest):
"""
Stream chat responses with trace ID sent at completion.
"""
async def generate() -> AsyncGenerator[str, None]:
try:
# Start MLflow trace
with mlflow.start_span(name="streaming_chat") as span:
# Update trace with request metadata
mlflow.update_current_trace(
request_message=request.message,
stream_start_time=datetime.now().isoformat()
)
# Stream tokens from your LLM
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
# Log the complete response to the trace
span.set_attribute("response", full_response)
span.set_attribute("token_count", len(full_response.split()))
# Get trace ID after completion
trace_id = span.trace_id
# Send trace ID as final event
yield f"data: {json.dumps({'type': 'done', 'trace_id': trace_id})}\n\n"
except Exception as e:
# Log error to trace if available
if mlflow.get_current_active_span():
mlflow.update_current_trace(error=str(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
}
)
Implémentation front-end pour le streaming
Gérez les événements en streaming et activez le feedback uniquement après avoir reçu l'identifiant de trace :
// React hook for streaming chat with feedback
import React, { useState, useCallback } from 'react';
function useStreamingChat() {
const [isStreaming, setIsStreaming] = useState(false);
const [streamingContent, setStreamingContent] = useState('');
const [traceId, setTraceId] = useState(null);
const [error, setError] = useState(null);
const sendStreamingMessage = useCallback(async (message) => {
// Reset state
setIsStreaming(true);
setStreamingContent('');
setTraceId(null);
setError(null);
try {
const response = await fetch('/chat/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
// Keep the last incomplete line in the buffer
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
switch (data.type) {
case 'token':
setStreamingContent((prev) => prev + data.content);
break;
case 'done':
setTraceId(data.trace_id);
setIsStreaming(false);
break;
case 'error':
setError(data.error);
setIsStreaming(false);
break;
}
} catch (e) {
console.error('Failed to parse SSE data:', e);
}
}
}
}
} catch (error) {
setError(error.message);
setIsStreaming(false);
}
}, []);
return {
sendStreamingMessage,
streamingContent,
isStreaming,
traceId,
error,
};
}
// Component using the streaming hook
function StreamingChatWithFeedback() {
const [message, setMessage] = useState('');
const [feedbackSubmitted, setFeedbackSubmitted] = useState(false);
const { sendStreamingMessage, streamingContent, isStreaming, traceId, error } = useStreamingChat();
const handleSend = () => {
if (message.trim()) {
setFeedbackSubmitted(false);
sendStreamingMessage(message);
setMessage('');
}
};
const submitFeedback = async (isPositive) => {
if (!traceId || feedbackSubmitted) return;
try {
const response = await fetch(`/feedback?trace_id=${traceId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
is_correct: isPositive,
comment: null,
}),
});
if (response.ok) {
setFeedbackSubmitted(true);
}
} catch (error) {
console.error('Feedback submission failed:', error);
}
};
return (
<div className="streaming-chat">
<div className="chat-messages">
{streamingContent && (
<div className="message assistant">
{streamingContent}
{isStreaming && ...</span>}
</div>
)}
{error && <div className="error-message">Error: {error}</div>}
</div>
{/* Feedback buttons - only enabled when trace ID is available */}
{streamingContent && !isStreaming && traceId && (
<div className="feedback-section">
Was this response helpful?</span>
<button onClick={() => submitFeedback(true)} disabled={feedbackSubmitted} className="feedback-btn positive">
👍 Yes
</button>
<button onClick={() => submitFeedback(false)} disabled={feedbackSubmitted} className="feedback-btn negative">
👎 No
</button>
{feedbackSubmitted && Thank you!</span>}
</div>
)}
<div className="chat-input-section">
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && !isStreaming && handleSend()}
placeholder="Type your message..."
disabled={isStreaming}
/>
<button onClick={handleSend} disabled={isStreaming || !message.trim()}>
{isStreaming ? 'Streaming...' : 'Send'}
</button>
</div>
</div>
);
}
Considérations clés pour le streaming
Lors de l'implémentation de la collecte de feedback avec des réponses en streaming, gardez ces points à l'esprit :
-
**Délai d'ID de trace** : l'ID de trace n'est disponible qu'après la fin du streaming. Concevez votre interface utilisateur pour gérer cela avec élégance en désactivant les contrôles de feedback jusqu'à ce que l'ID de trace soit reçu.
-
Structure d'événement : Utilisez un format d'événement cohérent avec un champ
typepour faire la distinction entre les jetons de contenu, les événements de complétion et les erreurs. Cela rend l'analyse et la gestion des événements plus fiables. -
Gestion de l'état : suivez séparément le contenu en streaming et l'ID de trace. Reset tout l'état au start de chaque nouvelle interaction pour éviter les problèmes de données obsolètes.
-
Gestion des erreurs : Incluez les événements d’erreur dans le Stream pour gérer les échecs avec élégance. Assurez-vous que les erreurs sont journalisées dans la trace lorsque cela est possible pour le debugging.
-
Gestion des tampons :
- Utilisez l'en-tête
X-Accel-Buffering: nopour désactiver la mise en mémoire tampon du proxy - Mettre en œuvre une mise en mémoire tampon de ligne appropriée dans le front-end pour gérer les messages SSE partiels.
- Envisagez de mettre en œuvre une logique de reconnexion pour les interruptions réseau
- Utilisez l'en-tête
-
Optimisation des performances :
- Ajoutez de petits délais entre les jetons (
asyncio.sleep(0.01)) pour éviter de surcharger les clients. - Traitez par batch plusieurs jetons s'ils arrivent trop rapidement.
- Envisagez de mettre en œuvre des mécanismes de régulation de la charge pour les clients lents
- Ajoutez de petits délais entre les jetons (
Analyse des données de feedback
Une fois que vous avez collecté les commentaires, vous pouvez les analyser pour obtenir des informations sur la qualité de votre application et la satisfaction des utilisateurs.
Affichage des commentaires dans l'interface utilisateur Trace

Obtention des traces avec des retours d'expérience à l'aide du SDK
Affichage des commentaires dans l'interface utilisateur Trace

Obtention des traces avec des retours d'expérience à l'aide du SDK
Tout d'abord, récupérez les traces d'une fenêtre de temps spécifique :
from mlflow.client import MlflowClient
from datetime import datetime, timedelta
def get_recent_traces(experiment_name: str, hours: int = 24):
"""Get traces from the last N hours."""
client = MlflowClient()
# Calculate cutoff time
cutoff_time = datetime.now() - timedelta(hours=hours)
cutoff_timestamp_ms = int(cutoff_time.timestamp() * 1000)
# Query traces
traces = client.search_traces(
experiment_names=[experiment_name],
filter_string=f"trace.timestamp_ms > {cutoff_timestamp_ms}"
)
return traces
Analyse des schémas de retour d'information à l'aide du SDK
Extraire et analyser les commentaires des traces :
def analyze_user_feedback(traces):
"""Analyze feedback patterns from traces."""
client = MlflowClient()
# Initialize counters
total_traces = len(traces)
traces_with_feedback = 0
positive_count = 0
negative_count = 0
# Process each trace
for trace in traces:
# Get full trace details including assessments
trace_detail = client.get_trace(trace.info.trace_id)
if trace_detail.data.assessments:
traces_with_feedback += 1
# Count positive/negative feedback
for assessment in trace_detail.data.assessments:
if assessment.name == "user_feedback":
if assessment.value:
positive_count += 1
else:
negative_count += 1
# Calculate metrics
if traces_with_feedback > 0:
feedback_rate = (traces_with_feedback / total_traces) * 100
positive_rate = (positive_count / traces_with_feedback) * 100
else:
feedback_rate = 0
positive_rate = 0
return {
"total_traces": total_traces,
"traces_with_feedback": traces_with_feedback,
"feedback_rate": feedback_rate,
"positive_rate": positive_rate,
"positive_count": positive_count,
"negative_count": negative_count
}
# Example usage
traces = get_recent_traces("/Shared/production-genai-app", hours=24)
results = analyze_user_feedback(traces)
print(f"Feedback rate: {results['feedback_rate']:.1f}%")
print(f"Positive feedback: {results['positive_rate']:.1f}%")
print(f"Total feedback: {results['traces_with_feedback']} out of {results['total_traces']} traces")
Analyse du feedback multidimensionnel
Pour des commentaires plus détaillés avec des évaluations :
def analyze_ratings(traces):
"""Analyze rating-based feedback."""
client = MlflowClient()
ratings_by_dimension = {}
for trace in traces:
trace_detail = client.get_trace(trace.info.trace_id)
if trace_detail.data.assessments:
for assessment in trace_detail.data.assessments:
# Look for rating assessments
if assessment.name.startswith("user_") and assessment.name != "user_feedback":
dimension = assessment.name.replace("user_", "")
if dimension not in ratings_by_dimension:
ratings_by_dimension[dimension] = []
ratings_by_dimension[dimension].append(assessment.value)
# Calculate averages
average_ratings = {}
for dimension, scores in ratings_by_dimension.items():
if scores:
average_ratings[dimension] = sum(scores) / len(scores)
return average_ratings
# Example usage
ratings = analyze_ratings(traces)
for dimension, avg_score in ratings.items():
print(f"{dimension}: {avg_score:.2f}/1.0")
Ressources supplémentaires
- Déployer des agents avec traçage - Comprendre l'enregistrement des traces en production
- Créez des datasets d'évaluation — utilisez les retours collectés pour créer des datasets de test et analyser les schémas pour l'amélioration de la qualité
- Configurer le monitoring de production – Surveillez les métriques de qualité basées sur les commentaires