Projects¶
One evolution · four chapters
From services, to two templates, to a platform¶
This is not a pile of projects — it is one line of work that compounds.
Three production ML services taught the lessons; a governed template
encoded them for one service and then refused to grow; ml-platform
became the enterprise substrate for the work above that boundary; and
agent-local is the agnostic agent core that platform absorbed without
replacing. Each chapter is the foundation of the next.
Why four repos, not one
Separate repositories with explicit, written contracts between them — and two
of them are templates on purpose. ml-service-template governs classical
ML — scikit-learn, XGBoost, LightGBM — and stays small enough to read in an
afternoon;
ml-platform covers lakehouse, feature store, orchestration, GitOps and LLM
serving, and consumes the template through copier rather
than reimplementing it. Widening the first to cover the second would have
destroyed what makes it recommendable. Knowing where to draw that boundary —
and writing the boundary down as a decision — is the point.
Chapter 1 · The ML-MLOps Portfolio¶
The portfolio is not three models — it is one production system that happens to serve three of them. Each service ships with its FastAPI contract, test suite, Docker image and Kubernetes manifests; around them sits the shared MLOps surface: Terraform-provisioned GKE and EKS clusters, Kustomize overlays per environment, GitHub Actions CI/CD with quality gates, MLflow tracking, Prometheus + Grafana monitoring, and the incident writeups that prove the system was operated, not just deployed.
395+ automated tests and 18 architecture decision records hold it together — every non-trivial choice is documented with the alternatives it rejected.
How to review it
Pick one service and follow its evidence chain: metrics → serving path → deployment → incident. The debugging deep dive on BankChurn is the strongest 10-minute read.

The three services inside the portfolio — each with its own deep-dive page. Hover to hold, drag to browse, or click a card to bring it to center:
churn classification
BankChurn Predictor¶
AUC 0.87 90% coverage FastAPI · K8s · SHAP
Cost-aware churn classification — and the serving incident that went from 81% errors to 0% at half the CPU.
financial nlp
NLPInsight Analyzer¶
80.6% accuracy 98% coverage CPU-only serving
Financial sentiment with an explainable, low-cost inference path — the heavier transformer documented as a rejected trade-off.
demand forecasting
ChicagoTaxi Pipeline¶
R² 0.96 6.3M rows PySpark · temporal CV
Demand forecasting at scale with strictly temporal validation — and the data leak that was caught before the metrics were published.
All the infrastructure, testing, security and monitoring proof behind the portfolio lives in one place:
Chapter 2 · The Production Template¶
The second project is what the first one taught: an open-source starter system that encodes the portfolio's production lessons as reusable defaults. Scaffold a new ML service and it arrives with the serving and training patterns, deployment overlays and operating guardrails already in place — the mistakes the portfolio paid for once, prevented by default.
Inside the box: a FastAPI serving scaffold with the single-worker + ThreadPoolExecutor pattern, training pipelines with quality gates (metric, fairness, leakage), 6 env×cloud Kustomize overlays for GCP and AWS, Terraform modules, CI/CD that signs images and attests SBOMs (SLSA L2), closed-loop drift monitoring, 38 documented anti-patterns and 52 ADRs.
Its differentiator is the governed AI-assisted development layer: behavior rules, skills, workflows and an audit trail that keep agentic coding reviewable and bounded — engineered, not hidden.
Its scope limit is itself an ADR: classical ML only, single team, 1–5 models, small-team calibration — "2–3 models → CronJob, not Airflow". LLM/GenAI, feature stores and data contracts are deferred there by decision, each with a revisit trigger. That limit is what Chapter 3 exists to respect.
Why it matters
The portfolio proves I can build and operate ML services. The template proves I can turn that experience into a system other teams can adopt.
Chapter 3 · ml-platform — the enterprise substrate¶
The third chapter exists because Chapter 2's scope limits were correct. A single-service scaffold cannot hold point-in-time feature retrieval, lakehouse table formats with schema evolution, orchestration with lineage, GitOps reconciliation, LLM systems with evaluation gates, or governance artifacts written for an auditor. All of that presumes a platform several projects share — so it got its own repository instead of widening the first.
ml-platform is the second template, and the enterprise one: a multi-project
monorepo whose unit of reuse is a library plus a running service, not a
scaffold you copy out. Projects span tabular ML, time series, deep learning,
LLM/RAG and agents — diversity is the point, because a substrate that serves
one problem shape has not been shown to be a substrate. It consumes
ml-service-template through copier and is forbidden from reimplementing
it: where the two disagree on serving, the template wins (ADR-003).
Inside: Apache Iceberg with BigLake / S3 Tables, DuckDB + Polars and dbt; point-in-time joins with a leakage detector; Airflow 3 + KFP v2 into Vertex AI and SageMaker Pipelines; ArgoCD with ApplicationSets and Argo Rollouts; OpenTelemetry into Grafana LGTM; LiteLLM with a prompt registry, semantic cache, guardrails and promptfoo eval gates; LoRA/PEFT for the document track; and four drift detectors for four project kinds on one shared contract.
The signal
Chapters 1–2 show I can build and systematize ML. Chapter 3 shows I know what a tool should refuse to absorb. Two templates exist because one of them said no — and the boundary is written down as a decision, not left to taste.
What it does not claim
Build state is generated from the filesystem, never hand-maintained: 48 done · 2 partial · 5 absent of 55 tracked components, with 37 proven at L1 (the suite passes) and 11 at L2 (the thing executes). L4 — a real rollout on GKE or EKS — is printed at zero, because none has happened. The taxonomy exists because six Kubernetes overlays once rendered green for weeks while their probes pointed at routes the service does not serve. A suite passing and a pod answering are not the same claim.
Chapter 4 · agent-local — the LLM core¶
The fourth chapter takes the template's governance philosophy — AUTO / CONSULT
/ STOP, eval-gated autonomy, policy-as-data, no fine-tuning until a written
gate fires — and generalizes it to a new domain: local, multi-tier LLM
agents. The hard-won logic (grammar-constrained routing, an adaptive reasoning
loop, objective escalation, a deterministic policy gate) lives in a reusable
core/; a new domain is a thin usecases/<name>/ folder, never a fork.
The shipped example use-case is a WhatsApp store assistant — but the point is the platform, not the store.
This core is also the LLM plane inside Chapter 3. ml-platform vendored
it in with its full git history (ADR-002): core/ became libs/llm-core/,
the tienda use-case became projects/store-assistant/. The standalone
repository stays live because the two are not the same value — the platform
gives this core one particular, governed use, while agent-local is the
standalone version of that core, for anyone who wants it by itself. The core
itself is developed in ml-platform.
The signal
The same governance generalizes to a domain it was never written for — and then travelled into a platform without being rewritten. That is the jump from "builds things" to "designs platforms".
This is an active build. Rather than rewrite this page each week, the
status below tracks against the published plan
(ACTION_PLAN_LLM_AGENT.md)
— ✅ done · 🔨 in progress · ⏳ gated/next.
✅ Phase 0–1 · foundation
Runtime, router & policy gate¶
- ✅ llama.cpp bench + tier contract (E4B router gate 20/20)
- ✅ grammar-constrained routing with confidence
- ✅ reusable
core/+ thinusecases/(ADR-001) - ✅ deterministic policy gate; all tools read-only
✅ Phase 2 · controller & verifier
Governed execution¶
- ✅ ExecutiveController + per-tier circuit breaker
- ✅ policies as versioned YAML +
decision_id - ✅ cross-tier verifier + bounded self-consistency, reflection notes isolated from evidence (ADR-009)
- ✅ 11 evaluation sets (incl. adversarial injection) + offline gate
🔨 Phase 3 · observability
Telemetry & learning loop¶
- ✅ decision telemetry contract + PII redaction
- ✅ shadow sampling
- 🔨 retrieval growth loop (alias mining from logs)
- 🔨 golden set + replay against real traffic
⏳ Phase 4 · gated / next
Scale & adapt¶
- ⏳ SQLite queue + sagas (durable multi-day flows)
- ⏳ live WhatsApp webhook + cloud overflow path
- ⏳ comparative experiment vs portfolio datasets
- ⏳ QLoRA / DPO — only when the written gate fires