Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Сбор и ведение журнала отзывов пользователей важны для понимания реального качества приложения GenAI. MLflow предоставляет структурированный способ сбора отзывов в виде оценок для трассировок, что позволяет отслеживать качество с течением времени, определять области улучшения и создавать наборы данных оценки из производственных данных.
Предпосылки
Выберите соответствующий метод установки в зависимости от среды:
Производство
Для рабочих развертываний mlflow-tracing
установите пакет:
pip install --upgrade mlflow-tracing
Пакет mlflow-tracing
оптимизирован для использования в рабочей среде с минимальными зависимостями и улучшенными характеристиками производительности.
Развитие
Для сред разработки установите полный пакет MLflow с дополнительными компонентами Databricks:
pip install --upgrade "mlflow[databricks]>=3.1"
Полный mlflow[databricks]
пакет включает все функции, необходимые для локальной разработки и экспериментирования в Databricks.
log_feedback
API доступен в обоих пакетах, поэтому вы можете собирать отзывы пользователей независимо от выбранного метода установки.
Замечание
MLflow 3 требуется для сбора отзывов пользователей. MLflow 2.x не поддерживается из-за ограничений производительности и отсутствующих функций, необходимых для использования в рабочей среде.
Зачем собирать отзывы пользователей?
Отзывы пользователей предоставляют правду о производительности приложения:
- Сигналы качества реального мира . Узнайте, как фактические пользователи воспринимают выходные данные приложения.
- Непрерывное улучшение . Определение шаблонов в отрицательных отзывах для руководства по разработке
- Создание обучающих данных . Использование обратной связи для создания высококачественных наборов данных оценки
- Мониторинг качества . Отслеживание метрик удовлетворенности с течением времени и между различными сегментами пользователей
- Точная настройка модели - Используйте данные обратной связи для улучшения базовых моделей
Типы отзывов
MLflow поддерживает различные типы отзывов через свою систему оценки:
Тип обратной связи | Описание | Распространенные варианты использования |
---|---|---|
Двоичная обратная связь | Простые пальцы вверх/вниз или правильно/неправильные | Быстрые сигналы о удовлетворенности пользователей |
Числовые оценки | Рейтинги по шкале (например, 1-5 звезд) | Подробная оценка качества |
Категориальная обратная связь | Параметры с несколькими вариантами выбора | Классификация проблем или типов ответов |
Обратная связь с текстом | Комментарии в свободной форме | Подробные объяснения пользователей |
Общие сведения о модели данных обратной связи
В MLflow отзывы пользователей записываются с помощью сущности Отзывы, которая является типом оценки и может быть присоединена к трассировкам или определённым интервалам. Сущность Обратной связи предоставляет структурированный способ хранения:
- Значение: фактическая обратная связь (логические, числовые, текстовые или структурированные данные)
- Источник: информация о том, кто или что предоставил отзыв (человеческий пользователь, судья LLM или код)
- Обоснование: Необязательное объяснение обратной связи
- Метаданные: дополнительный контекст, например метки времени или настраиваемые атрибуты
Понимание этой модели данных помогает разрабатывать эффективные системы сбора отзывов, которые легко интегрируются с возможностями оценки и мониторинга MLflow. Подробные сведения о схеме сущности обратной связи и всех доступных полях см. в разделе "Отзывы" в модели данных трассировки.
Сбор отзывов конечных пользователей
При реализации коллекции отзывов в рабочей среде необходимо связать отзывы пользователей с определенными трассировками. Существует два подхода, которые можно использовать:
- Использование идентификаторов запросов клиента — создание собственных уникальных идентификаторов при обработке запросов и ссылки на них позже для обратной связи
- Использование идентификаторов трассировки MLflow . Используйте идентификатор трассировки, автоматически созданный MLflow
Общие сведения о потоке сбора отзывов
Оба подхода соответствуют аналогичной схеме:
Во время первоначального запроса: приложение создает уникальный идентификатор запроса клиента или извлекает идентификатор трассировки, созданный MLflow.
После получения ответа: пользователь может предоставить отзыв, ссылаясь на один из идентификаторов. Оба подхода следуют аналогичному шаблону:
Во время первоначального запроса: приложение создает уникальный идентификатор запроса клиента или извлекает идентификатор трассировки, созданный MLflow.
После получения ответа: пользователь может предоставить отзыв, ссылаясь на любой идентификатор.
Отзыв регистрируется: API MLflow
log_feedback
создает оценку, подключенную к исходной трассировке.Анализ и мониторинг: Вы можете собирать и анализировать отзывы по всем данным трассировок.
Реализация коллекции отзывов
Подход 1. Использование идентификаторов трассировки MLflow
Самый простой подход — использовать идентификатор трассировки, который MLflow автоматически создает для каждой трассировки. Этот идентификатор можно получить во время обработки запроса и вернуть его клиенту:
Реализация бэкенда
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,
}
Пример реализации пользовательского интерфейса
Ниже приведен пример реализации внешнего интерфейса для приложения на основе 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>
);
}
Подход 2. Использование идентификаторов запросов клиента
Для управления отслеживанием запросов можно использовать собственные уникальные идентификаторы запросов клиента. Этот подход полезен, если необходимо поддерживать собственную систему отслеживания запросов или интегрироваться с существующей инфраструктурой:
Этот подход требует реализации отслеживания запросов, где каждая трассировка имеет client_request_id
атрибут. Дополнительные сведения о подключении идентификаторов запросов клиента к трассировкам во время первоначального запроса см. в разделе "Добавление контекста в трассировки".
Реализация бэкенда
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,
}
Пример реализации пользовательского интерфейса
Ниже приведен пример реализации внешнего интерфейса для приложения на основе React. При использовании идентификаторов запросов клиента интерфейс должен хранить и управлять этими идентификаторами:
// 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>
);
}
Основные детали реализации
AssessmentSource: AssessmentSource
объект определяет, кто или что предоставил отзыв:
-
source_type
: может быть "HUMAN" для отзывов пользователей или "LLM_JUDGE" для автоматической оценки -
source_id
: определяет конкретного пользователя или систему, предоставляющую отзывы
Хранилище отзывов: Отзывы хранятся как оценки на записи, что означает:
- Это постоянно связано с конкретным взаимодействием
- Его можно запрашивать вместе с данными трассировки
- Он отображается в пользовательском интерфейсе MLflow при просмотре трассировки данных
Обработка различных типов отзывов
Вы можете расширить любой подход для поддержки более сложных отзывов. Ниже приведен пример использования идентификаторов трассировки:
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
}
Обработка отзывов с потоковыми ответами
При использовании потоковых ответов (Server-Sent События или WebSockets) Идентификатор трассировки недоступен до завершения потока. Это представляет собой уникальную задачу для сбора отзывов, требующих другого подхода.
Почему потоковая передача отличается
В традиционных шаблонах ответа на запросы вы получаете полный ответ и идентификатор трассировки вместе. С потоковой передачей:
- Токены поступают постепенно: ответ формируется по мере поступления токенов из LLM
- Завершение трассировки отложено: идентификатор трассировки создается только после завершения всего потока.
- Пользовательский интерфейс обратной связи должен ждать: пользователи не могут предоставлять отзывы, пока у них не будет полный ответ и идентификатор трассировки.
Реализация серверной части с помощью SSE
Вот как реализовать потоковую передачу с доставкой идентификатора трассировки в конце потока:
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
}
)
Реализация внешнего интерфейса для потоковой передачи
Обработайте события потоковой передачи и включите обратную связь только после получения идентификатора трассировки:
// 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>
);
}
Основные рекомендации по потоковой передаче
При реализации сбора отзывов с потоковыми ответами следует учитывать следующие моменты:
Тайминг идентификатора трассировки: Идентификатор трассировки доступен только после завершения потоковой передачи. Настройте пользовательский интерфейс для корректной обработки этого, отключив элементы управления обратной связью до получения идентификатора трассировки.
Структура событий: используйте согласованный формат событий с
type
полем, чтобы различать маркеры содержимого, события завершения и ошибки. Это делает синтаксический анализ и обработку событий более надежным.Управление состоянием: отдельно отслеживайте как содержимое потоковой передачи, так и идентификатор трассировки. Сбросьте все состояние в начале каждого нового взаимодействия, чтобы предотвратить проблемы с устаревшими данными.
Обработка ошибок. Включите события ошибок в поток для корректной обработки сбоев. Убедитесь, что ошибки регистрируются в трассировку, когда это возможно для отладки.
Управление буферами:
- Отключение буферизации прокси-сервера с помощью
X-Accel-Buffering: no
заголовка - Реализация правильной буферизации строк в интерфейсе для обработки частичных сообщений SSE
- Рассмотрите возможность реализации логики повторного подключения для прерываний сети
- Отключение буферизации прокси-сервера с помощью
Оптимизация производительности:
- Добавьте небольшие задержки между токенами (
asyncio.sleep(0.01)
), чтобы не перегружать клиентов. - Сгруппируйте токены, если они поступают слишком быстро
- Рассмотрите возможность реализации механизмов обратного давления для медленных клиентов
- Добавьте небольшие задержки между токенами (
Анализ данных обратной связи
После сбора отзывов вы можете проанализировать его, чтобы получить аналитические сведения о качестве и удовлетворенности пользователей приложения.
Просмотр обратной связи в Trace UI
Получение трассировок с обратной связью с помощью пакета SDK
Просмотр обратной связи в Trace UI
Получение трассировок с обратной связью с помощью пакета SDK
Сначала извлеките трассировки из выделенного периода времени.
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
Анализ шаблонов обратной связи с помощью пакета SDK
Извлечение и анализ обратной связи из трассировок:
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")
Анализ многомерной обратной связи
Для получения более подробных отзывов с рейтингами:
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")
Факторы, учитываемые при производстве
Сведения о рабочих развертываниях см. в руководстве по наблюдаемости рабочей среды с трассировкой , которая охватывает:
- Реализация конечных точек сбора отзывов
- Связывание отзывов с трассировками с помощью идентификаторов запросов клиента
- Настройка мониторинга качества в режиме реального времени
- Лучшие практики для обработки отзывов в условиях большого объема
Дальнейшие шаги
Продолжайте свое путешествие с помощью этих рекомендуемых действий и руководств.
- Создание наборов данных оценки. Использование собранных отзывов для создания тестовых наборов данных
- Использование трассировок для улучшения качества — анализ шаблонов обратной связи для выявления улучшений
- Настройка мониторинга рабочей среды — мониторинг метрик качества на основе отзывов
Справочные руководства
Ознакомьтесь с подробной документацией по концепциям и функциям, упомянутым в этом руководстве.
- Запись оценок - Понимание того, как отзывы сохраняются в виде оценок
- Модель трассировки данных — сведения об оценках и структуре трассировки
- Трассировки запросов с помощью пакета SDK — расширенные методы анализа отзывов