September 1, 2026

Teaching a satire bot to actually be funny

Whoopee is a UK political satire account that posts once a day...and has trouble being funny.

Teaching a satire bot to actually be funny

What Whoopee is

Whoopee is a UK political satire account that posts once a day: a single image, styled like a real-world printed public sign, a heritage plaque, a rail departure board, a wartime poster, a product recall notice, carrying one short, sharp line about whatever's happening in UK politics that day.

Under the hood it's an LLM pipeline, not a person with a laptop and a grudge. Every day it pulls in fresh news events and tracks ongoing storylines (a minister's seventh reshuffle, a scandal that won't die, whatever's currently absurd), generates a batch of candidate one-liners against those storylines, scores each one against a rubric, and posts whichever scores best. No human picks the joke. No human even sees most of them.

That last part is the whole interesting problem.

The uncomfortable bit: writing jokes is hard, even for a very capable model

I went into this assuming the hard part would be the pipeline, reliably pulling news, avoiding repetition, not accidentally libelling someone, getting the image compositing right. All of that turned out to be genuinely solvable engineering.

Getting the model to be funny, on purpose, consistently? Much harder.

The default failure mode is what I've started calling "reportage in costume." Give a capable model a news fact and a format, say, a product recall notice, and it will very confidently restate the fact using recall-notice typography. "RECALL: THE HOME OFFICE. REASON: STILL DOESN'T WORK." That's not a joke. It's a fact wearing a costume. It reads as satire-shaped without actually landing a punch, and it took an embarrassingly long time for me to be able to articulate why a line like that doesn't work, precisely enough to put it in a prompt.

Chasing the actual mechanism, not the vibe

The turning point was realising I'd been asking the model to be funny without ever asking it to explain how it was going to be funny. It would write the line, then self-score it, but self-scoring after the fact is exactly the kind of judgement models are bad at making honestly. Of course a model rates its own homework well; it just wrote it.

So the prompt now forces the model to work in order, not backwards:

  1. State the underlying fact plainly and boringly, no joke, no format, just what actually happened.
  2. Name the specific comedic mechanism it's about to use, is this a reversal (a format that normally implies good news, used for something shameful)? A juxtaposition (language from one register slapped onto a mismatched target)? Literalism (a truth stated so flatly the bluntness itself lands)? An internal twist (a field inside the format's own structure quietly undercuts what it's supposed to say)?
  3. Only then write the actual line, built to deliver on the mechanism it just named, not vibes-based improvisation that gets rationalised into a mechanism afterwards.

The mechanism has to be named before the punchline exists, not invented to justify a line that already sounded good. That ordering, forcing the reasoning trace ahead of the output, rather than trusting a post-hoc self-check, turns out to be a documented idea in humour-generation research (a technique researchers call "chain of humour"), and it's made a real, readable difference in what comes out the other end.

Alongside that, I added two new things the model scores itself on: unexpectedness, does the punchline actually diverge from where a reader would predict it's going, not just deliver the expected beat in a fancier font, and personification, does the line read like a specific voice with a point of view said it, rather than a neutral AI paraphrase of the news. Neither is forced onto every line (a good literalism joke often shouldn't be personified), but naming them explicitly, instead of leaving them implicit inside a vaguer "is this funny" score, seems to matter.

Why bother

Right now Whoopee has a small following and, honestly, close to no measured engagement, very few likes, no replies, nothing to point at yet. That's ambiguous: it could mean the jokes aren't landing, or it could just mean almost nobody's actually seeing them, which is a distribution problem, not a writing problem. I don't have enough signal yet to know which.

But rather than guess, I decided to fix what I could actually verify was broken, the reportage-in-costume problem was real and visible on inspection, independent of any engagement number, and hold everything else steady for a week to see if the jokes read better before touching anything else. Chasing distribution while the writing itself is still mediocre felt like optimising the wrong variable.

I also went digging through recent research on getting LLMs to be funny on purpose, not fine-tuning papers (I don't have training infrastructure for this, and don't want it for a bot that posts once a day), but pure prompting techniques: forcing a model to brainstorm associations before it writes a punchline, distilling a reusable "what actually worked" style guide from a bot's own best output, replacing a model grading its own homework with a
head-to-head comparison between candidates. There's a genuine, small research field here, and some of it is very portable to a single-prompt system like this one. More of that is queued up to try next.

What's next

The plan for now is patience: let the current change run for about a week, read the actual posted lines rather than trust the self-scores blindly, and see whether the jokes are actually better before stacking more changes on top and losing the ability to tell what worked.

After that, there's a small backlog of ideas, a one-off "humor policy" distilled from whatever posts do eventually land, a cheap head-to-head judging pass instead of same-breath self-scoring, a couple of other prompt structures borrowed from the research above.

If any of it works, you'll see it, a bot that's actually landing jokes instead of just formatting facts. If you want to watch that happen in real time (or heckle it when it doesn't), Whoopee posts daily on Bluesky at @whoopee-news.uk.