{"slug": "how-i-implemented-memory-in-my-ai-agent-rexa", "title": "How I Implemented Memory in my AI Agent REXA", "summary": "A developer implemented a long-term memory pipeline for the REXA AI agent, enabling users to explicitly save preferences via a save_memory tool. The CLI sends memory text through an authenticated HTTP API to the REXA website backend, which verifies the bearer token, generates embeddings, and stores entries in PostgreSQL with pgvector. Memory retrieval is not yet implemented; only the saving pipeline is complete.", "body_md": "One thing I wanted REXA to eventually have was **long-term memory**.\n\nThe idea is simple: when I explicitly tell REXA something like:\n\n“Remember that I prefer PostgreSQL for my backend projects.”\n\nREXA should be able to save that information so it can be used later.\n\nThe important distinction is that **REXA does not currently retrieve these memories yet**. Today, the implemented part is the **memory-saving pipeline**. Retrieval is the next part I plan to build.\n\nI also didn't put the memory database directly inside the REXA CLI.\n\nInstead, I built the memory infrastructure behind the **REXA website backend** and let the CLI communicate with it through an authenticated HTTP API.\n\nThe architecture looks like this:\n\n```\nREXA CLI\n   │\n   │ POST + Bearer Token\n   │ { \"text\": \"...\" }\n   ▼\nREXA Website Backend\n   │\n   ├── Verify Token\n   ├── Identify User\n   ├── Process Text\n   ├── Generate Embeddings\n   └── Store Memory\n   │\n   ▼\nPostgreSQL + pgvector\n```\n\nOne important design decision is that REXA does **not** automatically save every preference or every piece of conversation.\n\nInstead, the model uses a tool called:\n\n```\nsave_memory\n```\n\nWhen the user explicitly asks REXA to remember something, the model calls this tool.\n\nFor example:\n\n```\nUser:\nRemember that I use Bun for my backend projects.\n```\n\nREXA can decide that the appropriate action is to call:\n\n```\nsave_memory(...)\n```\n\nSo memory creation is currently **explicit**, rather than REXA silently storing everything the user says.\n\n`save_memory`\nThe `save_memory` tool receives the text that the user wants to store.\n\nBefore sending it to the backend, the CLI performs some basic validation.\n\nThe CLI:\n\nThis means extremely large documents may never leave the CLI in the first place.\n\nFor normal memory entries, however, the text can then be sent to the backend.\n\nThe REXA CLI does not directly access PostgreSQL or pgvector.\n\nInstead, it sends a `POST` request to the REXA backend:\n\n```\nPOST https://rexa-server.onrender.com/api/cli/memory \nAuthorization: Bearer <token> \nContent-Type: application/json\n```\n\nThe request body contains only the memory text:\n\n```\n{ \n  \"text\": \"I prefer PostgreSQL for my backend projects.\" \n}\n```\n\nNotice that there is **no `userId` in the request body**.\n\nThe identity of the user comes from the authentication token.\n\nThe token comes from the REXA CLI login flow.\n\nThe CLI first authenticates through the REXA backend using:\n\n```\nPOST /api/cli/verify\n```\n\nThe same Bearer-token mechanism is then used when making authenticated CLI requests.\n\nThis means the CLI doesn't simply tell the backend:\n\n```\n{ \n  \"userId\": \"123\", \n  \"text\": \"...\" \n}\n```\n\nInstead, it says, effectively:\n\n```\nHere is my authentication token. \nHere is the memory I want to save.\n```\n\nThe backend is responsible for determining who that token belongs to.\n\nWhen the memory request reaches:\n\n```\n/api/cli/memory\n```\n\nthe backend first checks the Bearer token.\n\nIf the token is missing, invalid, or expired, the request is rejected.\n\nThe CLI can surface these authentication failures and provide a hint to run:\n\n```\nrexa login\n```\n\nThis makes the authentication layer separate from the actual memory-processing logic.\n\nAfter successful token verification, the backend knows which authenticated user is making the request.\n\nThis is important because memory is **per user**.\n\nThe CLI doesn't provide the user identity manually.\n\nInstead:\n\n```\nBearer Token \n      ↓ \nToken Verification \n      ↓ \nAuthenticated User \n      ↓ \nMemory belongs to that user\n```\n\nThis prevents the client from simply claiming that a memory belongs to some other user.\n\nOnce authentication succeeds, the backend receives the text.\n\n```\n\"I prefer PostgreSQL for my backend projects.\"\n```\n\nFrom here, the rest of the pipeline happens on the backend.\n\nThis is where the database and embedding infrastructure come into play.\n\nThe CLI doesn't need to know how the backend implements this processing.\n\nConceptually, the pipeline is:\n\n```\nText \n ↓ \nChunking \n ↓ \nBatch Creation \n ↓ \nEmbedding Generation \n ↓ \nVector \n ↓ \nPostgreSQL + pgvector\n```\n\nThese are backend responsibilities.\n\nThe backend can split larger pieces of text into smaller chunks.\n\nConceptually:\n\n```\nLarge Text \n   │ \n   ├── Chunk 1 \n   ├── Chunk 2 \n   ├── Chunk 3 \n   └── ...\n```\n\nFor a tiny memory such as:\n\n```\n\"I prefer PostgreSQL.\"\n```\n\nthere may not be much to split.\n\nBut chunking becomes useful as the amount of stored information grows.\n\nAfter chunking, the backend can group chunks into batches for embedding.\n\n```\nChunks \n   │ \n   ├── Batch 1 \n   │     ├── Chunk 1 \n   │     ├── Chunk 2 \n   │     └── Chunk 3 \n   │ \n   └── Batch 2 \n         ├── Chunk 4 \n         ├── Chunk 5 \n         └── Chunk 6\n```\n\nThese batches are then passed to the embedding stage.\n\nThe text is converted into a numerical vector using an embedding model.\n\n```\n\"I prefer PostgreSQL for my backend projects.\" \n                    │ \n                    ▼ \n             Embedding Model \n                    │ \n                    ▼ \n      [0.12, -0.38, 0.74, ...]\n```\n\nThe resulting vector represents the semantic information contained in the text.\n\nThis is what makes vector-based memory possible.\n\nInstead of treating a memory as only a string of characters, the backend also stores a mathematical representation of its meaning.\n\nThe generated vector is stored using **pgvector** alongside the memory data.\n\nThe database layer uses:\n\n```\nPostgreSQL \n   + \npgvector \n   + \nPrisma\n```\n\nA simplified representation might look like:\n\n```\nMemory \n───────────────────────────── \nuser_id \ntext \nembedding \ncreated_at \n...\n```\n\nThe exact schema can evolve, but the important part is that the memory is associated with the authenticated user and has a vector representation that can later be used for semantic retrieval.\n\nOnce the memory has been successfully processed and stored, the API returns a success response.\n\nThe actual response from the controller is:\n\n```\n{ \n  \"success\": true, \n  \"message\": \"Data saved in memory\" \n}\n```\n\nThe CLI can then use that result to tell the user that the memory was saved successfully.\n\nSo the complete flow is:\n\n```\nUser \n │ \n │ \"Remember this...\" \n ▼ \nREXA model \n │ \n │ calls save_memory \n ▼ \nREXA CLI \n │ \n │ validate + trim text \n │ \n │ POST /api/cli/memory \n │ Authorization: Bearer <token> \n │ { \"text\": \"...\" } \n ▼ \nREXA Backend \n │ \n │ verify token \n │ identify user \n │ \n │ process memory \n │ ├── chunk \n │ ├── batch \n │ ├── embed \n │ └── store vector \n ▼ \nPostgreSQL + pgvector \n │ \n │ success \n ▼ \nREXA Backend \n │ \n │ { \"success\": true, \n │   \"message\": \"Data saved in memory\" } \n ▼ \nREXA CLI\n```\n\nThe main architectural decision was to keep **REXA itself separate from the memory infrastructure**.\n\nThe CLI is responsible for interacting with the agent and invoking `save_memory`.\n\nThe backend handles authentication and memory processing.\n\nPostgreSQL and pgvector provide the persistent storage and vector representation.\n\nSo the responsibilities are roughly separated like this:\n\n```\nREXA CLI \n→ Agent interaction + save_memory \n\nBackend \n→ Authentication + memory processing \n\nPostgreSQL + pgvector \n→ Persistent memory storage\n```\n\nThis also means the CLI doesn't need database credentials or direct database access.\n\nIt only needs an authenticated API connection.\n\nAt the moment, the implemented functionality is the **save side** of memory.\n\n```\nUser \n  ↓ \nsave_memory \n  ↓ \nAuthenticated API \n  ↓ \nEmbedding \n  ↓ \nVector storage\n```\n\nThe retrieval side is not implemented in the CLI yet.\n\nThe next step is to build the other half:\n\n```\nCurrent: \n\nSAVE \nUser \n ↓ \nsave_memory \n ↓ \nBackend \n ↓ \nVector Database \n\nFuture: \n\nRECALL \nCurrent Task \n ↓ \nMemory Search \n ↓ \nSimilarity / Relevance \n ↓ \nRelevant Memories \n ↓ \nREXA\n```\n\nThat is where things become much more interesting.\n\nThe goal isn't simply to give REXA a database full of memories.\n\nThe real goal is to build a system where REXA can eventually **find the right memory when it is actually useful**.\n\nAnd that's the part I'm building next.", "url": "https://wpnews.pro/news/how-i-implemented-memory-in-my-ai-agent-rexa", "canonical_source": "https://dev.to/itssubhamoy/how-i-implemented-memory-in-my-ai-agent-rexa-4dbh", "published_at": "2026-09-17 18:41:35+00:00", "updated_at": "2026-09-17 19:23:08.559338+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-infrastructure", "developer-tools"], "entities": ["REXA", "PostgreSQL", "pgvector", "Bun"], "alternates": {"html": "https://wpnews.pro/news/how-i-implemented-memory-in-my-ai-agent-rexa", "markdown": "https://wpnews.pro/news/how-i-implemented-memory-in-my-ai-agent-rexa.md", "text": "https://wpnews.pro/news/how-i-implemented-memory-in-my-ai-agent-rexa.txt", "jsonld": "https://wpnews.pro/news/how-i-implemented-memory-in-my-ai-agent-rexa.jsonld"}}