Skip to content

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.

Portfolio deployment walkthrough — full build, test and serve cycle

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.

BankChurn Predictor

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.

NLPInsight Analyzer

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.

ChicagoTaxi Pipeline

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/ + thin usecases/ (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