All tutorials

Give your agent memory with Brains

15 min

Create a Brain, load knowledge, recall it, let it learn from conversations, then wire it ambiently into a chat completion. Your agent remembers across sessions and users, with per-tenant isolation and zero RAG code.

Prerequisites
  • A Ringside API key with api:write

  • A Ringside API key exported as FC_API_KEY

  • curl and jq (or any HTTP client)

  • Read the Brains concept page if scopes, tiers and the forgetting curve are unfamiliar

01

When you need this

Plain RAG answers "what is similar to this query". It does not decide what is worth remembering, age stale facts, or keep one customer's knowledge out of another's. A Brain is managed memory: a tree of scopes holding typed entries, recalled by a ranking that weighs meaning, importance, recency and use, that learns durable facts from conversations on its own and forgets the rest on a curve.

Reach for a Brain when your agent should remember across sessions and users, when some facts must stay fresh while others go stale, or when you need per-tenant memory walled off by construction. If you only need one-shot retrieval over a fixed corpus, plain managed RAG is enough.

Brains is live on the Ringside API and every account can call /v1/brains today, so you can work straight through this tutorial. A 404 on these endpoints means the flag is off in that environment, which is what you will see on a self-hosted or staging deployment.

02

Create a brain

A Brain is owned by your developer account. Pick an encryption mode now: none (fast default), standard (content sealed at rest), or strict (content and embeddings sealed). The embedding model is fixed for the life of the brain.

BRAIN=$(curl -s https://api.fightclub.pro/v1/brains \
  -H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"support-assistant","encryption":"none"}' | jq -r .id)
# => brn_...

If this returns 404, Brains is not enabled for your account yet — that is the early-access gate, not a bad request.

03

Load reference knowledge

Reference entries are your documents: chunked and embedded server-side, so you do not pre-split. Scope them with a path so a tenant only ever sees its own. A leaf scope sees itself plus every ancestor, never a sibling.

curl -X PUT https://api.fightclub.pro/v1/brains/$BRAIN/entries \
  -H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
  -d '{"scope":"/acme","class":"reference",
       "title":"Refund policy",
       "body":"Refunds are issued within 14 days for unused credit ..."}'

Entry bodies can be up to 100 MB, sized for whole legal acts and manuals. Embedding is synchronous in the request, so for a large corpus, ingest documents concurrently.

04

Recall

Recall embeds the query and ranks candidates across the inherited scopes by relevance, tier, recency and use. Add explain: true to see exactly why each hit surfaced. Add subtree: true to search a scope and everything beneath it (operator-wide search over your branch).

curl https://api.fightclub.pro/v1/brains/$BRAIN/recall \
  -H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
  -d '{"scope":"/acme","query":"can I get a refund?","explain":true}'
05

Let it learn

Send a finished turn to observe. A cheap gate drops greetings and off-task chatter; what survives is reconciled into the smallest change against existing memories. Most turns change nothing, which is correct. It is asynchronous by default; pass wait=true to see the result inline.

curl "https://api.fightclub.pro/v1/brains/$BRAIN/observe?wait=true" \
  -H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
  -d '{"scope":"/acme",
       "transcript":"user: our account manager is Dana Lee\nassistant: noted"}'
# => { "changed": 1, "proposed": 0, "conflicts": 0, "used": 0 }

The observe result also carries costUsd, so you can meter what the learning pipeline itself costs.

06

Make it ambient

Attach two headers to a normal chat completion. Ringside recalls relevant memory and injects it before the model call, then learns from the turn in the background. No recall or observe code on your side. A request without the Brain header takes the normal path unchanged.

curl https://api.fightclub.pro/v1/chat/completions \
  -H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
  -H "Brain: $BRAIN" -H "Brain-Scope: /acme" \
  -d '{"model":"match:anthropic/sonnet",
       "messages":[{"role":"user","content":"who is our account manager?"}]}'
# the model answers "Dana Lee" from injected memory

Pass an FC-Customer header on recall, observe and entry writes to bill the embeddings and learning to that customer instead of your dev pool.

07

Tune and forget

The defaults are sensible, but the memory_policy is yours: stop it learning off-task content with an ignore block, change how fast things fade with a per-type decay curve, or set decay_anchor: "last_used" so used facts never age out. For regulated workflows, set learn_mode: "review" so load-bearing changes wait in a queue. To erase everything tied to one end-user, call forget by scope or entity.

# spaced repetition + ignore off-task turns
curl -X PATCH https://api.fightclub.pro/v1/brains/$BRAIN \
  -H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
  -d '{"memory_policy":{"decay_anchor":"last_used",
       "ignore":{"require_task_relevance":true}}}'

# right to be forgotten: erase a tenant's branch
curl -X POST https://api.fightclub.pro/v1/brains/$BRAIN/forget \
  -H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
  -d '{"scope":"/acme"}'

Where to next