Back to DeployOps overview
Specialization 2 · AI / LLM / MLOps

Same loop, probabilistic parts

A model changes the lifecycle in five ways: behavior depends on data and weights as well as code, correctness is statistical, failures are silent, cost is per request, and the loop closes through data. This page applies each overview stage to machine learning and LLM systems, builds on the DevOps foundation for the deterministic parts, and says plainly where determinism stops.

Each topic is argued from first principles: what it is, why it exists, and how it works. The lifecycle chips link back to the overview stage the topic specializes.

What changes when the system contains a model

Five differences from classical software reshape every stage of the delivery lifecycle.

What is it?

Classical delivery assumes the same input and the same code give the same output. A system with a learned model or an LLM breaks that assumption in five specific ways, and each one changes how you version, test, release and watch the system.

Why does it exist?

First, behavior is no longer code alone. It is code plus data plus trained weights plus prompts plus a retrieval index, and changing any one of them changes what the system does, so all five need versions. Second, correctness is statistical: a model is right 93% of the time, not "right", so a pass/fail unit test is replaced by an eval that measures a pass rate on a sample and compares it to a threshold. Third, failures are silent: a crashed service returns a 500, but a model that has drifted or a retriever that returns the wrong passage still returns 200 OK with a fluent, wrong answer, so health checks cannot see it and you must measure output quality directly. Fourth, cost is per request: every call spends GPU time or tokens, so load, prompt length and caching become budget lines, not just capacity concerns. Fifth, the loop closes through data: production traffic is the best source of new test cases and new training data, so the system improves (or decays) based on what it observes, and the observe stage feeds back into define and build.

How does it work?

  • Version all five inputs of behavior (code, data, weights, prompts, index) and record them together for every run and release.
  • Replace single assertions with eval suites: sample, score, compare a pass rate to a threshold, and report uncertainty.
  • Monitor output quality, not only availability: sample real traces, score them, and alert on drift in inputs and outputs.
  • Track cost and latency per request as first-class metrics next to correctness.
  • Route production traces back into the eval set and the training set so the next release starts from what actually happened.

Where it stops holding: Every stage can leak nondeterminism: unversioned data, unseeded training, batch-dependent kernels, silent provider changes and stochastic judges. The guarantee you can usually keep is statistical, not bitwise.

The nine lifecycle stages, read through an ML or LLM lens
StageWhat changes for ML/LLM systemsRepresentative technologiesWhere determinism breaks
DefineRequirements become measurable targets: a metric, a threshold, a cost and latency budget, and the failure cases that matter.Eval specs, golden datasets, error-analysis notesTargets are statistical; "correct" is a distribution over inputs, not a single expected output.
VersionCode is not enough: data, weights, prompts, configs and the index need versions that move together.Git, DVC, lakeFS, MLflow Registry, prompt registriesMutable upstream data, floating model aliases and unlogged seeds make a version name point at different behavior.
BuildBuilding means training or fine-tuning (or assembling prompts and an index), not just compiling.PyTorch, DDP/FSDP, Optuna, Ray Tune, embedding pipelinesFloat reductions are not associative, GPU kernels may be nondeterministic, and data order and seeds vary.
VerifyTests become evals: scored samples against thresholds, with human-calibrated judges.Golden sets, LLM-as-judge, regression suites, trace analysisOutputs are sampled and the judge is itself a stochastic model, so results need repeats and confidence intervals.
ReleasePromotion is moving a pointer (alias, prompt version, index alias) after the evals pass, with shadow or canary traffic.MLflow aliases, shadow traffic, eval-gated rolloutA pointer move does not recreate a model whose data or provider snapshot is gone.
DeployShip an immutable serving image with the right GPU stack, plus the model artifact and its config.Docker, NVIDIA Container Toolkit, vLLM, Triton, TF ServingDriver, CUDA and kernel versions change numerics; dynamic batching makes outputs depend on concurrent traffic.
OperateCapacity is tokens and GPU memory; scaling and caching decide cost, and guardrails bound behavior.Autoscaling, KV-cache management, prompt caching, guardrails, model routingLatency and output vary with batch composition and load; providers may change backends silently.
ObserveA 200 OK can be a wrong answer, so watch quality, drift, cost and the full chain of a request.Tracing, PSI/KS drift tests, online evals, feedback signalsGround truth arrives late and "drifted" is a threshold judgment, not a fact.
LearnProduction traces become new eval cases and training data, which restarts the loop.Trace-to-dataset pipelines, labeling queues, scheduled retrainingThe eval set ages as inputs shift; a score from last quarter measures last quarter's traffic.
Data versioningExperiment trackingEval harnessesTracingModel registries

Data version control

A trained model is a function of code and data, so the data needs a lockfile too.

LifecycleVersionBuild

What is it?

Pin the exact dataset a model was trained or evaluated on, with content hashes and snapshot ids, and keep lineage from inputs to output.

Why does it exist?

Retraining on "the same code" a month later gives a different model if the table underneath changed. Git cannot hold gigabytes, so the usual answer is to store the data in object storage and commit a small pointer file containing a content hash. Because the hash is computed from the bytes, two people who hold the same hash hold the same data, and a changed byte produces a different hash. Lineage then follows: output hash is determined by input hashes, code commit and parameters, so any model can be traced back to what made it, and an unchanged stage can be skipped.

How does it work?

  • Store large files in object storage and commit pointer files with content hashes next to the code.
  • Use snapshot or time-travel features of table formats so a query can ask for "the table as of version N".
  • Record input hashes, code commit and parameters for every pipeline stage so outputs are traceable.
  • Set a retention and deduplication policy up front, because every snapshot costs storage.

Where it stops holding: Mutable upstream sources, non-reproducible sampling or shuffles, and deletion requests (such as GDPR erasure) that conflict with immutable history all break the guarantee. Branching is cheap when it is metadata-only and expensive if objects are copied.

DVC (pointer files + remote cache)lakeFS (git-like branches over object storage)Delta Lake / Iceberg time travel
ReadDesigning Machine Learning SystemsPractical MLOpsReliable Machine Learning

Feature stores and training/serving skew

Define each feature once, and serve it identically to training and to production.

What is it?

A feature store holds feature definitions and values so the offline (training) path and the online (serving) path compute the same thing.

Why does it exist?

A model learns from numbers produced by a transformation. If training computes "average spend over 30 days" in a batch SQL job and serving recomputes it in application code, the two implementations will eventually disagree on rounding, time zones or null handling, and the model receives inputs it never saw. This is training/serving skew, and it is silent: nothing crashes, accuracy just drops. A second trap is time: if a training row for last March accidentally includes a value computed in April, the model learns from the future and looks great offline and bad live. Point-in-time-correct joins prevent this by fetching, for each row, only values known at that row's timestamp.

How does it work?

  • Write each transformation once and let the store materialize it to both an offline table and a low-latency online store.
  • Join training data with point-in-time semantics so no row sees values from after its timestamp.
  • Set a staleness (TTL) budget for online values and alert when it is exceeded.
  • Log the features actually sent at serving time and compare their distributions with training features.

Where it stops holding: Skew returns through duplicated transformation code, time-travel leakage and stale online values. A shared definition also costs flexibility: notebook-only features must be promoted before they can ship.

FeastTecton
ReadDesigning Machine Learning SystemsPractical MLOpsReliable Machine Learning

Experiment tracking

Every training run leaves a record complete enough to explain or redo it.

What is it?

Log parameters, metrics, artifacts, code commit, data hash, environment and seeds for every run.

Why does it exist?

Training is exploratory: you try dozens of variants, and the winner is only useful if you can say what it was. Without a record, "the good run from Tuesday" is folklore. A run is reproducible only if every input that affects the result is captured, so the tracker's job is to make the input list complete and attach it to the output. Logging too little leaves runs unreproducible; logging everything costs storage and discipline, so the practical rule is to log whatever would change the result.

How does it work?

  • Create a run per training attempt and log params, metrics over time, and output artifacts.
  • Attach the code commit, the data version and the environment (container digest or lockfile) to the run.
  • Record every random seed, including data shuffling and weight initialization.
  • Package the entry point with its environment so a teammate can re-run it with one command.

Where it stops holding: A tracked run is only as reproducible as its unlogged inputs: untracked data, unpinned drivers, and unseeded nondeterminism.

MLflow TrackingMLflow Projects (MLproject + conda or Docker env)
ReadPractical MLOpsDesigning Machine Learning SystemsReliable Machine Learning

Model registry

Immutable model versions, plus movable aliases that say which one is live.

What is it?

A registry stores each trained model as an immutable numbered version with lineage, and uses aliases to mark roles such as champion and challenger.

Why does it exist?

Software has the same idea: commits never change, branches and tags move. If you edited a model version in place, you could no longer say what was running last week. So the version is frozen and linked to the run that made it, and "production" becomes a pointer you move. That makes promotion and rollback one cheap, auditable operation. MLflow originally modelled this with fixed stages (Staging, Production, Archived); those stages were deprecated (since 2.9) in favor of aliases and tags, because a fixed list of stages is too rigid for real workflows. Load by alias, as in models:/name@alias, and the serving code never needs to change on promotion.

How does it work?

  • Register each candidate as a new immutable version linked to its run.
  • Use aliases (champion, challenger) for roles and tags for review state; do not use stages.
  • Promote by moving the alias after the eval gate passes, and log who moved it.
  • Roll back by repointing the alias to the previous version.

Where it stops holding: Aliases move without code review unless changes are gated or audited. Rollback also only works if the data and feature definitions behind the old version still exist; repointing cannot recreate a model whose inputs were lost.

MLflow Registry aliases (models:/name@alias)Tags for review state
ReadPractical MLOpsReliable Machine LearningDesigning Machine Learning Systems

Distributed training and memory

Split the work or split the model; each choice trades speed, memory and exactness.

LifecycleBuild

What is it?

Training that does not fit one GPU in time or memory is spread across devices using data, sharded-state, tensor or pipeline parallelism.

Why does it exist?

Start with what must live in GPU memory: parameters, gradients, optimizer state (often two extra copies per parameter for Adam) and activations saved for the backward pass. Data parallelism (DDP) puts a full copy of the model on every GPU, gives each a slice of the batch, then averages gradients across GPUs with an all-reduce so all copies stay identical. It is the fastest option per GPU but caps at what one GPU can hold. FSDP (and DeepSpeed ZeRO) shards parameters, gradients and optimizer state across GPUs and gathers a layer's weights just in time, which fits much larger models at the price of extra communication. Tensor parallelism splits individual layers across GPUs in a node; pipeline parallelism assigns consecutive layers to different devices and pays idle "bubbles" while the pipeline fills and drains. Activation checkpointing frees activations and recomputes them in the backward pass, roughly a third more compute for a large memory cut. Gradient accumulation sums gradients over several small batches to mimic a large batch.

How does it work?

  • Use DDP first if the model, optimizer state and activations fit on one GPU.
  • Move to FSDP or ZeRO when memory is the limit, optionally with CPU offload when you can pay in speed.
  • Add tensor or pipeline parallelism for models too large for one node, accepting the bubble and the topology constraints.
  • Turn on activation checkpointing and gradient accumulation to trade compute and time for memory and batch size.
  • Define "reproducible" first: seeded-reproducible means the same seed gives statistically similar training; bitwise-reproducible means identical bytes, which is stricter and slower.

Where it stops holding: Seeded runs are comparable but not identical, because floating-point addition is not associative: summing the same gradients in a different order gives slightly different results. NCCL all-reduce order can vary with topology and timing, and some CUDA kernels are nondeterministic. Bitwise reproducibility needs deterministic-algorithm flags and a fixed topology, and costs speed.

Parallelism strategies and what each trades away
StrategyWhat is splitGainCost
DDPThe batch; the model is fully replicatedHighest throughput per GPU; simpleEvery GPU holds the whole model, so it caps at one GPU's memory
FSDP / ZeROParameters, gradients and optimizer stateFits much larger modelsExtra all-gather and reduce-scatter communication
Pipeline parallelConsecutive groups of layers across devicesScales across nodesIdle bubbles waste utilization
Activation checkpointingNothing; activations are recomputed instead of storedLarge activation-memory reductionRoughly one third more compute
PyTorch DDPPyTorch FSDP / DeepSpeed ZeROMegatron-style tensor and pipeline parallelismNCCL

Hyperparameter optimization

Spend a limited compute budget on the settings most likely to pay off.

LifecycleBuildVerify

What is it?

Search the space of settings that are not learned (learning rate, depth, regularization) for the combination that gives the best validation result.

Why does it exist?

Each trial is a full training run, so budget is the constraint. Grid search tries every combination and grows exponentially with the number of settings. Random search samples the space instead, and works better for the same budget because usually only a few settings matter a lot, and random sampling tries many distinct values of each, while a grid repeats the same few. Bayesian methods (Gaussian-process or TPE surrogates with an acquisition function such as Expected Improvement) model past results and pick the next trial where improvement looks likely; they use samples well but are sequential, so they parallelize less well. Schedulers add early stopping: ASHA and Hyperband start many trials with a small budget and keep only the best performers at each rung, so weak trials end cheaply. Population-Based Training goes further and copies good trials into weak ones while mutating hyperparameters mid-run.

How does it work?

  • Define the search space with sensible scales (log scale for learning rate) and fix the metric and the budget.
  • Start with random search or TPE; add ASHA or Hyperband to cut weak trials early.
  • Use PBT when the best schedule changes during training, and accept that it yields a schedule, not a single config.
  • Log the search space, seeds, every trial and the final schedule in the experiment tracker.

Where it stops holding: PBT mutates hyperparameters mid-run, so the final model cannot be reproduced from a single config; only the logged schedule can. Early stopping can also kill slow starters that would have won.

Optuna (TPE)Ray Tune with ASHA / HyperbandPopulation-Based Training

Continuous training and drift

The world changes after you ship, so measure the change and retrain through a gate.

What is it?

Retrain on a schedule, on measured drift, or on a performance drop, and validate each candidate before it replaces the live model.

Why does it exist?

A model is a snapshot of the data it saw. When production inputs move away from that snapshot, predictions degrade without any code change. Covariate (data) drift means the input distribution P(x) changes, and you can detect it right away by comparing live inputs to training inputs with PSI (population stability index), the Kolmogorov-Smirnov test or Jensen-Shannon divergence. Concept drift means the relationship P(y|x) changes, so the same input now deserves a different answer; it is usually visible only through labels, which often arrive days or weeks late. Because thresholds are statistical, "drifted" is a judgment you tune, not a fact. Retraining too eagerly chases noise; too rarely ships a stale model.

How does it work?

  • Log serving inputs and predictions, and compare their distributions with a training reference window.
  • Pick a drift metric per feature type and tune thresholds against past incidents to limit false alarms.
  • Trigger retraining by schedule, drift alert or delayed-label performance drop.
  • Gate the candidate with champion/challenger evaluation before moving the alias.

Where it stops holding: Ground truth arrives late and thresholds are statistical, so detection lags and is a tuned judgment, not an assertion. Retraining also changes the model itself, so each cycle must pass the same gates as a new model.

PSI / KS testsPrediction-drift dashboardsChampion/challenger evaluation
ReadDesigning Machine Learning SystemsReliable Machine LearningPractical MLOps

Model serialization

A model file is either data or a program; treat unknown ones as programs.

What is it?

Save a trained model in a portable format so it can run outside its training environment.

Why does it exist?

Python's pickle (used by torch.load on .pt and .pkl files) can store arbitrary objects, and loading them runs the code that rebuilds those objects. That means loading an untrusted model file is the same as running an untrusted program. Formats that store only numbers avoid this: safetensors holds raw tensors plus a small header, with no code path, and can be memory-mapped for fast loading. The trade-off is that safetensors stores weights only, not the architecture, so you still need the model definition. Portable graph formats such as ONNX decouple a model from its training framework, but operator support differs between runtimes, so the numeric output is not guaranteed to match the original.

How does it work?

  • Prefer safetensors for weights, and ship the architecture definition separately.
  • If you must use torch.load, pass weights_only=True and only on files you trust.
  • Verify hashes or signatures of downloaded artifacts and allow-list third-party hub models.
  • When exporting to ONNX or another portable format, compare outputs with the original on a fixed test set and set a tolerance.

Where it stops holding: torch.load on an untrusted .pt or .pkl file can execute arbitrary code. Even safe formats do not guarantee numerically identical output across runtimes.

Common model formats compared
FormatWhat it storesCode-execution riskMain limitation
pickle (.pt / .pkl)Arbitrary Python objectsYes: loading can run codeUnsafe for untrusted files
safetensorsTensors and a small headerNoWeights only; architecture is separate
ONNXA portable compute graph and weightsNo by designOperator support and numerics differ between runtimes
SavedModelTensorFlow graph, weights and signaturesGraph ops can include custom opsTied to the TensorFlow ecosystem
safetensorsONNXSavedModelTorchScript (maintenance mode; torch.export is the successor)

Serving and GPU containers

Serve a model behind a stable contract, in an image whose GPU stack is pinned.

LifecycleDeployOperate

What is it?

Package the model and its runtime as an immutable image and choose between batch and online inference by latency need.

Why does it exist?

Batch inference runs offline over a large input set: it is throughput-oriented, cheap per item, and can be made idempotent by keying outputs on an input hash, but results are stale between runs. Online inference answers one request within a latency budget, so you pay for always-on GPUs. A GPU image depends on a stack that must line up: host driver, CUDA and cuDNN. Pin the base image by digest, not by tag, and match it to the driver; detect GPU architecture at runtime rather than hardcoding it. For LLMs, a request has two phases. Prefill processes the whole prompt in parallel and is compute-bound. Decode generates one token at a time, each needing the keys and values of all earlier tokens; those are stored in a KV cache so they are not recomputed, which makes decode memory-bandwidth-bound and makes the cache the main memory consumer. Continuous batching lets a finished sequence leave the batch and a new one join at the next step, keeping the GPU busy instead of waiting for the slowest request.

How does it work?

  • Pick batch or online per workload: batch for throughput and cost, online for latency with a stated SLO.
  • Build a multi-stage, digest-pinned image with the NVIDIA Container Toolkit, and keep layers cached.
  • For LLMs, use a server that manages the KV cache (paged allocation) and continuous batching.
  • Measure time to first token (prefill) and inter-token latency (decode) separately, under realistic concurrency.
  • Expose a stable API contract and version it independently of the model behind it.

Where it stops holding: Batch size changes numerics (different kernels and reduction orders), so with dynamic or continuous batching a request's output can depend on what else was in flight. Driver and kernel updates also shift results slightly.

Batch versus online inference
ModeOptimized forCost profileLimitation
BatchThroughput; idempotent by input hashBest throughput per dollarResults are stale between runs
OnlineLatency SLO per requestAlways-on GPUsNeeds headroom for traffic spikes
vLLMTriton Inference ServerTensorFlow ServingFastAPIDocker + NVIDIA Container ToolkitApache Airflow (batch)

Prompt versioning

A prompt is source code for a model, so version it and tie it to its evals.

What is it?

Store prompts with a content hash, named template variables and a link to the eval results that approved that exact text.

Why does it exist?

Changing one sentence in a prompt can change the behavior of the whole system, but a text change looks harmless in a deploy. If prompts live inside code strings or a dashboard with no history, you cannot say which wording produced last week's results. A content hash makes "which prompt" exact; named variables make the template testable; the link to eval results makes approval auditable. The model is part of the contract too: the same prompt behaves differently on different model snapshots, so a prompt version is only meaningful together with the model it was tested on.

How does it work?

  • Keep prompts in version control or a registry with history and review.
  • Use named template variables and validate that all are filled before sending.
  • Store the eval run id and model snapshot next to each prompt version.
  • Re-run the eval suite whenever the prompt or the model changes.

Where it stops holding: The prompt hash alone is not enough: the same text can behave differently across model snapshots, so pin the prompt and the model together.

GitPrompt registriesEval-run metadata

RAG and vector index versioning

The retrieval index is part of the model; version it and swap it like a database migration.

What is it?

Retrieval-augmented generation fetches passages by vector similarity and pastes them into the prompt. The index, the embedding model and the chunking rules together decide what the model sees.

Why does it exist?

An embedding model maps text to a point in a vector space, and "similar" means "close" in that specific space. Two different embedding models produce two unrelated spaces, so a query vector from model B compared to document vectors from model A gives meaningless distances. That is why changing the embedding model forces a full reindex: you must re-embed every document, not just new ones. Chunk size and overlap change which text each vector represents, so they are part of the version too. Because a reindex is slow and costly, build the new index next to the old one, test it, then switch an alias (a blue-green deploy), which also gives instant rollback at the price of double storage and embedding cost during the migration.

How does it work?

  • Record embedding model and version, chunking configuration and index version together.
  • Build the new index in parallel and evaluate retrieval quality (hit rate on a labeled query set) before cutover.
  • Swap an alias to the new index; keep the old one until the new one proves stable.
  • Evaluate retrieval and generation separately so you know which one failed.

Where it stops holding: Approximate nearest-neighbor search is approximate and can differ between index builds, so retrieval results are not bit-stable. Source documents also change, so the index goes stale unless refreshed.

Vector databases with alias swapEmbedding-model pinningRetrieval eval sets

Evals as tests

When outputs are probabilistic, the test is a measured pass rate, and the judge needs testing too.

What is it?

An eval runs the system on a set of cases, scores each output, and compares the aggregate to a threshold that gates a merge or a promotion.

Why does it exist?

A unit test asserts that output equals a fixed value. That fails for generated text, where many different outputs are acceptable and the same input can give different ones. So replace equality with a score, and replace one run with a sample: run N cases, compute a pass rate, and gate on it with an interval so you do not mistake noise for regression. Where do the cases and metrics come from? Start with manual error analysis of real traces, name the failures you actually see, turn them into metrics, then automate. Scoring open-ended text is often done by an LLM-as-judge, but a judge is itself a model: it has biases (preferring the first answer, longer answers, or its own style) and it is stochastic. So calibrate it against human labels on a sample, track the agreement rate, and recalibrate when the judge model or the rubric changes. Human labels remain ground truth; they just do not scale.

How does it work?

  • Collect real traces, label failures by hand, and group them into categories.
  • Build a golden set with those cases and a metric per failure category.
  • Use deterministic checks where possible (schema, exact fields) and an LLM judge for the rest.
  • Calibrate the judge against human labels and report agreement.
  • Gate merges and promotions on pass rates with confidence intervals, and run each case several times when outputs vary.

Where it stops holding: Outputs are probabilistic and the judge is nondeterministic too, so assert on pass rates with confidence intervals, not equality. A fixed eval set also goes stale as real inputs shift.

Golden datasetsLLM-as-judgeTrace analysisCI eval runners

Guardrails

Constrain what goes in and out of a model with checks you can test.

What is it?

Validate inputs and outputs, filter sensitive data, constrain structured output to a schema, and keep refusal tests in the suite.

Why does it exist?

A language model produces text by sampling the next token, so nothing inherently forces its output to be valid JSON, to avoid personal data, or to refuse a harmful request. In ordinary software you would put a schema and input validation at the boundary; guardrails are the same idea. Constrained decoding goes further by masking, at every step, the tokens that would violate the grammar, so the output must match the schema. Without it, the model can still break the schema or a tool-call contract, so you validate the result and retry rather than trust it. Every guardrail adds latency and false positives, so measure both.

How does it work?

  • Validate every model output against a schema before using it, and retry or fall back on failure.
  • Use schema-constrained decoding where the serving stack supports it.
  • Filter or redact sensitive data on the way in and out.
  • Keep a refusal and safety test set in the eval suite and run it on every change.

Where it stops holding: Without constrained decoding the model can still violate the schema; with it, the content inside a valid structure can still be wrong. Filters are statistical and miss cases.

Schema-constrained decodingPII filtersRefusal test setsOutput validators

Cost, latency and caching

Every request spends tokens, so budget them and reuse work where it is safe.

What is it?

Track tokens, time to first token, inter-token latency and tail latency, and cut cost with caching and routing.

Why does it exist?

Cost scales with tokens processed, and the prompt is paid for on every call. Latency has two parts: time to first token (dominated by prefill, which grows with prompt length) and time between tokens (decode). Averages hide the pain, so track p95 and p99. Caching reuses work. Exact-match caching returns a stored answer for an identical request. Provider or server prompt caching reuses the computed state for a repeated prefix, so put stable content (instructions, examples) first and variable content last. Semantic caching returns a stored answer for a similar request, which raises the hit rate but risks returning the answer to a different question. Routing sends easy requests to a smaller, cheaper model, which then needs its own evals.

How does it work?

  • Set token budgets per request and per tenant, and cap usage.
  • Order prompts with a stable prefix to benefit from prefix caching.
  • Use exact-match caching first; add semantic caching only with a measured false-hit rate.
  • Route by difficulty to smaller models, and evaluate each route separately.
  • Load-test at realistic concurrency, because latency changes under batching.

Where it stops holding: Usage is unbounded without caps, and latency depends on batch composition under load, so SLOs measured on an idle system do not hold in production.

Token budgetsPrompt cachingSemantic cachesModel routing

Model and provider pinning

Pin the exact model snapshot, and treat a version change as a release.

What is it?

Reference dated model snapshot ids instead of floating aliases, and run evals before changing them.

Why does it exist?

A floating alias such as "latest" is a pointer the provider moves, so your system's behavior can change with no change on your side, exactly like an unpinned dependency. A dated snapshot id fixes the target. Providers still retire old models, so pinning delays the work rather than removing it: when a snapshot is deprecated you must migrate, which means re-running the eval suite on the new model, comparing with shadow traffic, and only then cutting over.

How does it work?

  • Configure the exact snapshot id in one place and record it with each prompt version and eval run.
  • Track deprecation dates and schedule migrations before cutoff.
  • Run the full eval suite on the candidate model, then shadow live traffic and compare outputs.
  • Roll out behind a flag with an eval-based gate.

Where it stops holding: Silent backend changes can alter outputs even for a pinned id; some providers expose a fingerprint-style field that helps detect this, but support varies and should be verified in the provider docs.

Dated model snapshotsShadow trafficEval-gated rollout

Nondeterminism controls and their limits

Temperature 0 and seeds reduce variance but do not make an LLM bitwise repeatable.

LifecycleVerifyOperate

What is it?

Set sampling parameters to lower variance, then accept that the remaining guarantee is statistical.

Why does it exist?

Sampling is one source of randomness: at temperature above 0 the next token is drawn from a probability distribution. Temperature 0 means always pick the most likely token (greedy), which removes that source. But the probabilities themselves are computed with floating-point arithmetic, and floating-point addition is not associative, so summing the same numbers in a different order gives slightly different results. Which order is used depends on the kernel, and inference kernels are often chosen by batch size, so the same prompt processed alongside different concurrent requests can produce tiny numeric differences. When two tokens are nearly tied, a tiny difference flips the choice, and then the rest of the text diverges. Seed parameters, where offered, are generally best effort rather than a guarantee, and some newer models restrict which sampling parameters you can set (check each provider's docs).

How does it work?

  • Use temperature 0 (or the lowest allowed), a fixed seed where supported, and constrained top_p for tests.
  • Run important cases N times and gate on pass rate, not on a single run.
  • Compare outputs semantically or by schema and key fields, not by exact string equality.
  • Record the sampling parameters and model snapshot with each run.

Where it stops holding: Greedy decoding is still not bitwise deterministic because of batch-dependent kernels and floating-point non-associativity. Lower temperature also reduces variety, which can hurt creative tasks. The practical standard is statistical.

temperature=0seed (best effort)N-run pass-rate thresholds

The agent harness

The runtime around an LLM agent decides what it can see, do and be held to.

What is it?

An agent harness is the software that wraps a model in a loop: it holds state, exposes tools, enforces permissions, hands work between agents and emits telemetry. (This is not an eval harness, which is test scaffolding that scores outputs.)

Why does it exist?

A model only maps text to text. To act, something must call it repeatedly, parse its requested actions, run them, and feed results back. That loop and everything around it is the harness, and most reliability comes from it rather than from the model. State: the context window is the model's only memory, so the harness decides what to keep, summarize or fetch, and where durable state lives so a crashed run can resume. Tools: each tool is an interface the model can misuse, so define narrow, typed inputs and validate them. Identity and capability boundaries: the agent should act with the least authority the task needs, with credentials scoped per task, because anything it can do, a mistaken or manipulated agent can do (prompt injection through retrieved text is the classic route). Handoff: when work passes between agents or to a human, the contract must say what context transfers and who owns the next step. Telemetry: log each step, tool call and decision as a trace so a failure can be replayed and analyzed.

How does it work?

  • Persist state outside the context window and make each step resumable.
  • Give tools typed schemas, validate arguments, and require approval for irreversible actions.
  • Scope credentials and permissions per task and treat retrieved or user text as untrusted.
  • Define explicit handoff payloads between agents and to humans.
  • Emit structured traces for every step, and cap steps, time and spend so loops terminate.

Where it stops holding: The loop compounds small error rates over many steps and the model's choices vary run to run, so end-to-end behavior is statistical. Prompt injection can bypass intent unless capability limits are enforced outside the model.

Tool schemasSandboxed executionScoped credentialsTracingStep and budget limits

The data flywheel

Production traces become new eval cases and training data, and drift ages yesterday's eval.

What is it?

Capture real requests and outcomes, label the informative ones, and feed them back into the eval set and the training or prompt-tuning data.

Why does it exist?

Whatever test set you wrote before launch reflects what you imagined users would do. Real traffic shows what they actually do, including the failures you did not predict. So the best new test cases are production failures: capture traces, sample and label them (with users' feedback, human review or a calibrated judge), and add them to the golden set so the same failure cannot return unnoticed. The same labeled data can improve the system, as fine-tuning data or as better examples and retrieval content. The loop only works if it is maintained: inputs drift, so an eval set frozen last quarter measures last quarter's traffic, and a passing score can mean the test is stale rather than the system is good. Refresh the set on a schedule and track the share of recent production cases in it.

How does it work?

  • Store traces with inputs, outputs, tool calls and user feedback, with sensitive data redacted.
  • Sample for review: failures, low-confidence cases and a random slice to avoid bias.
  • Label and add the cases to the eval set with a version and date.
  • Use the labeled data for fine-tuning or prompt improvements, and re-run evals to confirm the gain.
  • Compare the eval set's distribution with recent traffic and refresh when they diverge.

Where it stops holding: Feedback is biased toward users who respond, labels are noisy, and training on the system's own outputs can reinforce its errors. Privacy rules limit what traces you can keep and reuse.

Trace storesLabeling queuesFeedback signalsDataset versioning
Reading path

What to read for AI/LLM/MLOps

  1. Step 0

    Foundations: observability

    Shared foundation

    You can now explain: Why monitoring known failures is not enough, and how wide, queryable telemetry lets you debug a system you did not plan for.

  2. Step 3

    MLOps bridge: the lifecycle before LLMs

    AI / LLM / MLOps

    Reliable Machine LearningDesigning Machine Learning SystemsPractical MLOps

    You can now explain: How data, training, deployment and drift monitoring extend the software lifecycle, and where reliability engineering meets ML.

  3. Step 4

    Building AI applications

    AI / LLM / MLOps

    You can now explain: How to choose between prompting, RAG and finetuning, and what it takes to run an LLM application in production.

  4. Step 5

    Evals: the CI gate for probabilistic systems

    AI / LLM / MLOps

    You can now explain: How to turn failures seen in real traces into metrics, and how to check that an LLM-as-judge deserves trust.

  5. Step 6

    Serving and optimization

    AI / LLM / MLOps

    You can now explain: Why serving cost and latency come from prefill and decode, and which optimizations buy throughput at what cost.

  6. Step 7

    Agents: the harness around the model

    AI / LLM / MLOps

    You can now explain: Why the runtime around an agent, not the model alone, determines reliability, and how boundaries and telemetry fit in.

Vocabulary

Key terms

Key terms (54)Plain-English definitions of the vocabulary used on this page.
Artifact
A file or bundle that a build step produces and later steps consume, such as a container image or a trained model file. Treating it as a fixed, named output lets every later stage work on exactly the same thing.
Blast radius
How much of a system or how many users are affected if a change goes wrong. Releasing to a small slice first keeps the blast radius small.
Determinism
The same inputs always produce the same output. Many real systems are only partly deterministic, for example because of timestamps, random numbers or parallel timing.
Drift
A slow, often unnoticed gap that opens between how a system was set up or tested and how it behaves now. It can come from changed configuration, changed data or a changed world.
Feedback loop
A path that carries what happened in later stages (failures, measurements, user behavior) back to earlier decisions. Shorter loops let you find and fix mistakes sooner.
Gate
A checkpoint that a change must pass before moving to the next stage, such as passing tests or an approval. If the check fails, the change stops there.
Idempotent
An operation that leaves the system in the same state whether you run it once or several times. This makes retries and re-runs safe.
Immutable
Something that is never edited after it is created; a change means creating a new version instead. That way a given version always means the same thing.
Invariant
A condition that must stay true no matter what else changes, for example "production only runs images that passed tests". Each stage of a process exists to protect some invariant.
Lifecycle
The ordered stages a change passes through from idea to running software and back to lessons learned. Seeing the whole sequence helps you place where a given problem arises.
Reproducibility
The ability to rebuild or rerun something later and get the same result, using recorded inputs and steps. It is what lets you debug the past and trust the present.
Activation checkpointing
A way to save GPU memory during training by not storing some intermediate values and recomputing them when needed. It trades extra computation time for lower memory use.
Agent harness
The code around a language model that runs it in a loop: it supplies tools, passes along results, manages context and decides when to stop. The model supplies the reasoning, and the harness supplies the surrounding machinery.
All-reduce
A communication step where every GPU contributes its values, such as gradients, and every GPU receives the combined result, for example the sum or average. It is how GPUs keep their copies of a model in agreement.
ASHA
Asynchronous Successive Halving: a tuning method that starts many configurations, repeatedly stops the worst performers early, and gives the survivors more training. Being asynchronous means workers do not wait for each other.
Champion/challenger
Comparing a new candidate model (the challenger) against the one currently in use (the champion) on the same data or traffic. The challenger replaces the champion only if it does better.
Concept drift
The relationship between inputs and the correct answer changes over time, so a model that was right before becomes wrong even if inputs look similar. For example, what counts as fraud shifts as fraudsters change tactics.
Constrained decoding
Limiting which tokens a language model may produce at each step so the output follows a required format, such as valid JSON. It makes structured output reliable rather than just likely.
Continuous batching
A serving technique that lets new requests join a running group of requests, and finished ones leave, at each generation step. It keeps the GPU busy instead of waiting for the slowest request in a fixed batch.
Covariate drift
The kind of data a model receives changes compared with what it was trained on, even if the underlying rule linking inputs to answers has not changed. Also called data drift or input drift.
Data flywheel
A cycle where using a product produces data, that data improves the model, and the better model attracts more use. It only works if you actually collect, filter and feed back the useful data.
DDP
Distributed Data Parallel: each GPU holds a full copy of the model and trains on a different slice of the data, then the GPUs average their gradients. It is simple and common, but each GPU must fit the whole model.
Decode
The phase where a language model generates its answer one token at a time, each step depending on the previous ones. It is usually limited by memory speed rather than raw computation.
Embedding
A list of numbers that represents the meaning of a piece of text, image or other item, so that similar items get similar numbers. It lets software compare meaning by measuring distance.
FSDP
Fully Sharded Data Parallel: like data parallel training, but the model, gradients and optimizer state are split across GPUs and gathered only when needed. This lets models too large for one GPU be trained.
Golden set
A curated collection of example inputs with trusted expected outputs or ratings, used to check a model or prompt after every change. It acts like a regression test for AI behavior.
Gradient accumulation
Running several small batches and adding up their gradients before updating the model once. It imitates a larger batch size when memory cannot hold one.
Hyperband
A tuning method that tries many settings with a small training budget, keeps the promising ones and gives them more budget. It spends little on settings that look poor early.
Inter-token latency
The average time between one generated token and the next while a model is streaming its answer. It determines how fast the text appears to flow to the reader.
KS test
Kolmogorov-Smirnov test: a statistical test that compares two sets of numeric values and measures how different their distributions are. It is often used to flag that live data no longer resembles training data.
KV cache
Stored intermediate values (keys and values) from tokens a language model has already processed, so it does not recompute them for each new token. It speeds up generation but uses significant GPU memory that grows with context length.
Lineage
A record of where a model or dataset came from: which data, code, settings and earlier versions produced it. It lets you trace a bad result back to its cause.
LLM-as-judge
Using a language model to grade another model's outputs against a rubric. It is cheaper than human review but can be biased or inconsistent, so it is usually checked against human ratings.
MLflow alias
A movable name, such as "production", that points to a specific registered model version in MLflow's model registry. Moving the alias promotes or rolls back a model without changing the code that loads it.
ONNX
Open Neural Network Exchange: a common file format for trained models that lets a model built in one framework run in other tools and runtimes. Not every model feature converts cleanly.
Pickle (code-execution risk)
Python's built-in way of saving objects to a file. Loading a pickle file can run arbitrary code hidden in it, so only load pickles from sources you fully trust.
Pipeline parallelism
Splitting a model into consecutive chunks of layers, each on a different GPU, and passing data through them like an assembly line. Splitting each batch into smaller pieces keeps the GPUs from sitting idle.
Point-in-time-correct join
Combining data so each training example only uses information that was known at that example's timestamp. This avoids leaking future information into training, which would make a model look better than it really is.
Population-based training
Training many models at once and, during training, copying settings from the better ones to the worse ones with small random changes. It adjusts hyperparameters as training goes rather than fixing them upfront.
Prefill
The first phase of answering, where a language model reads the whole prompt in one pass before generating anything. It is usually limited by computation and determines how long you wait for the first token.
Prompt caching
Reusing the already-processed beginning of a prompt when later requests start with the same text. It lowers latency and often cost for repeated long instructions or documents.
PSI
Population Stability Index: a single number that summarizes how much a feature's distribution has shifted between two periods. Larger values mean larger shifts, and teams commonly pick their own alert thresholds.
Quantization
Storing a model's numbers with fewer bits, for example 8-bit or 4-bit instead of 16-bit. The model becomes smaller and faster, usually with some loss of accuracy.
RAG
Retrieval-Augmented Generation: looking up relevant documents first and giving them to the language model along with the question. It lets the model answer using information it was not trained on.
Safetensors
A file format for storing model weights that holds only numbers and cannot carry executable code. It is safer to load than pickle and is quick to read.
Semantic caching
Reusing a previous answer when a new question means roughly the same thing, found by comparing embeddings rather than exact text. It saves calls but can return a wrong answer if two questions only look similar.
Speculative decoding
A small, fast model guesses several upcoming tokens and the large model checks them all at once, keeping the ones it agrees with. Output quality stays the same while generation gets faster.
Surrogate model
A cheap model that predicts how an expensive experiment, such as a full training run, will turn out. Tuning methods use it to decide which settings to try next.
Tensor parallelism
Splitting the large math operations inside a single layer across several GPUs, which combine their partial results. It needs fast connections between the GPUs.
top_p
A text-generation setting that limits the model to the smallest set of most likely next tokens whose probabilities add up to p, then samples from that set. Lower values make output more predictable.
Training/serving skew
A mismatch between how data is prepared when a model is trained and how it is prepared when the model is used live. The model then sees inputs unlike what it learned from and performs worse than testing suggested.
TTFT
Time to first token: how long a user waits between sending a prompt and seeing the first piece of the answer. It is mostly driven by queueing and prefill.
Vector index
A data structure that finds the stored embeddings closest to a query embedding without checking every one. Many indexes are approximate, trading a little accuracy for much greater speed.
ZeRO
Zero Redundancy Optimizer: a family of techniques that splits optimizer state, gradients and optionally the weights across GPUs instead of copying them to each one. It reduces memory per GPU and is the idea behind FSDP-style training.