the lab · LetMeCheckThatBot

LetMeCheckThatBot.
It turns the group chat
into the interface.

Running daily since 2022 multi-user·in-thread·Telegram·OpenRouter·SQLite

Every group chat has the same dead moment: someone makes a claim, nobody believes it, and to settle it one person leaves the conversation, googles it, and pastes a link into a room that has already moved on. LetMeCheckThatBot does that part for you, in the thread. You say robot, is that actually true, and it fact-checks the claim to your face, with a source, before the moment passes.

It is the second of two agents I run in the open, the opposite shape from Penny. Penny works alone, for one person. This one lives in a Telegram group as its most useful member: anything you would leave the chat to look up, read, find, or hear, it does right there, so nobody leaves.

It is quiet by default. You summon it by saying robot, so it never noises up a room it was not invited into. Then it does the two jobs the room actually wants: it settles the argument with a citation, then memes the chat about it.

I designed and built it around one product decision: the conversation stays the interface. That forced the system to ingest every modality silently, retrieve old context quickly, choose when not to speak, and keep the model bill under a hard cap. It has run long enough for those choices to become operating constraints instead of demo decisions.

My role Product, architecture, code, and operations
Core decision Keep the group chat as the interface
Operating proof Daily since 2022 · 92,000+ messages · $5 monthly cap

It reads every kind of message, and answers in every kind.

Text is the easy part. The bot turns every artifact that lands in the room into text on the way in, keeps it all in one memory, and answers back in whatever format the moment wants.

Goes in → becomes text
text
read in place
voice note
Whisper, to a transcript
photo, screenshot
vision, read word for word
pasted link
headless browser, full page
video link
yt-dlp, title and transcript
video, GIF
sampled into frames
any file
contents pulled in
Comes back → in any format
an answer
in the thread, with a source
a video or clip
found and posted as the file
a meme
built and dropped in
a voice memo
spoken aloud, on the laptop
a quick poll
an either-or, to the room
a flashback
the room's best day, after a week quiet

One memory in, one voice out. Everything below is the records.

Four real exchanges. Actual screenshots.

Not mockups. Screenshots from a live group: settling a myth with a source, posting a video, meme-ing the chat, answering out loud in a voice memo. Click through, or let them rotate.

The reason it works is that it never stops reading

This room does not talk in text. It talks in voice notes, links, screenshots, and videos, so the bot treats every artifact as readable, each turned into text the moment it lands and filed away, with no reply. That silent reading is the trick, because the question almost always arrives weeks after the thing it is about.

The trace below is the minute that explains the whole bot. A voice note and a menu photo go by without a peep. Weeks later, robot, what was that thai place from like a month ago searches the whole history by meaning.

ONE MINUTE IN THE ROOM · real shape, representative content
7:02pmike[voice note · 0:48] 7:02pbotsilent · whisper → transcript → vector → memory. no reply 7:03psara[screenshot of a menu] 7:03pbotsilent · vision reads every word on it → memory. still no reply 7:04pmike“robot, what was that thai place from like a month ago” 7:04pbotsearch_messages · keyword + indexed vectors over 92,000+ messages · 3 hits 7:04pbot“the khao soi place. you said you were crying in a good way”

That is the part that took the work. The persona was cheap: I wrote it once and have barely touched it. Reading every artifact as it lands, then finding the right one out of tens of thousands later, is what makes it a member of the room instead of a search box you @-mention.

It has run daily since 2022, on the room's own message count

The room has passed 92,000 messages as of July 2026, all read, transcribed, and remembered. The chart below is the first 87,787 through June 2026, drawn by the bot on request.

A bar chart the bot generated of how many messages the group sent each month from July 2022 to June 2026. The bars are tiny for the first two years, then climb sharply from late 2024 onward, peaking around 6,400 in November 2024 and staying high through 2026.
The first 87,787 messages, through June 2026. Quiet for two years, then it woke up.

All of it runs on a key hard-capped at $5 a month, less than a Telegram Premium subscription, and the cap has never been hit. A multi-user agent that reads everything, remembers everything, and answers in its own voice costs less than the chat app it lives in.

A $5 cap invites a fair question: is a model this cheap any good? I measured it against GDPval, OpenAI’s set of real professional work. The $5 model came within eight points of the experts, 76 to their 84 in a standard setup. The jobs and scores are in the benchmark explorer, with the method below.

Drop a link and it looks at the actual page.

Drop a link and it does not guess from a search snippet. These four are live full-page grabs through the bot’s own browser, including this page.

A full-page screenshot the bot took of the Wikipedia article for 'Large language model', captured top to bottom in one scroll.
wikipedia · full page
A full-page screenshot the bot took of the GitHub home page, captured top to bottom in one scroll.
github.com · full page
A full-page screenshot the bot took of the Stack Overflow home page, captured top to bottom in one scroll.
stack overflow · full page
A full-page screenshot the bot took of this very page, the LetMeCheckThatBot lab page on dnakhla.com, captured top to bottom in one scroll.
this page · yes, the one you’re reading
How it reads a link, and a video, and an image the translator layer

Each kind of artifact gets its own reader, and they all output the same thing: text. A pasted link is fetched and opened in a headless Chromiuma full web browser running invisibly, with no window, driven entirely by code., scrolled the whole way to wake the lazy-loaded parts, and captured top to bottom. If the link is a video, yt-dlpan open command-line tool that pulls a video’s title, description, and transcript. pulls its title, channel, description, and transcript. A screenshot or image is handed to a vision modelan AI model that can look at an image and read or describe what’s in it. that transcribes the words inside it verbatim. A video or silent GIF is sampled into frames and read across them. A shared file has its contents pulled in. A voice note nobody played goes to a local Whisperan open speech-to-text model that turns voice notes into text. container and comes back as a transcript. Once everything is text, one memory holds it all, and the bot can answer about a menu photo or a music video the same way it answers about a typed line.

This is why ingest happens the instant a thing lands. The question arrives weeks after the artifact. If the voice note was not transcribed on arrival, the answer is not there when someone asks what that restaurant was. Silent ingest is the whole memory.

Under one diagram: read everything, answer on call

The whole system has two halves: read everything while the room is talking, answer only when someone calls it. The diagram below is the whole machine.

Architecture diagram of LetMeCheckThatBot, rendered with Graphviz. A Telegram group is summoned by saying 'robot', which feeds the agent: an augmented model in a loop over nineteen tools, up to four passes. The agent uses MiMo v2.5 through OpenRouter, with a hosted Gemini fallback and a narrowed local Functionary fallback. In parallel, everything dropped in the room flows through a translator layer: Whisper for voice and video, GLM vision for images, headless Chrome for links, and yt-dlp for video transcripts. Each turns its input into text. A local worker running Qwen3-Embedding-0.6B on Ollama writes a 1,024-dimension vector beside each message in one SQLite file holding more than 92,000 rows. A sqlite-vec index retrieves candidates, the system re-scores them against the full vectors, and blends the strongest hits with exact keyword matches. A separate ambient watcher reads the same memory and, after a week of quiet, posts the room's best day back unprompted.
One agent, a loop, nineteen tools the answering half

The core is the primitive Penny uses and the one Anthropic argues for in Building Effective Agents: an augmented model in a loopthe whole architecture in five words: one AI model that can read, decide, and call tools in a repeating cycle until it has an answer.. The bot reads the recent thread, decides whether to act, and if it acts it calls a toola capability the model can invoke on its own: search the web, read a link, build a meme, look up a memory., reads the result, and decides again, up to four passes before it owes the chat an answer. There are nineteen: it searches the web, reads and screenshots links, digs through Reddit, finds a video and clips the part that matters, builds memes and gifs, reads images and the text inside them, transcribes voice notes with one local Whisper container and answers out loud through another, synthesizing its reply into a Telegram voice memo with a local Kokoroan open text-to-speech model; it generates the spoken voice locally on the laptop with no cloud call. model, recalls the chat’s own history by meaning, and can even put a quick either-or to the group.

One detail earned its place the hard way: not every model honors the function-callingthe standard way a model asks to use a tool: it emits the tool’s name and arguments in a fixed, machine-readable format. contract, so the loop carries a recovery path that parses malformed calls back into real ones instead of giving up. That single piece of defensiveness is the difference between a loop that works on one model and a loop that works on whatever model I drop underneath it.

How the memory actually works SQLite, local embeddings, indexed recall

The moment a message lands and the translators have turned it into text, the row is handed to a single background worker. The worker calls a local embedding model, Qwen3-Embedding-0.6B quantized to Q8shrinking a model’s numbers so it runs on modest hardware, trading a sliver of precision for speed and size., running on Ollamaa tool for running AI models locally on your own computer, with no cloud and no per-use bill. on the same ThinkPad, and gets back a list of 1,024 numbers: the message’s coordinates in meaning-space. That vectora list of numbers that captures a message’s meaning, so similar ideas land near each other. is written straight into the message’s own row in SQLitea tiny database that lives in a single file on disk, with no separate server to run., next to the text. There is no separate vector database and no embedding API. The worker takes one call at a time so it never crowds the GPU that Penny is also using, and the whole thing costs nothing per message.

A hosted embedding API would have quietly turned more than 92,000 messages into a recurring bill, which is most of why this runs locally. Cost is the smaller half. The bigger half is dependency: a model on my own shelf cannot raise its price, deprecate its weights, rate-limit me, or go down at the wrong moment.

Recall does not pick between keyword and meaning, it blends them. It embeds the question once, uses a binary-quantized sqlite-vec indexa vector-search extension inside the same SQLite file. It narrows the room to the most likely candidates without adding another database service. to retrieve candidates from that room, then re-scores those candidates against the full 1,024-dimension vectors before merging the strongest hits with exact keyword matches. So “that thai place” finds the right night whether you remember the words or only the feeling. The index stays inside the same SQLite file, so search is fast without adding another service. The full vectors remain the durable asset; the index is replaceable.

It also sketches each regular from their own messages, so it knows who is in the room before it speaks. The result is a multimodal RAGretrieval-augmented generation: fetch the few relevant memories first, then answer from them, instead of guessing. “Multimodal” means it works for images and audio too, beyond plain text. over the group chat: the room’s whole history, in every modality, instead of the last fifty lines.

One primary model, two narrow fallbacks, and why it is this cheap MiMo v2.5 over OpenRouter

The bot reaches models through OpenRouter. MiMo v2.5 remains primary because it tool-calls reliably, holds a large contextthe context window: how much text the model can hold in mind at once. A bigger one means it can read more of the room before answering., and stays within a dedicated key hard-capped at $5 a month. Gemini 2.5 Flash Lite is the hosted fallback for a provider or model failure. If both hosted paths fail, a small local Functionary model can answer from local knowledge with a narrowed tool set. This is not the old budget cascade that quietly traded quality for price. The fallbacks protect availability, not a silent downgrade.

A $5 cap invites an obvious question: is a model this cheap any good? So I measured it. I gave the same model real work from GDPval, OpenAI’s set of actual job tasks across 44 occupations, each with a human professional’s own finished work and an expert grading checklist. Then Claude Sonnet 4.6, a model from a different company, scored its work and the human’s the same way. I ran one task from each of 44 jobs. The standard setup averaged 76, this bot’s harness averaged 70, and the human experts averaged 84. It also failed quietly: asked to build a musician-payroll spreadsheet it produced a flawless one with every pay rate off by about a dollar, so every paycheck was wrong. The failures look like finished work. The narrow, real thing it proves is that the floor for this kind of work is higher, and far cheaper, than it looks. Every task, output, and score is published, so you can judge it yourself in the benchmark explorer.

When the room goes quiet, it hands back the room’s best day

Everything above keeps people in the chat while they are talking. This is the move after they stop. After a week of real silence, the bot does not post a hollow “miss you guys.” It reaches into the room’s own past and hands back its best day, in the room’s own words.

A phone face-down on a coffee table in a dark, still living room at dusk
Day six. Nobody has said anything since Tuesday.
Why it waits a week, then speaks first the trigger, the payload, the silence

A bot that pings on a schedule is noise. So the trigger is a dying room: a full week of real silence, never the calendar. The payload is the room’s own best day, this same day in an earlier year, posted back as a flashback in everyone’s own words. The notification is suppressed, so it surfaces only when someone next opens the chat. You find it; it never pings you.

It argues in memes too six, in its own voice

The fact-checks above are text. The other register is the image: when words will not do, it builds a meme through memegen.link and drops it in the thread. Six below, in the format it posts, one for each thing it is actually for: staying in the room, remembering everything, the trigger word, pulling records, reviving a dead room, and costing almost nothing.

Drake meme. Drake waves off 'leaving the chat to google it' and points approvingly at 'saying robot and never leaving.'
the whole thesis
Buzz and Woody meme: 'everything you drop in the chat' on top, 'becomes memory' below.
the multimodal RAG, in one line
Lord of the Rings 'one does not simply' meme: 'one does not simply get a reply without saying robot.'
quiet until you call it
Distracted-boyfriend meme. A man labelled 'the group' turns away from his girlfriend 'arguing from memory' to stare at a woman in red labelled 'the robot's 2024 records.'
fact-checking, to your face
Gru's Plan four-panel meme. Panel one: the room goes quiet. Panel two: do nothing for a week. Panel three: post its best day from 2019. Panel four: Gru stares at the same plan.
its signature move
Success Kid meme: 'ran a year in production' on top, 'for less than Telegram Premium' below.
the entire bill

LetMeCheckThatBot grew out of Everything Bot, an earlier, still-public version of the same idea: an agent that lives in a group chat, answers when summoned, and remembers the thread. This is where that idea got hardened into something I run every day.

The fastest way to understand it is to put it in a chat.

It is free, it never leaves the thread, and you can remove it in two taps. Say robot, then ask.

HOW TO USE IT
robot, is that actually true? fact-checked, with a source
robot, find that clip the video, posted as a file
robot, what did we say about that pulled from the whole history
robot, screenshot this the live page, rendered
Drop a voice note and it transcribes it. Go quiet for a week and it revives the room.
Add LetMeCheckThatBot to your group chat