Data & AI Engineer with experience at NLR and the Dutch Police. I build modeled data warehouses, tested pipelines, and AI systems in environments where handling confidential and privacy-sensitive data with care is essential.
Working daily with technology like
Two environments where a wrong figure has real consequences, and one where explaining is the whole job.

- 2024
Data Specialist - Cryptocurrency
Politie Noord-Holland
Data analysis, automation and AI within criminal law — under hard privacy and information-security requirements, where privacy by design is the starting point rather than an appendix.
- 2023
Data Engineer
Netherlands Aerospace Centre (NLR)
Built a wide range of data projects in a research environment where the numbers are checked, and the pipelines behind them have to hold up to that.
- Since 2020
Course Instructor
Udemy
Courses that explain complex technology to people without a technical background: 241 students with a 4.2 rating. A dashboard nobody understands isn't a dashboard — and the same goes for a handover.
A digital twin for this site: a LangGraph agent over my CV, projects and writing that streams cited answers, refuses when it has no source, stays under a daily cost cap, and pauses for my approval before it acts on my behalf.
Guards before tokens
Prompt-injection, PII and off-topic screens run in plain Python before a single token is spent — every guard fails closed, never crashes.
Human in the loop
Drafting a message on my behalf is high-risk: the graph interrupts, queues it for my approval, and tells the visitor it's waiting for review.
Evals gate every change
Two labelled datasets against the real agent — tool selection ≥ 95%, task completion ≥ 90% — with failure injection to prove the retries hold.
# one chat turn as a state machine; only generate() calls a model graph.add_edge(START, "guard_input")+ graph.add_conditional_edges("guard_input", route, ["refuse", "generate"]) graph.add_conditional_edges("generate", route,+ ["request_approval", "execute_tools", "verify"])+ if budget.exceeded or tool_rounds >= MAX_TOOL_ROUNDS: return refuse() # interrupt: Ruud approves graph.add_edge("request_approval", "execute_tools") + if not citations and len(answer) > 240: flags += ["uncited-answer"]Four habits that come back in every project on this site — and in the code behind them.
Most AI and reporting projects fail on the data, not the model. I start with the grain, the definitions and the tests — and only then with what sits on top.
At the police and in aerospace you work under hard privacy and information-security requirements. Privacy by design is how I start, not an afterthought.
Since 2020 I've explained complex technology on Udemy to people without a technical background: 241 students with a 4.2 rating. I do the same with stakeholders and teammates.
Tests, CI and documentation are part of every project — check the repos. The goal is work a team can carry on with, not work that depends on me.
Foundations first
Most AI and reporting projects fail on the data, not the model. I start with the grain, the definitions and the tests — and only then with what sits on top.
Used to strict frameworks
At the police and in aerospace you work under hard privacy and information-security requirements. Privacy by design is how I start, not an afterthought.
Explaining is the job
Since 2020 I've explained complex technology on Udemy to people without a technical background: 241 students with a 4.2 rating. I do the same with stakeholders and teammates.
Tested, documented, handed over
Tests, CI and documentation are part of every project — check the repos. The goal is work a team can carry on with, not work that depends on me.
I'm open to new roles in data engineering and AI. Send me a message, or connect on LinkedIn.