Bernardo Vega
I build end-to-end ML systems, from raw data to a service in production.
- Role
- Machine Learning Engineer
- Location
- Mexico
- [email protected]
- GitHub
- github.com/bernhardtwo
I build ML systems end to end: ingestion, features, training, and the service that serves them. I came from full-stack, so the model and the infrastructure around it are the same job to me.
Most of the work in ML is not the model. It is the splits, the evaluation, and everything that has to keep running after the notebook is closed.
- Machine Learning
- Learning-to-rank, Clustering, Feature engineering, Evaluation / metrics, MLflow
- Languages
- Python, TypeScript, SQL
- Backend / Serving
- FastAPI, NestJS, PostgreSQL, MCP, Claude Agent SDK
- Frontend
- Next.js, React, Tailwind CSS
- Platform
- Docker, GitHub Actions, Azure, Cloudflare
GeoPlay Recommender 2025
Open RepoPlayer segmentation and geospatial recommendation for location-based mobile games.
End-to-end ML system for a synthetic Pokémon GO-style game. It generates realistic behavior data, segments players into archetypes via clustering, and ranks geographic hexes (H3) by visit probability using learning-to-rank. Built as a demonstration of production-grade ML engineering.
- Streaming feature engineering over 146M partitioned events, without loading everything into memory.
- Leak-free temporal splits: the ranker features are computed only over the training period.
- Hard negative mining: negatives are drawn from each player’s visited-hex universe, not random.
- LightGBM Ranker (LambdaRank) tuned by random search; MLflow for tracking and registry.
- Containerized FastAPI service that serves from an exported bundle, with no MLflow dependency at runtime.
- Honest analysis of the model plateau (NDCG@10 ≈ 0.63) and discarded features, documented.
- Stack
- Python 3.12, LightGBM, H3, HDBSCAN, MLflow, FastAPI, Docker, pytest, Ruff
LedgerLens 2026
Open RepoChat with an agent about your bank statements. It explains; the code does the arithmetic.
Upload your bank statements and chat with an agent that reasons over your finances. Built determinism-first: the LLM decides and explains, deterministic code does the math, and money is handled as currency-aware integers so there is no floating-point drift.
- Streaming agent chat with live tool calls over SSE.
- Read-only MCP server exposing finance tools to the agent, scoped per account.
- Deterministic ingestion and money handling, no FX.
- Eval harness with a golden set gating CI.
- Deployed to Azure Container Apps, CI/CD via GitHub Actions with OIDC (no long-lived cloud secrets).
- Stack
- TypeScript, pnpm workspaces, Next.js, NestJS, PostgreSQL, Drizzle, Claude Agent SDK, MCP, Zod, Vitest, Testcontainers, Docker, Azure Container Apps, OpenTelemetry
CaseLens 2026
Open RepoWarranty-claim triage where the tenant scope lives below the model, so a prompt injection cannot cross it.
A Cohere-native agent that triages warranty claims over a simulated multi-tenant domain. It runs a Command tool-use loop that retrieves policy with RAG, reads claims scoped to the caller’s tenant, and proposes status changes behind RBAC and a deterministic state machine. Scoping lives below the model, citations are produced by Cohere over the final answer, and every action is written to an append-only audit log.
- Command tool-use loop with four tools: RAG search, tenant-scoped claim reads, and an RBAC-guarded status update.
- The tenant context is bound below the model, so a prompt injection cannot reach another tenant’s data.
- Grounded RAG over pgvector: Embed 4 for vectors, rerank-v3.5, and native inline citations over the final answer.
- Append-only audit log for every tool call, scoped read, mutation, and denied attempt.
- Human-in-the-loop actions: the agent proposes a status change and a human confirms it through a guarded endpoint.
- Two golden-set eval harnesses (retrieval and agent) plus CI running the linters and tests against a pgvector Postgres.
- Stack
- Python 3.11, FastAPI, Cohere, PostgreSQL, pgvector, Next.js 16, React 19, Tailwind CSS, Docker, GitHub Actions
VGC Replay Coach 2026
Open RepoLocal-LLM coach for Pokémon VGC replays: it grounds every fact in real game mechanics and flags your misplays.
Takes a Pokémon Showdown replay, reconstructs the battle turn by turn, computes the hard facts that decide each turn, and lets a local language model reason over those facts to write a short coaching report. Damage, speed order, type effectiveness, and usage priors are computed deterministically and handed to the model as ground truth, so the analysis stays accurate, reproducible, and explainable. It runs entirely on your machine through Ollama, with no API keys and no cost.
- Tool-grounded architecture: damage, speed order, type effectiveness, and usage priors are computed in code and handed to the model as ground truth.
- The model reasons only over serialized facts and returns a CoachReport validated with Pydantic, so it never invents mechanics.
- Calibrated to Pokémon Champions Stat Points (IVs fixed at 31, every battle at level 50), so expected damage is not wrong by construction.
- Bayesian inference reweights a small space of spread hypotheses from real Smogon usage, using the replay’s own speed and damage evidence.
- Per-field provenance tracks whether each input was revealed, from your team export, a usage prior, or a generic fallback.
- A misplay-detection eval harness scores the coach against hand-labeled replays with turn-level precision, recall, and F1.
- Stack
- Python 3.11, Pydantic, Ollama, Gemma 4, Node.js, @smogon/calc, uv, Ruff, pytest
01 Production, not notebooks
A model only matters if it can be served, versioned, and reproduced. I design the full pipeline: ingestion, features, training, registry, and a deployable service.
02 No data leakage
Temporal splits respect time: features are computed only over the training period. If a metric looks too good, there is almost always leakage.
03 Honest evaluation
I report what the model actually does, including its limits. I document the plateau, the discarded features, and why one algorithm beats another.
04 Determinism where it matters
In sensitive domains (finance), the LLM decides what to compute and explains; pure functions compute the numbers. Correctness is never delegated to a probabilistic model.
05 Quality as a gate, not a wish
Lint, strict types, tests, and evals run in CI and block the merge. Quality is a property of the pipeline, not a good intention.
06 Full-stack, end to end
I come from web development, so I don’t stop at the model: I also build the API, the UI, and the infrastructure that put it in front of users.
// The model decides; a pure function computes the money.function netBalance(tx: Transaction[]): Money {return tx.reduce((sum, t) => sum.add(t.amount), Money.zero());}
Email me
I am looking for ML engineering work. Email is the fastest way to reach me.
- [email protected]
- Also at
- [email protected]
- GitHub
- github.com/bernhardtwo
- linkedin.com/in/bernhardtwo