YAAR
Yesterday's Answers, Always Recalled
How Ahoy remembers, end to end: what it writes down and why, how it finds the right thing for a request in milliseconds, how it keeps time, and how it keeps every user's memory apart. Every number on this page was measured on real data.
Principles
The layers
One memory is not enough for an assistant that works for months. YAAR keeps several, each with its own job, store and way into the prompt.
Note kinds: profile · preference · goal · plan · project · decision · skill · person · fact · intention · event · knowledge
- Intention: “next time we work on billing, remind me to check VAT” fires once, only when a later request is that situation, then retires itself.
- Event: has its day, so “what do I have next week?” finds it by date.
- Temporary state: “this week I work from home” stops counting after its last day, in your time zone.
Writing a memory
After a reply, a background worker reads your message. Nothing is written while you wait.
- Queued safely. An outbox row (ids only, never text) and a queue message; delivered at least once.
- Claimed. One reader per message, so a retried job never writes anything twice.
- Cleaned. Policy and secret redaction run before any model sees the text.
- Compared. Your closest existing notes are fetched as correction candidates.
- Extracted. One model call gets your message (long ones in parts), its date so “tomorrow” becomes a day, the chat's project, the text of attached files, and the message Ahoy wrote before it, so “yes, let's do that” records exactly what was accepted.
- Written. Notes with kind, importance, optional start and end days and up to three links to existing memories; corrected notes are superseded and hand over their links; temporal facts are added, replaced or closed.
| Source | Starting confidence |
|---|---|
| written by you in the memory window | 0.95 |
| saved on purpose by Ahoy | 0.85 |
| your raw message | 0.80 |
| a distilled note | 0.75 |
| derived | 0.55 |
| the assistant's own words | 0.35 |
Saying the same thing again re-verifies a note and raises its confidence. An extractor outage makes the job retry; the night re-queues a user's failed jobs once.
Finding a memory
- One embedding, first. The request is embedded once, before anything else, and memory search, skill ranking and past-experience lookup all share that vector.
- In parallel: the memory search, the diary, dossiers and temporal facts.
- Two searches, pooled. One over every kind and one over notes only, so hundreds of raw excerpts can never take every place.
- Access policy. Filtered inside the vector store, then re-checked on every result.
- Only what is true now. Joined with the ledger: active, not expired, not already in the prompt.
- Gates. In a project's chat another project's memories come only when you name it; raw excerpts only when they add something and share a real word with the request; notes the judge keeps calling unrelated lose ground.
- Rank, and for questions about you or your past, a cross-encoder re-reads the best ten together with the question.
- Linked memories of the best three come along.
- Deep recall. For a question about the past where nothing found shares a real word with it, one rewrite call turns it into a few searches (names, places, synonyms) plus the diary, within 9 seconds. Otherwise Ahoy says plainly it doesn't remember, never guesses.
“Yesterday”, “last week”, “three days ago”, juče, prošle nedelje: a phrase about a time becomes a date range in your time zone, and memories from that range lead.
Ranking
Function words (the, of, sam, je) are left out of the word side. With a common 0.6 / 0.4 split and those words counted, a question in other words found the right note first 13 % of the time; by meaning, 80 %.
| Signal | Weight | Note |
|---|---|---|
| hybrid score | 0.55 | meaning and words |
| strength | 0.08 | grows with use, never falls with age |
| source trust | 0.08 | you > tools > diary > derived > assistant |
| is a note | 0.08 | notes over raw excerpts |
| kind hint | 0.06 | what the request is about → which kinds |
| importance | 0.06 | bounded: can never beat relevance |
| confidence | 0.05 | from the source, rises when repeated |
| usefulness | 0.04 | how often replies actually drew on it |
Then a cross-encoder (bge-reranker-v2-m3) for questions about you, with a hard 5-second limit and the previous order as fallback, and up to three linked memories of the best matches.
Notes at hand and the prompt cache
Your notes go into the prompt whole in your private chats, not only when a search finds them, and the search spends its places on everything else. To keep that affordable:
- The first two blocks are cached by the AI provider; everything for one request rides at the end.
- A corrected or forgotten note leaves at once: the frozen text stays identical and the request names it as no longer true.
- When notes outgrow their budget, the night rewrites them as pages by subject, and a page line survives only if every number, address and name in it appears in the notes it cites.
Measured: the right memory in the context went from 76 % to 85 % while uncached memory tokens per turn fell from ~2,400 to ~900.
Time
People and projects
Dossiers. For every person, company, project or server that two or more notes mention, the night keeps a page: name, aliases as the sources write them, and lines that each cite their sources. When a request names one (also inflected, through a Serbian stemmer), its dossier comes along. A line whose note was corrected leaves at once.
| Link | Weight | When |
|---|---|---|
| named by the extractor | 0.7 | a new note is directly connected to an existing memory |
| said together | 0.5 | notes from the same message |
| used together | 0.3, +0.1 each time | memories one reply drew on together: used together, linked firmer |
| carried | — | a superseded note hands its links to the newer one |
The night: the Dreamer
- Near-identical notes with the same words are merged; close pairs are judged by a model as same, replaced or both; notes past their end date are retired.
- Pairs are ordered by the date of the message, so an old note read in later never retires a newer one.
- It never writes note text: every change is a supersede or an invalidation, with history kept. One user at a time, so the server stays calm.
Teach: whole archives
Facebook and Instagram (JSON and HTML, old exports too), Telegram, WhatsApp, mail and Gmail Takeout, PDF, Word, Excel, CSV, calendars, contacts and git repositories.
- Read in place. A multi-gigabyte zip is never unpacked; raw messages stay searchable as full text and are never embedded one by one.
- Who said it. Nothing is spent before you pick who you are in the messages. Notes about you come only from your lines.
- Dated. Every note carries the day of its message, so it may only correct what was said before it.
- Tidy. At the end, duplicates are compared all at once and retired, never deleted.
- Clones. Any person in the messages can become a personality, tested blind on messages it never saw: real and imitated answers mixed and unlabelled, each rated on whether that person wrote it. Measured on real archives: 9.4/10 and 10/10.
- Repositories are read to be understood: a graph of the whole code, each part with its most central files, history and connections. One real project went from ~15M tokens to ~1.1M.
Learning what helps
After replies that used memory, one background call rates each memory that was in the context: unrelated, related, or exactly what was needed. Once a day the ranking weights are fitted to those ratings and adopted only with at least 300 rated rows and a measurably better order on replies held out from the fit. A note rated unrelated again and again loses ground unless the request shares a word with it.
Isolation and privacy
Every stored memory carries its team and exactly one access key: private to a user, a project's members, one conversation, one agent for one user, the team, or global.
| Where | Reads | Writes |
|---|---|---|
| Your private chat (web, Telegram, phone, Bata) | everything personal + your projects | personal or project |
| Shared project | the project and the conversation | project; what you say about yourself goes to your own memory |
| Channel group, guests | the project and the conversation | project only |
| Webhook trigger | restricted, nothing personal | nothing |
| Outgoing call | only what the call's goal needs | — |
Deleting is two-phase: gone from search at once, the stored text and graphs removed in the background by id and by filter, so nothing is left behind. “Delete all memory” takes notes, rules, the notebook, experience, diary and dossiers. Your server's memory never leaves your server.
When things fail
| Failure | What happens |
|---|---|
| Fast storage down | short timeouts and a circuit breaker; caches skipped, history from the database, jobs wait |
| Memory service down | the reply goes on with your profile and recent messages |
| Cross-encoder slow | after 5 seconds, the previous order |
| Embeddings unavailable | words only |
| Extractor down | the job retries; the night re-queues it once |
| Main database down | the request fails: it is the source of truth |
Measurements
Facebook archive: a real project with 2,586 notes, 30 questions per set.
| older model | BGE-M3 | + cross-encoder | |
|---|---|---|---|
| Other words: right note first | 11 / 30 | 22 / 30 | 26 / 30 |
| Other words: in the context | 22 / 30 | 29 / 30 | 29 / 30 |
| Same words: right note first | 24 / 30 | 29 / 30 | 29 / 30 |
| Same words: in the context | 28 / 30 | 30 / 30 | 30 / 30 |
Recall on real messages: questions built from an owner's own messages, each asked as in a new chat.
A memory like this, on your own server
Your chats, notes and archives stay on a server that is only yours.
Launch your Ahoy