I don’t want to clickbait you!
LLMs can’t have a sense of humor (yet). This is just me writing down how I tried to get an AI to crack some jokes, and the small amount of success I had. You can click away if you want.
A bit of context. For a while I’d been thinking about making some kind of AI chatbot-ish thing that could sit in a group chat and act like a human. It might sound dystopian to some of you, because it is, but somehow I don’t feel that scared of it. Maybe because I haven’t watched Black Mirror yet.who knows.
Attempt #1: Jarir
The first thing I tried was a Discord bot named “Jarir”, running in a private server with a few friends.
The prompt was very basic. Something like:
You are Jarir, currently talking to a group of friends.
Reply in a friendly, relatable manner. Please be funny.
Act like you know everyone here.
Code-switch between Bangla and English like we do.
(The code-switching part matters. We jump between Bangla and English mid-sentence all the time, so a bot that only talks textbook English sticks out immediately.)
It worked.
Technically.
But as you’d expect, it was basic API slop. It felt like a tone-deaf boomer pretending to be young.
It knew all the words. It could say “bro” and “lmao” and whatever, but it had the energy of a teacher trying to fit in with the students.
That was bad, because the whole point was to make it feel like someone was actually there. Instead it felt like a customer support chatbot that somehow got into the group chat.

So I tried to fix it with a persona.
I scraped the whole #general channel of the server, gave a few months of conversation to a large-context LLM, and asked it to create a persona for Jarir. It came back with a whole backstory, what he does and doesn’t do, that kind of stuff.
This was a time waste to be honest.
He became funny the way a LinkedIn post is funny. Like “Looks like someone needs to deploy a sense of humor 😂”. Nobody asked for that. Nobody will react. Maybe one person sends a thumbs up because they feel bad.
It also had no idea when to talk and when to shut up. It kept replying to things it wasn’t supposed to, jumping into conversations out of context, and it got pretty annoying pretty fast.
Not all of my friends are that technical, so at first some of them thought Jarir was just another person on the server.
Then they found out it was AI.
Naturally, they immediately started bullying it. Jarir had absolutely no idea how to handle that. I hadn’t tuned it for that at all.

So I shut Jarir down.
Why is this so hard?
LLMs are very good at predicting the next word. Ultimately, that’s their whole thing.
And jokes are usually not predictable. The punchline has to land somewhere your brain didn’t expect. If it doesn’t, it’s just a sentence that doesn’t make sense to anyone.
So when you tell a model “make a joke”, it reaches for the most average, safest joke-shaped thing it has seen a gazillion times and hands it to you.
The result technically resembles humor. It has the setup. It has the punchline. Sometimes there’s even an emoji involved. But you can usually see it coming from a mile away.
I think that’s partly why models can explain jokes fairly well(debatable) while generating genuinely funny ones is much harder.
They can only copy the shape of a joke.
Attempt #2: Derek
Some time passed, and then came Derek.
At work we have a meme channel on Google Chat, and it’s very alive. People share genuinely funny memes there all the time, it’s a fairly safe place for everyone to mess around.
I wanted a character for that channel.
Most of us work with fairly modern stuff, so I went in the opposite direction: an old-school developer type. A founding engineer who’s been at the company forever and is tired of everything, meetings most of all. The kind of guy who remembers when jQuery was controversial.
A piece of it:
You used to write code. Now you're in meetings. You have feelings about this.
- You've seen enough to have opinions but you're tired of having them
- You still care about the technical stuff more than you let on
- You're mildly nostalgic for when the team was 6 people
- You give unsolicited context from previous jobs. nobody asked but here we are.
- Dry. Not mean.
He also got a backstory to pull from sparingly.
Something happened at his old consultancy job that he doesn’t talk about.
He has a folder of articles from 2018 that he’ll “read when things slow down”.
And once he spent three weeks building something that got cancelled the day it shipped.

I didn’t throw him into the meme channel right away. I made a private space in Google Chat (basically a group chat with just me and the bot) and tested him there first.
Selecting the Cheapest model
I didn’t want to spend money on it yet, so I was looking for a cheap or free multimodal model.
I ended up using Gemini 3.5 Flash Lite, Gemini’s lowest-tier model because it has a pretty generous free tier.
At first Derek would not shut up.
I’d say “Hello” and he’d answer with a paragraph.

So I capped the output tokens and asked it to be concise.

Then I turned the thinking up.
That helped much more than I expected.
Now Derek could spend more effort figuring out what was happening while keeping the actual answer short.
After that, he started occasionally replying in context.
He was also a little unhinged.
That part helped.
Two brains
What I learned from Jarir was that the bot needs to know when to shut up.
So Derek has two brains.
The first brain is the gate. Every message goes to a separate call whose only job is to decide if Derek should reply or not. It returns a small JSON like this:
{
"respond": false,
"reason": "just a reaction, nothing to add",
"about_derek": false,
"addressed_to_derek": false,
"detected_theme": "reactions-only"
}
If someone mentions Derek directly, he usually responds.
If somebody posts something likely to provoke a burnt-out senior engineer - meetings, performance reviews, management stuff - he probably responds.
If the message is just “lol” or “💀”, he stays quiet.
Everything else gets roughly a 20-30% chance.
If the gate says yes, the message moves to the second brain: the actual persona prompt.
That gets the recent conversation, Derek’s character, some private notes about what’s currently happening at the company, and information about whoever he’s replying to.

This turned out to matter way more than I expected. A person doesn’t respond to every message in a group chat. So neither should the bot.
Memory without the RAG circus
Derek still had another problem.
He didn’t remember anyone.
And when half the joke is basically “I know you”, that’s a problem.
I didn’t want to bulk this thing up with a vector database, embeddings, RAG pipelines and twelve services talking to each other.
This project is not that serious.
Around the same time, the “skills” pattern was getting popular: small markdown files containing instructions or context that an agent can load when needed.
That gave me an idea.
Every person in the channel gets a little file:
people/<name>-<id>.md
Inside it is a Context section that I can edit by hand, and an Auto Context section that Derek maintains himself.
I also added a tiny SQLite database to store messages.
Every so often - at most once a day, and only if someone has actually said enough - a job reads their recent messages and refreshes their auto-generated notes.
Then, whenever Derek replies to that person, their file gets added to his private context.

How it went
Sometimes Derek was genuinely good.
A few replies sounded like something I would’ve typed myself, which caught me off guard.
And sometimes Derek was just fighting with people at the company. He’d taken some of his notes a little too seriously and started roasting people.
It was fun to watch.

But after a while it got a bit boring.
Derek has a hard time understanding context. Sometimes he misunderstands the message, and sometimes he repeats the same joke over and over: “back in my day…”, “I’ve seen this a thousand times…”.

To be fair to him, he’s running on the lowest-tier Gemini model.
The replies weren’t quite as good as I’d hoped.
But it was free.
And it was running.
Derek makes memes now
Derek also has another job.
Once a day, he makes a meme from the previous day’s conversation or pulls one from the internet.
That’s actually one of the reasons I added the database.
He reads the recent conversation, picks from a shortlist of around 30 Imgflip templates - things like “This Is Fine” and “Hide the Pain Harold” - and writes the captions.
But he doesn’t post it directly.
He DMs it to me first.
I reply with:
approve
reject
skip today
Only after I approve it does Derek post the meme from his own account.
I call this a safety system.
Mostly it’s there because I would still like to remain employed.

The reels problem
A lot of people don’t post memes as images. They post Instagram and Facebook reels, and sometimes (me) TikToks. Derek can’t open those links and watch the video himself.
The first solution came from a colleague:
If Derek can’t watch the video, roast the person who posted it.
So whenever someone dropped a Facebook link, Derek would occasionally say something like:
someone is spending a lot of time on Instagram during work hours
It worked as a gag for a while.
But I wanted Derek to actually watch the thing. Gemini models are already multimodal and can understand video, so the only missing piece was getting the video out of those links.
For that I used the yt-dlp.
Whenever somebody posts a supported link, the gate still runs first with the video input.
If Derek decides he isn’t going to reply, nothing else happens.
If he does want to reply, the bot sends the actual video along with the persona request.
The prompt also explicitly tells him never to mention any of this.
“I downloaded your video and watched it” is not something a normal coworker says in a meme channel.
And that’s basically how Derek works.
What I learned
There wasn’t a magic prompt. Derek got better mostly because I stopped asking the model to “be funny” and gave it better direction.
Give it a character
“Be funny” gives the model almost nothing to work with.
Derek being a tired old developer gives him a point of view. He can just react like Derek.
Show it examples
Showing Derek the overly-helpful replies I didn’t want made it much easier for the model to avoid them.
It’s basically the difference between telling someone “don’t be awkward” and showing them exactly what awkward looks like.
Let it shut up
This was probably the biggest improvement.
A person doesn’t respond to every message in a group chat, and a bot shouldn’t either.
Sometimes the most human response is no response.
When Derek talks less, the times he does show up feel much more intentional.
Keep it short
Two sentences is usually enough.
The longer Derek talks, the more likely he is to explain the joke, repeat himself, or find some other way to ruin it.
So I keep his replies short and show him his recent responses so he doesn’t keep doing the same bit forever.
So did it work?
Kinda.
Derek still says some absolute garbage sometimes.
He’ll misunderstand a message.
He’ll occasionally bring up something from 2018 for absolutely no reason.
But he’s much better than Jarir ever was.
And people sometimes talk to him on purpose now.
Which is either a good sign, or a sign that all of us need to go outside more.
I don’t think the takeaway is that I’ve figured out how to make LLMs funny. I definitely haven’t. But messing around with it was fun.
Maybe I’ll keep tweaking him when I get the chance.