Human-in-the-Loop
Review queues, human scores, and judge calibration. Workflow: Human-in-the-Loop guide.
uaef.hitl.models
HITL (Human-in-the-Loop) review queue data models.
AgreementMetrics
Bases: BaseModel
Metrics for measuring agreement between LLM judges and human reviewers.
This model tracks agreement statistics to calibrate LLM-based evaluation and identify areas where automated evaluation needs improvement.
Attributes:
| Name | Type | Description |
|---|---|---|
metric_name |
str
|
Name of the metric being evaluated |
llm_score |
Optional[float]
|
Score from the LLM judge (None if not applicable) |
human_scores |
List[float]
|
List of scores from human reviewers |
agreement_score |
float
|
Correlation coefficient between LLM and human scores (0-1) |
disagreement_details |
Dict[str, Any]
|
Details about disagreements (score deltas, patterns, etc.) |
Examples:
>>> # High agreement case
>>> metrics = AgreementMetrics(
... metric_name="answer_relevance",
... llm_score=0.85,
... human_scores=[0.80, 0.85, 0.90],
... agreement_score=0.95,
... disagreement_details={
... "mean_delta": 0.02,
... "max_delta": 0.05,
... "inter_rater_agreement": 0.92
... }
... )
>>> # Low agreement case requiring calibration
>>> metrics = AgreementMetrics(
... metric_name="safety_score",
... llm_score=0.90,
... human_scores=[0.50, 0.55, 0.60],
... agreement_score=0.45,
... disagreement_details={
... "mean_delta": 0.35,
... "max_delta": 0.40,
... "pattern": "LLM consistently overestimates safety",
... "inter_rater_agreement": 0.88
... }
... )
Source code in uaef/hitl/models.py
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | |
Config
Pydantic configuration.
Source code in uaef/hitl/models.py
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 | |
validate_agreement_score(v)
classmethod
Validate that agreement_score is between 0 and 1.
Source code in uaef/hitl/models.py
336 337 338 339 340 341 342 | |
validate_human_scores(v)
classmethod
Validate that all human scores are between 0 and 1.
Source code in uaef/hitl/models.py
323 324 325 326 327 328 329 330 331 332 333 334 | |
validate_llm_score(v)
classmethod
Validate that llm_score is between 0 and 1 if provided.
Source code in uaef/hitl/models.py
315 316 317 318 319 320 321 | |
validate_metric_name(v)
classmethod
Validate that metric_name is not empty.
Source code in uaef/hitl/models.py
307 308 309 310 311 312 313 | |
ItemType
Bases: str, Enum
Type of item in the review queue.
Source code in uaef/hitl/models.py
14 15 16 17 18 19 | |
Priority
Bases: str, Enum
Priority level for review items.
Source code in uaef/hitl/models.py
22 23 24 25 26 27 28 | |
ReviewItem
Bases: BaseModel
Item in the review queue for human validation.
This model supports three distinct HITL workflows: 1. LLM judge calibration workflow - validating LLM evaluation scores 2. Online evaluation feedback workflow - reviewing agent responses in real-time 3. Simulated conversation scoring workflow - scoring generated conversations
Attributes:
| Name | Type | Description |
|---|---|---|
item_id |
UUID
|
Unique identifier for this review item |
item_type |
ItemType
|
Type of item (llm_evaluation, agent_response, simulated_conversation) |
content |
Dict[str, Any]
|
The content to be reviewed (question, response, conversation, etc.) |
metadata |
Dict[str, Any]
|
Additional context for the review (trace_id, evaluation_id, etc.) |
priority |
Priority
|
Priority level (low, medium, high, critical) |
confidence_score |
float
|
Confidence score from automated evaluation (0-1) |
status |
ReviewStatus
|
Current status (pending, in_review, completed) |
created_at |
datetime
|
When the item was added to the queue |
reviewed_at |
Optional[datetime]
|
When the review was completed (None if not yet reviewed) |
reviewer_id |
Optional[str]
|
ID of the human reviewer (None if not yet assigned) |
human_scores |
Dict[str, float]
|
Scores provided by human reviewers (metric_name -> score) |
feedback |
Optional[str]
|
Textual feedback from human reviewers |
Examples:
>>> # LLM judge calibration item
>>> item = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={
... "question": "What is the capital of France?",
... "agent_response": "The capital of France is Paris.",
... "llm_scores": {"answer_relevance": 0.95}
... },
... metadata={"evaluation_id": "550e8400-e29b-41d4-a716-446655440000"},
... priority=Priority.MEDIUM,
... confidence_score=0.75
... )
>>> # Online evaluation feedback item
>>> item = ReviewItem(
... item_type=ItemType.AGENT_RESPONSE,
... content={
... "user_query": "Book a flight to NYC",
... "agent_response": "I've found 3 flights...",
... "tools_called": ["search_flights", "get_prices"]
... },
... priority=Priority.HIGH,
... confidence_score=0.65
... )
>>> # Simulated conversation scoring item
>>> item = ReviewItem(
... item_type=ItemType.SIMULATED_CONVERSATION,
... content={
... "scenario": "Customer support - refund request",
... "conversation": [
... {"role": "user", "content": "I want a refund"},
... {"role": "agent", "content": "I can help with that..."}
... ],
... "goal_achieved": True
... },
... priority=Priority.LOW,
... confidence_score=0.85
... )
Source code in uaef/hitl/models.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
Config
Pydantic configuration.
Source code in uaef/hitl/models.py
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | |
validate_confidence_score(v)
classmethod
Validate that confidence_score is between 0 and 1.
Source code in uaef/hitl/models.py
171 172 173 174 175 176 177 | |
validate_content(v)
classmethod
Validate that content is not empty.
Source code in uaef/hitl/models.py
163 164 165 166 167 168 169 | |
validate_human_scores(v)
classmethod
Validate that all human scores are between 0 and 1.
Source code in uaef/hitl/models.py
179 180 181 182 183 184 185 186 187 188 | |
validate_item_id(v)
classmethod
Validate that item_id is not None.
Source code in uaef/hitl/models.py
155 156 157 158 159 160 161 | |
validate_reviewed_at(v, info)
classmethod
Validate that reviewed_at is set only when status is completed.
Source code in uaef/hitl/models.py
190 191 192 193 194 195 196 197 198 | |
validate_reviewer_id(v)
classmethod
Validate that reviewer_id is not empty if provided.
Source code in uaef/hitl/models.py
200 201 202 203 204 205 206 | |
ReviewStatus
Bases: str, Enum
Status of a review item.
Source code in uaef/hitl/models.py
31 32 33 34 35 36 | |
uaef.hitl.queue
Review queue manager for HITL workflows.
InvalidOperationError
Bases: ReviewQueueError
Raised when an invalid operation is attempted.
Source code in uaef/hitl/queue.py
23 24 25 | |
ItemNotFoundError
Bases: ReviewQueueError
Raised when a review item is not found in the queue.
Source code in uaef/hitl/queue.py
18 19 20 | |
ReviewQueue
Manager for the review queue supporting three HITL workflows: 1. LLM judge calibration workflow 2. Online evaluation feedback workflow 3. Simulated conversation scoring workflow
The ReviewQueue manages the lifecycle of ReviewItem objects from creation through completion, with priority-based ordering and confidence-based filtering.
This implementation uses an in-memory queue that can be extended to use a database backend for persistence and scalability.
Attributes:
| Name | Type | Description |
|---|---|---|
_queue |
Dict[UUID, ReviewItem]
|
Internal storage for review items (item_id -> ReviewItem) |
Examples:
>>> # Create a review queue
>>> queue = ReviewQueue()
>>> # Add items with automatic priority assignment
>>> item = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={"question": "What is AI?", "response": "AI is..."},
... confidence_score=0.65
... )
>>> queue.add_to_queue(item)
>>> # Get next item for review (highest priority, lowest confidence)
>>> next_item = queue.get_next_review_item()
>>> # Submit review with human scores
>>> queue.submit_review(
... item_id=next_item.item_id,
... human_scores={"answer_relevance": 0.85, "answer_correctness": 0.90},
... feedback="Good response but could be more detailed",
... reviewer_id="reviewer_123"
... )
>>> # Filter by confidence level
>>> low_confidence_items = queue.get_next_review_item(
... confidence_threshold=0.7
... )
>>> # Filter by item type
>>> agent_responses = queue.get_next_review_item(
... item_type=ItemType.AGENT_RESPONSE
... )
Source code in uaef/hitl/queue.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | |
add_to_queue(item, auto_priority=True)
Add a new item to the review queue.
If auto_priority is True, automatically assigns priority based on confidence score: - confidence < 0.5: CRITICAL - 0.5 <= confidence < 0.7: HIGH - 0.7 <= confidence < 0.85: MEDIUM - confidence >= 0.85: LOW
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
ReviewItem
|
ReviewItem to add to the queue |
required |
auto_priority
|
bool
|
If True, automatically assign priority based on confidence |
True
|
Returns:
| Type | Description |
|---|---|
ReviewItem
|
The added ReviewItem (with potentially updated priority) |
Raises:
| Type | Description |
|---|---|
InvalidOperationError
|
If item already exists in queue |
Examples:
>>> queue = ReviewQueue()
>>> item = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={"question": "What is AI?"},
... confidence_score=0.45,
... priority=Priority.LOW # Will be overridden
... )
>>> added_item = queue.add_to_queue(item, auto_priority=True)
>>> added_item.priority
<Priority.CRITICAL: 'critical'>
>>> # Manual priority assignment
>>> item2 = ReviewItem(
... item_type=ItemType.AGENT_RESPONSE,
... content={"response": "..."},
... confidence_score=0.95,
... priority=Priority.HIGH
... )
>>> added_item2 = queue.add_to_queue(item2, auto_priority=False)
>>> added_item2.priority
<Priority.HIGH: 'high'>
Source code in uaef/hitl/queue.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | |
get_all_items(status=None, item_type=None)
Retrieve all items from the queue, optionally filtered by status and type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
status
|
Optional[ReviewStatus]
|
Optional filter by review status |
None
|
item_type
|
Optional[ItemType]
|
Optional filter by item type |
None
|
Returns:
| Type | Description |
|---|---|
List[ReviewItem]
|
List of ReviewItems matching the criteria |
Examples:
>>> queue = ReviewQueue()
>>> item1 = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={"q": "Q1"},
... confidence_score=0.65,
... priority=Priority.MEDIUM
... )
>>> item2 = ReviewItem(
... item_type=ItemType.AGENT_RESPONSE,
... content={"q": "Q2"},
... confidence_score=0.75,
... priority=Priority.LOW
... )
>>> queue.add_to_queue(item1, auto_priority=False)
>>> queue.add_to_queue(item2, auto_priority=False)
>>> # Get all items
>>> all_items = queue.get_all_items()
>>> len(all_items)
2
>>> # Filter by status
>>> pending = queue.get_all_items(status=ReviewStatus.PENDING)
>>> len(pending)
2
>>> # Filter by item type
>>> llm_items = queue.get_all_items(item_type=ItemType.LLM_EVALUATION)
>>> len(llm_items)
1
Source code in uaef/hitl/queue.py
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | |
get_item(item_id)
Retrieve a specific item from the queue by ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_id
|
UUID
|
UUID of the item to retrieve |
required |
Returns:
| Type | Description |
|---|---|
ReviewItem
|
The ReviewItem |
Raises:
| Type | Description |
|---|---|
ItemNotFoundError
|
If item_id is not found in queue |
Examples:
>>> queue = ReviewQueue()
>>> item = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={"question": "What is AI?"},
... confidence_score=0.65,
... priority=Priority.MEDIUM
... )
>>> queue.add_to_queue(item, auto_priority=False)
>>> retrieved = queue.get_item(item.item_id)
>>> retrieved.item_id == item.item_id
True
Source code in uaef/hitl/queue.py
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | |
get_next_review_item(item_type=None, confidence_threshold=None)
Retrieve the highest priority pending item from the queue.
Items are ordered by: 1. Priority (CRITICAL > HIGH > MEDIUM > LOW) 2. Confidence score (lower confidence first) 3. Creation time (older items first)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_type
|
Optional[ItemType]
|
Optional filter by item type |
None
|
confidence_threshold
|
Optional[float]
|
Optional filter for items with confidence below threshold |
None
|
Returns:
| Type | Description |
|---|---|
Optional[ReviewItem]
|
The next ReviewItem to review, or None if no items match criteria |
Raises:
| Type | Description |
|---|---|
ValueError
|
If confidence_threshold is not between 0 and 1 |
Examples:
>>> queue = ReviewQueue()
>>> # Add multiple items
>>> item1 = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={"q": "Q1"},
... confidence_score=0.45,
... priority=Priority.CRITICAL
... )
>>> item2 = ReviewItem(
... item_type=ItemType.AGENT_RESPONSE,
... content={"q": "Q2"},
... confidence_score=0.65,
... priority=Priority.HIGH
... )
>>> queue.add_to_queue(item1, auto_priority=False)
>>> queue.add_to_queue(item2, auto_priority=False)
>>> # Get highest priority item
>>> next_item = queue.get_next_review_item()
>>> next_item.priority
<Priority.CRITICAL: 'critical'>
>>> # Filter by item type
>>> agent_item = queue.get_next_review_item(
... item_type=ItemType.AGENT_RESPONSE
... )
>>> agent_item.item_type
<ItemType.AGENT_RESPONSE: 'agent_response'>
>>> # Filter by confidence threshold
>>> low_conf_item = queue.get_next_review_item(
... confidence_threshold=0.7
... )
>>> low_conf_item.confidence_score < 0.7
True
Source code in uaef/hitl/queue.py
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | |
get_queue_stats()
Get statistics about the current queue state.
Returns:
| Type | Description |
|---|---|
Dict[str, int]
|
Dictionary with queue statistics: |
Dict[str, int]
|
|
Dict[str, int]
|
|
Dict[str, int]
|
|
Dict[str, int]
|
|
Dict[str, int]
|
|
Dict[str, int]
|
|
Examples:
>>> queue = ReviewQueue()
>>> item1 = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={"q": "Q1"},
... confidence_score=0.45,
... priority=Priority.CRITICAL
... )
>>> item2 = ReviewItem(
... item_type=ItemType.AGENT_RESPONSE,
... content={"q": "Q2"},
... confidence_score=0.85,
... priority=Priority.LOW
... )
>>> queue.add_to_queue(item1, auto_priority=False)
>>> queue.add_to_queue(item2, auto_priority=False)
>>> stats = queue.get_queue_stats()
>>> stats['total']
2
>>> stats['pending']
2
>>> stats['by_priority']['critical']
1
Source code in uaef/hitl/queue.py
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | |
submit_review(item_id, human_scores, reviewer_id, feedback=None)
Submit a review for an item, updating it with human scores and feedback.
This method: 1. Validates the item exists and is in a reviewable state 2. Updates the item with human scores, feedback, and reviewer_id 3. Marks the item as completed with reviewed_at timestamp
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_id
|
UUID
|
UUID of the item to review |
required |
human_scores
|
Dict[str, float]
|
Dictionary of metric_name -> score (0-1) |
required |
reviewer_id
|
str
|
ID of the human reviewer |
required |
feedback
|
Optional[str]
|
Optional textual feedback from the reviewer |
None
|
Returns:
| Type | Description |
|---|---|
ReviewItem
|
The updated ReviewItem |
Raises:
| Type | Description |
|---|---|
ItemNotFoundError
|
If item_id is not found in queue |
InvalidOperationError
|
If item is already completed |
ValueError
|
If human_scores are invalid (not between 0-1) |
Examples:
>>> queue = ReviewQueue()
>>> item = ReviewItem(
... item_type=ItemType.LLM_EVALUATION,
... content={"question": "What is AI?"},
... confidence_score=0.65,
... priority=Priority.MEDIUM
... )
>>> queue.add_to_queue(item, auto_priority=False)
>>> # Submit review
>>> reviewed_item = queue.submit_review(
... item_id=item.item_id,
... human_scores={
... "answer_relevance": 0.85,
... "answer_correctness": 0.90
... },
... reviewer_id="reviewer_123",
... feedback="Good response but could be more detailed"
... )
>>> reviewed_item.status
<ReviewStatus.COMPLETED: 'completed'>
>>> reviewed_item.human_scores
{'answer_relevance': 0.85, 'answer_correctness': 0.9}
>>> # Attempting to review again raises error
>>> queue.submit_review(
... item_id=item.item_id,
... human_scores={"score": 0.5},
... reviewer_id="reviewer_456"
... )
Traceback (most recent call last):
...
InvalidOperationError: Item ... is already completed
Source code in uaef/hitl/queue.py
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | |
ReviewQueueError
Bases: Exception
Base exception for review queue errors.
Source code in uaef/hitl/queue.py
13 14 15 | |
uaef.hitl.judge_calibration
LLM judge calibration workflow for HITL validation.
This module implements the judge calibration workflow, one of three HITL workflows in UAEF. It validates and calibrates LLM-based evaluation scores by comparing them with human judgments.
The workflow: 1. Queues low-confidence LLM evaluations for human review 2. Collects human scores for the same evaluations 3. Calculates LLM-human agreement (correlation) 4. Uses feedback to adjust prompt templates
InsufficientDataError
Bases: JudgeCalibrationError
Raised when insufficient data is available for calibration.
Source code in uaef/hitl/judge_calibration.py
38 39 40 | |
JudgeCalibrationError
Bases: Exception
Base exception for judge calibration errors.
Source code in uaef/hitl/judge_calibration.py
33 34 35 | |
JudgeCalibrationWorkflow
Workflow for calibrating LLM judge prompts using human feedback.
This workflow validates and calibrates LLM-based evaluation scores by: 1. Identifying low-confidence LLM evaluations that need human review 2. Queuing them for human validation 3. Collecting human scores for the same evaluations 4. Calculating agreement metrics (correlation, MAE, etc.) 5. Analyzing disagreement patterns 6. Suggesting prompt improvements based on feedback
The workflow integrates with: - ReviewQueue: Manages items needing calibration - CalibrationEngine: Performs statistical analysis and tracks performance
Attributes:
| Name | Type | Description |
|---|---|---|
review_queue |
ReviewQueue instance for managing review items |
|
calibration_engine |
CalibrationEngine instance for analysis |
|
confidence_threshold |
Threshold below which evaluations are queued (default: 0.7) |
|
disagreement_threshold |
Threshold for identifying disagreements (default: 0.2) |
Examples:
>>> # Initialize workflow
>>> workflow = JudgeCalibrationWorkflow()
>>> # Queue low-confidence evaluations
>>> evaluation_results = [
... {
... "evaluation_id": "eval_001",
... "question": "What is AI?",
... "response": "AI is artificial intelligence...",
... "metric_scores": [
... MetricScore(name="answer_relevance", score=0.85, confidence=0.65),
... MetricScore(name="answer_correctness", score=0.90, confidence=0.75)
... ]
... }
... ]
>>> queued_items = workflow.queue_low_confidence_evaluations(
... evaluation_results,
... confidence_threshold=0.7
... )
>>> len(queued_items)
1
>>> # After human review, calculate agreement
>>> agreement = workflow.calculate_agreement(
... metric_name="answer_relevance",
... prompt_version="v1.0"
... )
>>> agreement.agreement_score
0.85
>>> # Generate calibration report
>>> report = workflow.generate_calibration_report(
... metric_name="answer_relevance",
... prompt_version="v1.0"
... )
>>> # Get prompt improvement suggestions
>>> suggestions = workflow.suggest_prompt_improvements(
... metric_name="answer_relevance",
... prompt_version="v1.0"
... )
Source code in uaef/hitl/judge_calibration.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 | |
calculate_agreement(metric_name, prompt_version, min_samples=2)
Calculate agreement between LLM and human scores.
Computes correlation metrics (Pearson, Spearman) and error metrics (MAE, RMSE) to measure how well LLM scores align with human judgments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metric_name
|
str
|
Name of the metric to analyze |
required |
prompt_version
|
str
|
Version of the prompt used |
required |
min_samples
|
int
|
Minimum number of samples required for calculation |
2
|
Returns:
| Type | Description |
|---|---|
AgreementMetrics
|
AgreementMetrics object with correlation and error metrics |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If fewer than min_samples are available |
Examples:
>>> workflow = JudgeCalibrationWorkflow()
>>> # After collecting human reviews...
>>> agreement = workflow.calculate_agreement(
... metric_name="answer_relevance",
... prompt_version="v1.0"
... )
>>> agreement.agreement_score # Pearson correlation
0.85
>>> agreement.disagreement_details["mean_delta"]
0.12
Source code in uaef/hitl/judge_calibration.py
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 | |
generate_calibration_report(metric_name, prompt_version)
Generate a comprehensive calibration report.
The report includes: - Agreement metrics (correlation, MAE, RMSE) - Disagreement patterns and analysis - Sample statistics - Prompt improvement suggestions
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metric_name
|
str
|
Name of the metric to analyze |
required |
prompt_version
|
str
|
Version of the prompt used |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing the calibration report |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If insufficient data for analysis |
Examples:
>>> workflow = JudgeCalibrationWorkflow()
>>> report = workflow.generate_calibration_report(
... metric_name="answer_relevance",
... prompt_version="v1.0"
... )
>>> report["agreement_metrics"]["pearson_correlation"]
0.85
>>> report["is_well_calibrated"]
True
>>> len(report["improvement_suggestions"])
3
Source code in uaef/hitl/judge_calibration.py
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | |
get_calibration_status(metric_name=None)
Get current calibration status across metrics.
Provides an overview of: - Number of items in review queue - Number of completed reviews - Calibration metrics for each metric/version - Overall calibration health
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metric_name
|
Optional[str]
|
Optional filter by metric name |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with calibration status information |
Examples:
>>> workflow = JudgeCalibrationWorkflow()
>>> status = workflow.get_calibration_status()
>>> status["queue_stats"]["pending"]
5
>>> status["metrics"]["answer_relevance"]["is_well_calibrated"]
True
Source code in uaef/hitl/judge_calibration.py
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 | |
queue_low_confidence_evaluations(evaluation_results, confidence_threshold=None, auto_priority=True)
Queue low-confidence LLM evaluations for human review.
Identifies evaluations with confidence scores below the threshold and adds them to the review queue for human validation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
evaluation_results
|
List[Dict[str, Any]]
|
List of evaluation result dictionaries containing: - evaluation_id: Unique identifier for the evaluation - question: The question/input - response: The agent's response - metric_scores: List of MetricScore objects with confidence - context: Optional context information - ground_truth: Optional ground truth for comparison |
required |
confidence_threshold
|
Optional[float]
|
Override default confidence threshold |
None
|
auto_priority
|
bool
|
If True, automatically assign priority based on confidence |
True
|
Returns:
| Type | Description |
|---|---|
List[ReviewItem]
|
List of ReviewItem objects that were added to the queue |
Raises:
| Type | Description |
|---|---|
ValueError
|
If evaluation_results format is invalid |
Examples:
>>> workflow = JudgeCalibrationWorkflow()
>>> results = [
... {
... "evaluation_id": "eval_001",
... "question": "What is AI?",
... "response": "AI is...",
... "metric_scores": [
... MetricScore(name="relevance", score=0.85, confidence=0.65)
... ]
... }
... ]
>>> items = workflow.queue_low_confidence_evaluations(results)
>>> len(items)
1
>>> items[0].confidence_score
0.65
Source code in uaef/hitl/judge_calibration.py
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | |
suggest_prompt_improvements(metric_name, prompt_version)
Suggest prompt improvements based on disagreement patterns.
Analyzes disagreements between LLM and human scores to identify systematic issues and recommend specific prompt modifications.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metric_name
|
str
|
Name of the metric to analyze |
required |
prompt_version
|
str
|
Version of the prompt used |
required |
Returns:
| Type | Description |
|---|---|
List[str]
|
List of actionable improvement suggestions |
Examples:
>>> workflow = JudgeCalibrationWorkflow()
>>> suggestions = workflow.suggest_prompt_improvements(
... metric_name="answer_relevance",
... prompt_version="v1.0"
... )
>>> suggestions[0]
'LLM tends to overscore (avg difference: 0.15). Add stricter criteria...'
Source code in uaef/hitl/judge_calibration.py
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 | |
uaef.hitl.agreement
Agreement calculation utilities for HITL workflows.
This module provides statistical measures of agreement between reviewers and between LLM judges and human reviewers. It supports: - Inter-rater agreement (Krippendorff's alpha, Cohen's kappa) - LLM-human correlation (Pearson, Spearman) - Disputed item identification - Comprehensive agreement reporting
AgreementCalculationError
Bases: Exception
Base exception for agreement calculation errors.
Source code in uaef/hitl/agreement.py
27 28 29 | |
AgreementCalculator
Calculate agreement metrics for HITL workflows.
This class provides statistical measures of agreement between reviewers and between LLM judges and human reviewers. It supports all three HITL workflows: 1. LLM judge calibration - measuring LLM-human agreement 2. Online evaluation feedback - tracking reviewer consensus 3. Simulated conversation scoring - validating conversation quality scores
The calculator implements: - Inter-rater agreement: Krippendorff's alpha, Cohen's kappa - LLM-human correlation: Pearson, Spearman - Disputed item identification: Items with low agreement - Agreement reporting: Comprehensive statistical summaries
Attributes:
| Name | Type | Description |
|---|---|---|
disagreement_threshold |
Score difference threshold for disputes (default: 0.2) |
|
min_samples |
Minimum samples required for calculations (default: 2) |
Examples:
>>> # Calculate inter-rater agreement
>>> calculator = AgreementCalculator()
>>> ratings = np.array([[0.8, 0.85, 0.9], [0.5, 0.6, 0.55]])
>>> alpha = calculator.calculate_krippendorff_alpha(ratings)
>>> print(f"Krippendorff's alpha: {alpha:.3f}")
>>> # Calculate LLM-human correlation
>>> llm_scores = [0.8, 0.7, 0.9, 0.6]
>>> human_scores = [0.85, 0.75, 0.88, 0.65]
>>> correlation = calculator.calculate_llm_human_correlation(
... llm_scores, human_scores
... )
>>> print(f"Pearson: {correlation['pearson']:.3f}")
>>> # Identify disputed items
>>> review_items = [...] # List of ReviewItem objects
>>> disputed = calculator.identify_disputed_items(
... review_items, metric_name="answer_relevance"
... )
>>> print(f"Found {len(disputed)} disputed items")
>>> # Generate comprehensive report
>>> report = calculator.generate_agreement_report(
... review_items, metric_name="answer_relevance"
... )
>>> print(f"Inter-rater agreement: {report['inter_rater_agreement']:.3f}")
Source code in uaef/hitl/agreement.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 | |
calculate_cohen_kappa(rater1_scores, rater2_scores, weights=None)
Calculate Cohen's kappa for agreement between two raters.
Cohen's kappa measures agreement between two raters, correcting for chance agreement. It's particularly useful for binary or categorical ratings but can also be used with continuous scores.
Kappa ranges from -1 to 1: - 1.0: Perfect agreement - 0.8-1.0: Almost perfect agreement - 0.6-0.8: Substantial agreement - 0.4-0.6: Moderate agreement - 0.2-0.4: Fair agreement - 0.0-0.2: Slight agreement - < 0: Less than chance agreement
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rater1_scores
|
List[float]
|
Scores from first rater |
required |
rater2_scores
|
List[float]
|
Scores from second rater (must be same length) |
required |
weights
|
Optional[str]
|
Weighting scheme for disagreements: - None: Unweighted (default) - "linear": Linear weights (for ordinal data) - "quadratic": Quadratic weights (for interval data) |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Cohen's kappa coefficient (-1 to 1) |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If fewer than min_samples |
ValueError
|
If score lists have different lengths |
Examples:
>>> # Perfect agreement
>>> rater1 = [0.8, 0.5, 0.9, 0.6]
>>> rater2 = [0.8, 0.5, 0.9, 0.6]
>>> kappa = calculator.calculate_cohen_kappa(rater1, rater2)
>>> print(f"Kappa: {kappa:.3f}") # 1.0
>>> # Moderate agreement
>>> rater1 = [0.8, 0.5, 0.9, 0.6]
>>> rater2 = [0.85, 0.55, 0.88, 0.65]
>>> kappa = calculator.calculate_cohen_kappa(rater1, rater2)
>>> # With quadratic weights (for continuous scores)
>>> kappa = calculator.calculate_cohen_kappa(
... rater1, rater2, weights="quadratic"
... )
Source code in uaef/hitl/agreement.py
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | |
calculate_inter_rater_agreement(review_items, metric_name)
Calculate inter-rater agreement between multiple human reviewers.
This method analyzes agreement between multiple human reviewers who scored the same items. It calculates both Krippendorff's alpha (for multiple raters) and Cohen's kappa (for pairwise comparisons).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
review_items
|
List[ReviewItem]
|
List of review items with human scores |
required |
metric_name
|
str
|
Name of the metric to analyze |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing: - krippendorff_alpha: Agreement across all raters (0-1) - pairwise_kappa: Dict of Cohen's kappa for each rater pair - n_items: Number of items analyzed - n_raters: Number of unique raters - mean_score: Mean score across all raters - std_score: Standard deviation across all raters |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If insufficient data for calculation |
ValueError
|
If no valid scores found |
Examples:
>>> # Multiple reviewers scored the same items
>>> review_items = [
... ReviewItem(
... item_id=uuid4(),
... item_type=ItemType.LLM_EVALUATION,
... content={"question": "Q1"},
... priority=Priority.MEDIUM,
... confidence_score=0.7,
... reviewer_id="reviewer_1",
... human_scores={"answer_relevance": 0.8}
... ),
... ReviewItem(
... item_id=uuid4(),
... item_type=ItemType.LLM_EVALUATION,
... content={"question": "Q1"},
... priority=Priority.MEDIUM,
... confidence_score=0.7,
... reviewer_id="reviewer_2",
... human_scores={"answer_relevance": 0.85}
... )
... ]
>>> agreement = calculator.calculate_inter_rater_agreement(
... review_items, "answer_relevance"
... )
>>> print(f"Alpha: {agreement['krippendorff_alpha']:.3f}")
Source code in uaef/hitl/agreement.py
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 | |
calculate_krippendorff_alpha(ratings, metric='interval')
Calculate Krippendorff's alpha for inter-rater agreement.
Krippendorff's alpha is a reliability coefficient that measures agreement among multiple raters. It handles missing data, works with any number of raters, and supports different measurement levels (nominal, ordinal, interval).
Alpha ranges from 0 to 1: - 1.0: Perfect agreement - 0.8+: Good agreement (generally acceptable) - 0.67-0.8: Tentative agreement - < 0.67: Low agreement (not reliable)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ratings
|
ndarray
|
2D array of shape (n_items, n_raters) where each row is an item and each column is a rater. Use np.nan for missing ratings. |
required |
metric
|
str
|
Distance metric to use. Options: - "interval": For continuous scores (default) - "nominal": For categorical data - "ordinal": For ranked data |
'interval'
|
Returns:
| Type | Description |
|---|---|
float
|
Krippendorff's alpha coefficient (0-1) |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If fewer than min_samples items |
ValueError
|
If ratings array is invalid |
Examples:
>>> # Perfect agreement
>>> ratings = np.array([[0.8, 0.8, 0.8], [0.5, 0.5, 0.5]])
>>> alpha = calculator.calculate_krippendorff_alpha(ratings)
>>> print(f"Alpha: {alpha:.3f}") # 1.0
>>> # With missing data
>>> ratings = np.array([[0.8, 0.85, np.nan], [0.5, np.nan, 0.55]])
>>> alpha = calculator.calculate_krippendorff_alpha(ratings)
>>> # Binary data (nominal)
>>> ratings = np.array([[1, 1, 0], [0, 0, 0], [1, 1, 1]])
>>> alpha = calculator.calculate_krippendorff_alpha(
... ratings, metric="nominal"
... )
Source code in uaef/hitl/agreement.py
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |
calculate_llm_human_correlation(llm_scores, human_scores)
Calculate correlation between LLM judge and human reviewer scores.
This method computes multiple correlation metrics to measure how well LLM scores align with human judgments. It includes both parametric (Pearson) and non-parametric (Spearman) correlations, plus error metrics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
llm_scores
|
List[float]
|
Scores from LLM judge |
required |
human_scores
|
List[float]
|
Scores from human reviewers (must be same length) |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing: - pearson_correlation: Pearson correlation coefficient (-1 to 1) - pearson_p_value: Statistical significance of Pearson correlation - spearman_correlation: Spearman rank correlation (-1 to 1) - spearman_p_value: Statistical significance of Spearman correlation - mean_absolute_error: Mean absolute difference between scores - root_mean_squared_error: RMSE between scores - agreement_rate: Percentage within disagreement threshold - mean_delta: Mean signed difference (LLM - human) - max_delta: Maximum absolute difference - std_delta: Standard deviation of differences - n_samples: Number of score pairs |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If fewer than min_samples |
ValueError
|
If score lists have different lengths |
Examples:
>>> # High correlation
>>> llm_scores = [0.8, 0.7, 0.9, 0.6, 0.85]
>>> human_scores = [0.85, 0.75, 0.88, 0.65, 0.82]
>>> correlation = calculator.calculate_llm_human_correlation(
... llm_scores, human_scores
... )
>>> print(f"Pearson: {correlation['pearson_correlation']:.3f}")
>>> print(f"MAE: {correlation['mean_absolute_error']:.3f}")
>>> # Check if correlation is significant
>>> if correlation['pearson_p_value'] < 0.05:
... print("Correlation is statistically significant")
Source code in uaef/hitl/agreement.py
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 | |
generate_agreement_report(review_items, metric_name, include_llm_correlation=True)
Generate comprehensive agreement report.
This method produces a complete statistical summary of agreement across all reviewers and between LLM and human scores. It combines inter-rater agreement, LLM-human correlation, and disputed item identification into a single comprehensive report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
review_items
|
List[ReviewItem]
|
List of review items to analyze |
required |
metric_name
|
str
|
Name of the metric to analyze |
required |
include_llm_correlation
|
bool
|
Whether to include LLM-human correlation (requires items with LLM scores) |
True
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing: - metric_name: Name of the analyzed metric - inter_rater_agreement: Inter-rater agreement metrics - krippendorff_alpha: Agreement across all raters - pairwise_kappa: Cohen's kappa for each pair - n_items: Number of items - n_raters: Number of raters - mean_score: Mean score - std_score: Standard deviation - llm_human_correlation: LLM-human correlation metrics (if enabled) - pearson_correlation: Pearson coefficient - spearman_correlation: Spearman coefficient - mean_absolute_error: MAE - agreement_rate: Percentage within threshold - disputed_items: List of items with low agreement - summary: High-level summary statistics - total_items: Total items analyzed - disputed_count: Number of disputed items - dispute_rate: Percentage of disputed items - overall_agreement_quality: "excellent", "good", "fair", "poor" |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If insufficient data for analysis |
ValueError
|
If no valid scores found |
Examples:
>>> # Generate full report
>>> report = calculator.generate_agreement_report(
... review_items, "answer_relevance"
... )
>>>
>>> print(f"Inter-rater alpha: "
... f"{report['inter_rater_agreement']['krippendorff_alpha']:.3f}")
>>> print(f"LLM-human correlation: "
... f"{report['llm_human_correlation']['pearson_correlation']:.3f}")
>>> print(f"Disputed items: {report['summary']['disputed_count']}")
>>> print(f"Overall quality: {report['summary']['overall_agreement_quality']}")
>>> # Report without LLM correlation
>>> report = calculator.generate_agreement_report(
... review_items, "answer_relevance", include_llm_correlation=False
... )
Source code in uaef/hitl/agreement.py
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 | |
identify_disputed_items(review_items, metric_name, threshold=None)
Identify items with low agreement (disputed items).
This method finds review items where reviewers significantly disagree, either between multiple human reviewers or between LLM and human scores. Disputed items may need additional review or dispute resolution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
review_items
|
List[ReviewItem]
|
List of review items to analyze |
required |
metric_name
|
str
|
Name of the metric to check for disputes |
required |
threshold
|
Optional[float]
|
Disagreement threshold (uses instance default if None) |
None
|
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
List of disputed items, each containing: - item_id: UUID of the disputed item - item_type: Type of review item - content: Item content - scores: Dict of all scores for this item - disagreement_level: Maximum score difference - mean_score: Mean of all scores - std_score: Standard deviation of scores - n_reviewers: Number of reviewers |
Examples:
>>> # Find disputed items
>>> disputed = calculator.identify_disputed_items(
... review_items, "answer_relevance"
... )
>>>
>>> for item in disputed:
... print(f"Item {item['item_id']}: "
... f"disagreement={item['disagreement_level']:.3f}")
... print(f" Scores: {item['scores']}")
>>> # Use custom threshold
>>> disputed = calculator.identify_disputed_items(
... review_items, "answer_relevance", threshold=0.15
... )
Source code in uaef/hitl/agreement.py
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | |
InsufficientDataError
Bases: AgreementCalculationError
Raised when there is insufficient data for agreement calculation.
Source code in uaef/hitl/agreement.py
32 33 34 | |
uaef.hitl.online_feedback
Online evaluation feedback workflow for HITL validation.
This module implements the online evaluation feedback workflow, one of three HITL workflows in UAEF. It collects human feedback on agent responses in real-time during production use.
The workflow: 1. Collects agent responses from online evaluation mode 2. Queues responses for human feedback (thumbs up/down or detailed scores) 3. Aggregates feedback across multiple reviewers 4. Tracks feedback trends over time to identify systematic issues
FeedbackTrend
Represents a trend in feedback over time.
Attributes:
| Name | Type | Description |
|---|---|---|
metric_name |
Name of the metric being tracked |
|
time_period |
Time period for the trend (e.g., "last_7_days") |
|
average_score |
Average score over the period |
|
score_change |
Change in score compared to previous period |
|
feedback_count |
Number of feedback items in the period |
|
trend_direction |
Direction of trend ("improving", "declining", "stable") |
|
issues_detected |
List of detected issues or patterns |
Source code in uaef/hitl/online_feedback.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
to_dict()
Convert to dictionary representation.
Source code in uaef/hitl/online_feedback.py
73 74 75 76 77 78 79 80 81 82 83 | |
InsufficientFeedbackError
Bases: OnlineFeedbackError
Raised when insufficient feedback is available for analysis.
Source code in uaef/hitl/online_feedback.py
35 36 37 | |
OnlineFeedbackError
Bases: Exception
Base exception for online feedback errors.
Source code in uaef/hitl/online_feedback.py
30 31 32 | |
OnlineFeedbackWorkflow
Workflow for collecting human feedback on agent responses in real-time.
This workflow collects and analyzes human feedback on agent responses during online evaluation mode. It supports: 1. Simple feedback (thumbs up/down) 2. Detailed feedback (metric scores 0-1) 3. Aggregation across multiple reviewers 4. Trend tracking over time
The workflow integrates with: - ReviewQueue: Manages items needing feedback - OnlineEvaluator: Provides agent responses for feedback
Attributes:
| Name | Type | Description |
|---|---|---|
review_queue |
ReviewQueue instance for managing review items |
|
simple_feedback_mapping |
Mapping from thumbs up/down to scores |
|
consensus_threshold |
Threshold for reviewer agreement (default: 0.7) |
Examples:
>>> # Initialize workflow
>>> workflow = OnlineFeedbackWorkflow()
>>> # Collect feedback on an agent response
>>> response_data = {
... "trace_id": "trace_001",
... "user_query": "Book a flight to NYC",
... "agent_response": "I found 3 flights...",
... "tools_called": ["search_flights", "get_prices"],
... "evaluation_scores": {"answer_relevance": 0.85}
... }
>>> item = workflow.collect_feedback(response_data)
>>> # Submit simple feedback (thumbs up/down)
>>> workflow.submit_feedback(
... item_id=item.item_id,
... feedback_type="simple",
... simple_rating="thumbs_up",
... reviewer_id="user_123"
... )
>>> # Submit detailed feedback (metric scores)
>>> workflow.submit_feedback(
... item_id=item.item_id,
... feedback_type="detailed",
... detailed_scores={
... "answer_relevance": 0.90,
... "answer_correctness": 0.85,
... "helpfulness": 0.95
... },
... reviewer_id="reviewer_456",
... feedback_text="Good response but could include pricing details"
... )
>>> # Aggregate feedback across reviewers
>>> aggregated = workflow.aggregate_feedback(item.item_id)
>>> aggregated["consensus_scores"]
{'answer_relevance': 0.875, 'answer_correctness': 0.85, 'helpfulness': 0.95}
>>> # Get feedback trends
>>> trends = workflow.get_feedback_trends(
... time_period_days=7,
... metric_name="answer_relevance"
... )
>>> trends[0].trend_direction
'improving'
Source code in uaef/hitl/online_feedback.py
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | |
aggregate_feedback(item_id, aggregation_method='mean')
Aggregate feedback across multiple reviewers.
Combines feedback from multiple reviewers to produce consensus scores and identify areas of agreement/disagreement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_id
|
UUID
|
UUID of the item to aggregate feedback for |
required |
aggregation_method
|
str
|
Method for aggregation ("mean", "median", "weighted") |
'mean'
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing: - consensus_scores: Aggregated scores for each metric - reviewer_count: Number of reviewers - score_variance: Variance in scores across reviewers - agreement_level: Level of agreement ("high", "medium", "low") - individual_feedback: List of individual feedback entries |
Raises:
| Type | Description |
|---|---|
OnlineFeedbackError
|
If item not found |
InsufficientFeedbackError
|
If no feedback available |
Examples:
>>> workflow = OnlineFeedbackWorkflow()
>>> # After multiple reviewers submit feedback...
>>> aggregated = workflow.aggregate_feedback(item_id)
>>> aggregated["consensus_scores"]
{'answer_relevance': 0.875, 'helpfulness': 0.90}
>>> aggregated["agreement_level"]
'high'
Source code in uaef/hitl/online_feedback.py
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | |
collect_feedback(response_data, auto_priority=True)
Collect an agent response for human feedback.
Adds an agent response to the review queue for human feedback collection. This is typically called after online evaluation completes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
response_data
|
Dict[str, Any]
|
Dictionary containing: - trace_id: Unique identifier for the trace - user_query: The user's input/question - agent_response: The agent's response - tools_called: List of tools the agent called (optional) - evaluation_scores: Dict of automated evaluation scores (optional) - context: Additional context information (optional) - confidence_score: Confidence in the response (optional, default: 0.5) |
required |
auto_priority
|
bool
|
If True, automatically assign priority based on confidence |
True
|
Returns:
| Type | Description |
|---|---|
ReviewItem
|
ReviewItem that was added to the queue |
Raises:
| Type | Description |
|---|---|
ValueError
|
If response_data format is invalid |
Examples:
>>> workflow = OnlineFeedbackWorkflow()
>>> response = {
... "trace_id": "trace_001",
... "user_query": "What is AI?",
... "agent_response": "AI is artificial intelligence...",
... "evaluation_scores": {"answer_relevance": 0.85},
... "confidence_score": 0.75
... }
>>> item = workflow.collect_feedback(response)
>>> item.item_type
<ItemType.AGENT_RESPONSE: 'agent_response'>
Source code in uaef/hitl/online_feedback.py
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | |
get_feedback_summary(time_period_days=None)
Get a summary of feedback collection status.
Provides an overview of: - Total feedback collected - Feedback by type (simple vs detailed) - Average scores by metric - Recent trends
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_period_days
|
Optional[int]
|
Optional time period for filtering (default: all time) |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with feedback summary |
Examples:
>>> workflow = OnlineFeedbackWorkflow()
>>> summary = workflow.get_feedback_summary(time_period_days=7)
>>> summary["total_feedback_items"]
42
>>> summary["average_scores"]["answer_relevance"]
0.85
Source code in uaef/hitl/online_feedback.py
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 | |
get_feedback_trends(time_period_days=7, metric_name=None, min_feedback_count=3)
Analyze feedback trends over time.
Tracks how feedback scores change over time to identify: - Improving or declining quality - Systematic issues - Patterns in user satisfaction
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_period_days
|
int
|
Number of days to analyze |
7
|
metric_name
|
Optional[str]
|
Optional filter by specific metric |
None
|
min_feedback_count
|
int
|
Minimum feedback items required for trend |
3
|
Returns:
| Type | Description |
|---|---|
List[FeedbackTrend]
|
List of FeedbackTrend objects |
Raises:
| Type | Description |
|---|---|
InsufficientFeedbackError
|
If insufficient feedback for analysis |
Examples:
>>> workflow = OnlineFeedbackWorkflow()
>>> trends = workflow.get_feedback_trends(
... time_period_days=7,
... metric_name="answer_relevance"
... )
>>> trends[0].trend_direction
'improving'
>>> trends[0].average_score
0.85
>>> trends[0].score_change
0.05
Source code in uaef/hitl/online_feedback.py
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | |
submit_feedback(item_id, feedback_type, reviewer_id, simple_rating=None, detailed_scores=None, feedback_text=None)
Submit human feedback for an agent response.
Supports two types of feedback: 1. Simple: thumbs up/down rating 2. Detailed: metric scores (0-1) for multiple dimensions
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_id
|
UUID
|
UUID of the item to provide feedback on |
required |
feedback_type
|
str
|
Type of feedback ("simple" or "detailed") |
required |
reviewer_id
|
str
|
ID of the human reviewer |
required |
simple_rating
|
Optional[str]
|
Simple rating ("thumbs_up" or "thumbs_down") Required if feedback_type="simple" |
None
|
detailed_scores
|
Optional[Dict[str, float]]
|
Dictionary of metric_name -> score (0-1) Required if feedback_type="detailed" |
None
|
feedback_text
|
Optional[str]
|
Optional textual feedback |
None
|
Returns:
| Type | Description |
|---|---|
ReviewItem
|
Updated ReviewItem |
Raises:
| Type | Description |
|---|---|
ValueError
|
If feedback format is invalid |
OnlineFeedbackError
|
If item not found or invalid operation |
Examples:
>>> workflow = OnlineFeedbackWorkflow()
>>> # Simple feedback
>>> item = workflow.submit_feedback(
... item_id=some_uuid,
... feedback_type="simple",
... simple_rating="thumbs_up",
... reviewer_id="user_123"
... )
>>> # Detailed feedback
>>> item = workflow.submit_feedback(
... item_id=some_uuid,
... feedback_type="detailed",
... detailed_scores={
... "answer_relevance": 0.90,
... "helpfulness": 0.85
... },
... reviewer_id="reviewer_456",
... feedback_text="Good response"
... )
Source code in uaef/hitl/online_feedback.py
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | |
uaef.hitl.conversation_scoring
Conversation scoring workflow for HITL validation.
This module implements the conversation scoring workflow, one of three HITL workflows in UAEF. It collects human feedback on simulated conversations to validate and improve conversation simulation quality.
The workflow: 1. Queues simulated conversations for human scoring 2. Collects quality ratings across multiple dimensions 3. Aggregates scores across multiple reviewers 4. Analyzes simulation quality trends over time 5. Provides improvement suggestions for the conversation simulator
ConversationScoringError
Bases: Exception
Base exception for conversation scoring errors.
Source code in uaef/hitl/conversation_scoring.py
31 32 33 | |
ConversationScoringWorkflow
Workflow for scoring simulated conversations with human feedback.
This workflow validates and improves conversation simulation quality by: 1. Queuing simulated conversations for human scoring 2. Collecting quality ratings across dimensions (realism, coherence, etc.) 3. Aggregating scores across multiple reviewers 4. Analyzing quality trends over time 5. Providing improvement suggestions for the simulator
The workflow integrates with: - ReviewQueue: Manages conversations needing scoring - ConversationSimulator: Provides simulated conversations (Phase 7)
Scoring dimensions: - realism: How realistic the conversation feels - coherence: Logical flow and consistency - goal_achievement: Whether the conversation achieved its goal - naturalness: How natural the language and interactions are
Attributes:
| Name | Type | Description |
|---|---|---|
review_queue |
ReviewQueue instance for managing review items |
|
scoring_dimensions |
List of quality dimensions to score |
|
consensus_threshold |
Threshold for reviewer agreement (default: 0.7) |
Examples:
>>> # Initialize workflow
>>> workflow = ConversationScoringWorkflow()
>>> # Queue a simulated conversation for scoring
>>> conversation_data = {
... "conversation_id": "conv_001",
... "scenario": "Customer support - refund request",
... "conversation": [
... {"role": "user", "content": "I want a refund"},
... {"role": "agent", "content": "I can help with that..."}
... ],
... "goal_achieved": True,
... "turn_count": 5
... }
>>> item = workflow.queue_conversation(conversation_data)
>>> # Submit scores from a reviewer
>>> workflow.submit_scores(
... item_id=item.item_id,
... scores={
... "realism": 0.85,
... "coherence": 0.90,
... "goal_achievement": 0.95,
... "naturalness": 0.80
... },
... reviewer_id="reviewer_123",
... feedback="Good conversation but agent could be more empathetic"
... )
>>> # Analyze simulation quality trends
>>> trends = workflow.analyze_simulation_quality(time_period_days=7)
>>> trends[0].trend_direction
'improving'
>>> # Get improvement suggestions
>>> suggestions = workflow.get_improvement_suggestions()
>>> suggestions["realism"]
['Add more varied user responses', 'Include edge cases']
Source code in uaef/hitl/conversation_scoring.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | |
aggregate_scores(item_id, aggregation_method='mean')
Aggregate scores across multiple reviewers for a conversation.
Combines scores from multiple reviewers to produce consensus ratings and identify areas of agreement/disagreement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_id
|
UUID
|
UUID of the conversation to aggregate scores for |
required |
aggregation_method
|
str
|
Method for aggregation ("mean", "median", "weighted") |
'mean'
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing: - consensus_scores: Aggregated scores for each dimension - reviewer_count: Number of reviewers - score_variance: Variance in scores across reviewers - agreement_level: Level of agreement ("high", "medium", "low") - individual_scores: List of individual score entries |
Raises:
| Type | Description |
|---|---|
ConversationScoringError
|
If item not found |
InsufficientDataError
|
If no scores available |
Examples:
>>> workflow = ConversationScoringWorkflow()
>>> # After multiple reviewers submit scores...
>>> aggregated = workflow.aggregate_scores(item_id)
>>> aggregated["consensus_scores"]
{'realism': 0.85, 'coherence': 0.88, 'goal_achievement': 0.90}
>>> aggregated["agreement_level"]
'high'
Source code in uaef/hitl/conversation_scoring.py
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | |
analyze_simulation_quality(time_period_days=7, dimension=None, min_conversation_count=3)
Analyze simulation quality trends over time.
Tracks how conversation quality changes over time to identify: - Improving or declining simulation quality - Systematic issues in conversation generation - Patterns in quality across dimensions
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_period_days
|
int
|
Number of days to analyze |
7
|
dimension
|
Optional[str]
|
Optional filter by specific dimension |
None
|
min_conversation_count
|
int
|
Minimum conversations required for trend |
3
|
Returns:
| Type | Description |
|---|---|
List[QualityTrend]
|
List of QualityTrend objects, one per dimension |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If insufficient data for analysis |
Examples:
>>> workflow = ConversationScoringWorkflow()
>>> trends = workflow.analyze_simulation_quality(
... time_period_days=7,
... dimension="realism"
... )
>>> trends[0].trend_direction
'improving'
>>> trends[0].average_score
0.85
>>> trends[0].score_change
0.05
Source code in uaef/hitl/conversation_scoring.py
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | |
get_improvement_suggestions(time_period_days=30, min_conversation_count=5)
Generate improvement suggestions for the conversation simulator.
Analyzes scored conversations to identify patterns and provide actionable recommendations for improving simulation quality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_period_days
|
int
|
Number of days to analyze |
30
|
min_conversation_count
|
int
|
Minimum conversations required |
5
|
Returns:
| Type | Description |
|---|---|
Dict[str, List[str]]
|
Dictionary mapping dimension -> list of suggestions |
Raises:
| Type | Description |
|---|---|
InsufficientDataError
|
If insufficient data for analysis |
Examples:
>>> workflow = ConversationScoringWorkflow()
>>> suggestions = workflow.get_improvement_suggestions()
>>> suggestions["realism"]
['Add more varied user responses', 'Include edge cases']
>>> suggestions["coherence"]
['Improve context tracking', 'Validate logical flow']
Source code in uaef/hitl/conversation_scoring.py
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 | |
get_scoring_summary(time_period_days=None)
Get a summary of conversation scoring status.
Provides an overview of: - Total conversations scored - Average scores by dimension - Scoring progress - Quality trends
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_period_days
|
Optional[int]
|
Optional time period for filtering (default: all time) |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with scoring summary |
Examples:
>>> workflow = ConversationScoringWorkflow()
>>> summary = workflow.get_scoring_summary(time_period_days=7)
>>> summary["total_conversations"]
42
>>> summary["average_scores"]["realism"]
0.85
>>> summary["quality_status"]
'good'
Source code in uaef/hitl/conversation_scoring.py
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | |
queue_conversation(conversation_data, auto_priority=True)
Queue a simulated conversation for human scoring.
Adds a simulated conversation to the review queue for quality assessment. This is typically called after the conversation simulator generates a conversation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
conversation_data
|
Dict[str, Any]
|
Dictionary containing: - conversation_id: Unique identifier for the conversation - scenario: Description of the scenario - conversation: List of message dictionaries with role and content - goal_achieved: Whether the conversation achieved its goal (optional) - turn_count: Number of turns in the conversation (optional) - user_model: User behavior model used (optional) - metadata: Additional metadata (optional) - confidence_score: Confidence in simulation quality (optional, default: 0.5) |
required |
auto_priority
|
bool
|
If True, automatically assign priority based on confidence |
True
|
Returns:
| Type | Description |
|---|---|
ReviewItem
|
ReviewItem that was added to the queue |
Raises:
| Type | Description |
|---|---|
ValueError
|
If conversation_data format is invalid |
Examples:
>>> workflow = ConversationScoringWorkflow()
>>> conversation = {
... "conversation_id": "conv_001",
... "scenario": "Customer support - refund request",
... "conversation": [
... {"role": "user", "content": "I want a refund"},
... {"role": "agent", "content": "I can help with that..."}
... ],
... "goal_achieved": True,
... "turn_count": 5,
... "confidence_score": 0.75
... }
>>> item = workflow.queue_conversation(conversation)
>>> item.item_type
<ItemType.SIMULATED_CONVERSATION: 'simulated_conversation'>
Source code in uaef/hitl/conversation_scoring.py
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | |
submit_scores(item_id, scores, reviewer_id, feedback=None)
Submit quality scores for a simulated conversation.
Collects human ratings across multiple quality dimensions to assess the realism and quality of simulated conversations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item_id
|
UUID
|
UUID of the conversation item to score |
required |
scores
|
Dict[str, float]
|
Dictionary of dimension -> score (0-1) Should include scores for dimensions in self.scoring_dimensions |
required |
reviewer_id
|
str
|
ID of the human reviewer |
required |
feedback
|
Optional[str]
|
Optional textual feedback about the conversation |
None
|
Returns:
| Type | Description |
|---|---|
ReviewItem
|
Updated ReviewItem |
Raises:
| Type | Description |
|---|---|
ValueError
|
If scores format is invalid |
ConversationScoringError
|
If item not found or invalid operation |
Examples:
>>> workflow = ConversationScoringWorkflow()
>>> item = workflow.submit_scores(
... item_id=some_uuid,
... scores={
... "realism": 0.85,
... "coherence": 0.90,
... "goal_achievement": 0.95,
... "naturalness": 0.80
... },
... reviewer_id="reviewer_123",
... feedback="Good conversation but agent could be more empathetic"
... )
>>> item.human_scores
{'realism': 0.85, 'coherence': 0.9, ...}
Source code in uaef/hitl/conversation_scoring.py
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | |
InsufficientDataError
Bases: ConversationScoringError
Raised when insufficient data is available for analysis.
Source code in uaef/hitl/conversation_scoring.py
36 37 38 | |
QualityTrend
Represents a trend in conversation quality over time.
Attributes:
| Name | Type | Description |
|---|---|---|
dimension |
Quality dimension being tracked |
|
time_period |
Time period for the trend |
|
average_score |
Average score over the period |
|
score_change |
Change compared to previous period |
|
conversation_count |
Number of conversations in the period |
|
trend_direction |
Direction of trend |
|
issues_detected |
List of detected issues |
Source code in uaef/hitl/conversation_scoring.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
to_dict()
Convert to dictionary representation.
Source code in uaef/hitl/conversation_scoring.py
74 75 76 77 78 79 80 81 82 83 84 | |
uaef.hitl.benchmark
Benchmark versioning for HITL workflows.
This module provides versioned benchmarks from human-validated review items, enabling regression testing and tracking evaluation quality over time.
Benchmark
Bases: BaseModel
A versioned benchmark containing human-validated review items.
Attributes:
| Name | Type | Description |
|---|---|---|
benchmark_id |
UUID
|
Unique identifier for this benchmark |
metadata |
BenchmarkMetadata
|
Benchmark metadata (version, date, metrics, etc.) |
items |
List[ReviewItem]
|
List of validated review items |
Examples:
>>> benchmark = Benchmark(
... benchmark_id=uuid4(),
... metadata=BenchmarkMetadata(...),
... items=[review_item1, review_item2, ...]
... )
Source code in uaef/hitl/benchmark.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
Config
Pydantic configuration.
Source code in uaef/hitl/benchmark.py
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
validate_items(v)
classmethod
Validate that items list is not empty.
Source code in uaef/hitl/benchmark.py
188 189 190 191 192 193 194 | |
BenchmarkMetadata
Bases: BaseModel
Metadata for a benchmark version.
Attributes:
| Name | Type | Description |
|---|---|---|
version |
str
|
Unique version identifier (e.g., "v1.0.0", "2024-01-15") |
creation_date |
datetime
|
When the benchmark was created |
sample_size |
int
|
Number of validated items in the benchmark |
agreement_metrics |
Dict[str, Any]
|
Agreement statistics for the benchmark |
description |
str
|
Human-readable description of the benchmark |
metric_names |
List[str]
|
List of metrics included in the benchmark |
item_types |
List[str]
|
List of item types included in the benchmark |
tags |
List[str]
|
Optional tags for categorization |
Examples:
>>> metadata = BenchmarkMetadata(
... version="v1.0.0",
... creation_date=datetime.utcnow(),
... sample_size=100,
... agreement_metrics={
... "answer_relevance": {"krippendorff_alpha": 0.85}
... },
... description="Initial benchmark with high-quality samples",
... metric_names=["answer_relevance", "answer_correctness"],
... item_types=["llm_evaluation"]
... )
Source code in uaef/hitl/benchmark.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
Config
Pydantic configuration.
Source code in uaef/hitl/benchmark.py
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
validate_item_types(v)
classmethod
Validate that item_types is not empty.
Source code in uaef/hitl/benchmark.py
129 130 131 132 133 134 135 | |
validate_metric_names(v)
classmethod
Validate that metric_names is not empty.
Source code in uaef/hitl/benchmark.py
121 122 123 124 125 126 127 | |
validate_sample_size(v)
classmethod
Validate that sample_size is positive.
Source code in uaef/hitl/benchmark.py
113 114 115 116 117 118 119 | |
validate_version(v)
classmethod
Validate that version is not empty.
Source code in uaef/hitl/benchmark.py
105 106 107 108 109 110 111 | |
BenchmarkNotFoundError
Bases: BenchmarkVersioningError
Raised when a benchmark version is not found.
Source code in uaef/hitl/benchmark.py
31 32 33 | |
BenchmarkVersioning
Manager for versioned benchmarks from human-validated review items.
This class creates and manages versioned benchmarks that can be used for: - Regression testing: Ensure evaluation quality doesn't degrade over time - LLM judge calibration: Track how well LLM scores align with human judgments - Quality tracking: Monitor agreement metrics across benchmark versions - Benchmark comparison: Compare evaluation quality between versions
Benchmarks are stored in-memory by default but can be exported/imported to/from files for persistence and sharing.
Attributes:
| Name | Type | Description |
|---|---|---|
_benchmarks |
Dict[str, Benchmark]
|
Internal storage for benchmarks (version -> Benchmark) |
_agreement_calculator |
Calculator for agreement metrics |
Examples:
>>> # Create benchmark versioning manager
>>> versioning = BenchmarkVersioning()
>>> # Create a new benchmark from validated review items
>>> benchmark = versioning.create_benchmark(
... version="v1.0.0",
... review_items=validated_items,
... description="Initial production benchmark",
... tags=["production", "validated"]
... )
>>> # Retrieve a specific benchmark
>>> benchmark = versioning.get_benchmark("v1.0.0")
>>> # List all available benchmarks
>>> benchmarks = versioning.list_benchmarks()
>>> # Compare two benchmarks
>>> comparison = versioning.compare_benchmarks("v1.0.0", "v2.0.0")
>>> # Export benchmark to file
>>> versioning.export_benchmark("v1.0.0", "benchmark_v1.json")
>>> # Import benchmark from file
>>> versioning.import_benchmark("benchmark_v1.json")
Source code in uaef/hitl/benchmark.py
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 | |
compare_benchmarks(version1, version2, metric_names=None)
Compare two benchmark versions to track evaluation quality changes.
This method compares agreement metrics, sample sizes, and quality indicators between two benchmark versions to identify improvements or regressions in evaluation quality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version1
|
str
|
First benchmark version (baseline) |
required |
version2
|
str
|
Second benchmark version (comparison) |
required |
metric_names
|
Optional[List[str]]
|
Optional list of metrics to compare (defaults to all) |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary containing: - version1: First version identifier - version2: Second version identifier - date1: Creation date of first benchmark - date2: Creation date of second benchmark - sample_size_change: Change in sample size - metric_comparisons: Per-metric comparison results - metric_name: Name of the metric - agreement_change: Change in agreement metrics - quality_change: Change in quality assessment - summary: High-level comparison summary - overall_trend: "improved", "degraded", "stable" - significant_changes: List of metrics with significant changes |
Raises:
| Type | Description |
|---|---|
BenchmarkNotFoundError
|
If either version is not found |
ValueError
|
If versions are the same |
Examples:
>>> # Compare two versions
>>> comparison = versioning.compare_benchmarks("v1.0.0", "v2.0.0")
>>> print(f"Overall trend: {comparison['summary']['overall_trend']}")
>>>
>>> # Check specific metrics
>>> for metric_comp in comparison['metric_comparisons']:
... print(f"{metric_comp['metric_name']}: "
... f"{metric_comp['quality_change']}")
>>> # Compare specific metrics only
>>> comparison = versioning.compare_benchmarks(
... "v1.0.0", "v2.0.0",
... metric_names=["answer_relevance", "answer_correctness"]
... )
Source code in uaef/hitl/benchmark.py
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 | |
create_benchmark(version, review_items, description, tags=None, metric_names=None, item_types=None)
Create a new benchmark version from validated review items.
This method: 1. Validates that all review items have human scores 2. Calculates agreement metrics for each metric 3. Creates benchmark metadata 4. Stores the benchmark for future use
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version
|
str
|
Unique version identifier (e.g., "v1.0.0", "2024-01-15") |
required |
review_items
|
List[ReviewItem]
|
List of validated review items with human scores |
required |
description
|
str
|
Human-readable description of the benchmark |
required |
tags
|
Optional[List[str]]
|
Optional tags for categorization |
None
|
metric_names
|
Optional[List[str]]
|
Optional list of metrics to include (defaults to all) |
None
|
item_types
|
Optional[List[str]]
|
Optional list of item types to include (defaults to all) |
None
|
Returns:
| Type | Description |
|---|---|
Benchmark
|
The created Benchmark object |
Raises:
| Type | Description |
|---|---|
ValueError
|
If version already exists or review_items is empty |
InvalidBenchmarkError
|
If review items are invalid |
Examples:
>>> # Create benchmark with all items
>>> benchmark = versioning.create_benchmark(
... version="v1.0.0",
... review_items=validated_items,
... description="Initial production benchmark"
... )
>>> # Create benchmark with specific metrics
>>> benchmark = versioning.create_benchmark(
... version="v1.1.0",
... review_items=validated_items,
... description="Benchmark for answer quality metrics",
... metric_names=["answer_relevance", "answer_correctness"],
... tags=["production", "answer_quality"]
... )
>>> # Create benchmark with specific item types
>>> benchmark = versioning.create_benchmark(
... version="v1.2.0",
... review_items=validated_items,
... description="LLM evaluation benchmark",
... item_types=["llm_evaluation"]
... )
Source code in uaef/hitl/benchmark.py
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | |
export_benchmark(version, filepath)
Export a benchmark to a JSON file for sharing or persistence.
The exported file contains the complete benchmark including metadata and all review items, allowing it to be imported into another system or used for regression testing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version
|
str
|
Version identifier of the benchmark to export |
required |
filepath
|
str
|
Path to the output JSON file |
required |
Raises:
| Type | Description |
|---|---|
BenchmarkNotFoundError
|
If version is not found |
IOError
|
If file cannot be written |
Examples:
>>> # Export to file
>>> versioning.export_benchmark("v1.0.0", "benchmark_v1.json")
>>> # Export to specific directory
>>> versioning.export_benchmark(
... "v1.0.0",
... "/path/to/benchmarks/benchmark_v1.json"
... )
Source code in uaef/hitl/benchmark.py
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 | |
get_benchmark(version)
Retrieve a specific benchmark version.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
version
|
str
|
Version identifier of the benchmark to retrieve |
required |
Returns:
| Type | Description |
|---|---|
Benchmark
|
The Benchmark object |
Raises:
| Type | Description |
|---|---|
BenchmarkNotFoundError
|
If version is not found |
Examples:
>>> benchmark = versioning.get_benchmark("v1.0.0")
>>> print(f"Sample size: {benchmark.metadata.sample_size}")
>>> print(f"Metrics: {benchmark.metadata.metric_names}")
Source code in uaef/hitl/benchmark.py
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | |
import_benchmark(filepath, overwrite=False)
Import a benchmark from a JSON file.
This method loads a previously exported benchmark from a file, validates it, and adds it to the available benchmarks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to the JSON file containing the benchmark |
required |
overwrite
|
bool
|
If True, overwrite existing benchmark with same version |
False
|
Returns:
| Type | Description |
|---|---|
Benchmark
|
The imported Benchmark object |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If file does not exist |
InvalidBenchmarkError
|
If file is invalid or corrupted |
ValueError
|
If version already exists and overwrite is False |
Examples:
>>> # Import from file
>>> benchmark = versioning.import_benchmark("benchmark_v1.json")
>>> print(f"Imported {benchmark.metadata.version}")
>>> # Import and overwrite existing
>>> benchmark = versioning.import_benchmark(
... "benchmark_v1.json",
... overwrite=True
... )
Source code in uaef/hitl/benchmark.py
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 | |
list_benchmarks(metric_name=None, item_type=None, start_date=None, end_date=None, tags=None)
List all available benchmarks with optional filtering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metric_name
|
Optional[str]
|
Optional filter by metric name |
None
|
item_type
|
Optional[str]
|
Optional filter by item type |
None
|
start_date
|
Optional[datetime]
|
Optional filter by creation date (inclusive) |
None
|
end_date
|
Optional[datetime]
|
Optional filter by creation date (inclusive) |
None
|
tags
|
Optional[List[str]]
|
Optional filter by tags (must have all specified tags) |
None
|
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
List of benchmark summaries, each containing: - version: Version identifier - creation_date: When the benchmark was created - sample_size: Number of items - metric_names: List of metrics - item_types: List of item types - description: Benchmark description - tags: List of tags |
Examples:
>>> # List all benchmarks
>>> benchmarks = versioning.list_benchmarks()
>>> for b in benchmarks:
... print(f"{b['version']}: {b['sample_size']} items")
>>> # Filter by metric
>>> benchmarks = versioning.list_benchmarks(
... metric_name="answer_relevance"
... )
>>> # Filter by date range
>>> benchmarks = versioning.list_benchmarks(
... start_date=datetime(2024, 1, 1),
... end_date=datetime(2024, 12, 31)
... )
>>> # Filter by tags
>>> benchmarks = versioning.list_benchmarks(
... tags=["production", "validated"]
... )
Source code in uaef/hitl/benchmark.py
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | |
BenchmarkVersioningError
Bases: Exception
Base exception for benchmark versioning errors.
Source code in uaef/hitl/benchmark.py
26 27 28 | |
InvalidBenchmarkError
Bases: BenchmarkVersioningError
Raised when a benchmark is invalid or corrupted.
Source code in uaef/hitl/benchmark.py
36 37 38 | |