You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- **Immutable.** Scorers are stored as a tuple and exposed via a read-only property.
217
156
- **Blocks duplicates on construction.** `__init__` raises an error if duplicate scorers (same type and name) are passed. This is explicit — users know immediately if they have a conflict, rather than duplicates being silently removed.
218
-
- **Set union via `|`.** Supports `__or__`/`__ror__` for combining presets with deduplication: `Agent() | [my_scorer]` or `Agent() | Rag()`. Uses `|` instead of `+` because the deduplication behavior matches set union semantics. Deduplication on `|` is silent because combining presets with overlapping scorers is expected usage.
157
+
- **Set union via `|`.** Combines presets with deduplication and returns a new `Preset`: `Agent() | [my_scorer]` or `Agent() | Rag()`. Results can be chained and registered. Uses `|` instead of `+` because the deduplication behavior matches set union semantics.
219
158
- **Not a `Scorer` subclass.** A preset doesn't produce feedback -- it's a container. The evaluation loop assumes one scorer = one result column. Making `Preset` a scorer would require changes throughout the pipeline (aggregation, telemetry, serialization).
220
159
- **Stores instances, not classes.** Users pass already-configured scorer instances.
221
160
222
161
### Built-in Presets as Subclasses
223
162
224
-
Each built-in preset is a subclass of `Preset` that hardcodes its scorer list. This means each call creates **fresh scorer instances** (no shared mutable singletons) and supports preset-specific customization.
225
-
226
-
```python
227
-
class Agent(Preset):
228
-
def __init__(self):
229
-
super().__init__("agent", [
230
-
ToolCallCorrectness(),
231
-
ToolCallEfficiency(),
232
-
RelevanceToQuery(),
233
-
Safety(),
234
-
Completeness(),
235
-
])
236
-
237
-
class Rag(Preset):
238
-
def __init__(self):
239
-
super().__init__("rag", [
240
-
RetrievalRelevance(),
241
-
RetrievalGroundedness(),
242
-
RelevanceToQuery(),
243
-
Safety(),
244
-
Completeness(),
245
-
])
246
-
247
-
class ConversationalAgent(Preset):
248
-
def __init__(self):
249
-
super().__init__("conversational-agent", [
250
-
ToolCallCorrectness(),
251
-
ToolCallEfficiency(),
252
-
RelevanceToQuery(),
253
-
Safety(),
254
-
Completeness(),
255
-
UserFrustration(),
256
-
ConversationCompleteness(),
257
-
ConversationalSafety(),
258
-
ConversationalToolCallEfficiency(),
259
-
KnowledgeRetention(),
260
-
])
261
-
```
163
+
Each built-in preset is a subclass of `Preset` that hardcodes its scorer list. Each call creates **fresh scorer instances** (no shared mutable singletons) and supports preset-specific customization. See the Built-in Preset Summary table below for the scorers in each preset.
Presets can be registered to the MLflow server so teams can share them across sessions. This leverages the existing scorer registration infrastructure.
@@ -337,33 +229,20 @@ result = mlflow.genai.evaluate(data=eval_dataset, scorers=[preset])
337
229
- **Team sharing.** A persisted preset is available to any team member with access to the experiment.
338
230
- **Customization without code.** Teams can customize and persist presets without modifying source code or templates.
339
231
232
+
**Persistence behavior:**
233
+
234
+
- **Scope.** Presets are scoped to experiments, consistent with how scorer registration already works in MLflow. This prevents name collisions across teams and ensures presets are organized alongside the experiments they evaluate. If no `experiment_id` is provided, the active experiment is used.
235
+
- **Custom scorer portability.** If a preset contains custom scorers, those scorers must be registered first. When a teammate loads the preset, the custom scorers are resolved from the registry. If a custom scorer is not registered, `preset.register()` will raise an error.
236
+
- **Discovery.** `list_presets()` returns all registered presets for the current experiment, allowing teams to discover what presets are available. This follows the same pattern as `list_scorers()`.
237
+
340
238
### Deduplication
341
239
342
240
When presets are combined using `|`, the same scorer type can appear more than once. For example, `Agent()` and `Rag()` both contain `Safety()` and `RelevanceToQuery()`. Running the same scorer twice wastes LLM API calls and produces duplicate result columns.
343
241
344
242
Deduplication happens in two places:
345
243
346
244
- **In `__or__`** — when presets are combined using `|`, duplicates are removed using `(type(scorer), scorer.name)` as the key. This is expected behavior when combining presets with overlapping scorers.
347
-
- **In `validate_scorers()`** — when multiple presets are passed directly in a list (e.g., `scorers=[Agent(), Rag()]`) without using `|`, `__or__` is never called. `validate_scorers()` flattens and deduplicates as a safety net:
- **In `validate_scorers()`** — when multiple presets are passed directly in a list (e.g., `scorers=[Agent(), Rag()]`) without using `|`, `__or__` is never called. `validate_scorers()` flattens presets into individual scorers and deduplicates as a safety net.
367
246
368
247
Scorers of the same class with different names are preserved (e.g., two `Guidelines` with different rules). Only true duplicates — same class and same name — are removed.
0 commit comments