AI and data engineering — Singapore.
Eight years turning business problems into systems that stay in production: enterprise RAG and agentic workflows, cloud data platforms, and the machine learning that feeds them. Across consulting, healthcare and travel technology, working from requirements and architecture through to deployment and the support calls afterwards.
The part I care most about is what happens after the demo — evaluation, reconciliation, and the checks that catch a regression before a user does.
Enterprise AI — A production assistant on Azure OpenAI serving 200 staff across a 50,000-document corpus. Hybrid semantic and keyword retrieval with chunking, metadata filtering and source attribution; agentic tool-calling with intent and persona routing; and an evaluation layer measuring retrieval accuracy, groundedness, hallucination rate and citation accuracy, alongside guardrails and token tracking.
Data platforms — A medallion-architecture migration to Microsoft Fabric covering 75 tables across 6 source systems, from OneLake ingestion through Delta modelling to Direct Lake semantic models. Incremental pipelines built on an extract–merge–deduplicate pattern with soft-delete handling and source reconciliation.
Machine learning — Customer propensity and lead-scoring models feeding conversion signals back into CRM and ad platforms, moving conversion from 4% to nearly 8%. Forecasting and reporting automation that removed 700+ hours of manual work a month in healthcare.
Multi-channel AI contact centre — A multi-tenant serverless platform on AWS fielding enquiries across seven channels including phone and in-browser voice, grounded in live business data via Claude on Bedrock with tool calling. Reduced enquiries needing a human by 70%, on roughly US$200/month, behind a 1,700-test backend suite.
Six repositories, each built around a decision that is usually skipped rather than a technique that is usually shown. Every one runs in a single command.
agent-loop — A tool-calling agent loop where the interesting part is what happens when things go wrong: turn limits, loop detection on repeated identical calls, a token budget, a tool-failure policy, and an explicit way for the model to hand over to a human instead of inventing an answer. Tool errors go back to the model so it can correct itself, which it does. Every guard is tested against a scripted model, so the suite runs offline in 0.17s.
Python · zero-dependency core · optional Claude backend · 72 tests
hybrid-retrieval — BM25 and vector search fused with reciprocal rank fusion. Both arms score 0.667 top-1 alone; fused they score 0.722, because they fail on different queries. Covers why rank fusion beats score fusion, why filtering has to happen before ranking rather than after, and the trap where an unreduced SVD makes the vector arm silently contribute nothing.
Python · numpy · BM25 from scratch · RRF · MMR · 86 tests
rag-eval-harness — Measure whether a RAG system is actually grounded, and fail the build when it stops being. Retrieval metrics plus claim-level groundedness, hallucination rate and citation accuracy. No API key and no dependencies, so it gates every commit; an LLM judge is one flag away. Reports the sentence that had no support, not just an aggregate.
Python · zero-dependency core · optional Claude judge · 120 tests
The two compose: hybrid export writes the dataset rag-eval scores. Two independently written scorers in separate repositories, agreeing to three decimals — which is the point of having the second one.
medallion-duckdb — A bronze/silver/gold lakehouse that runs on a laptop. Incremental loads that don't double-count, hybrid type-1/type-2 dimensions whose validity windows abut exactly, a fact table joined to the dimension version in force when the order was placed, and a declarative quality contract — 30 assertions including the SCD2 invariants most warehouses never check.
Python · DuckDB · SCD2 · dimensional modelling · 68 tests
propensity-scoring — Lead scoring that ends in a decision instead of a leaderboard. The threshold is derived from the economics (p > cost/value), which is why calibration is a precondition rather than a nicety. Contacting everyone loses 62,550; the derived rule returns 120,900, within 1.4% of the best any threshold could do. Includes the table showing the F1-optimal threshold sitting still while the economics quintuple.
Python · scikit-learn · calibration · expected-value thresholds · generated model card · 60 tests
forecast-backtest — Time series evaluation that tries to prove the model wrong: rolling-origin backtesting, MASE against a seasonal naive baseline, and Croston's method for intermittent demand. The headline finding is uncomfortable and reproducible — every model's 80% prediction interval covers only 60–67% of outcomes, because quantiles fitted on the past do not hold when the series drifts. Safety stock sized from those bands runs out far more often than the service level promises.
Python · numpy · Holt-Winters and Croston from scratch · coverage checks · 57 tests
AI Azure OpenAI · AI Search · Amazon Bedrock · RAG · agentic tool calling · RAG evaluation · LLM-as-judge · guardrails Data Microsoft Fabric · OneLake · Delta · Data Factory · BigQuery · DuckDB · dimensional modelling · dbt-style testing Cloud Azure · AWS (Lambda, DynamoDB, API Gateway, CDK, Cognito) · GCP Languages Python · SQL · R BI Power BI · QuickSight · Looker Studio · Tableau
Predictive-Modelling — class imbalance, sampling and ensembles python-data-analysis — pandas notebooks, segmentation and campaign analysis k-means-clustering · Exploratory-Data-Analysis · Data-Preparation · NLP
Most of my professional work sits in private client repositories and is covered by confidentiality, so what is public here is written from scratch to stand on its own.