Available for opportunities Mexico

Bernardo VegaMachine Learning Engineer

I build end-to-end ML systems, from raw data to a service in production.

About

From data to product

Machine Learning Engineer focused on shipping models to production with engineering rigor: reproducible pipelines, honest evaluation, and deployable services. I come from full-stack development, so I design the model and the infrastructure that serves it.

My approach blends two worlds: the rigor of machine learning (honest evaluation, reproducibility, data at scale) with the software-engineering discipline that turns a model from an experiment into a reliable service.

Machine Learning

Learning-to-rankClusteringFeature engineeringEvaluation / metricsMLflow

Languages

PythonTypeScriptSQL

Backend / Serving

FastAPINestJSPostgreSQLMCPClaude Agent SDK

Frontend

Next.jsReactTailwind CSS

Platform

DockerGitHub ActionsAzureCloudflare
Career

Experience

Machine Learning Engineer · Neural GT

Design and production deployment of end-to-end machine learning models, from data ingestion to a deployed service.

2024 - Present
Remote
  • Built reproducible ML pipelines with experiment tracking and versioned evaluation.
  • Shipped containerized inference services (FastAPI) with CI/CD.
  • Worked on large-scale data with streaming feature engineering and leak-free splits.
PythonPyTorchLightGBMMLflowFastAPIDocker

Machine Learning Engineer · Fintech

Applied ML in the financial domain, focused on numerical correctness, traceability, and reliable deployment.

2022 - 2024
Remote
  • Predictive modeling over transactional data with an emphasis on robust validation.
  • Integrated models into product flows with monitoring and automated tests.
  • Full-stack collaboration: from the data backend to the interface consuming the model.
PythonTypeScriptPostgreSQLscikit-learnNext.js
Selected work

Projects

GeoPlay Recommender

Completed2025

Player segmentation and geospatial recommendation for location-based mobile games.

Repo
146M
Synthetic events
0.63 · +137%
NDCG@10 (ranking)
0.864
Clustering ARI (HDBSCAN)
10 / 10
Phases completed

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.
Python 3.12LightGBMH3HDBSCANMLflowFastAPIDockerpytestRuff

LedgerLens

In progress2026

AI-native financial analyst.

Repo
Determinism-first
Boundary
Custom MCP server
Core
Eval gate in CI
Quality
Deployed on Azure
Status

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).
TypeScriptpnpm workspacesNext.jsNestJSPostgreSQLDrizzleClaude Agent SDKMCPZodVitestTestcontainersDockerAzure Container AppsOpenTelemetry

CaseLens

Completed2026

Cohere-native enterprise agent for warranty-claims triage, with grounded citations and a security-first design.

Repo
Below the model
Tenant scoping
Cohere-native
Citations
Append-only
Audit trail
13 + 5
Golden evals

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.
Python 3.11FastAPICoherePostgreSQLpgvectorNext.js 16React 19Tailwind CSSDockerGitHub Actions

VGC Replay Coach

In progress2026

Local-LLM coach for Pokémon VGC replays: it grounds every fact in real game mechanics and flags your misplays.

Repo
Fully local
Runtime
@smogon/calc
Damage
Bayesian prior
Opponent read
Tool-grounded
Facts

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.
Python 3.11PydanticOllamaGemma 4Node.js@smogon/calcuvRuffpytest
Method

How I work

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.

determinism-first.ts
// 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());
}
Contact

Let's build something

Open to Machine Learning Engineering opportunities and collaborations. Email is the best way to reach me.

Also at: [email protected]