pip install evidentlyCore library. Also on conda-forge:conda install -c conda-forge evidently.pip install evidently[llm]Adds the extra dependencies for LLM judges and semantic evals. Other extras:spark,s3,gcs,fsspec,sql.pip install tracelySister package for OpenTelemetry-based tracing of a live LLM app.from evidently import Dataset, DataDefinition, ReportThe three objects you need every single time. All top-level since 0.7.from evidently.presets import *Pre-built Report templates. Note:evidently.presets, not the oldevidently.metric_preset.from evidently.metrics import *Individual dataset- and column-level metrics.from evidently.descriptors import *Row-level text / LLM evaluators.from evidently.tests import *The condition operators:eq gt gte lt lte not_eq is_in not_in.
eval_data = Dataset.from_pandas(df, data_definition=DataDefinition())An emptyDataDefinition()auto-maps columns by dtype and by name. Good enough to start.Dataset.from_pandas(df, data_definition=schema, descriptors=[...])Compute row-level scores at the same time you build the Dataset.eval_data.add_descriptors(descriptors=[...])Or add them later to an existing Dataset. Same result, two styles.eval_data.as_dataframe()Get the frame back — original columns plus every descriptor column. Your main debugging view.report.run(df_raw, None)You can pass a bare DataFrame for simple summary and drift checks, but the docs recommend always building the Dataset explicitly.
DataDefinition(numerical_columns=["age", "salary"])Explicit beats automatic — stops numeric columns with few unique values being read as categorical.DataDefinition(categorical_columns=["dept"])Default catch-all: everything non-numeric and non-datetime.DataDefinition(text_columns=["question", "answer"])No automatic mapping exists for text. Required for text drift detection.DataDefinition(datetime_columns=["joined"])A column type. You can have many. Ignored in drift calculations.# a column you leave out of an explicit mapping is ignored everywhereExcluding columns from the mapping silently drops them from all evaluations.
DataDefinition(id_column="Id", timestamp="Date")Auto-mapped only from columns literally namedidandtimestamp. Both are ignored in drift.# datetime_columns = a TYPE (many allowed)timestampis a role — exactly one, used as the plot index. Different things.DataDefinition(numerical_descriptors=["user_rating"])Tell Evidently that a column you computed elsewhere should be treated as a descriptor.DataDefinition(categorical_descriptors=["model_type"])Descriptors get picked up byTextEvals()and plotted as descriptors in the UI.# descriptors you generate are mapped automaticallyYou only need the two options above for externally computed scores.
DataDefinition(regression=[Regression(target="y_true", prediction="y_pred")])Defaults aretarget/prediction. It is a list — several regressions in one table are allowed.DataDefinition(classification=[BinaryClassification(target="y", prediction_labels="pred")])Binary defaults:prediction_probas="prediction",pos_label=1.MulticlassClassification(target=..., prediction_labels=..., prediction_probas=["0","1","2"])Probability column names must match the class labels exactly; target/prediction values should be strings.DataDefinition(ranking=[Recsys()])Defaultsuser_id,item_id,target,prediction. Prediction is a score by default, or a rank.labels={"0": "legit", "1": "fraud"}Display only — renames classes in the report, changes nothing in the maths.
my_eval = report.run(current_data, reference_data)Current first. The dataset you are evaluating, then the baseline you compare it against.my_eval = report.run(current_data=cur, reference_data=ref)Keywords work too, and are worth using until the order is muscle memory.my_eval = report.run(eval_data, None)Single dataset. Perfectly normal — only drift genuinely requires two.# reference unlocks three thingsSide-by-side comparison, drift detection, and auto-generated test conditions derived from the baseline.# both datasets must share an identical data definitionBuild them from the sameschemaobject.
evidently uiServes the dashboard over the workspace folder in the current directory. Opens onlocalhost:8000.evidently ui --workspace ./workspace --port 8080Point at a specific workspace, move off the default port.evidently ui --demo-projects allLaunches pre-populated demo projects — the fastest way to see what the UI does.uv run --with evidently evidently ui --demo-projects allSame thing with no install, if you haveuv.ws = Workspace.create("evidently_ui_workspace")from evidently.ui.workspace import Workspace. Backends: filesystem, any SQL database, or S3-compatible storage viafsspec.ws.add_run(project.id, my_eval, include_data=True)Log a snapshot.include_data=Trueships the scored rows too, so you can sort and browse them.