Repository navigation
topic search
You remember that a chat discussed something, but not the words people used. Topic search finds that discussion by its meaning. Ask "where did we talk about renting a flat?" and it finds a conversation that says "apartment", "lease" and "deposit", even if nobody wrote "renting a flat". It also works across languages: an English question finds a discussion in Spanish or Russian.
After reading this page you can prepare a chat for topic search, ask questions in your own words, read
the results, and decide when ordinary word search is the better tool. Topic search uses only the messages
tg has already saved on this computer, so download the history first: tg personal messages sync <chat>
(download a chat's history).
Terms used on this page:
- Conversation: one discussion inside a chat. In a busy group several conversations run at the same time and their messages mix. tg separates them; a conversation is the list of messages that belong together, oldest first.
- Piece: a short part of a conversation, about 1,200 characters. Long conversations are cut into pieces so that each piece is about one thing.
- Vector: a list of numbers that stands for what a text means. Texts about the same thing get similar vectors, even when the words or the language differ.
- Model: the small program that turns text into a vector. It runs on your computer by default.
Topic search is not the topic: field of message search. topic: keeps one forum topic of
a Telegram group. A conversation here is something tg finds by itself, in any chat.
| Task | Command |
|---|---|
| Find a discussion by its subject, in one chat or in every prepared chat | tg personal search conversations "<question>" |
| See the conversations of a chat, newest first | tg personal conversations list <chat> |
| Read one conversation from start to end | tg personal conversations show <id> |
| Open the whole conversation a message belongs to | tg personal conversations show <chat> <message> |
| Find other conversations about the same thing, in every chat | tg personal conversations related <chat> <message> |
| Limit results to a person, a period or a word | tg personal search conversations "<question>" --filter '<query>' |
| See what is out of date and catch up |
tg personal conversations status, tg personal search conversations "<question>" --catch-up
|
| Let your own AI agent improve how messages are grouped | tg skill show link-conversations |
Word search (tg personal search messages) |
Topic search (tg personal search conversations) |
|
|---|---|---|
| You know | the exact words, a name, a date, a file | only what it was about |
| It finds | single messages that contain your words | whole conversations about your question |
| Other words for the same thing | no: "flat" does not find "apartment" | yes |
| Other languages | no | yes, about 100 languages with the default model |
| Exact filters (sender, date, file, link) | yes, the full query language | yes, through --filter
|
| Preparation | download the history | download the history, then build and embed
|
| Asks Telegram | yes, by default | no, it reads only this computer |
Use word search when you remember a word, a number, a name or a file. Use topic search when you remember the subject but not the wording, when a discussion is spread over many short messages, or when people wrote in another language. If you are not sure, try topic search first: it also matches your words, so a conversation that uses them still comes near the top.
Prepare one chat once, then ask as many questions as you like.
Your request to your AI agent:
In Book club, find where we discussed moving the meetings to another place. Show the conversation.
What happens, step by step:
tg personal messages sync "Book club" # 1. download the history, if it is not saved yet
tg personal conversations build --chat "Book club" # 2. separate the chat into conversations
tg personal models text download e5-small # 3. once: download the model, 135 MB, shared with max
tg personal conversations embed --chat "Book club" # 4. compute a vector for each piece
tg personal search conversations "where do we meet" --chat "Book club" # 5. ask
tg personal conversations show 91 # 6. read the conversation that was foundSteps 2 to 4 run on your computer and never ask Telegram. embed continues where it stopped if you
interrupt it. Later questions need only step 5; keep it current explains when to
repeat steps 2 and 4.
Example result (fictional):
0.874 91 2026-09-14 18:02–18:40 23 messages · 4 people · from message 4180 (messages 4185–4192) msg:telegram/500/7/4185
0.851 64 2026-08-02 10:15–10:31 9 messages · 3 people · from message 3302 (messages 3302–3310) msg:telegram/500/7/3302
A result is a lead, not an answer: open the conversation and read the messages before you rely on it.
Topic search has three stages. The first two prepare a chat once; the third runs for each question.
tg personal conversations build reads the saved messages of a chat, oldest first, and decides for each
message which earlier message it continues. It uses no AI and does not ask Telegram. The links come from,
in this order:
- Telegram's own replies. A message sent as a reply belongs to the message it answers.
- Your agent's answers, if you asked your agent to link the chat (below).
-
A mention. A message that mentions someone belongs with that person's latest message. A mention
is an
@username, a mention Telegram marks, or a message that starts with the person's username and a colon or comma (anna: agreed). tg looks back at most 50 messages, and only in the same forum topic. - The same person writing again. A person's next message continues their previous one if it comes within 5 minutes and within the last 10 messages.
A message with no link starts a new conversation. A conversation can skip the messages in between that
belong to other conversations. The rules guess: they can split one discussion in two or join two
discussions into one. A new build replaces the last one, so take a conversation's number from a fresh
list rather than keeping it.
tg personal conversations embed cuts each conversation into pieces of about 1,200 characters, at message
boundaries. Each line of a piece is "sender: text". A message longer than a piece is split into
overlapping parts, so the model reads all of a long message, not only its start. Messages without text
add nothing. The model then turns each piece into a vector and tg saves it. The text of the piece is not
saved again; only its vector and a fingerprint that shows when the text changes.
tg personal search conversations looks for your question in two ways and joins the results:
-
By meaning. The question becomes a vector too. tg compares it with the vectors of all pieces and
ranks each conversation by its closest piece. With the default model
e5-small, a match by meaning must be close enough: its similarity score must be above 0.80, on a scale where 1 means the same meaning. -
By words. tg also looks for each word of your question in the saved messages. A word of three or
more letters also matches longer words that start with it (
meetfindsmeeting). Typos are not corrected.
A conversation found both ways ranks above one found only one way. Each result says how it was found:
"by": ["meaning"], ["words"] or both. Without a downloaded model, search still runs by words alone
and the answer says "meaning": "unavailable". A chat that was never built is not searched: build it
first.
Why this finds more than word search: word search needs the same word in the message. Meaning search compares what the texts are about, and it reads a piece of the conversation, not one message. So "where do we meet" can find a discussion where one person asks "library or café?" and another answers "the café on Main Street is quieter". The word half keeps exact names and rare words from being missed.
The technical details — the measured rules, the pieces, the vectors and the ranking — are on how search works.
In the terminal each result is one line:
- the similarity score, or
—when only words found it; - the conversation number, when it started and ended, how many messages and people, its first message;
-
(messages 4185–4192): the piece that matched best (or the message, when only words found it), so you know where to start reading; - the message locator (
msg:…), whichtg personal messages showandtg personal messages contextaccept; -
stalewhen the text changed after its vector was computed: the score is for the old text.
With --json, the answer also has meaning (searched or unavailable), the model used and
readiness: which chats were searched by meaning, which only by words, which are out of date and which
were never built. When something limited the search, stderr names the chats and the command that fixes
it, for example conversations embed --chat <chat>.
tg personal conversations list "Book club" started_at>=7d
tg personal conversations show 91 # one conversation, oldest first
tg personal conversations show "Book club" 204 # the conversation message 204 is in
tg personal conversations related "Book club" 204 # other conversations about the same thing, in every chat
tg personal messages links "Book club" 204 # why that message is where it isrelated uses the vectors embed saved and runs no model, so it answers quickly. It is search by
meaning only.
You remember the subject, not the words.
tg personal search conversations "who is bringing food to the picnic"This searches every prepared chat. It can find a conversation where people wrote "I'll take sandwiches" and "Bob has the drinks", which word search would miss.
The discussion was in another language.
tg personal search conversations "renting a flat" --chat "Valencia expats"With the default model, this can find a discussion written in Spanish about a "piso".
Narrow by person and period. --filter takes the strict query language. A
conversation qualifies when at least one of its messages matches the whole filter. Your question is
still matched by meaning.
tg personal search conversations "budget for the trip" --filter 'from:"Alice Synthetic" date:30d'tg personal search conversations "contract terms" --filter 'has:file' --timezone Europe/MadridStart from one message. You found a message with word search and want everything else on that subject:
tg personal search messages '"deposit back" chat:"Valencia expats"'
tg personal conversations related "Valencia expats" 5120Search your bots' chats too. By default topic search uses the active account. --source personal,
bots, all or a messenger name widens it; each result then says which account it came from.
tg personal search conversations "delivery delayed" --source allFetch new messages before you ask. --sync-first downloads new messages first, within the chat,
time and message bounds of message search.
tg personal search conversations "where do we meet" --chat "Book club" --sync-firstNew messages reach a conversation only after the next build, and a vector only after the next embed.
tg personal conversations status # what is behind, chat by chat
tg personal conversations build # every chat that changed, and groups never built
tg personal conversations embed # every built chat with pieces left
tg personal search conversations "renting a flat" --catch-up # catch up first, then searchstatus counts, for each built chat, the messages the build has not seen (new, edited, deleted), the
pieces whose vector is current, stale or missing, and how many groups were never built. Without --chat,
build, embed and search conversations --catch-up take at most 20 chats a run (--max-chats) and embed at most
2,000 pieces a run (--max-chunks); run them again to go on. They never download a model.
--catch-up cannot be combined with --filter or --source: build and embed the chats you want first.
When the rules change in a new version, status and tg personal store check name the chats built with the
older rules; build them again.
When a message is deleted, its text leaves the vectors too.
tg personal messages sync <chat> --catch-up can also build and embed a chat right after it downloads it
(download a chat's history).
The rules miss links that only the meaning shows. Your own AI agent (for example Claude Code, Codex, Cursor or Gemini CLI) can add them:
tg skill show link-conversations # the agent's instructions
tg personal conversations batches status --chat "Book club" # how many messages and batches, how much textThe agent reads the instructions, tells you how much text it would read and waits for your yes. Then it
takes the chat a batch at a time (tg personal conversations batches next), decides which earlier message each one
answers and saves its answer (tg personal conversations links add). The next build uses them: Telegram's own
replies come first, then the agent's links, then the rules. tg personal conversations links clear --chat "Book club"
drops the agent's answers; build again afterwards. In this workflow tg itself does not call a model. The
profile permission conversations.links decides whether answers may be saved.
tg can also send the batches to a model service itself. Ordinary build uses rules and saved links;
tg personal conversations build --chat <chat> --analyze sends bounded batches to a configured OpenAI-compatible
or Anthropic endpoint. An explicit --chat is required. Before the first batch it shows the volume, the
endpoint and the token limit, and asks for consent. Consent is remembered for that account, chat and
provider until you revoke it. Defaults are 50 messages per batch (--size) and at most 100,000 tokens
reserved per run (--max-tokens); --yes grants consent in scripts. tg personal conversations consents list
lists consents; consents revoke --chat <chat> revokes them. Built-in analysis works only from the
command line, not over MCP, and keys never go into the settings file.
By default nothing leaves your computer. The model runs here, and a model is downloaded only when you ask:
tg personal models text list # the models, and which are downloaded
tg personal models text download embeddinggemma --accept-termse5-small is the default: small and fast, about 100 languages. embeddinggemma finds more but runs
about seven times slower, and downloads only with --accept-terms, since it comes under Google's Gemma
terms. Vectors of two models are never mixed: search with the model you embedded with. A chat embedded
only with another model is named on stderr.
On a recent laptop e5-small embeds about 30 pieces a second; a group of 100,000 messages takes a
little over 20 minutes, once. Later runs embed only what changed.
A service can compute the vectors instead, with your own key:
tg personal models text key set openai
tg personal conversations embed --chat "Book club" --provider openai
tg personal search conversations "renting a flat" --provider openaiThen the text of the chat's conversations goes to that service, and each search sends your question.
Before sending anything, embed says how many pieces, at most how many tokens and at most what price,
and waits for your yes (--yes in scripts; --max-tokens sets a limit). --base-url takes any server
with OpenAI's embeddings API, such as Ollama or LM Studio on your own computer, with --model and
--dims. tg personal models text key remove openai forgets the key. Remote embedding settings also apply to
searches your agent runs over MCP, so its questions go to that service too.
- Message search: exact words, people, dates and files.
- How search works: the technical side of the rules, pieces, vectors and ranking.