Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Zbieranie i rejestrowanie opinii użytkowników jest niezbędne do zrozumienia rzeczywistej jakości aplikacji GenAI. Platforma MLflow zapewnia ustrukturyzowany sposób przechwytywania opinii jako ocen dotyczących śladów, umożliwiając śledzenie jakości w czasie, identyfikowanie obszarów poprawy i tworzenie zestawów danych oceny na podstawie danych produkcyjnych.
Wymagania wstępne
Wybierz odpowiednią metodę instalacji na podstawie środowiska:
Produkcja
W przypadku wdrożeń produkcyjnych zainstaluj mlflow-tracing
pakiet:
pip install --upgrade mlflow-tracing
Pakiet mlflow-tracing
jest zoptymalizowany pod kątem użycia w środowisku produkcyjnym z minimalnymi zależnościami i lepszą charakterystyką wydajności.
Rozwój
W przypadku środowisk deweloperskich zainstaluj pełny pakiet MLflow przy użyciu dodatków usługi Databricks:
pip install --upgrade "mlflow[databricks]>=3.1"
Pełny mlflow[databricks]
pakiet zawiera wszystkie funkcje potrzebne do lokalnego programowania i eksperymentowania w usłudze Databricks.
Interfejs log_feedback
API jest dostępny w obu pakietach, dzięki czemu można zbierać opinie użytkowników niezależnie od wybranej metody instalacji.
Uwaga / Notatka
Platforma MLflow 3 jest wymagana do zbierania opinii użytkowników. Środowisko MLflow 2.x nie jest obsługiwane z powodu ograniczeń wydajności i brakujących funkcji niezbędnych do użycia w środowisku produkcyjnym.
Dlaczego warto zbierać opinie użytkowników?
Opinie użytkowników zapewniają podstawowe informacje na temat wydajności aplikacji:
- Rzeczywiste sygnały jakości — informacje o tym, jak rzeczywiste użytkownicy postrzegają dane wyjściowe aplikacji
- Ciągłe ulepszanie — identyfikowanie wzorców w negatywnych opiniach w celu kierowania rozwojem
- Tworzenie danych treningowych — tworzenie zestawów danych oceny wysokiej jakości przy użyciu opinii
- Monitorowanie jakości — śledzenie metryk zadowolenia w czasie i w różnych segmentach użytkowników
- Dostrajanie modelu — wykorzystanie danych opinii w celu ulepszenia modeli bazowych
Typy opinii
Platforma MLflow obsługuje różne typy opinii za pośrednictwem systemu oceny:
Typ opinii | Opis | Typowe przypadki użycia |
---|---|---|
Opinie binarne | Po prostu kciuki w górę/w dół lub poprawnie/niepoprawnie | Sygnały szybkiego zadowolenia użytkowników |
Wyniki liczbowe | Oceny na dużą skalę (np. 1–5 gwiazdek) | Szczegółowa ocena jakości |
Opinie podzielone na kategorie | Wiele opcji wyboru | Klasyfikowanie problemów lub typów odpowiedzi |
Opinia tekstowa | Komentarze w dowolnej formie | Szczegółowe wyjaśnienia użytkowników |
Zrozumienie modelu danych opinii
W rozwiązaniu MLflow opinie użytkowników są przechwytywane przy użyciu jednostki Feedback , która jest typem oceny , który może być dołączony do śladów lub określonych zakresów. Encja Opinii zapewnia ustrukturyzowany sposób przechowywania:
- Wartość: rzeczywista opinia (dane logiczne, liczbowe, tekstowe lub ustrukturyzowane)
- Źródło: informacje o tym, kto lub co dostarczyło opinię (użytkownik ludzki, sędzia LLM lub kod)
- Uzasadnienie: opcjonalne wyjaśnienie opinii
- Metadane: Dodatkowy kontekst, taki jak znaczniki czasu lub atrybuty niestandardowe
Zrozumienie tego modelu danych pomaga zaprojektować efektywne systemy zbierania opinii, które bezproblemowo integrują się z możliwościami oceny i monitorowania platformy MLflow. Aby uzyskać szczegółowe informacje na temat schematu jednostki Opinie zwrotne i wszystkich dostępnych pól, zobacz sekcję Opinie zwrotne w modelu danych Tracing.
Zbieranie opinii użytkowników końcowych
Podczas implementowania zbierania opinii w środowisku produkcyjnym należy połączyć opinie użytkowników z określonymi śladami. Istnieją dwa podejścia, których można użyć:
- Używanie identyfikatorów żądań klienta — generowanie własnych unikatowych identyfikatorów podczas przetwarzania żądań i odwoływania się do nich później w celu uzyskania opinii
- Używanie identyfikatorów śledzenia MLflow — użyj identyfikatora śledzenia generowanego automatycznie przez bibliotekę MLflow
Omówienie przepływu zbierania opinii
Oba podejścia są zgodne z podobnym wzorcem:
Podczas początkowego żądania: Aplikacja generuje unikatowy identyfikator żądania klienta lub pobiera identyfikator śledzenia wygenerowany przez platformę MLflow
Po otrzymaniu odpowiedzi: użytkownik może przekazać opinię, odwołując się do jednego z identyfikatorów. Oba podejścia kierują się podobnym wzorcem.
Podczas początkowego żądania: Aplikacja generuje unikatowy identyfikator żądania klienta lub pobiera identyfikator śledzenia wygenerowany przez platformę MLflow
Po otrzymaniu odpowiedzi: użytkownik może przekazać opinię, odwołując się do dowolnego identyfikatora
Opinia jest rejestrowana: API MLflow
log_feedback
tworzy ocenę powiązaną z oryginalnym ślademAnaliza i monitorowanie: możesz wykonywać zapytania i analizować opinie we wszystkich zapisach
Implementowanie zbierania opinii
Podejście 1. Używanie identyfikatorów śledzenia MLflow
Najprostszym podejściem jest użycie identyfikatora śledzenia generowanego automatycznie przez platformę MLflow dla każdego śladu. Ten identyfikator można pobrać podczas przetwarzania żądania i zwrócić go do klienta:
Implementacja warstwy serwerowej
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,
}
Przykład implementacji frontonu
Poniżej przedstawiono przykład implementacji frontonu dla aplikacji opartej na platformie 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 && <span>Thanks for your feedback!</span>}
</div>
)}
</div>
);
}
Podejście 2. Używanie identyfikatorów żądań klienta
Aby uzyskać większą kontrolę nad śledzeniem żądań, możesz użyć własnych unikatowych identyfikatorów żądań klienta. Takie podejście jest przydatne w przypadku konieczności obsługi własnego systemu śledzenia żądań lub integracji z istniejącą infrastrukturą:
Takie podejście wymaga zaimplementowania śledzenia żądań, w którym każdy ślad ma client_request_id
atrybut. Aby uzyskać więcej informacji na temat dołączania identyfikatorów żądań klienta do swoich śladów podczas początkowego żądania, zobacz Dodawanie kontekstu do śladów.
Implementacja warstwy serwerowej
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
mlflow.update_current_trace(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
experiment = client.get_experiment_by_name("/Shared/production-app")
traces = client.search_traces(
experiment_ids=[experiment.experiment_id],
filter_string=f"attributes.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,
}
Przykład implementacji frontonu
Poniżej przedstawiono przykład implementacji frontonu dla aplikacji opartej na platformie React. W przypadku korzystania z identyfikatorów żądań klienta fronton musi przechowywać następujące identyfikatory i zarządzać nimi:
// 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 && <span>✓ 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>
);
}
Kluczowe szczegóły implementacji
AssessmentSource: AssessmentSource
obiekt określa, kto lub co dostarczyło opinię:
-
source_type
: Może to być wartość "HUMAN" dla opinii użytkowników lub "LLM_JUDGE" na potrzeby zautomatyzowanej oceny -
source_id
: identyfikuje określonego użytkownika lub systemu przekazującego opinię
Przechowywanie opinii: Opinie są przechowywane jako oceny na śladzie, co oznacza:
- Jest on trwale skojarzony z określoną interakcją
- Można odpytywać je wraz z danymi śledzenia.
- Jest on widoczny w interfejsie użytkownika platformy MLflow podczas wyświetlania śladu
Obsługa różnych typów opinii
Możesz rozszerzyć dowolne z podejść, aby obsługiwać bardziej złożone opinie. Oto przykład użycia identyfikatorów śledzenia:
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
}
Obsługa informacji zwrotnej za pomocą odpowiedzi strumieniowych
W przypadku korzystania z odpowiedzi przesyłanych strumieniowo (zdarzeniaServer-Sent lub WebSocket) identyfikator śledzenia nie jest dostępny, dopóki strumień nie zostanie ukończony. Stanowi to unikatowe wyzwanie dla zbierania opinii, które wymaga innego podejścia.
Dlaczego przesyłanie strumieniowe jest inne
W tradycyjnych wzorcach odpowiedzi na żądanie otrzymujesz razem pełną odpowiedź i identyfikator śledzenia. Za pomocą przesyłania strumieniowego:
- Tokeny są dostarczane przyrostowo: odpowiedź jest tworzona stopniowo w miarę strumieniowania tokenów z usługi LLM
- Uzupełnianie śledzenia jest odroczone: identyfikator śledzenia generowany jest dopiero po zakończeniu całego strumienia
- Interfejs opinii musi czekać: użytkownicy nie mogą przeprowadzić opinii, dopóki nie otrzymają pełnej odpowiedzi i identyfikatora śledzenia
Implementacja backendu z użyciem SSE
Oto jak zaimplementować streaming z dostarczaniem identyfikatora śledzenia na końcu strumienia:
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
}
)
Implementacja frontonu na potrzeby przesyłania strumieniowego
Obsługuj zdarzenia przesyłania strumieniowego i włączaj opinie dopiero po otrzymaniu identyfikatora śledzenia.
// 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 className="typing-indicator">...</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">
<span>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 && <span className="feedback-thanks">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>
);
}
Kluczowe zagadnienia dotyczące przesyłania strumieniowego
Podczas implementowania zbierania opinii przy użyciu odpowiedzi strumieniowych, należy pamiętać o następujących kwestiach:
Czas identyfikatora śledzenia: identyfikator śledzenia jest dostępny dopiero po zakończeniu przesyłania strumieniowego. Zaprojektuj interfejs użytkownika, aby obsłużyć ten proces bezpiecznie, wyłączając kontrolki opinii do momentu odebrania identyfikatora śledzenia.
Struktura zdarzeń: użyj spójnego formatu zdarzeń z polem
type
, aby odróżnić tokeny zawartości, zdarzenia ukończenia i błędy. Dzięki temu analizowanie i obsługa zdarzeń jest bardziej niezawodne.Zarządzanie stanem: Śledzenie zarówno zawartości przesyłania strumieniowego, jak i identyfikatora śledzenia oddzielnie. Zresetuj wszystkie stany na początku każdej nowej interakcji, aby zapobiec problemom ze nieaktualnymi danymi.
Obsługa błędów: uwzględnij zdarzenia błędów w strumieniu, aby bezpiecznie obsłużyć błędy. Upewnij się, że błędy są rejestrowane w logach, w miarę możliwości dla celów debugowania.
Zarządzanie buforem:
- Wyłączanie buforowania serwera proxy za pomocą
X-Accel-Buffering: no
nagłówka - Implementacja prawidłowego buforowania liniowego w interfejsie użytkownika w celu obsługi częściowych wiadomości SSE
- Rozważ zaimplementowanie logiki ponownego łączenia w przypadku przerw w działaniu sieci
- Wyłączanie buforowania serwera proxy za pomocą
Optymalizacja wydajności:
- Dodawanie małych opóźnień między tokenami (
asyncio.sleep(0.01)
), aby zapobiec przeciążeniu klientów - Wsaduj liczne tokeny, jeśli przybędą zbyt szybko
- Rozważ zaimplementowanie mechanizmów przeciążenia zwrotnego dla powolnych klientów
- Dodawanie małych opóźnień między tokenami (
Analizowanie danych opinii
Po zebraniu opinii możesz je przeanalizować, aby uzyskać szczegółowe informacje o jakości aplikacji i zadowoleniu użytkowników.
Wyświetlanie informacji zwrotnych w interfejsie użytkownika Trace
Pobieranie śladów z informacją zwrotną za pośrednictwem zestawu SDK
Wyświetlanie informacji zwrotnych w interfejsie użytkownika Trace
Pobieranie śladów z informacją zwrotną za pośrednictwem zestawu SDK
Najpierw pobierz ślady z określonego przedziału czasu:
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
Analizowanie wzorców opinii za pośrednictwem zestawu SDK
Wyodrębnianie i analizowanie opinii ze śladów:
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")
Analizowanie wielowymiarowych opinii
Aby uzyskać bardziej szczegółową opinię z ocenami:
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")
Zagadnienia dotyczące środowiska produkcyjnego
Aby zapoznać się z wdrożeniami produkcyjnymi, zapoznaj się z naszym przewodnikiem dotyczącym obserwacji środowiska produkcyjnego z śledzeniem , które obejmuje:
- Implementowanie punktów końcowych zbierania opinii
- Łączenie opinii ze śladami przy użyciu identyfikatorów żądań klienta
- Konfigurowanie monitorowania jakości w czasie rzeczywistym
- Najlepsze praktyki dotyczące przetwarzania opinii na dużą skalę
Dalsze kroki
Kontynuuj pracę z tymi zalecanymi akcjami i samouczkami.
- Tworzenie zestawów danych oceny — tworzenie zestawów danych testowych przy użyciu zebranych opinii
- Używanie śladów w celu poprawy jakości — analizowanie wzorców opinii w celu identyfikowania ulepszeń
- Konfigurowanie monitorowania produkcji — monitorowanie metryk jakości na podstawie opinii
Przewodniki referencyjne
Zapoznaj się ze szczegółową dokumentacją dotyczącą pojęć i funkcji wymienionych w tym przewodniku.
- Rejestrowanie ocen — Zrozumienie, jak opinie są przechowywane jako oceny
- Śledzenie modelu danych — informacje o ocenach i strukturze śledzenia
- Śledzenie zapytań za pomocą zestawu SDK — zaawansowane techniki analizowania opinii