A few weeks ago I shared how I moved the Bouvet PenguinPool from Erik Stubø’s legendary Excel sheets to a fully automated commentary system on Databricks. The jobs run, the Slack messages land, the penguin fans are happy.
But a nagging thought lingered: the automation was a notebook. A very good notebook, but still a script. No versioning, no API contract, no easy way to call it from anywhere other than a scheduled job.
So this evening I went back in — with a little help from Genie Code — and turned it into a proper MLflow agent.

The agent layer: MLflow ChatAgent
The entire pipeline now lives inside an mlflow.pyfunc.ChatAgent class. Feed it a natural language message like “Preview tonight’s evening message” and it returns a fully-formatted Norwegian Slack post.
It has a defined input/output contract, it’s testable in isolation, and it behaves the same whether you call it from a notebook, a job, or an API endpoint.
The context layer: Genie and Tavily
Before the LLM sees anything, the agent assembles context from two sources.
Genie queries the lakehouse for live match results, current standings, and prediction patterns from the pool participants. Tavily then runs targeted web searches to pull in recent news articles about the specific teams playing that evening, adding the kind of colour that makes a commentary feel current rather than purely statistical.
The registry layer
The agent is registered in Unity Catalog. Any notebook, job, or endpoint in the workspace can load it with three lines of code. Versioned, aliased, auditable.
The observability layer: MLflow Tracing
Every run generates a structured trace across all spans, including the Genie queries, the web search and the LLM call.

Three runs in, average end-to-end latency sits around 70 seconds, with the two sequential Genie queries as the obvious bottleneck. Now that it’s properly instrumented, that’s a very measurable thing to fix.
Worth it?
Same two daily Slack messages as before. Same Norwegian sportskommentator energy. But now the thing generating them is a registered, versioned, loadable model you can call over REST.
Is it overkill for a football prediction pool? Almost certainly. Is it a satisfying evening’s work? Absolutely.
This post first appeared on LinkedIn.