ML Ops and LLM Engineering Principles
M2 · P2 (announced) · Year 2 · Period 2
Contents
- Introduction and overview
- 0. Reminders: from notebook to system
- 1. Lifecycle: versioning data, features, models
- 2. CI/CD for ML: testing data and models
- 3. LLM serving: continuous batching, P99 latency
- 4. Costs: euros per million tokens
- 5. Evaluation in production: drift and monitoring
- 6. RAG Ops: freshness and continuous evaluation
- 7. Fine-tuning Ops: versioned LoRAs
- 8. Agents in production: tracing and guardrails
- 9. Operational security: secrets, sandbox, supply chain
- 10. Case study and green ops
- Exercises / Labs
- Projects and assessment
- References
Introduction and overview
| Module | ML Ops and LLM Engineering Principles |
| Code | M2 · P2 (announced) — official code not published (course description not published) |
| Team and workload | Instructor: master’s team · ~24 h · from notebook to system: versioning, testing, serving, monitoring, and making LLM-based applications profitable — RAG, fine-tuning, agents — with their latencies, their per-token costs, and their operational guardrails. |
| Status in the program | M2Both routes · preselected by both routes of the home page (Graph × LLM and AI systems) · M2 program being finalized |
Audience: an engineer who has a RAG or fine-tuning prototype in a notebook and must ship it.
Empirical first: each chapter starts from a concrete failure — a p95 latency of 4.2 s because the retriever is not batched, a cost multiplied by three after an unversioned prompt change — and only then names the practice that prevents it (tracing, evaluation harness, guardrails).
The module goes from notebook to system: how to version, serve, monitor and control the cost of LLM applications.
What you will be able to do
- Version data, features, and models with usable lineage (reproduce an experiment from six months ago);
- Build a CI/CD pipeline that tests data and models, not just code;
- Serve an LLM with continuous batching and reason in P99 latency rather than averages;
- Quantify the cost of an application in euros per million tokens and arbitrate between APIs, fine-tuning, and distillation;
- Detect and handle drift (data, predictions) with monitoring that alerts when appropriate;
- Operate a RAG system: index freshness, continuous evaluation, unanswered queries;
- Deploy versioned LoRA fine-tunings and traced agents with their guardrails;
- Secure the whole: secrets, sandbox, supply chain, and master the carbon footprint of your services.
Official objectives and syllabus
Official course description not published (S3/S4 catalog empty): as of the start of the 2026 academic year, the “LLGA 2 – Semestre 3” and “LLGA 2 – Semestre 4” tracks of the 2026-2027 SynapseS catalog still list no courses, and the master’s wiki indicates that the detailed M2 program is “under development” (period 2: “to be completed soon”).
The program below therefore reflects the master’s brochure (period 2), with an indicative volume of ~24 h; these course materials are calibrated to that volume, i.e. about two hours of work per chapter, labs included.
The following table maps each brochure theme to the course sections; the materials’ own additions are marked “going beyond the syllabus”.
| Brochure theme | Course section covering it |
|---|---|
| From notebook to system: lifecycle and versioning of data, features, and models | ch. 0 (reminders: what scaling up breaks) and ch. 1 (lineage, reproducibility, artifacts) |
| CI/CD for ML: testing data and models | ch. 2 |
| Serving LLMs: latencies and batching | ch. 3 (continuous batching, P99 latency) |
| Per-token costs: making an application profitable | ch. 4 (euros per million tokens; 4.2 break-even of API, fine-tuning, distillation) |
| Production monitoring: drift and monitoring | ch. 5 |
| RAG in production | ch. 6 (index freshness, continuous evaluation) |
| Fine-tuning in production | ch. 7 (versioned LoRA adapters, rollback) |
| Agents and operational guardrails | ch. 8 (agent tracing) and ch. 9 (operational security: secrets, sandbox, supply chain) |
| Going beyond the syllabus — case study of a complete architecture and green ops (carbon cost) | ch. 10 |
| Going beyond the syllabus — instrumentation labs (versioning, CI, serving, drift monitoring) | Exercises / Labs |
Where this course fits in the program
M2 module announced for period 2 and preselected by both routes of the home page (Graph × LLM and AI systems): it is the master’s “systems” course, designed for profiles aiming at engineering as much as research.
Prerequisites: Large Language Models provides the architecture and inference foundations instrumented here, and Deep Learning (P2 variant) raised the training-cost and serving questions now handled in production.
Downstream: this module directly arms the master’s cross-cutting project and the end-of-studies internship — ML/LLM engineer job interviews probe almost word for word its chapters (latency, costs, drift, RAG, agents). It also gives research profiles the vocabulary and constraints for their prototypes to become deployable.
Course outline
- Reminders: from notebook to system — why scaling up breaks the implicit assumptions of notebook work.
- Lifecycle: versioning data, features, models — lineage, reproducibility, and long-term artifact management.
- CI/CD for ML: testing data and models — extending continuous integration to datasets and model metrics.
- LLM serving: continuous batching, P99 latency — what happens between the prompt and the first token, and how to control it.
- Costs: euros per million tokens — modeling the total cost of a service and steering it.
- Evaluation in production: drift and monitoring — detecting that the world has changed under the model’s feet.
- RAG Ops: freshness and continuous evaluation — operating a living index and measuring answer quality over time.
- Fine-tuning Ops: versioned LoRAs — lightweight trainings, adapters managed like code, possible rollback.
- Agents in production: tracing and guardrails — trajectory observability, action limits, error budgets.
- Operational security: secrets, sandbox, supply chain — protecting the accesses, execution, and dependencies of an ML system.
- Case study and green ops — a complete architecture examined end to end, financial and carbon costs included.
- Exercises / Labs — instrumenting a mini-system: versioning, CI, serving, drift monitoring.
- Projects and assessment — solo and group project subjects, a written-exam proposal and a grading rubric.
- References — documented tools and reference texts of ML Ops.
Session plan
24 h in class (announced) = 8 sessions of 3 h. The SynapseS 2026–2027 sheet publishes no volume for this module; 24 h is the indicative volume quoted with the brochure programme in the fiche above, and also the M2 volume published for the Large Language Models course (CSC_53432_EP: 24 h, 2 ECTS); the session split below is our proposal for covering these notes in that time. Level 1 = foundations (reviews, definitions, first worked examples), 2 = core methods; the level never goes down from one session to the next.
| Session | Hours | Level | Content | Practice |
|---|---|---|---|---|
| 1 | 3 h | 1 | ch. 0 Reminders: from notebook to system; ch. 1 Lifecycle and versioning | Ex. 1 |
| 2 | 3 h | 2 | ch. 2 CI/CD for ML: data contracts, statistical and paired gates, canary and shadow | Ex. 2, 6 |
| 3 | 3 h | 2 | ch. 3.1–3.2 Continuous batching, KV cache (recall) and PagedAttention | Ex. 3 |
| 4 | 3 h | 2 | ch. 3.3–3.4 Latency SLOs, Little’s law, autoscaling; ch. 4 Costs per million tokens, break-even of API, fine-tuning and distillation | Ex. 7, 4 |
| 5 | 3 h | 2 | ch. 5 Evaluation in production: drift, monitoring, LLM judge | Ex. 8 |
| 6 | 3 h | 2 | ch. 6 RAG Ops; ch. 7 Fine-tuning Ops | Ex. 9, 10 |
| 7 | 3 h | 2 | ch. 8 Agents in production; ch. 9 Operational security | Ex. 11, 5 |
| 8 | 3 h | 2 | ch. 10 Case study and green ops | Ex. 12 |
Self-study, outside the 24 h: no chapter — every chapter is covered in class; the labs each need 1–3 h of personal work after their session, and the common-thread lab (Ex. 12) about 6 h spread over the module.
How to study this module
- Treat each chapter as a code review more than a course: what matters is the operational detail (alert thresholds, budgets, naming conventions), not the grand principles.
- Instrument from the first lab onward: measuring the latency and cost of your API calls builds the intuition that reading alone does not give.
- Keep a cost spreadsheet up to date throughout the module: it is the artifact that later serves in interviews and internships.
- Read the public post-mortems and incident reports of ML systems cited by the module: the essentials of the trade are learned from others’ failures.
- Connect with Large Language Models in every chapter: every ops decision (batching, quantization, caching) corresponds to an architecture mechanism you have already studied.
The LLGA perspective The LLGA master trains profiles able to do research and to put it into production; this module is the pivot of that promise. LLMs are treated here as what they have become in companies: components of a system, with their latencies, their costs, and their failure modes — and the reflexes acquired here (measure, version, monitor) apply equally well to the graph models and tabular pipelines encountered in the other modules.
0. Reminders: from notebook to system
A model that works in a notebook is not yet a service: it must be rebuilt, tested, served, watched and paid for. This chapter recalls what the earlier modules provide and explains what changes when a model becomes a component of a system.
Prerequisites (M1/M2 modules)
- LLMs (LLMs M2 module): architecture and training (ch. 2-3), SFT, LoRA and distillation (ch. 4), RLHF/DPO (ch. 5), inference and serving with vLLM (ch. 6), RAG (ch. 7), agents (ch. 8), evaluation and safety (ch. 9: judges 9.2, attacks 9.4) — this module extends ch. 6 into engineering;
- Deep Learning (M1): PyTorch training loops, GPU, checkpoints — the foundation of what must be industrialized;
- ML (M1): concentration (Hoeffding, Theorem 0.3), subgroup evaluation (4.8) and paired model comparison (4.9) — used in ch. 2 to build non-regression tests;
- Explainability, Security, Privacy of LLMs (ESP; M2 P2, co-requisite, optional): attacks (ch. 5) and defenses (ch. 6) in depth; the basics used here are in LLMs 9.4;
- RL (M1): bandits (ch. 4) for A/B tests and model routing;
- Refresher CS (M1): containers, queues, complexity — the system vocabulary of serving (ch. 3).
The founding observation. A model reaching 92% F1 in a notebook does not “work”: it works in an environment.
The founding paper of the field (Sculley et al., NeurIPS 2015) shows that ML technical debt lives not in the model but around it: data pipelines, glue code, dependency chains, configurations, and feedback loops — the model in production influences the data that will be used to retrain it.
The LLM does not abolish this debt; it adds three specificities: the model is non-deterministic, its input space is all of natural language (hence not boundable by a schema), and its unit cost (the token) is billed on every request.
Definition 0.1 — ML Ops The set of software, data, and systems engineering practices applied to the complete lifecycle of a machine learning system: development, testing, integration, deployment, monitoring, retraining. LLM engineering is the special case where the central component is a language model (API or open weights), typically within a RAG, a fine-tuning, or an agent.
| Classical software | Traditional ML | LLM application | |
|---|---|---|---|
| Unit of test | Function, API contract | Validation set, metric | Eval set + judge + prompt contracts |
| Visible regression | Exception, crash | Metric drop | Fuzzy degradation: style, hallucinations |
| Inputs | Typed, validatable | Tabular, schema | Natural language + unreliable documents |
| Execution cost | CPU, nearly zero | CPU/GPU inference | Billed tokens or dedicated GPUs, P99 latency |
| External dependency | Few | Batch data | Third-party API, vector index, agent tools |
Module thread Each chapter contributes one building block of the same application: an enterprise assistant over internal documents (Confluence/SharePoint), with RAG, guardrails, and a budget. Lab 12 assembles it. The references “LLMs module ch. N” point to the M2 module Large Language Models; “Graph ML ch. 10” to the GraphRAG of the M1 module Graph Machine and Deep Learning.
1. Lifecycle: versioning data, features, models
A business RAG assistant passes every demo, then fails on 12% of real queries in production; nobody can say which version of the chunking, the prompt or the index produced the demo results. This chapter makes every element of the system identifiable and replayable, so that such failures can be traced and measured.
What breaks Four mistakes recur across the module: prompts that are not versioned, no regression evaluation before a release (ch. 2), serving without token budgets or timeouts (ch. 3), and judging latency by its mean instead of its tail percentiles (ch. 3).
The ML lifecycle extends well beyond training: collection → labeling → features → training → evaluation → registry → deployment → monitoring → (re)training. Every arrow in this cycle is an artifact that must be identified immutably; failing that, no metric is reproducible — neither for debugging, nor for audits, nor in front of a client.
Definition 1.1 — Artifact and lineage An artifact is any object produced or consumed by the cycle: dataset, feature schema, weights, tokenizer, prompt template, vector index, configuration, container image. The lineage is the graph of artifact → artifact dependencies. A training is reproducible when code + data + configuration + environment are all identified by fingerprints (hashes) and replayable.
Proposition 1.2 — The four fingerprints A training run is reproducible only if one can replay the four components of Definition 1.1: code, data, configuration and environment.
In practice: git versions the code (commit hash), DVC versions the data and the weights (content fingerprint, the living file staying out of the repository), params.yaml links the two, and the container image digest (sha256, last table of this chapter) pins the environment: libraries, CUDA, drivers.
Every published metric references the four fingerprints — otherwise it is not a measurement, it is a rumor.
# Bulky data versioned by content (DVC) -- only the pointer goes into git
$ dvc add data/faq_2026.parquet # creates data/faq_2026.parquet.dvc (hash + size)
$ git add data/faq_2026.parquet.dvc && git commit -m "faq v3 (dedup + PII scrub)"
# Declarative pipeline: stages re-run if and only if their inputs change
# dvc.yaml
stages:
prepare:
cmd: python src/prepare.py data/raw/ data/train.parquet
deps: [data/raw/, src/prepare.py]
outs: [data/train.parquet]
finetune:
cmd: python src/train_lora.py --config params.yaml
deps: [data/train.parquet, src/train_lora.py, params.yaml]
outs: [artifacts/lora_faq_v3/]
evaluate:
cmd: python src/evaluate.py --run artifacts/lora_faq_v3/
deps: [artifacts/lora_faq_v3/, data/eval_v3.parquet]
metrics: [artifacts/lora_faq_v3/metrics.json:
{cache: false}]
$ dvc exp run # replays the chain, records the experiment
$ dvc metrics diff # compares v2 -> v31.1. Feature stores and registries
For tabular ML, a feature store (e.g. Feast) serves the same feature definition offline (training, batch) and online (serving, low-latency), eliminating training/inference skew — the sneakiest class of bug in industrial ML. For LLMs, the functional equivalent is twofold: the prompt cache (shared prefixes; cf. ch. 4) and the embedding bank of the RAG (ch. 6), whose schema (chunk type, embedding model, index version) must be managed like a feature: identified, versioned, replayable.
The model registry (MLflow, or a privatized hf_hub directory) records for each version: the four fingerprints (Proposition 1.2), the qualification metrics, the signature (inputs/outputs), and the status (staging / production / archived). A deployment never references “the latest training” but a frozen version of the registry.
LLM specificity: prompts and evals are artifacts In an LLM application, three families of artifacts add to code, data, configuration and environment: (i) prompt templates (versioned like code, with tests — ch. 2); (ii) evaluation sets (golden sets question → expected answer / relevant documents); (iii) the embedding models and index of the RAG, coupled: changing the embedding model invalidates the whole index (ch. 6). Forgetting to version a prompt is equivalent to not versioning the weights.
| Step | Artifact | Typical tool | Fingerprint |
|---|---|---|---|
| Data | train/eval sets | DVC, lakeFS | content hash |
| Features / embeddings | definitions, index | Feast, vector database | schema version |
| Training | run + weights + LoRA | MLflow, HF Hub | run id + commit |
| Evaluation | reports, metrics | MLflow, RAGAS | eval set hash |
| Deployment | container image | OCI registry | sha256 digest |
| Serving | logs, traces | OTel, Langfuse | model + prompt + index |
2. CI/CD for ML: testing data and models
The continuous integration of an ML system tests four layers, in order of increasing cost: the code, the data, the model, the infrastructure. The golden rule: make the pipeline fail as early as possible, at the cheapest level.
2.1. Data tests
Data is an interface like any other: one writes contracts for it. A data contract checks: the schema (types, columns, bounds), business invariants (allowed nulls, ranges), and statistical properties (duplication rate, distribution of a key column). Tools: Pandera (contracts in Python, executed in the pipeline), Great Expectations (declarative validation suites), quality servers integrated into platforms.
import pandera as pa
import pandas as pd
schema = pa.DataFrameSchema({
"question": pa.Column(str, pa.Check.str_length(3, 2000)),
"answer": pa.Column(str, pa.Check.str_length(1, 8000), nullable=True),
"lang": pa.Column(str, pa.Check.isin(["fr", "en"])),
"doc_ids": pa.Column(pa.List(str), pa.Check.list_length(1, 10)),
"n_tokens": pa.Column(int, pa.Check.ge(0), pa.Check.le(4096)),
})
def validate(df: pd.DataFrame) -> pd.DataFrame:
return schema.validate(df, lazy=True) # collects ALL errors2.2. Model tests — and prompt tests
Definition 2.1 — Model non-regression test Let a fixed qualification set D_{\text{qual}} and a metric m (F1, accuracy rate, refusal rate, mean cost). The candidate f_{\text{new}} replaces f_{\text{prod}} if m(f_{\text{new}}) \geq m(f_{\text{prod}}) - \tau, where \tau is the tolerance margin.
The subtle point: m is measured on a sample — deciding with a measured gap requires knowing when it is significant.
Proposition 2.2 — Decision threshold by concentration (Hoeffding) Let \ell be a loss bounded in [0,1], measured on n i.i.d. examples. With probability \geq 1-\delta (on both sides): \bigl|\;\mathbb{E}[\ell] - \widehat{\ell}_n\;\bigr| \;\leq\; \sqrt{\frac{\log(2/\delta)}{2n}}.
To decide a rollback at \delta = 0.05 on n = 1000 examples, the mean loss of one model is known to within \sqrt{\log 40 / 2000} \approx 0.043 . For the per-example difference of two models, which lies in [-1, 1], the bound doubles to \approx 0.086.
Two models whose mean losses differ by less than that are not distinguishable by this test: the observed gap may be sampling noise.
Hoeffding’s bound uses only the range of the loss; a paired test that uses the observed variance of the differences is usually much tighter.
Proof. The Hoeffding bound (Machine Learning, Theorem 0.3) applies to the n i.i.d. variables \ell_i \in [0,1]: \mathbb{P}\bigl(|\overline{\ell} - \mathbb{E}\ell| \geq t\bigr) \leq 2e^{-2nt^2}. Setting the right-hand side to \delta yields t = \sqrt{\log(2/\delta)/(2n)}.
To compare two models one applies it to the variable \ell_{\text{new}} - \ell_{\text{prod}}, bounded in [-1,1]: for a variable of range r, Hoeffding’s bound reads 2e^{-2nt^2/r^2}, so a range twice as large doubles t.
With n=1000 and \delta = 0.05: t \approx 0.043 for one model, t \approx 0.086 for the difference. ∎
Practical consequence A “+0.4% F1” measured on 500 examples proves nothing: the 95% confidence interval is on the order of ±4 points for a metric at 50%. CI gateways must therefore require a minimal gap (margin \tau and significance), under pain of “dancing on the noise” — a rollback here, a rollback there, and a slow drift of quality.
Hoeffding’s bound is a worst case. The gate actually run in CI pairs the two models item by item, because both models fail on the same hard items and that shared noise cancels in the difference.
Definition 2.3 — Paired non-regression gate Score both models on the same n qualification items and keep the per-item loss differences \delta_i = \ell_i(f_{\text{new}}) - \ell_i(f_{\text{prod}}), \qquad i = 1, \dots, n . With \bar\delta their mean and s_\delta their standard deviation, the candidate passes if the upper end of a one-sided 95 % interval stays below the margin: \bar\delta + 1.645\, \frac{s_\delta}{\sqrt n} \;\le\; \tau . For a metric that is not a per-item mean (F1) or is far from normal (judge win rate, RAGAS score), use instead the 95th percentile of the metric difference recomputed on 10^4 bootstrap resamples of the items.
Worked example — n = 1000 right/wrong items; the candidate loses 40 items that production gets right and wins 30 that production gets wrong.
- The differences are +1 on 40 items, -1 on 30 and 0 elsewhere, so \bar\delta = 0.010 and s_\delta \approx 0.265.
- The upper bound is 0.010 + 1.645 \times 0.0084 \approx 0.024; the bootstrap gives the same value.
- The one-sided Hoeffding bound for a difference would add 0.077 instead of 0.014: pairing is about five times tighter, because the 930 concordant items carry no noise.
- With \tau = 0.02 the candidate is held back. McNemar’s test on the 70 discordant items gives \chi^2 = 10^2/70 \approx 1.43 (p \approx 0.23): the regression is not proven, but neither is the claim that it stays under 2 points.
These are the paired comparisons of Machine Learning 4.9 and LLMs 9.6, the evaluation kit shared by every Year 2 project.
Model tests also come in slices: performance per language, per context length, per topic, per user category — a global aggregate can mask a localized drop (the subgroup evaluation of Machine Learning 4.8). For an LLM, prompt tests add on: output format (valid JSON, schema), refusal guarantees (forbidden phrases), determinism wrappers (temperature 0, seed) and an LLM judge as oracle (ch. 5) — the judge itself being versioned and tested on a golden set.
# .github/workflows/eval-gate.yml (excerpt) -- CPU gates first, GPU last
jobs:
unit: # 1. code: fast tests, no model
steps: [run: pytest tests/unit -x -q]
data: # 2. data: pandera contracts on the day's delta
steps: [run: python -m src.validate_delta --date $(date -d yesterday +%F)]
eval: # 3. model: golden set + judge + prompt contracts
steps:
- run: python -m src.eval_llm --model staging --golden data/eval_v3.parquet
- run: python -m src.gate --min-faithfulness 0.85 --tau 0.02 --paired bootstrap # Def. 2.3
deploy: # 4. progressive deployment (canary 5% -> 50% -> 100%)
needs: [unit, data, eval]
steps: [run: ./deploy.sh --canary 5]2.3. Continuous delivery: canary and shadow
The deployment of an ML component is progressive: shadow (the new model receives duplicated traffic, its output is logged but not served — zero risk, double cost), canary (5% of traffic, automatic rollback if the guard metrics — latency, error rate, negative feedback — degrade), then full rollout. Rollback must be a single button: the previous version (weights + prompt + index) stays deployable in one command, thanks to the registry of ch. 1.
3. LLM serving: continuous batching, P99 latency
A served LLM must answer many users at once, fast enough and at an acceptable cost per token. This chapter explains how a server batches requests, why the memory of the KV cache is the real limit, and how latency is specified and measured.
Reminder — LLMs module ch. 6 (inference)
Autoregressive decoding generates one token per forward pass; attention requires retaining the keys/values of all past tokens (KV cache); quantization reduces the weights and the KV; the per-token complexity grows with the context length. This chapter moves from “how it computes” to “how it holds a load”.
3.1. Batching: from one request to N
A GPU executes a decoding step in a time nearly independent of the number of sequences present in the batch (as long as memory and bandwidth allow): the operations are parallel and the GPU is memory-bound on the weights. Serving B sequences simultaneously therefore divides the cost per token by almost B — this is the economic argument for shared serving.
Naive batching (static batching) waits to have B requests, processes them as a block, and leaves the GPUs idle as soon as a short sequence finishes: yet a 20-token request ends quickly, and the batch becomes sparse.
Definition 3.1 — Continuous batching (Orca, OSDI 2022) Schedule serving at the iteration level rather than the request level: at every decoding step, finished sequences leave the batch and waiting requests enter it. The GPU no longer idles between two requests. Combined with selective batching (batched operations where possible, per-request loops elsewhere), Orca claims up to 36.9× the throughput of prior systems at equal latency.
# Continuous batching loop (simplified) -- the heart of every modern LLM server
running, queue = [], deque()
def step():
# 1) decoding: one iteration for ALL active sequences (a single GPU call)
outputs = model.forward_batch([s.tokens for s in running])
# 2) remove finished sequences (EOS or max length)
for s in [s for s in running if s.finished(outputs)]:
running.remove(s); s.finalize()
# 3) admit new requests as long as the KV memory allows
while queue and kv_pool.has_room(queue[0].prompt_len):
running.append(queue.popleft().prefill())
# 4) preemptive scheduling: if the KV saturates, evict (recompute/swap)3.2. Memory: the KV cache and PagedAttention
Recall 3.2 — KV-cache size (stated in LLMs, Proposition 2.4)
For a transformer with n_{\text{layers}} layers and n_{\text{kv}} key/value heads (GQA) of dimension d_{\text{head}}, in precision b bytes, the cache of a sequence of T tokens occupies: M_{\text{KV}} \;=\; 2 \cdot n_{\text{layers}} \cdot n_{\text{kv}} \cdot d_{\text{head}} \cdot b \cdot T \;\text{bytes}. The count is direct: each layer stores, for each token, one key and one value of n_{\text{kv}} d_{\text{head}} coefficients each, at b bytes per coefficient. Hence the factor 2, and only the K/V heads count, however many query heads there are.
Example (Llama-3-8B: n_{\text{layers}}=32, n_{\text{kv}}=8, d_{\text{head}}=128, fp16 b=2): 2\times32\times8\times128\times2 = 131,072 bytes ≈ 128 KiB per token, i.e. 1 GiB for 8,192 tokens — as much memory as the weights of a small quantized model.
Continuous batching therefore makes memory occupancy oscillate at every iteration (sequences entering/leaving). Allocating the KV as one maximal contiguous block (maximum output length reserved in advance) wastes.
vLLM (SOSP 2023) treats the KV cache like the virtual memory of an OS — pages of 16 tokens, allocated on demand, non-contiguous, shareable between sequences derived from the same prefix (system prompt shared by thousands of requests): waste reduced to ~4% versus more than 60% for static allocation, throughput 2-4× prior systems.
Ops consequence: the real capacity of a server is measured in “available KV tokens”, not in concurrent requests.
3.3. Latency: TTFT, TPOT, P99
Definition 3.3 — LLM serving SLO TTFT (time to first token): request → first token delay — dominated by the prefill (prompt) and the waiting queue. TPOT (time per output token): generation cadence — dominated by decoding and batch contention. P99: 99th percentile of a latency distribution — the relevant contractual commitment; the average masks queues. A typical interactive assistant SLO: TTFT P99 < 1.5 s, TPOT P99 < 80 ms/token.
Theorem 3.4 — Little’s law In a stable system in steady state, with L average clients present, \lambda clients arriving per second on average and W mean sojourn time:
Key result — Little’s law L \;=\; \lambda\, W.
Serving corollary: at an imposed generation cadence (W fixed by the number of tokens and the TPOT), the number of concurrent requests supported is L = \lambda W — the necessary concurrency grows linearly with the targeted throughput.
Proof. Proof by an output argument (direct). Over an interval [0, t], let A(t) be the number of arrivals and D(t) the number of departures. The mean number of clients present is written L_t = \frac{1}{t}\int_0^t N(u)\,du, where N(u) = present at instant u.
Each request contributes to this integral exactly its sojourn time: \int_0^t N(u)\,du = \sum_{i \,\text{departed by} \leq t} W_i + \text{(residuals of requests still present)}.
Dividing by t and letting t\to\infty (stability: A(t)/t \to \lambda, bounded residuals become negligible), one obtains L = \lambda\, W — the argument requires no independence or distribution assumption; it is purely accounting. ∎
Throughput versus latency: the batch trade-off Increasing B (target batch) improves throughput (amortized cost/token) but lengthens the waiting queue — hence the TTFT P99. Modern servers expose this trade-off: max_num_seqs, token budgets per step, and preemption policies (recompute vs swap). Rule of thumb: size the target batch so that KV occupancy stays below ~90%, and let the autoscaler (section 3.4) absorb spikes rather than growing the batch beyond the SLO.
3.4. Autoscaling
The relevant signal for scaling an LLM server is not CPU but: the waiting queue length (requests pending admission), the mean waiting time (TTFT − prefill time) and the KV occupancy rate. Horizontal autoscaling (HPA on a custom Prometheus metric) adds replicas; watch out for warm-up spikes (weight loading: tens of seconds to minutes — pre-pull the images, or pay the P99). For multi-GPU models (tensor parallelism, Deep Learning P2, ch. 2), each replica is a group of GPUs; scaling changes the granularity of parallelism, not just the number of pods.
# Launch a vLLM server exposing an (OpenAI-compatible) API on port 8000
$ docker run --gpus all -p 8000:8000 vllm/vllm-openai:v0.6 \
--model meta-llama/Meta-Llama-3-8B-Instruct \
--max-num-seqs 256 --gpu-memory-utilization 0.92 \
--enable-prefix-caching # reuse the KV of shared prefixes
# Async benchmark: TTFT / TPOT P50-P99 as a function of concurrency
import asyncio, json, time, httpx
async def one(client, prompt, n_out=256):
t0 = time.perf_counter(); first = None; n = 0
async with client.stream("POST", "/v1/chat/completions",
json={"model": "...", "messages": [{"role": "user", "content": prompt}],
"max_tokens": n_out, "stream": True}) as r:
async for line in r.aiter_lines():
if not line.startswith("data:") or line.strip() == "data: [DONE]":
continue
choices = json.loads(line[5:]).get("choices") or [{}]
if choices[0].get("delta", {}).get("content"): # skip the empty role chunk
if first is None:
first = time.perf_counter() - t0 # TTFT
n += 1 # content chunks (~ tokens)
if first is None or n < 2:
return first, None # no stream: count it as a failure
# after the first token there are n - 1 inter-token gaps, not n
return first, (time.perf_counter() - t0 - first) / (n - 1) # (TTFT, TPOT)
async def main(conc=64):
async with httpx.AsyncClient(base_url="http://localhost:8000",
timeout=300) as c:
res = await asyncio.gather(*[one(c, "Summarize this report in 5 points.")
for _ in range(conc)])
p = lambda x, q: sorted(x)[int(q * len(x))]
ttft = [r[0] for r in res if r[0] is not None]
tpot = [r[1] for r in res if r[1] is not None]
print(f"failed streams: {conc - len(tpot)}")
if not tpot:
return # every stream failed
print(f"conc={conc} TTFT p50/p99 = {p(ttft,.5):.2f}/{p(ttft,.99):.2f}s"
f" TPOT p99 = {p(tpot,.99)*1e3:.0f}ms")
for conc in (1, 4, 16, 64, 256): asyncio.run(main(conc))| Metric | Definition | Dominated by | Typical target (chat) |
|---|---|---|---|
| TTFT P50 / P99 | first token | prefill, queue | < 0.4 / < 1.5 s |
| TPOT P99 | ms/token afterwards | decoding, contention | < 80 ms |
| Throughput (tokens/s/GPU) | aggregated output | batch size, KV available | maximize under SLO |
| KV occupancy | % pages used | concurrency × length | < 90% |
| Rejected requests | timeouts / 429 | saturation, slow autoscale | ~0 |
4. Costs: euros per million tokens
Every LLM architecture decision sooner or later reduces to a cost per million tokens (Mt). APIs bill the input (prompt: amortizable by caching) and the output (generation: expensive) separately; the 2025-2026 orders of magnitude roughly span, from the small open-weight model to the frontier model, from a few cents to a few tens of euros per Mt — rates that change several times a year: any precise figure in course materials would be stale; the method of computation, on the other hand, remains.
Proposition 4.1 — Unit cost of self-hosted serving If a server delivers a sustained throughput of R tokens/s (outputs, continuous batching) on GPUs costing c €/h (rental all inclusive), the operational cost per million output tokens equals:
Key result — cost per million tokens \text{cost/Mt} \;=\; \frac{c}{R} \cdot \frac{10^6}{3600} \;\approx\; 278 \cdot \frac{c}{R} \;\; \text{€/Mt}.
Example: an 8B on a GPU at ~2 €/h delivering R = 2,000 tokens/s in batch → ≈ 0.28 €/Mt; the same GPU on isolated requests (batch 1, R \approx 60) → ≈ 9 €/Mt.
Batching is a factor of 30 on the bill.
Proof. The server produces R tokens per second; each billed hour at c € therefore produces 3600\,R tokens; the cost per token is c/(3600\,R) €, and per million tokens 10^6 c/(3600 R) = 277.8\, c/R . The direct division by R: doubling the throughput halves the cost per token at constant hardware — this is the economic justification of ch. 3. ∎
4.1. The saving levers
- Prefix cache: shared system prompt + history → the prefill is billed/consumed once; on the API side, prompt caching reduces the input cost of repeated prefixes (often billed at a discount); on the vLLM side, the KV is reused for free.
- Model routing (cascade): small model first, escalation to the big one if the small one “is not sure” (confidence score, agreement of two candidates, or a classifier). The sequential decision formalized as a bandit (cf. RL module ch. 4): one arbitrates cost vs quality request by request.
- Output limit: output costs ~5-10× the input; constraining
max_tokensand the format (answer in k points, short JSON) is the most underrated lever. - Quantization (cf. LLMs ch. 6; Advanced Topics ch. 7): 4 bits ≈ ×2-3 in throughput on the weights — and smaller GPUs.
- Deferred batch processing: non-interactive tasks (archive classification) go in nightly batches; some APIs bill these modes ~50% cheaper.
| Criterion | Managed API | Self-hosted (open weights) |
|---|---|---|
| Low-usage cost | Pay-per-token, optimal | Brutal GPU amortization |
| High-usage cost | Linear, becomes expensive | ~0.1-1 €/Mt depending on batch (prop. 4.1) |
| Sensitive data | Contract, jurisdiction, zero-retention to negotiate | Total control (closed network) |
| P99 latency | The provider’s | Controllable (ch. 3), at your own expense |
| Ability to fine-tune | Limited (hosted LoRAs depending on the offering) | Total (ch. 7) |
| Ops | Nearly zero | Team + on-call + GPU supply |
Multilingual and real costs The per-token bill interacts with tokenization (cf. LLMs module ch. 1): a French corpus typically costs 15-25% more tokens than an equivalent English corpus with the dominant tokenizers. The “price per word” therefore depends on the language — an argument for measuring (exercise 4) rather than trusting round conversion tables.
4.2. API, fine-tuning or distillation: the break-even volume
The table compares two ways of serving; the next question is which model to serve. An API model is paid per request. A model of your own, fine-tuned or distilled, adds a one-off training cost and a fixed GPU bill, then costs almost nothing per extra request. The methods are taught in LLMs 4.1 and 4.3; this section covers their economics.
Proposition 4.2 — Break-even volume An API request costs a € (its input and output tokens at their prices). An own model costs F € per month of reserved GPUs, a training cost C_{\text{train}} amortized over H months before retraining, and b < a € per request at the margin. Over a month of V requests, the own model is cheaper as soon as V \;\ge\; V^\star = \frac{F + C_{\text{train}}/H}{a - b}.
Proof. The two monthly bills, aV and F + C_{\text{train}}/H + bV, are affine in V with slopes a > b, so they cross once, at V^\star. Below it the API is cheaper; above it the own model is, provided its quality passes the gate of ch. 2. ∎
Worked example — 10 k requests a day (300 k a month), 1,500 input tokens (RAG context) and 300 output tokens, with illustrative API prices of 1 €/Mt in and 4 €/Mt out:
- API: a = 0.0015 + 0.0012 = 0.0027 € per request, i.e. 810 € a month.
- Fine-tuned 8B (LoRA, ch. 7) on one GPU at 2 €/h, always on: F \approx 1{,}460 € a month, C_{\text{train}} \approx 600 € (data curation and GPU) over H = 6 months, and b \approx 0, since one batched GPU serves millions of requests a month (Proposition 4.1). Then V^\star \approx 1{,}560 / 0.0027 \approx 580 k requests a month, about 19 k a day.
- Distilled 1B student on a small GPU at 0.6 €/h: F \approx 440 € a month and C_{\text{train}} \approx 370 € (100 k teacher answers bought from the API, plus training), over the same H = 6 months. Then V^\star \approx 500 / 0.0027 \approx 185 k requests a month, about 6 k a day.
- Verdict: at 10 k requests a day only the distilled student pays off, and only if it passes the paired gate (Definition 2.3) against the API model. A GPU idle at night still bills F: scaling it down off-peak moves V^\star as much as a change in token prices does.
5. Evaluation in production: drift and monitoring
A model that passed every test at release time degrades silently when the world it serves changes. This chapter defines the kinds of drift, gives the statistics that detect them, and organizes monitoring and LLM-based evaluation in production.
5.1. Data drift, concept drift
Definition 5.1 — Drift Data drift: the distribution of inputs changes (P(X) moves — new topics, new user population, new documentary source). Concept drift: the input → correct answer relation changes (P(Y\mid X) moves — yesterday’s “good” answer becomes wrong: regulation, company merger, vocabulary). For a RAG, a third drift: the corpus changes (ch. 6).
Proposition 5.2 — PSI = Jeffreys divergence The population stability index (PSI), summed over the bins i of a histogram: \mathrm{PSI}(p, q) \;=\; \sum_i \bigl(p_i - q_i\bigr)\, \ln\frac{p_i}{q_i} \;=\; \mathrm{KL}(p \,\|\, q) + \mathrm{KL}(q \,\|\, p), is exactly the Jeffreys divergence: symmetric, zero iff p = q.
Usual thresholds in credit risk: PSI < 0.1 stable; 0.1-0.25 to watch; > 0.25 alert.
Proof. Let us expand: \mathrm{KL}(p\|q) = \sum_i p_i \ln(p_i/q_i), \qquad \mathrm{KL}(q\|p) = \sum_i q_i \ln(q_i/p_i). The sum equals \sum_i (p_i - q_i)\ln(p_i/q_i) — precisely the PSI.
Each term is positive (the two factors p_i - q_i and \ln(p_i/q_i) have the same sign); zero if and only if p_i = q_i for all i. Symmetry is obvious from the summed form. ∎
The classic detectors: PSI/KL per feature (interpretable, bin by bin), the Kolmogorov–Smirnov test for continuous features, the MMD distance between feature distributions (Graph ML 9.6, which uses it to compare generated graphs), and stream detectors (ADWIN) for online signals.
Kolmogorov–Smirnov in three lines. Compare a reference window of n values with a current window of m values through the largest gap between their empirical distribution functions: D = \sup_x \bigl|\widehat F_{\text{ref}}(x) - \widehat F_{\text{cur}}(x)\bigr| . Under “same continuous law” and for large windows, D\sqrt{nm/(n+m)} approximately follows the Kolmogorov distribution, whose 95 % quantile is about 1.36: alert when D > 1.36\,\sqrt{(n+m)/(nm)}.
For an LLM, the “features” to monitor are: prompt length, language, topics (lightweight classifier), presence of injected documents, truncation rate — and output proxies: refusal rate, “I don’t know” rate, median answer length, JSON parse failure rate.
Without online labels, one monitors symptoms; the true metrics come from feedback and sampling.
5.2. Monitoring: three time loops
| Loop | Latency | Signals | Action |
|---|---|---|---|
| Reactive | second-minute | latency, 5xx errors, 429, KV saturation, cost/req | autoscale, throttle, rollback |
| Daily | day | PSI per feature, refusal rate, feedback 👎, sampled RAGAS eval | ticket, investigation, prompt fix |
| Strategic | week-month | concept drift (refreshed golden set eval), cost drift, adoption | retraining/fine-tune, model migration |
The metrics of an LLM application stack up: infra (GPU, memory); serving (ch. 3); quality (explicit feedback, sampled evals); safety (guardrail trigger rate, injection attempts — cf. ch. 8 and Explainability, Security, Privacy of LLMs, ch. 5).
The structural watchpoint: the feedback loop — the data collected in production (real questions, feedback) become tomorrow’s training data: the model steers the distribution it will later see (selection bias, rich-get-richer). Documenting this loop is part of the audit (ch. 9; group fairness in Machine Learning 4.8).
5.3. The LLM judge in CI and production
Judge: recall and ops consequences A judge model evaluates (question, answer) pairs against a rubric (fidelity to context, completeness, tone). Zheng et al. (MT-Bench, NeurIPS 2023) show agreement with humans > 80% on conversation tasks; its biases — position, verbosity, self-preference — and their mitigations are listed in LLMs 9.2.
Ops consequences: alternated order, explicit rubric, reference provided, human golden set to calibrate the judge, judge version frozen in CI (ch. 1). The judge is a noisy sensor — one uses it to detect regressions, not to certify.
# Mini regression judge in CI: compares prod vs candidate on the golden set
def judge_pair(question, ref, ans_prod, ans_cand, judge):
rub = "Expected answer (reference): {ref}\nA: {a}\nB: {b}\n" \
"Which one is more faithful to the reference? Answer A, B, or EQUAL."
first = judge(f"Q: {question}\n" + rub.format(ref=ref, a=ans_prod, b=ans_cand)).strip()
second = judge(f"Q: {question}\n" + rub.format(ref=ref, a=ans_cand, b=ans_prod)).strip()
# alternated order, anti-bias: the candidate wins only if it wins in both positions
if first.startswith("B") and second.startswith("A"):
return "cand"
if first.startswith("A") and second.startswith("B"):
return "prod"
return "equal" # EQUAL, or inconsistent verdicts (position bias)
# gate: no significant regression. "equal" verdicts carry no evidence either way,
# so test prod wins against candidate wins on the decisive pairs only (sign test,
# the logic of McNemar's test in section 2.2)
from scipy.stats import binomtest
verdicts = [judge_pair(**ex, judge=judge) for ex in golden]
w_cand, w_prod = verdicts.count("cand"), verdicts.count("prod")
n_dec = w_cand + w_prod
p = binomtest(w_prod, n_dec, 0.5, alternative="greater").pvalue if n_dec else 1.0
assert p >= 0.05, f"judge regression: prod {w_prod} vs cand {w_cand}, p = {p:.3f}"A fixed threshold on candidate wins alone would count every tie against the candidate: a candidate identical to production would get no wins and fail. The sign test ignores ties and fails only when production wins significantly more decisive pairs.
6. RAG Ops: freshness and continuous evaluation
A RAG system is only as good as its index, and the documents behind the index change every day. This chapter keeps the index fresh without rebuilding it, and evaluates answer quality continuously without a human in the loop.
Reminder — LLMs module ch. 7 (RAG)
Pipeline: chunking → embeddings → vector index → retrieval (hybrid BM25 + dense, cf. NLP ch. 2 and 7) → reranking → conditioned generation. This chapter treats that pipeline as a data system with freshness and evaluation contracts — not as a demo.
6.1. Incremental indexing and freshness
The enterprise corpus moves: Confluence pages edited, contracts signed, FAQs enriched. The index must follow with an explicit freshness SLA (“a published document is queryable within 15 minutes”). The standard pattern: change data capture (webhooks/API of the document repository) → event queue → incremental upsert into the index, key = (source, doc_id, version).
Subtleties:
- Idempotence: replaying the same event twice must create only one batch of vectors; deduplicate by key, mark obsolete chunks (soft delete) so as not to break the evaluation links;
- Per-document atomicity: the chunks of a document v2 replace those of v1 as a block (otherwise retrieval mixes the versions);
- Embeddings ↔︎ index coupling: changing the embedding model requires re-embedding the whole corpus (the spaces are mutually incompatible); dual-boot of the index during the migration, then switchover — the index is a versioned artifact in the sense of ch. 1;
- Freshness ≠ total reindexing: measure the cost (summary tokens, GPU time) and aim for incremental everywhere possible.
6.2. Evaluating a RAG without humans: RAGAS
Definition 6.1 — RAGAS metrics (Es et al., 2023, and the ragas library) Es et al. define three reference-free metrics, computed by LLM decomposition: faithfulness (is every claim of the answer attested by the context? — anti-hallucination), answer relevance (does the answer answer the question?) and context relevance (how much of the retrieved context is actually needed?).
The open-source ragas library adds two metrics that compare the retrieval with a ground-truth reference answer: context precision (are the relevant chunks ranked first?) and context recall (are the necessary chunks retrieved?).
The context metrics isolate the retrieval, faithfulness and answer relevance the generation: this is the diagnostic grid missing from a global F1.
from ragas import evaluate
from ragas.metrics import (faithfulness, answer_relevancy,
context_precision, context_recall)
from datasets import Dataset
ds = Dataset.from_dict({
"question": q_list, # golden set (ch. 1: version this set!)
"answer": answers, # outputs of the candidate pipeline
"contexts": retrieved_chunks,
"ground_truth": ref_answers, # reference: context_precision and context_recall
})
report = evaluate(ds, metrics=[faithfulness, answer_relevancy,
context_precision, context_recall])
# CI gate: faithfulness >= 0.85; weekly panel: the 4 time seriesReading a RAGAS report like an engineer Low recall, high faithfulness: retrieval is failing — improve chunking, hybrid, reranker. High recall, low faithfulness: the model invents despite the context — prompt, stronger model, forced citation. Low relevancy: off-topic or verbose answer. The trap of reference-free metrics: their judge has the biases of ch. 5 — one calibrates them on a hand-annotated sample before making them CI thresholds.
6.3. The graph to the rescue of RAG
Vector RAG fails on global questions (“what are the recurring themes of the 2025 reports?”) that require aggregating the whole corpus; GraphRAG (Edge et al., 2024; cf. Graph ML module ch. 10) builds an entity graph (LLM extraction), partitions it into communities (Leiden) and summarizes each community — the global answer becomes a map-reduce over the summaries.
The ops cost: LLM indexing is expensive (every document goes through extraction calls), hence incremental by necessity; communities are recomputed periodically.
The arbitration (vector vs graph vs hybrid) belongs in the system’s ablation table — and must be re-measured at every model migration (ch. 2).
Freshness of a graph index or KG snapshot A GraphRAG index, or a knowledge-graph snapshot fed to the model, is a derived artifact with its own version, and it ages differently from a vector index.
- Local vs global parts: entity and relation extraction is incremental per document, keyed by (doc_id, version) as in 6.1; communities and their summaries are global, since one new document can merge or split several communities.
- Two freshness numbers: the SLA states when a document is retrievable by vector search (minutes) and when it reaches graph-based answers (after the next community run, e.g. nightly). Re-summarize only the communities whose membership changed.
- Lineage: the snapshot id (extraction model and prompt hash, community run, date) goes into the registry (ch. 1) and every trace (ch. 8), like the vector index version.
- Deletion: removing a document removes its triples and invalidates the summaries that cited it — the right to erasure applies to derived artifacts too.
7. Fine-tuning Ops: versioned LoRAs
Fine-tuning produces small adapters that are trained often and deployed on a shared base model; without strict bookkeeping, an adapter ends up running on the wrong base or with the wrong prompt. This chapter defines what must be versioned and organizes the fine-tuning cycle in production.
Reminder — SFT and DPO (LLMs module, ch. 4–5), and LoRA
Instruction fine-tuning (SFT, LLMs module ch. 4) adapts the model. LoRA (Hu et al., ICLR 2022), introduced in LLMs 4.3, makes this affordable: it freezes the weights W and learns only \Delta W = BA of rank r \ll d: a few tens of MB instead of GB, trainable on a single GPU. DPO/RLHF (LLMs module, ch. 5) then align the preferences. This chapter is the operations view of LoRA.
Definition 7.1 — Versioned adapter An adapter (LoRA) is usable only when coupled with: (i) its exact base (name + version of the weights); (ii) its prompt template; (iii) its training data (hash); (iv) its hyperparameters (r, \alpha, target modules).
The unit of versioning is not the .safetensors file but this quintuple — a LoRA on the “wrong” base (same architecture, different checkpoint) produces silently degraded outputs.
# Adapter registry: the record is the authority (MLflow / private HF Hub / simple CSV)
adapter: faq-v3-lora
base: meta-llama/Meta-Llama-3-8B-Instruct@9c6b4c1d # exact hash
template: prompts/faq_chat_v2.jinja # git version
data: data/faq_2026.parquet @ sha256=4f2a... # DVC
lora_config: {r: 16, alpha: 32, target: [q_proj, v_proj]}
metrics: {eval_v3_f1: 0.91, faithfulness: 0.88, judge_winrate: 0.56}
trained_by: pipeline#2026-03-11, run_id 7c3e...
deployed: canary 5% since 2026-03-13, SLO OK7.1. The fine-tuning cycle in production
- Collect: feedback 👍/👎, expert corrections, questions without a good answer (failed queries from monitoring — ch. 5);
- Clean / deduplicate / anonymize: deduplication reduces memorization (Explainability, Security, Privacy of LLMs, ch. 7);
- Build the pairs (SFT) then preferences (DPO) — balance of domains, slices evaluated separately (ch. 2);
- Train LoRA on the frozen base; log the quintuple;
- Qualify: golden set + judge + safety tests (fine-tuning can degrade refusals!) — gate of ch. 2;
- Deploy in canary with rollback (ch. 2.3);
- Measure the impact (winrate, cost, latency) before generalization — fine-tuning must win against simply improving the prompt and against RAG, otherwise it has no reason to exist.
Multi-LoRA serving Use case: a single 8B engine serves ten clients, each with its own LoRA (tone, domain, language). Modern servers load several adapters on a base shared in memory — a single copy of the weights, the \Delta W (a few tens of MB) being applied per request.
The ops work consists of guaranteeing the routing (client header → adapter) and the isolation of evaluations per tenant (slices of ch. 2). The base rarely changes; the adapters often: two distinct deployment cadences in the same pipeline.
Proposition 7.2 — Size of a LoRA A rank-r LoRA on a layer W \in \mathbb{R}^{d_{\text{out}} \times d_{\text{in}}} stores B \in \mathbb{R}^{d_{\text{out}} \times r} and A \in \mathbb{R}^{r \times d_{\text{in}}}, i.e. r\,(d_{\text{out}} + d_{\text{in}}) \text{ parameters instead of } d_{\text{out}} d_{\text{in}}.
On the q and v projections of the 32 layers of an 8B model (d = 4096; with GQA the v projection has output dimension 1024) at r = 16: 16 \times (4096 + 4096) + 16 \times (4096 + 1024) \approx 2.1 \times 10^5 parameters per layer, about 6.8 M in total — ~0.1% of the parameters, about 14 MB in fp16.
The storage saving is a bonus; the decisive saving is the training cost (no optimizer state on the frozen weights) and the deployment cadence (adapter hot-swap).
8. Agents in production: tracing and guardrails
An agent makes dozens of model and tool calls per request, and any of them can fail, loop or be hijacked. This chapter makes agents observable, bounds what they can spend and do, and explains how to evaluate them.
Reminder — LLMs module ch. 8 (agents)
An agent = an LLM in a loop (observation → reasoning → action via tools), with memory and possibly multi-step planning. This chapter adds no intelligence: it adds observability, the budgets and the permissions without which an agent is undebuggable and dangerous.
8.1. Tracing: the tree of calls
An agent request unfolds in dozens of nested LLM and tool calls. The central datum is the trace: a tree of spans (one span per call: input, output, token cost, latency, model, attempt), OpenTelemetry-compliant — the vendors Langfuse/W&B Weave plug into it.
Without a trace: no ability to answer “why did the agent send this email?”. With a trace: deterministic replay (by logging the seeds/times), bottleneck analysis (which tool dominates the latency?), per-step costs, and detection of behavioral anomalies (loops: same tool, same arguments, N times).
// OTel span of an agent call (excerpt) -- everything is there for replay and cost
{
"trace_id": "a1b2...", "span_id": "17", "parent_id": "03",
"name": "llm.call",
"attributes": {
"llm.model": "llama3-8b-instruct@v3-lora-faq",
"llm.prompt_hash": "9e21...", // -> versioned template (ch. 1)
"llm.input_tokens": 1842, "llm.output_tokens": 96,
"llm.cost_eur": 0.00042, "llm.latency_ms": 741,
"agent.step": 4, "agent.tool": "search_confluence",
"rag.index_version": "2026-03-11.2"
}
}8.2. Guardrails: in layers, around the loop
| Layer | Mechanism | Example |
|---|---|---|
| Input | validation, injection classifiers, quota | block 500 kB prompts, injection detector (LLMs 9.4; ESP ch. 5-6) |
| Loop | maximum budgets | max 15 steps, max 0.10 €/task, global timeout |
| Action | per-tool permissions, scoping | read-only on Confluence; sending e-mails = human confirmation |
| Output | format and content validators | strict JSON schema; no bank details in the answers |
| Execution | sandbox (ch. 9) | code run in an ephemeral container with no outbound network |
| Observation | behavioral alerts | loop rate, success per task, cost drift |
Example 8.1 — The budget as the first guardrail A monitoring agent performs on average 6 LLM calls + 3 tool calls per task. A malicious user (or a booby-trapped document — indirect injection, LLMs 9.4) can make it loop: each iteration re-injects a bit of context. Cap per task: steps ≤ 15, tokens ≤ 50 k, cost ≤ 0.10 € — and cut off cleanly (degraded answer: “I could not finish”): the failure becomes a bounded cost instead of a billing incident.
Proposition 8.2 — Per-task success collapses with length If each step succeeds independently with probability p, a task of k steps succeeds with p^k: p = 0.95 and k = 20 give 36%.
Hence: (i) shorten the chains (more reliable tools, shallower plans); (ii) local retries and reflexivity; (iii) evaluate agents per complete task, never per step.
8.3. Evaluating an agent
Agent metrics: success rate per task (on scripted tasks with an initial state and a success criterion — e.g. “the ticket is created with the right fields”), average cost per task (tokens × price + tools), end-to-end P95 latency, human intervention rate. The evaluation environments must be replayable: simulated tools (mock Confluence, mock mail) for CI, a sampled real environment for production — the simulation runs hundreds of tasks for a few cents.
Definition 8.3 — Error budget (Beyer et al., Site Reliability Engineering, 2016) An SLO with target s over a window of N requests or tasks allows (1 - s)\,N failures: this allowance is the error budget. While budget remains, releases go ahead; once it is spent, releases freeze and the team works on reliability until the window rolls over.
For an agent, the SLO is stated per complete task. With a 95 % task-success SLO and 10,000 tasks a month, the budget is 500 failed tasks. Proposition 8.2 tells how fast a longer plan spends it: at p = 0.997 per step, a 10-step plan fails about 3 % of tasks (some 300 failures, within budget), a 20-step plan about 5.8 % (some 580, over budget) — unless its steps become more reliable.
9. Operational security: secrets, sandbox, supply chain
An LLM application holds credentials, executes generated code and depends on weights and libraries downloaded from elsewhere; each is an entry point for an attacker. This chapter secures the system around the model.
Scope The attacks against the model (jailbreaks, injections, poisoning, extraction) are introduced in LLMs 9.4 and treated in depth by the M2 P2 co-requisite Explainability, Security, Privacy of LLMs (ESP, ch. 5–6). Here: the security of the system around the model — the ops of an LLM application; the reference checklist of the field is the OWASP Top 10 for LLM Applications.
9.1. Secrets and identities
- API keys, database passwords, index tokens: never in the code nor the images — a vault (Vault, Secret Manager) injected at startup; periodic rotation; repository scanners in CI (a secret committed = compromised, even “just on a branch”);
- Each service has its identity (service account) and least privilege: the RAG engine does not need write access to the corpus; the agent does not need the credentials of the administrative database;
- The outputs of the LLM are data, never instructions: a model’s response must never be evaluated/executed (eval, shell, SQL) without validation — the flaw class “the model generated the code, we execute it” is a special case of indirect injection (LLMs 9.4; ESP 5.3).
9.2. Sandbox and isolation
When the agent executes code (notebooks, “calculator” tools, plugins), execution must be confined: an ephemeral container, a temporary file system, outbound network blocked by default, CPU/RAM limits, a hardened runtime (e.g. gVisor/Firecracker for kernel-level isolation), and filtering of exfiltrations — including over slow channels (DNS queries, timing).
An agent compromised by injection will try to exfiltrate via the only channel it was left (the tools! the mail! the displayed output?). Hence the duo permissions (ch. 8) + sandbox: the same principle as in system security — compromise is likely, blast radius minimal.
9.3. Supply chain
- Model weights: downloaded from verified sources, sha256 fingerprint recorded in the registry (ch. 1) — an open-weight model is a dependency like a library, with “logical” CVEs (backdoors: ESP 5.4; Security and Robustness, ch. 6) and no automatic scanner;
- Dependencies: lockfiles, SBOM, classic vulnerability scans in CI (same as any app);
- Prompts and adapters: mandatory review — a prompt is code in disguise; a LoRA trained on unvalidated data is an opaque dependency;
- Logs: the traces contain user text (personal data?) — limited retention, anonymization, controlled access; cf. ESP ch. 8–10, whose ch. 10 covers the AI Act.
| Ops threat | Vector | Countermeasure |
|---|---|---|
| API key theft | repo, image, logs | vault + rotation + provider quotas |
| Exfiltration by the agent | tool, mail, code | scoped permissions, egress deny, sandbox |
| Payment denial (bill shock) | agent loop, spam | budgets (ch. 8), per-client quotas |
| Booby-trapped model | backdoored weights | provenance + hash + safety evals (ch. 2) |
| Leak via logs | verbose traces | PII scrubbing, short retention, logged access |
10. Case study and green ops
The previous chapters each solved one problem; a real service must solve them all at once, within a budget in euros and in energy. This chapter assembles the full stack of an enterprise assistant and shows how to measure and reduce its energy cost.
10.1. The stack of an enterprise assistant
Let us assemble the bricks. Specifications: 2,000 employees, SSO, Confluence + SharePoint corpus (~500 kB documents), questions in French, sourced answers, no data outside the perimeter, annual budget of the order of a few tens of k€ of inference.
| Component | Typical choice | Chapter | Points of attention |
|---|---|---|---|
| Auth & gateway | SSO OIDC, per-user quotas | ch. 9 | identity = the key to agent permissions |
| ingestion | CDC Confluence → chunks → embeddings | ch. 6 | idempotence, 15 min freshness SLA |
| Index | vector + BM25 (hybrid) | ch. 6 | versioned, dual-boot during migration |
| Reranker | light cross-encoder | ch. 6 | +30 ms, −20% of context errors typically |
| Generation | front model API or self-hosted 8B | ch. 3-4 | routing according to document sensitivity |
| Adaptation | LoRA “internal tone” | ch. 7 | rollback = adapter swap |
| Agents | search + writing, read permissions | ch. 8 | budgets, tracing, sandbox |
| Evaluation | golden set of 500 Q/A + weekly RAGAS + CI judge | ch. 2, 5, 6 | versioned set, slices by business line |
| Monitoring | Prometheus + OTel + alerting | ch. 3, 5, 8 | P99 TTFT, PSI, cost/req, refusal rate |
Why not GraphRAG? If the dominant usage becomes “which teams work on topic X?” (relational questions), the entity graph (Graph ML module ch. 10; GraphRAG of Edge et al.) becomes profitable despite its indexing cost: the answer to the question “do we need a graph?” is a measured trade-off (RAGAS eval + ablation), not a fad.
10.2. Green ops: measuring energy
The energy cost of LLMs concentrates (i) in pre-training — Patterson et al. (2021) formalize the carbon accounting of large trainings (consumption, regional electricity mix, PUE); Luccioni et al. (2022) do the same for BLOOM including the amortization of training; (ii) in aggregated inference — billions of requests of a few Wh each.
For a developer, the useful unit is the consumption per request: measurable with CodeCarbon (CPU/GPU/PUE meter) around each serving point.
from codecarbon import EmissionsTracker
with EmissionsTracker(measure_power_secs=1, log_level="warning") as tracker:
answer = rag_pipeline.answer(question) # full request
print(f"{tracker.total_energy_kWh * 1000:.1f} Wh for this request",
f"({tracker.total_emissions * 1e3:.1f} gCO2e on the local mix)")Proposition 10.1 — The four green levers, in order of effectiveness
- Functional sobriety: routing to small models, shorter outputs, cache (ch. 4) — divides the energy without touching the hardware.
- Quantization: ~2-4× on the energy throughput per token (cf. LLMs ch. 6).
- Batching: the same GPU delivers more tokens per Wh in batch (ch. 3). Proposition 4.1 holds with the power drawn, P watts (GPU, host share and PUE), in place of the price c €/h, since P watts for one hour is P Wh: \text{energy/Mt} \;=\; \frac{P}{R} \cdot \frac{10^6}{3600} \;\approx\; 278 \cdot \frac{P}{R} \;\; \text{Wh/Mt}. At P = 500 W and R = 2{,}000 tokens/s this is about 70 Wh/Mt; at batch 1 (R \approx 60) and the same power, about 2.3 kWh/Mt — the factor of 30 of the bill. A lightly loaded GPU draws less power, so the real energy factor is somewhat smaller.
- Geographic and temporal scheduling: moving the nightly batches to the low-carbon-mix regions at the right moment.
The order matters: one does not buy green GPUs before having shortened the outputs.
LLGA position This module is deliberately transversal to the heart of the master: serving (ch. 3) extends the LLMs module, graph RAG (ch. 6) extends the Graph ML module, agents (ch. 8) extend the RL module, and evaluation in production (ch. 5) prepares the safety track of the Explainability, Security, Privacy module. The instructors of this proposed course are not yet confirmed: the cited standards and tools are authoritative; the LLGA (LLMs & Graphs) group of the X is mobilized when relevant.
Exercises / Labs
- ★ Reproducibility (ch. 1). Build a mini DVC pipeline (prepare → embed → eval) over 100 documents; modify a single document and predict which stages re-execute; verify. Show that a metric without the four fingerprints (code, data, configuration, container digest) is not replayable.
- ★ Data contracts (ch. 2). Write a Pandera schema for a FAQ set (question/answer/language/tokens) and make CI fail on: exact duplicate, empty answer, unknown language, abnormal length. Add the error report as an artifact.
- ★ Serving (ch. 3). Deploy an 8B on vLLM; plot the TTFT P50/P99 and TPOT P99 curves for concurrency 1 → 256; locate the breaking point (KV occupancy); compare static batching (vary it) and continuous batching.
- ★ Accounting (ch. 4). For a given workload (short questions, 300-token answers, 10 k req/day), quantify: API (input × in price, output × out price, prefix cache), self-hosted (proposition 4.1 with R measured in lab 3). Identify the crossover point in requests/day (Proposition 4.2) and its sensitivity to the utilization rate.
- ★ Ops audit (ch. 9). Put a mini LLM application through the sieve of the OWASP Top 10 LLM: list the findings, severity, fixes; check in particular: secrets, tool permissions, output validation, log retention.
- ★★ Significance of a gate (ch. 2). Simulate two models with losses 0.30 and 0.31 on n = 500; how many times does the “no regression” test wrongly fail over 100 draws? Deduce from it a realistic minimal margin for your CI. Repeat with the paired gate of Definition 2.3 when the two models share 90 % of their errors, and compare the margins.
- ★★ Little’s law in practice (ch. 3). From the traces of lab 3, estimate \lambda, W and L over a 10 min window; verify L = \lambda W within ±10%; explain the gap (non-stationarity, preemption).
- ★★ Drift (ch. 5). Generate two weeks of simulated “logs” with a topic drift on day 9; implement PSI (prop. 5.2) and KS over the lengths and topics; tune the threshold for ≤ 1 false alert per week; document the detection delay / false alerts trade-off.
- ★★ RAG Ops (ch. 6). Take the RAG of the LLMs module ch. 7: move to incremental indexing (adding 20 documents at D+1 without full re-indexing); measure RAGAS before/after; trigger an embedding migration and run the index dual-boot.
- ★★ Versioned LoRAs (ch. 7). Train two LoRAs (r = 8, 32) on the same set; record the quintuples; serve both in multi-LoRA; evaluate; deliberately “forget” the base version and observe the silent degradation — then add the compatibility check to the registry.
- ★★ Observable agent (ch. 8). Instrument a search + writing agent with OTel spans; inject three failures (a tool that times out, a retry loop, a too-long context); diagnose each one only through the traces; add budgets and caps, replay.
- ★★★ Common-thread lab (ch. 10). Assemble the assistant: hybrid RAG + reranker + LoRA + guardrails + dashboard (latency, PSI, RAGAS, cost/req, Wh/req via CodeCarbon); present a “simulated post-mortem” (a game day): another team injects a faithfulness regression, and your team must detect it (dashboard?) and roll back (registry) in less than 15 minutes.
Projects and assessment
Official assessment: not displayed. The SynapseS 2026–2027 sheet of this module is not published (the LLGA 2 – S3/S4 lists are empty; see the fiche), so it gives no modality, weighting or resit rule. Everything below is a proposal: a project, solo or in a group, plus a written exam, weighted as the instructor decides. Each subject can also serve as the operations work package of a Year 2 capstone (end-of-year projects). The projects exercise O7 (hands-on and industry practice, the core), O3 (LLMs in production) and O8 (guardrails, security, drift); the first group subject adds O5 (graph × LLM).
Solo projects
- [Solo · ≈ 20 h] How many false rollbacks? Statistically sound evaluation gates. Which gate blocks real regressions without “dancing on the noise”, and can an LLM judge be trusted as its sensor? A paper study of Zheng et al., “Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena” (NeurIPS 2023).
- Implement: four gates — Hoeffding (Proposition 2.2), the paired gate (Definition 2.3, normal and bootstrap), McNemar, and Wald’s sequential probability ratio test (1945). Measure the false-rollback rate and the detection power against n, on simulated correlated losses and on real model pairs.
- Data: per-item outcomes of small open models (for example Qwen2.5-1.5B-Instruct against its own 4-bit version, a near-tie, and against Qwen2.5-0.5B-Instruct, a clear regression) on GSM8K (Cobbe et al., 2021) and MMLU (Hendrycks et al., 2021), logged by lm-evaluation-harness; the MT-Bench human judgments released with the paper, to measure a judge’s position and length biases.
- Deliverables: a small gate library with tests, a 4–6 page report, a 10-minute talk. Chapters: ch. 2, 5.3.
- Success: error-rate curves with intervals that match the theory on simulations; the judge’s agreement with humans measured with and without ties, as in the paper; the sign-test gate of 5.3 shown to pass an identical candidate and to block a worse one.
- [Solo · ≈ 20 h] An LLM serving capacity and cost planner. How many GPUs, and which server settings, does a given traffic need to hold a P99 SLO at the lowest €/Mt and Wh/Mt? A paper study of Kwon et al., “Efficient Memory Management for Large Language Model Serving with PagedAttention” (SOSP 2023). It extends solo subject 2 of Large Language Models from one server to a fleet.
- Implement: benchmark an 8B model on vLLM (the code of 3.4) across concurrency,
max_num_seqs, prefix caching and 4-bit quantization; check Recall 3.2 against the measured KV occupancy and Little’s law on the traces (Ex. 7); fit a throughput–latency model; derive €/Mt and Wh/Mt curves (Propositions 4.1 and 10.1). - Simulate: an autoscaler driven by queue length and KV occupancy (3.4), on a replayed day of arrivals, against a TTFT P99 < 1.5 s SLO.
- Data: Llama-3.1-8B-Instruct in AWQ int4 on a 16 GB T4 (free Colab or Kaggle), or Qwen2.5-1.5B-Instruct in fp16; ShareGPT prompts, as in vLLM’s benchmark scripts; arrival times from the Azure LLM inference trace released with Splitwise (Patel et al., ISCA 2024).
- Deliverables: benchmark and planner code, a 4–6 page report, a 10-minute talk. Chapters: ch. 3, 4, 10.2.
- Success: measured KV memory within 10 % of Recall 3.2; Little’s law within 10 % (Ex. 7); the planner’s predicted TTFT P99 within 20 % of a held-out load test.
- Implement: benchmark an 8B model on vLLM (the code of 3.4) across concurrency,
- [Solo · ≈ 20 h] A drift observatory for a chat assistant. Which detector sees a topic or population shift in real chat logs first, at no more than one false alarm per week? A paper study of Rabanser, Günnemann & Lipton, “Failing Loudly: An Empirical Study of Methods for Detecting Dataset Shift” (NeurIPS 2019).
- Implement: PSI with smoothing (Proposition 5.2), KS tests with a Bonferroni correction over features, MMD on sentence embeddings, and ADWIN (Bifet & Gavaldà, SDM 2007) on the output proxies of 5.1 (refusal rate, answer length, JSON failures).
- Data: WildChat-1M (Zhao et al., ICLR 2024), a year of real chatbot conversations with timestamps; inject controlled drifts (a new topic, a language mix, longer prompts) on top of the natural ones.
- Deliverables: code, an alert dashboard, a 4–6 page report, a 10-minute talk. Chapters: ch. 5, 6.1. Stretch: wire an alert to a LoRA retraining cycle with canary rollback (7.1, 2.3).
- Success: detection delay against false alarms per week, per detector, over 20 injected drifts with intervals; the paper’s finding — univariate KS tests on a reduced representation compete with multivariate MMD — confirmed or refuted on text.
Group projects
- [Group of 4 · ≈ 30 h each] Ship a GraphRAG enterprise assistant under SLOs. Can the common-thread assistant (10.1, Ex. 12) hold its SLOs, freshness SLA and budget under replayed traffic and document edits — and survive a game day? The game day is the W4 red-team swap of the Year 2 transverse project.
- Implement: a hybrid vector + GraphRAG index kept fresh by change data capture (6.1 and the graph-freshness note of 6.3); CI gates from RAGAS and the order-swapped judge (Definition 2.3, 5.3); a multi-LoRA tone adapter (ch. 7); OTel tracing and an SLO dashboard with TTFT P99, PSI, cost and Wh per request (ch. 3, 5, 8).
- Data: the public GitLab Handbook (CC BY-SA 4.0), whose git history replays real document edits; a 300-question golden set written by the team (local, multi-hop and global questions), 30 % held out.
- Other modules: GraphRAG from Graph ML ch. 10; RAG (ch. 7) and LoRA (4.3) from Large Language Models.
- Split: ingestion and indexes; evaluation and gates; serving, adapters, cost and energy; observability and the game day.
- Deliverables: a repository with one-command deployment, the dashboard, an 8-page cost and carbon report, a live game day in which another team injects a regression. Chapters: ch. 1–8, 10.
- Success: TTFT P99 within the SLO under replayed load; the freshness SLA met on replayed edits; the injected regression detected and rolled back in under 15 minutes; cost and Wh per request within 25 % of Propositions 4.1 and 10.1.
- [Group of 3 · ≈ 30 h each] Hardening a tool-using agent: from red team to ops. How much attack success can operational guardrails remove from a tool-using agent, and at what cost in utility? A paper study of Debenedetti et al., “AgentDojo: A Dynamic Environment to Evaluate Prompt Injection Attacks and Defenses for LLM Agents” (NeurIPS 2024, Datasets and Benchmarks).
- Implement: an OTel-instrumented agent (8.1) on the AgentDojo suites (workspace, banking, travel, Slack), with an open tool-calling model served by vLLM or an API model. The red team runs indirect injections and exfiltration attempts (LLMs 9.4, ESP ch. 5); the blue team adds per-task budgets, scoped tool permissions, an egress-denied sandbox and output validators (8.2, ch. 9).
- Other modules: attacks and defenses from Explainability, Security, Privacy of LLMs, ch. 5–6; the threat model of Security and Robustness, ch. 1.
- Split: red team; blue team; ops (tracing, cost, and CI regression tests built from every successful attack).
- Deliverables: a repository, an OWASP Top 10 LLM audit (Ex. 5), a 6–8 page report, a 15-minute talk with a live attack. Chapters: ch. 2, 8, 9.
- Success: utility and attack success rate before and after each defense, with intervals; per-task success set against p^k (Proposition 8.2); cost per task; three failures diagnosed from the traces alone (Ex. 11).
Written exam (proposal)
Format: 2 hours; one handwritten A4 sheet and a non-programmable calculator allowed; 4 questions — a course question, a computation, a short proof and a drift exercise.
Question (5 points). A team releases a new prompt template with a new LoRA adapter for the enterprise assistant. (a) List the artifacts and fingerprints the release must record (2 pts). (b) Give the CI layers in order, the deployment stages and the rollback unit (2 pts). (c) On 100 decisive pairs, the order-swapped judge prefers the candidate 55 times: may the team claim an improvement (1 pt)?
Expected:
- The four fingerprints (code commit, data hash, configuration, container digest); the prompt-template version; the exact base-model hash, adapter data hash and hyperparameters (Definition 7.1); the evaluation-set hash and the judge version.
- Code tests, data contracts, model and prompt tests on the golden set with the paired gate, then infrastructure; shadow, canary at 5 %, full rollout. The rollback unit is a registry version (weights, adapter, prompt, index), redeployed in one command.
- No. Under “no difference”, 55 or more wins out of 100 has probability about 0.18, so 55 % is within the noise; ties carry no evidence either way.
Question (6 points). A model has 32 layers and 8 KV heads of dimension 128 (fp16 cache); 60 GiB of GPU memory are left for the cache. Requests have 1,500 prompt and 300 output tokens; TTFT is 0.5 s and TPOT 50 ms. (a) KV bytes per token and KV tokens available (1.5 pts). (b) Maximum concurrent requests, each counted at full length (1 pt). (c) With Little’s law, the largest sustainable arrival rate (1.5 pts). (d) At that rate, the cost per million output tokens and per request on a GPU rented at 3 €/h (2 pts).
Expected:
- 2 \times 32 \times 8 \times 128 \times 2 = 131{,}072 bytes, i.e. 128 KiB per token; 60 \times 2^{30} / 2^{17} = 491{,}520 tokens.
- 491{,}520 / 1{,}800 \approx 273 requests.
- W = 0.5 + 299 \times 0.05 \approx 15.5 s (299 gaps after the first token), so \lambda \le 273 / 15.5 \approx 17.6 requests/s. Check: 273 sequences at 20 tokens/s each decode about 5,460 tokens/s, close to R = 17.6 \times 300 \approx 5{,}280 tokens/s.
- Proposition 4.1 gives 278 \times 3 / 5{,}280 \approx 0.16 €/Mt; per request, 3 / (17.6 \times 3600) \approx 4.7 \times 10^{-5} €, about 5 cents per thousand requests.
Question (5 points). Two models are scored on the same n i.i.d. items.
- The per-item losses have variances \sigma_1^2 and \sigma_2^2 and correlation \rho. Show that the mean paired difference has variance (\sigma_1^2 + \sigma_2^2 - 2\rho\sigma_1\sigma_2)/n, and say when pairing helps (2 pts).
- For right/wrong scores, let a count the items only the candidate gets right, b those only production gets right, and m = a + b. Show that under “equal accuracy”, conditionally on m, a \sim \mathrm{Bin}(m, 1/2); deduce McNemar’s statistic (a - b)^2/(a + b) (2 pts).
- Apply it with a = 30 and b = 40 (1 pt).
Expected:
- The differences \delta_i are i.i.d. with variance \sigma_1^2 + \sigma_2^2 - 2\rho\sigma_1\sigma_2, and averaging n i.i.d. terms divides the variance by n. Pairing helps when \rho > 0, i.e. when both models fail on the same hard items; independent test sets would give \rho = 0.
- Equal accuracy forces the two kinds of discordance to be equally likely, so each discordant item favours either model with probability 1/2, independently: a has mean m/2 and variance m/4. Hence z = \frac{a - m/2}{\sqrt{m}/2} = \frac{a - b}{\sqrt{a + b}} is approximately standard normal, and z^2, McNemar’s statistic, approximately follows \chi^2_1.
- \chi^2 = 100/70 \approx 1.43 < 3.84: no significant difference at 5 % (p \approx 0.23), as in the worked example of 2.2.
Question (4 points). A prompt-length feature is split into three bins. The reference week has proportions (0.5, 0.3, 0.2), the current week (0.3, 0.3, 0.4). (a) Compute the PSI and classify it with the usual thresholds (2 pts). (b) What happens if a bin is empty in the current week, and how is it fixed (1 pt)? (c) Which monitoring loop of 5.2 handles this signal, and with what action (1 pt)?
Expected:
- 0.2 \ln(5/3) + 0 + 0.2 \ln 2 \approx 0.102 + 0.139 = 0.24: “to watch” (0.1–0.25), just under the alert threshold.
- The term \ln(p_i/0) is infinite, and so is the PSI. Smooth the histograms (add a small \epsilon to every bin) or merge sparse bins.
- The daily loop: open a ticket and investigate (a new user population? a new document source?), and confirm on the refreshed golden set before any retraining.
Grading rubric (proposal)
| Criterion | Weight | What earns the marks |
|---|---|---|
| Question, SLOs and baselines | 15 % | One precise question; SLOs, budgets and a simple baseline (API model, vector-only RAG, no guardrail) fixed before any optimization |
| Correct implementation | 20 % | Code that runs with one command; formulas checked on a toy case (KV memory against Recall 3.2, PSI against a hand computation) |
| Measurement and statistics | 25 % | Tail percentiles rather than means; paired intervals and gates (Definition 2.3); several runs; false-alarm and detection rates measured, not assumed |
| Operations quality | 15 % | Lineage with the four fingerprints, CI gates that actually block, rollback in one command, traces that explain every failure shown |
| Cost, energy and safety | 10 % | €/Mt and Wh/Mt measured and set against Propositions 4.1, 4.2 and 10.1; attacks and failure modes reported honestly |
| Report, dashboard and talk | 15 % | A clear 4–8 page report, a readable dashboard, a talk or game day that answers the question within the time |
References
- D. Sculley, G. Holt, D. Golovin, et al., Hidden Technical Debt in Machine Learning Systems, NeurIPS 2015 — the founding act; the rest of the module unrolls its debt.
- G.-I. Yu, J. S. Jeong, G.-W. Kim, S. Kim, B.-G. Chun, Orca: A Distributed Serving System for Transformer-Based Generative Models, OSDI 2022 — continuous batching (iteration-level scheduling); W. Kwon, Z. Li, S. Zhuang, et al., Efficient Memory Management for Large Language Model Serving with PagedAttention (vLLM), SOSP 2023 — paged KV, shared prefixes.
- S. Es, J. James, L. Espinosa-Anke, S. Schockaert, RAGAS: Automated Evaluation of Retrieval Augmented Generation, EACL 2024 (demo) — faithfulness, answer relevance, context relevance (context precision and recall come from the ragas library).
- E. J. Hu, Y. Shen, P. Wallis, et al., LoRA: Low-Rank Adaptation of Large Language Models, ICLR 2022 — the foundation of ch. 7 (SFT and LoRA themselves are taught in the LLMs module, 4.1 and 4.3).
- D. Edge, H. Trinh, N. Cheng, et al., From Local to Global: A Graph RAG Approach to Query-Focused Summarization, 2024 — the graph option of ch. 6 (cf. Graph ML module ch. 10).
- L. Zheng, W.-T. Chiang, Y. Sheng, et al., Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena, NeurIPS 2023 — agreement with humans, position/length/self bias.
- J. Gama, I. Žliobaitė, A. Bifet, M. Pechenizkiy, A. Bouchachia, A Survey on Concept Drift Adaptation, ACM Computing Surveys 2014 — the vocabulary of ch. 5.
- D. Patterson, J. Gonzalez, Q. Le, et al., Carbon Emissions and Large Neural Network Training, 2021; S. Luccioni, A. Hernandez-Garcia, A. Voznyuk, et al., Estimating the Carbon Footprint of BLOOM, 2022 — the carbon accounting of ch. 10.
- B. Beyer, C. Jones, J. Petoff, N. Murphy, Site Reliability Engineering, O’Reilly 2016 — SLOs, error budgets, post-mortems; H. Huyen, Designing Machine Learning Systems, O’Reilly 2022 — the ML Ops reference in book form.
- Papers of the project subjects: S. Rabanser, S. Günnemann, Z. C. Lipton, Failing Loudly: An Empirical Study of Methods for Detecting Dataset Shift, NeurIPS 2019; E. Debenedetti, J. Zhang, M. Balunović, et al., AgentDojo: A Dynamic Environment to Evaluate Prompt Injection Attacks and Defenses for LLM Agents, NeurIPS 2024 (Datasets and Benchmarks); P. Patel, E. Choukse, C. Zhang, et al., Splitwise: Efficient Generative LLM Inference Using Phase Splitting, ISCA 2024; W. Zhao, X. Ren, J. Hessel, et al., WildChat: 1M ChatGPT Interaction Logs in the Wild, ICLR 2024; A. Bifet, R. Gavaldà, Learning from Time-Changing Data with Adaptive Windowing (ADWIN), SIAM SDM 2007.
- Tooling: DVC (versioned data), MLflow (registry), Feast (feature store), Pandera and Great Expectations (data contracts), OpenTelemetry / Langfuse (tracing), CodeCarbon (energy), vLLM (serving).
- Standards: OWASP Top 10 for LLM Applications; NIST AI Risk Management Framework (Govern/Map/Measure/Manage) — the backbone of chapters 8-9, extended by the Explainability, Security, Privacy module.