Kitaru: turn agent failures into regression tests — replay real traces against your real code.Meet Kitaru →

The unified layer for ML and AI

Replayable agent evals with Kitaru. Reproducible ML pipelines with ZenML. One platform, on the infrastructure you already use.

RUN VS RUNReplay the fix against the 90 recorded sessions.
evaluator hazmat-flag-preserved
BASELINErun 2 · version v1
SESSIONS90
FAILING90 / 90
COST / RUN$0.14
CANDIDATErun 3 · version pr-311
SESSIONS
90same cohort
FAILING
4 / 90+86
COST / RUN
$0.12−11%
4 still failing — opened for you to read.No verdict, no blended score. Two runs, side by side, read by you.
RUN VS RUNThe run every candidate is read against.
evaluator hazmat-flag-preserved
BASELINErun 2 · version v1
SESSIONS90
FAILING90 / 90
COST / RUN$0.14
CANDIDATEpick a run above to compare
SESSIONS
90same cohort
FAILING
90 / 90unchanged
COST / RUN
$0.14unchanged
90 failing — opened for you to read.The cohort is immutable, so this run keeps meaning what it meant.
RUN VS RUNSame 90 sessions, one model swapped.
evaluator hazmat-flag-preserved
BASELINErun 2 · version v1
SESSIONS90
OUTPUTS IDENTICAL
COST / RUN$0.14
CANDIDATErun 1 · model glm-5.4
SESSIONS
90same cohort
OUTPUTS IDENTICAL
84 / 906 to read
COST / RUN
$0.05−64%
6 diverged — opened for you to read.No verdict, no blended score. Two runs, side by side, read by you.
One wrap, every call recordedEach run records as a session, and that recording is what replay reads back.
intake-agent · 12 Julintake-agent · 12 Julcheckout-agent · 11 Julsupport-agent · 11 Julcheckout-agent · 10 Jul
import · intake-agentKITARU
kitaru session import ./traces.jsonl --agent intake-agent→ 1,824 sessions read→ 1,712 READY · 94 PARTIAL · 18 UNAVAILABLE→ ses_8f3a91c2 · 5 checkpoints · READY
Checkpoint spine
sessionmodeltoolcheckpoint
Node
0s12s24s36s48s
ses_8f3a91c248s
runstart
lookup_order6s
retrieve11s
model_request28s
reply3s
imported from ./traces.jsonl · every call recorded · replayableREADY · 5 checkpoints
capture · intake-agentKITARU
agent = KitaruAgent(intake_agent)→ recording ses_4c1d77e0→ 2 tool calls · 1 model call captured→ ses_4c1d77e0 · 5 checkpoints · READY
Checkpoint spine
sessionmodeltoolcheckpoint
Node
0s9s18s27s36s
ses_4c1d77e036s
runstart
lookup_order4s
retrieve8s
model_request21s
reply3s
captured live by the adapter · every call recorded · replayableREADY · 5 checkpoints
import · checkout-agentKITARU
kitaru session import ./traces.jsonl --agent checkout-agent→ 612 sessions read→ ses_9b3e21af · price_quote result missing→ ses_9b3e21af · 4 of 5 recorded · PARTIAL
Checkpoint spine
sessionmodeltoolcheckpoint
Node
0s10s20s30s41s
ses_9b3e21af41s
runstart
lookup_cart5s
price_quoteno result
model_request30s
reply2s
imported from ./traces.jsonl · price_quote result missing · replays to that nodePARTIAL · 4 of 5 recorded
import · support-agentKITARU
kitaru session import ./traces.jsonl --agent support-agent→ 438 sessions read→ 431 READY · 7 PARTIAL→ ses_2d5a08c4 · 5 checkpoints · READY
Checkpoint spine
sessionmodeltoolcheckpoint
Node
0s16s32s48s1m 04s
ses_2d5a08c41m 04s
runstart
lookup_order9s
search_docs17s
model_request34s
reply4s
imported from ./traces.jsonl · every call recorded · replayableREADY · 5 checkpoints
import · checkout-agentKITARU
kitaru session import ./traces.jsonl --agent checkout-agent→ 612 sessions read→ ses_7e6b33d1 · no tool or model calls in trace→ ses_7e6b33d1 · reply only · UNAVAILABLE
Checkpoint spine
sessionmodeltoolcheckpoint
Node
runtooltoolmodelreply
ses_7e6b33d1unknown
runstart
lookup_cartnot recorded
price_quotenot recorded
model_requestnot recorded
replytext only
imported from ./traces.jsonl · no calls in the trace · cannot be replayedUNAVAILABLE · reply only
COHORT COMPOSITIONThe sessions that matter, frozen as a named set — so results keep meaning what they meant.
checkout-flow · immutable
FROM YOUR NOTES
your judgmentthe agent's work
1,824 importedsessions
20 readby you
3 cohortsfrom your notes
1 evaluatorhazmat-flag-preserved
MEMBER SESSIONS
ses_8f3a91c2ses_4c1d77e0ses_2d5a08c4ses_1a90cc7bses_66df2e14+85 more
IMMUTABLEfrozen 12 Jul
90 sessions, fixed. Add sessions and you have made a new cohort, not edited this one.
CREATED FROM YOUR READING12 Jul
$kitaru cohort create "checkout-flow"
Froze90 sessions· reads throughhazmat-flag-preserved
checkout-flow·90 sessions·frozen 12 Jul
immutable
PURE CONFIGURATIONModel, prompt, tool policy. Swap one and you have stated a new hypothesis.
cheap-model · 1 run
modelsystem_prompttool_policy
EXPERIMENT CONFIGcheap-model
model="glm-5.4"the hypothesis
system_prompt=rev 7unchanged
tool_policy=History(scope="cohort", on_miss="fail")unchanged
RUNSa run is config + cohort + version
run 1·checkout-flow·v184 / 90 outputs identical · $0.05 / run
3 experiments · 4 runs · cohort checkout-flowconfig only
HARNESSyour agent SDK
Harness stays.Kitaru wraps around it.
KITARU ADDSrecorded session
KitaruAgent(agent)
Kitaru runtimeouter layer
sessionone recorded run
checkpointevery call, a node
cohortsessions, frozen
replayone thing changed
PydanticAI
typed depstoolsoutput
Model + tools
OpenAIMCPdb
Good fitTyped agents where you want schema validation on every step.
Trade-offAdds a Pydantic dependency and some per-call overhead.
KITARU ADDSrecorded session
KitaruRunner(agent)
Kitaru runtimeouter layer
sessionone recorded run
checkpointevery call, a node
cohortsessions, frozen
replayone thing changed
OpenAI Agents
toolshandoffsoutput
Model + tools
OpenAIMCPdb
Good fitMulti-agent runs with handoffs via the Agents SDK Runner.
Trade-offTied to OpenAI-hosted models and their rate limits.
KITARU ADDSrecorded session
KitaruGraphRunner(graph)
Kitaru runtimeouter layer
sessionone recorded run
checkpointevery call, a node
cohortsessions, frozen
replayone thing changed
LangGraph
graphstoolssubagents
Model + tools
OpenAIMCPdb
Good fitGraph agents — LangChain create_agent and Deep Agents included.
Trade-offReplay depth depends on how the graph was constructed.
KITARU ADDSrecorded session
new KitaruAgent(agent)
Kitaru runtimeouter layer
sessionone recorded run
checkpointevery call, a node
cohortsessions, frozen
replayone thing changed
Mastra
agentstoolsworkflows
Model + tools
OpenAIMCPdb
Good fitTypeScript agents on Mastra — generate() behaves unchanged.
Trade-offNon-streaming calls only for now.
KITARU ADDSrecorded session
createKitaruGenerateText(...)
Kitaru runtimeouter layer
sessionone recorded run
checkpointevery call, a node
cohortsessions, frozen
replayone thing changed
Vercel AI SDK
generateTexttoolsproviders
Model + tools
OpenAIMCPdb
Good fitDrop-in for generateText — same call signature, now recorded.
Trade-offWraps non-streaming generateText; streamText isn't covered.
KITARU ADDSrecorded session
createSession(...)
Kitaru runtimeouter layer
sessionone recorded run
checkpointevery call, a node
cohortsessions, frozen
replayone thing changed
Custom loop
import tracesclient API
Model + tools
OpenAIMCPdb
Good fitAny framework — import Langfuse, LangSmith, Braintrust or OTel traces.
Trade-offYou define the session's nodes; Kitaru can't infer them.
EVERY CALL RECORDEDses_8f3a91c2 · recorded
recorded
SPAN
0s12s24s36s48s
runstart
model_request13s
lookup_order6s
checkpointrecorded
model_request24s
replyrecorded
with kitaru2 model calls and 1 tool call recorded · this session replays
withoutone log line per run · nothing to replay
EVERY CALL RECORDEDses_7834ab10 · recorded
recorded
SPAN
0s10s20s30s40s
runstart
model_request9s
web_search11s
checkpointrecorded
model_request16s
replyrecorded
with kitaruevery handoff and tool call recorded · this session replays
withoutone log line per run · nothing to replay
EVERY CALL RECORDEDses_3019c4de · recorded
recorded
SPAN
0s13s26s39s52s
runstart
model_request10s
tool_use14s
checkpointrecorded
model_request22s
replyrecorded
with kitaruevery graph, model and tool callback recorded · this session replays
withoutone log line per run · nothing to replay
EVERY CALL RECORDEDses_2291f70b · recorded
recorded
SPAN
0s9s18s27s36s
runstart
model_request7s
retrieve9s
checkpointrecorded
model_request15s
replyrecorded
with kitaruevery generate() call recorded · this session replays
withoutone log line per run · nothing to replay
EVERY CALL RECORDEDses_1102de55 · recorded
recorded
SPAN
0s13s26s39s52s
runstart
model_request12s
lookup_order8s
checkpointrecorded
model_request27s
replyrecorded
with kitaruevery generation recorded · this session replays
withoutone log line per run · nothing to replay
EVERY CALL RECORDEDses_66df2e14 · recorded
recorded
SPAN
0s12s24s36s48s
runstart
model_request11s
tool_call13s
checkpointrecorded
model_request19s
replyrecorded
with kitaruimported or client-recorded sessions replay like any other
withoutone log line per run · nothing to replay
Integrate your MLOps stackFor compute-intense, distributed ML pipelines.
simple_pipeline · run #7
quickstart.pyZENML
from typing import Annotatedfrom zenml import pipeline, step @stepdef simple_step(name: str = "World") -> Annotated[str, "greeting"]:    return f"Hello, {name}! Welcome to ZenML!" @pipelinedef simple_pipeline(name: str = "World"):    return simple_step(name=name)
name
namestr
defaultWorld
simple_step
2s
greetingstr · v7
local · default stackhealthy
Integrate your MLOps stackFor compute-intense, distributed ML pipelines.
training · run #32
pipelines/training.pyZENML
from zenml import pipeline, step @stepdef data_loader(random_state: int) -> pd.DataFrame:    return load_breast_cancer(as_frame=True).frame @stepdef model_trainer(dataset_trn: pd.DataFrame) -> ClassifierMixin:    return SGDClassifier().fit(dataset_trn.drop("target", axis=1), dataset_trn.target) @pipelinedef training(model_type: str = "sgd"):    model_trainer(dataset_trn=data_loader(random_state=17))
data_loader
7s
dataset_trnDataFrame
dataset_tstDataFrame
model_trainer
4m 5s
sklearn_classifiersklearn · v32
kubernetes-prod · 12 podshealthy
Integrate your MLOps stackFor compute-intense, distributed ML pipelines.
llm_peft_full_finetune · run #18
pipelines/train.pyZENML
from zenml import pipelinefrom steps import prepare_data, finetune, evaluate_model, promote @pipelinedef llm_peft_full_finetune(    base_model_name: str = "microsoft/phi-2",    dataset_name: str = "gem/viggo",):    datasets_dir = prepare_data(base_model_name, dataset_name)    ft_model_dir = finetune(base_model_name, datasets_dir)    evaluate_model(base_model_name, ft_model_dir, datasets_dir)    promote(ft_model_dir)
prepare_data
3m 41s
datasets_dirPath
tokenizerPreTrainedTokenizer
finetune
58m 22s
ft_model_dirphi-2 · v18
vertex-gcp · 1× A100 80GBhealthy
Integrate your MLOps stackFor compute-intense, distributed ML pipelines.
churn_inference_pipeline · run #44
pipelines/inference_pipeline.pyZENML
from zenml import pipelinefrom zenml.config import DeploymentSettingsfrom steps.inference import predict_churn @pipeline(    on_init=init_model,    settings={"deployment": DeploymentSettings(        app_title="Churn Prediction API",        dashboard_files_path="ui",    )},)def churn_inference_pipeline(customer_features: Dict) -> Dict:    return predict_churn(customer_features=customer_features)
init_model
warm
modelRandomForest
customerDict
predict_churn
87ms
predictionDict · v44
sagemaker-aws · 2 replicaslive
Integrate your MLOps stackFor compute-intense, distributed ML pipelines.
object_detection_training · run #12
pipelines/training_pipeline.pyZENML
from zenml import pipelinefrom steps import load_coco_dataset, train_yolo, fiftyone_analysis @pipelinedef object_detection_training_pipeline(    max_samples: int = 50,    epochs: int = 1,    model_name: str = "yolov8n.pt",):    dataset = load_coco_dataset(max_samples=max_samples)    model = train_yolo(dataset=dataset, epochs=epochs, model_name=model_name)    fiftyone_analysis(dataset=dataset, model=model)
load_coco_dataset
1m 02s
datasetFiftyOneDataset
labelsCOCO80
train_yolo
17m 14s
yolo-modelultralytics · v12
airflow · 4× T4 GPUhealthy
Stack composerSwap orchestrator, store, tracker — same pipeline code.
5 components
StackOrchestratorArtifact storeContainer regTracker
local-dev
locallocaldefaultmlflow
kubernetes-prod
kubeflows3://prodecrmlflow
vertex-gcp
vertexgcs://prodgcrneptune
sagemaker-aws
sagemakers3://eu-westecrw&b
airflow-staging
airflowgcs://staginggcrmlflow
azureml-eu
azuremlazure://euacrcomet
Register the stack1 command
$zenml stack register local-dev -o default -a default --set
Register the stack3 commands
$zenml service-connector register aws-prod --type aws -i
$zenml orchestrator register kubeflow-orch --flavor kubeflow --connector aws-prod
$zenml stack register kubernetes-prod -o kubeflow-orch -a s3-prod --set
Register the stack3 commands
$zenml service-connector register gcp-prod --type gcp -i
$zenml orchestrator register vertex-orch --flavor vertex --connector gcp-prod
$zenml stack register vertex-gcp -o vertex-orch -a gcs-prod --set
Register the stack3 commands
$zenml service-connector register aws-eu --type aws -i
$zenml orchestrator register sagemaker-orch --flavor sagemaker --connector aws-eu
$zenml stack register sagemaker-aws -o sagemaker-orch -a s3-eu --set
Register the stack3 commands
$zenml service-connector register gcp-staging --type gcp -i
$zenml orchestrator register airflow-orch --flavor airflow --connector gcp-staging
$zenml stack register airflow-staging -o airflow-orch -a gcs-staging --set
Register the stack3 commands
$zenml service-connector register azure-eu --type azure -i
$zenml orchestrator register azureml-orch --flavor azureml --connector azure-eu
$zenml stack register azureml-eu -o azureml-orch -a blob-eu --set
churn_predictorv18 of 18
6c5e0a14 · sklearn 1.4 · 2.4 MB
Version Evolution18 versions · +4.2pt since v1
v1v8 · DEVv12 · STAGEv18 · PROD
Metadata4 fields
accuracy0.926
f1 score0.911
train rows47,392
promotedv18 → PROD
LineageProduced by step predict_on_endpoint · used by model churn_predictor·v18 · deployed to kubernetes-prod
Where it runs3 envs · 4 endpoints
PROD
v18vertex · 2 endpoints
99.97% uptime
STAGE
SHADOW
v18k8s · shadow traffic
promoted 3h ago
CANARY
v18·rc2vertex · 5% rollout
accuracy +0.4pt vs v18
Registry42 models · 248 artifacts
last promoted 3h ago
INSIDE THE @STEPUse PyTorch in any @step.Bring your own. ZenML wraps it — you don't change your training loop.
torch 2.4.1 · CUDA 12.1
training_pipeline.py
@step · zenml
import torchfrom zenml import step @step(enable_cache=False)def train_model(    X: torch.Tensor, y: torch.Tensor) -> torch.nn.Module:    model = torch.nn.Sequential(        torch.nn.Linear(784, 256), torch.nn.ReLU(),        torch.nn.Linear(256, 10),    )    return model  # auto-versioned by ZenML
What ZenML gives youautomatic versioningGPU pinningany torch version
Good fitCustom training loops and research code that changes often.
Trade-offLarge CUDA images mean slower cold starts on remote stacks.
INSIDE THE @STEPTrain Keras models in any @step.ZenML snapshots your SavedModel automatically — no boilerplate.
tensorflow 2.16.1 · Keras 3
train_classifier.py
@step · zenml
import tensorflow as tffrom zenml import step @step(enable_cache=False)def train_classifier(    X_train: tf.Tensor, y_train: tf.Tensor) -> tf.keras.Model:    model = tf.keras.Sequential([        tf.keras.layers.Dense(128, activation='relu'),        tf.keras.layers.Dense(10, activation='softmax'),    ])    return model  # saved as SavedModel artifact
What ZenML gives youSavedModel artifactKeras 3 supportauto caching
Good fitProduction Keras models with a stable SavedModel format.
Trade-offHeavier dependency — version pinning matters across environments.
INSIDE THE @STEPFit any sklearn estimator in a @step.ZenML auto-pickles your model and registers it in the model registry.
scikit-learn 1.5.2
pipelines/training.py
@step · zenml
import pandas as pdfrom sklearn.ensemble import RandomForestClassifierfrom typing_extensions import Annotatedfrom zenml import ArtifactConfig, step @stepdef model_trainer(    dataset_trn: pd.DataFrame,) -> Annotated[RandomForestClassifier,               ArtifactConfig(is_model_artifact=True)]:    model = RandomForestClassifier()    model.fit(dataset_trn.drop('target', axis=1), dataset_trn['target'])    return model
What ZenML gives youpickle materializermodel registryArtifactConfig
Good fitTabular models where fast iteration beats raw scale.
Trade-offPickled estimators are Python-version sensitive across envs.
INSIDE THE @STEPReturn DataFrames from any @step.ZenML materializes your DataFrame as a versioned artifact — no manual saving.
pandas 2.2.2
steps/data_loader.py
@step · zenml
import pandas as pdfrom sklearn.datasets import load_breast_cancerfrom typing_extensions import Annotatedfrom zenml import step @stepdef data_loader(    random_state: int,) -> Annotated[pd.DataFrame, 'dataset']:    df = load_breast_cancer(as_frame=True).frame    df.reset_index(drop=True, inplace=True)    return df  # versioned DataFrame artifact
What ZenML gives youDataFrame artifactversioned by runlazy loading
Good fitFeature prep and ETL when the dataset fits in memory.
Trade-offIn-memory DataFrames strain on very large datasets.
INSIDE THE @STEPFine-tune any HF model in a @step.ZenML saves your model checkpoint as a versioned artifact on any cloud.
transformers 4.44.2 · PEFT 0.12
steps/finetune.py
@step · zenml
from transformers import AutoModelForCausalLMfrom peft import get_peft_model, LoraConfigfrom zenml import step @step(enable_cache=False)def finetune_step(    base_model_name: str, datasets_dir: str) -> str:    model = AutoModelForCausalLM.from_pretrained(base_model_name)    model = get_peft_model(model, LoraConfig(r=16, lora_alpha=32))    # trainer.train() — ZenML tracks the checkpoint    return datasets_dir
What ZenML gives youLoRA / PEFTcheckpoint artifactremote GPU stack
Good fitFine-tuning transformers and LLMs with PEFT or LoRA.
Trade-offCheckpoints are large — budget artifact-store space and transfer.
INSIDE THE @STEPTrain XGBoost models in a @step.ZenML registers your Booster as a model artifact with full lineage.
xgboost 2.1.1
steps/train_xgb.py
@step · zenml
import pandas as pdimport xgboost as xgbfrom zenml import step @stepdef train_xgb_model(    df_train: pd.DataFrame, label_col: str = 'target') -> xgb.Booster:    dtrain = xgb.DMatrix(        df_train.drop(columns=[label_col]), df_train[label_col]    )    return xgb.train({'max_depth': 6}, dtrain, num_boost_round=100)
What ZenML gives youBooster artifactfull lineagecache-aware
Good fitStrong tabular baselines with minimal tuning.
Trade-offBooster objects need the matching XGBoost version to reload.
INSIDE THE @STEPRun LightGBM training in a @step.ZenML saves your LGBMModel as an artifact and links it to the run.
lightgbm 4.5.0
steps/train_lgbm.py
@step · zenml
import lightgbm as lgbimport pandas as pdfrom zenml import step @stepdef train_lgbm(    df_train: pd.DataFrame, label: str = 'target') -> lgb.LGBMClassifier:    clf = lgb.LGBMClassifier(n_estimators=300, learning_rate=0.05)    clf.fit(df_train.drop(columns=[label]), df_train[label])    return clf
What ZenML gives yousklearn APIGPU treesversioned model
Good fitFast gradient boosting on wide tabular data.
Trade-offGPU builds need extra setup in the orchestrator image.
INSIDE THE @STEPPass ndarrays between @steps.ZenML serializes NumPy arrays automatically — share them across steps.
numpy 2.1.0
steps/preprocess.py
@step · zenml
import numpy as npfrom typing_extensions import Annotatedfrom zenml import step @stepdef normalize_features(    X_raw: np.ndarray,) -> tuple[    Annotated[np.ndarray, 'X_norm'],    Annotated[np.ndarray, 'mean'],]:    mean = X_raw.mean(axis=0)    return (X_raw - mean) / X_raw.std(axis=0), mean
What ZenML gives youndarray artifacttuple outputscontent-hashed
Good fitPassing numerical arrays cleanly between steps.
Trade-offRaw ndarrays carry no schema — annotate outputs for clarity.
INSIDE THE @STEPUse Polars DataFrames in a @step.ZenML materializes Polars DataFrames — fast ETL without Spark overhead.
polars 1.9.0
steps/feature_eng.py
@step · zenml
import polars as plfrom typing_extensions import Annotatedfrom zenml import step @stepdef build_features(    raw_path: str,) -> Annotated[pl.DataFrame, 'features']:    return (        pl.scan_parquet(raw_path)        .filter(pl.col('value') > 0)        .collect()    )
What ZenML gives youParquet artifactlazy executionfast ETL
Good fitLarge ETL that's too big for pandas, too small for Spark.
Trade-offNewer ecosystem — fewer integrations than pandas.
INSIDE THE @STEPLog experiments to W&B from a @step.ZenML connects your stack's experiment tracker — one decorator, full lineage.
wandb 0.18.3
steps/train_with_tracking.py
@step · zenml
import wandbfrom zenml import stepfrom zenml.integrations.wandb.flavors import WandbExperimentTrackerSettings @step(    experiment_tracker='wandb_tracker',    settings={'experiment_tracker.wandb':        WandbExperimentTrackerSettings(tags=['training', 'v2'])})def train_and_log(X_train, y_train) -> float:    wandb.log({'loss': 0.42, 'accuracy': 0.91})    return 0.91
What ZenML gives youexperiment trackersweep supportrun linking
Good fitRich experiment tracking and sweep visualization.
Trade-offAdds an external service and API key to manage.
1f42d62dproduction-gpu-poolHIGH LOAD
GPUs75%
6/ 8
CPU (Cores)75%
18/ 24
Memory (GB)75%
48/ 64
Parallel Pipelines100%
21/ 21
Parallel Steps68%
68/ 100
A10050%
4/ 8
Active Jobs
step_0321f42d62d
pipeline_032 #0045
2 GPUs · 4 CPU · +1
19s
CRITICAL
step_0334e7a34bc
pipeline_032
4 GPUs · 8 CPU · +1
45s
HIGH
step_0348c5e1abc
pipeline_032
1 GPU · 16 GB · +1
59s
MEDIUM
CONNECTEDkubernetesdockeraws_ec2google_cloudazure
5 active

Trusted by teams shipping ML pipelines and AI agents

AXA
JetBrains
ADEO
Leroy Merlin
Brevo
Safran
AECOM
Airbus Defence & Space
Rohlik
Knuspr
Maven Robotics
CrossScreen Media
GEMA
Homa Games
Koble
IKEA
Sciemo
Vodafone
Stepstone
Neara
Rivian
Happening XYZ
Veridas
AXA
JetBrains
ADEO
Leroy Merlin
Brevo
Safran
AECOM
Airbus Defence & Space
Rohlik
Knuspr
Maven Robotics
CrossScreen Media
GEMA
Homa Games
Koble
IKEA
Sciemo
Vodafone
Stepstone
Neara
Rivian
Happening XYZ
Veridas
Two products · one team

Agent systems today. ML systems all along. One engineering team underneath both.

Kitaru — Agent Evals

Turn agent failures into regression tests.

Your agent’s real traces become frozen, replayable worlds. Score what happened, replay your real code against it, and keep every fix as a regression test. Self-hosted, framework-agnostic, no lock-in.

  • Score thousands of traces — the agent never runs
  • Replay with one thing changed — model, tool, or prompt
  • Every fix becomes a regression test that guards CI
Explore Kitaru

ZenML — ML/AI Orchestration

The open-source platform for production ML systems.

Orchestrate workflows across your existing tools, clouds, and environments. Modular, agnostic, no lock-in.

  • Pipelines and stacks across any cloud
  • Model registry, lineage, and reproducibility built in
  • Open source — your stack, your data, your governance
Explore ZenML

The platform advantage

One foundation. ML pipelines and AI agents.

78%

faster time‑to‑market

65%

reduced engineering overhead

3x

more workflows in production

5x

faster time to production

Unified workflow orchestration dashboard showing ML and agent runs
Artifact and checkpoint versioning view
Infrastructure abstraction across clouds
Smart caching and deduplication across runs
Governance and security dashboard

Your stack, not ours

Run in your VPC, point at your object store, train on your clusters. The platform is a metadata layer — your artifacts, prompts, and code stay inside your infrastructure end to end. No lock-in on either side.

From local prototype to production

Stop rewriting code to move between environments. The same pipeline step or agent flow runs locally for debugging and on Kubernetes for production — without changing your logic. The platform handles the wiring.

Lineage and replay across both workspaces

Every execution is recorded and every artifact version is tracked in the same metadata store. When something breaks, replay the exact recorded run to reproduce it — and the fix becomes a regression test that guards against it coming back.

Open source, enterprise ready

Apache 2.0 from day one, with thousands of teams running it in production. Self-host forever, or adopt the managed control plane when you need governance, SSO, and an SLA. SOC2 and ISO 27001 certified.

Pick your workspace and start shipping.

Open source at the core. ML pipelines, agent flows, or both — same plans, same control plane.

Works with the tools you already use

60+ integrations across the AI ecosystem — from scikit-learn to LangGraph, PyTorch to OpenAI Agents SDK.

Apache AirflowAmazon S3ArgillaAutoGenAWSAWS StrandsMicrosoft AzureAzure Blob StorageAzure Container RegistryAzureML PipelinesBentoMLCometCrewAIDatabricksDatabricks DeploymentDeepchecksDiscordDockerElastic Container RegistryEvidentlyFacetsFeastGoogle Cloud Vertex AI PipelinesGithub ActionsGitHub Container RegistryGoogle ADK AgentGoogle Artifact RegistryGoogle CloudGoogle Cloud Storage (GCS)Great ExpectationsHaystackHugging FaceHugging Face (Inference Endpoints)HyperAIKanikoKubeflowKubernetesLabel StudioLangChainLangGraphLightGBMLightning AILlamaIndexMLflowModalNeptuneNeuralProphetOpenAI Agents SDKPigeonPillowProdigyPydanticAIPyTorchPyTorch LightningSagemaker PipelinesSeldonSemantic Kernelscikit-learn (sklearn)Skypilot VMSlackTektonTensorBoardTensorFlowWeights & BiasesWhyLabs whylogsXGBoost

Whitepaper

ZenML as your Enterprise-Grade AI Platform

We have put down our expertise around building production-ready, scalable AI platforms, building on insights from our top customers.

Customer Stories

How engineering teams cut time-to-production and simplify their AI infrastructure.

Track production ML and AI deployments across the industry

See the LLMOps database →

HashiCorp
ZenML offers the capability to build end-to-end ML workflows that seamlessly integrate with various components of the ML stack. This enables teams to accelerate their time to market by bridging the gap between data scientists and engineers.
Harold Gimenez

Harold Gimenez

SVP R&D at HashiCorp

Salesforce
ZenML allows orchestrating ML pipelines independent of any infrastructure or tooling choices. ML teams can free their minds of tooling FOMO from the fast-moving MLOps space, with the simple and extensible ZenML interface.
Richard Socher

Richard Socher

Former Chief Scientist Salesforce and Founder of You.com

ADEO
ZenML allowed us a fast transition between dev to prod. It's no longer the big fish eating the small fish – it's the fast fish eating the slow fish.
François Serra

François Serra

ML Engineer / ML Ops / ML Solution architect at ADEO Services

Stanford University
Many teams still struggle with managing models, datasets, code, and monitoring as they deploy ML models into production. ZenML provides a solid toolkit for making that easy in the Python ML world.
Chris Manning

Chris Manning

Professor of Linguistics and CS at Stanford

WiseTech Global
Thanks to ZenML we've set up a pipeline where before we had only Jupyter notebooks. It helped us tremendously with data and model versioning.
Francesco Pudda

Francesco Pudda

Machine Learning Engineer at WiseTech Global

MadeWithML
ZenML allows you to quickly and responsibly go from POC to production ML systems while enabling reproducibility, flexibility, and above all, sanity.
Goku Mohandas

Goku Mohandas

Founder of MadeWithML

No compliance headaches

Your VPC, your data

ZenML is a metadata layer on top of your existing infrastructure, meaning all data and compute stays on your side.

ZenML architecture — metadata layer on top of your infrastructure
SOC2 Type II certifiedISO 27001 certified

ZenML is SOC2 and ISO 27001 Compliant

We Take Security Seriously

ZenML is SOC2 and ISO 27001 compliant, validating our adherence to industry-leading standards for data security, availability, and confidentiality in our ongoing commitment to protecting your ML workflows and data.

Getting Ahead in Pipelines, Agents & Evals?

Subscribe to the ZenML newsletter and receive regular product updates, tutorials, examples, and more.

We care about your data in our privacy policy.

Support

Frequently asked questions

Everything you need to know about the product.

What is the difference between ZenML and other machine learning orchestrators?
ZenML doesn't take an opinion on the orchestration layer. Start writing locally, deploy on any orchestrator. We support many orchestrators natively and can be extended to work with custom orchestrators. Read more about how ZenML compares to orchestrators.
Does ZenML integrate with my MLOps stack?
Yes! ZenML supports Kubernetes, AWS, GCP Vertex AI, Kubeflow, Apache Airflow, and many more. Artifact, secrets, and container storage for all major cloud providers.
Does ZenML help in GenAI / LLMOps use-cases?
Yes, ZenML is fully compatible and intended for productionalizing LLM applications, and Kitaru extends this to evaluating and regression-testing live agents. We have examples with LlamaIndex, OpenAI, LangChain, and more. Check out our projects for real-world examples.
What is Kitaru?
Kitaru is ZenML's agent experimentation platform. It records your agents' real production runs as replayable executions. You can score a recording without ever running the agent again, or replay your real code against the recorded world — with one thing changed, like a model, tool, or prompt — to see what would have happened. Re-run a replay in CI and it becomes a regression test. Read more in the Kitaru docs.
How can I build my MLOps/LLMOps platform using ZenML?
Start simple with our user guides, then extend with experiment trackers, model deployers, model registries and more from the stack components library.
What is the difference between the open source and Pro product?
The core framework is Apache 2.0 on GitHub. Pro offers a managed version plus Pro-only features for scaling teams. Learn more on the comparison page.

Ship agents you can prove, and pipelines you can trust.

  • Open-source foundation, no vendor lock-in
  • Works with any infrastructure
  • Upgrade to managed Pro features
Dashboard displaying machine learning models with version tracking