Agents that remember
Think about the difference between a contractor who has worked on your house for two years and one who turns up on day one. The first needs four words. The second needs the whole history again, and gets some of it wrong, and you find yourself explaining the boiler for the fourth time. Most AI assistants are permanently the second contractor. Users notice within a week, and they stop trusting the product somewhere around the third time it asks a question they already answered.
This page is for teams selling an assistant that is supposed to get better with use. Advisory tools, account management, anything where the same person comes back next month expecting continuity.
Brains is live on the Ringside API and every account can call it today, so the API below is what you build against right now. If those routes answer 404, the flag is off in that environment, which is what a self-hosted or staging deployment looks like.
Search is not memory
The usual answer is RAG. Embed the transcripts, drop them in a vector store, retrieve the top few on every turn. That gets you a long way and then hits a wall, because a search index answers one question: what did I store that looks like this? It has no view on whether any of it is still true. A decision the customer reversed in March scores exactly as well as the one that replaced it, and both get injected, and the model has to guess.
Memory has to answer the harder question, which is what is still worth knowing. A Brain reads the turn after it happens and writes down the durable parts as facts rather than transcript. When you recall, those facts are ranked by meaning, by how important they were, by how recently they mattered and by how often they have actually been used since. Facts that stop earning their place decay on a curve you configure. Forgetting is a feature here, not an oversight: an assistant that never forgets slowly becomes an assistant that cannot tell you what the current answer is.
Isolation is a shape, not a check
Scopes are a path, N levels deep. A recall running at /acme/q3-audit sees its own scope and every ancestor above it, all the way to the root. It never sees a sibling. That is not a permission check sitting in front of the query, it is the query: matching is defined over the caller's own path and the paths above it, so there is no code path that reaches sideways into another client. A cross-client leak is not a bug you could introduce by forgetting something. It is a result shape the recall cannot express.
In practice your operators can search the whole practice at once with subtree: true, which extends a recall downward through descendants and stays bounded to the caller's own Brain. Their clients never get that view of each other.
What you write versus what you skip
- • Two headers instead of a pipeline. Send
BrainandBrain-Scopeon a normal chat completion and Ringside recalls before the model call, injects what it found and learns from the result afterwards. There is no recall step or observe step in your application code. - • Documents load the same way facts do. PUT an entry with a scope, a class and a body, and it is chunked and embedded server-side.
- • Billed to the end customer. Pass
FC-Customerand the embedding and learning work bills against that customer's budget, the same as any other call. - • Encryption per Brain, set at create time. The sample below uses
standard.
Architecture
In code
# One Brain per deployment; scopes per tenant + per engagement
brain = client.post("/v1/brains", json={"name": "advisor", "encryption": "standard"}).json()
# Load a tenant's documents (chunked + embedded server-side)
client.put(f"/v1/brains/{brain['id']}/entries", json={
"scope": "/acme", "class": "reference",
"title": "Master services agreement", "body": msa_text,
})
# Ambient memory on a normal completion: recall before, learn after.
# No recall/observe code on your side.
resp = client.post("/v1/chat/completions",
headers={"Brain": brain["id"], "Brain-Scope": "/acme/q3-audit",
"FC-Customer": tenant.id},
json={"model": "match:anthropic/sonnet",
"messages": [{"role": "user", "content": "what did we agree on payment terms?"}]})
# The operator searches the whole practice at once; clients stay isolated.
client.post(f"/v1/brains/{brain['id']}/recall",
json={"scope": "/acme", "query": "open audit findings", "subtree": True})