Blog · Design notes

Agents are moving to the edge.
Their memory should too.

August 24, 2026 · Anup Talwalkar · Polign

For quite some time I've been thinking about the memory problems with agents. They all get treated as information retrieval problems hiding behind issues with the infrastructure itself.

What I've observed in my own agent use is that there are certain behaviors and patterns that agents and LLMs can't follow and sometimes miss or forget. Users change their minds, contradict themselves, facts go stale, and so on. Right now we make LLMs sort this out by rereading and recalling old text, at the cost of either tokens or accuracy. A schema with types and supersession rules answers it instantly, and a simple SQL database has done that for decades.

Going forward, this will matter more as agents move to edge devices. Embedders already run on laptops, while small models get better every few months. At one point in GCS, there was an internal directive not to rely heavily on internal AI platforms. All the while, memory still assumed access to a search cluster or a managed API on the other side of a network link. A device that gets unplugged at closing time can't keep its memory in someone else's cloud.

I wrote a small prototype. The agent's memory is a typed database on top of polign_db's hybrid vector + BM25 engine, living in an object store or a plain local directory on an edge box. The server holds nothing durable, so even when the devices and processes are killed or restarted, the memory remains intact.

While the agents use and LLMs have advanced farther, memory is the piece that has not caught up. Most agent stacks still put memory on a big self-hosted search cluster or in a managed cloud service. Either way the agent drags along a network dependency, hardware, and operational cost, and in the managed case its memory sits on someone else's servers all while chugging the retrieval count meter and costing pennies.

This should change, or at least get much simpler.

The local agent needs local memory that is durable, searchable, typed, and able to survive the process around it being killed or restarted. That is not what I originally designed Polign/polign_db for. I wanted it to serve search from object storage with as little resident state as possible, and with query costs that do not grow with the size of the data. It turns out that those properties map well to edge hardware.

The model can be probabilistic, but the memory needs to be deterministic.

There is a fundamental problem with asking an LLM to manage its own memory. The model has to decide whether a statement is new or a correction, whether it contradicts something already stored, whether a value is a number or a string, and whether a new value should replace an old one.

A typed memory store moves those decisions out of the model and into the schema. The model extracts a fact; the database determines what that fact means.

For example, take two memories: prefers_editor = "vim" and daily_step_goal = 9000. The schema says prefers_editor holds one value, so when the user switches to emacs, the new value replaces vim and the old record is kept as history. The schema also says daily_step_goal is a number, so "8000" as a string gets rejected, and 9000 goes in as a real number you can hit with a range filter later.

In my experience, the model never has to notice the contradiction or remember that the value is a number. It should just be recalled from the memory. This split matters more and more as models get smaller and shift towards edge devices. Instead of hoping a model to infer the right semantics, let the rules live in the database.

Why a cold-first database fits on small hardware

The design decision that makes this possible is described on the how it works page: in polign_db, the bucket is the database. The server holds nothing durable. Vectors, indexes, text, and metadata all live in the store; the server reads what a query needs, caches what is hot, and comes back from a restart with nothing to rebuild.

I made that choice for cost and operational reasons, at a small cost of initial latency. At the edge it solves a different set of problems.

Small footprint: The server is one lightweight binary with one flag to configure the underlying store. For example, the Wikipedia demo I built serves 12.5 million passages from S3 while the server idles at about 37 MiB RSS, and the whole demo, embedder and web app included, fits on a 2 GB ARM machine. Most of the memory is used for embedding generation and query concurrency, not the database itself.

The store can be local. -store takes S3, GCS, an S3-compatible server like MinIO, or a plain directory: fs:/var/lib/polign. Point it at a directory and the device itself holds the durable state. Nothing more.

The failure model is simple. Edge machines get rebooted, crash, and lose power. All it needs is to restart the server pointing to the same bucket and the state is restored.

The contract does not change with the store. Code written against a local directory works against an S3 bucket, because the durable state was never inside the server to begin with.

The agent memory prototype

To make this concrete I built another memory prototype: a terminal agent whose memory is a typed database instead of retrieved text. You can try running it on your own machine with a local directory as the store, or point it to your bucket.

Each memory is a record: kind, subject, predicate, value, confidence, status. Predicates come from a registry that says what type each value is and whether it holds one value or many. So prefers_editor holds one value, and daily_step_goal is a number.

You need three things to run it:

Then point the demo at a local directory:

./run-demo.sh fs:./demo-bucket

Claude models are the default. To use an OpenAI model instead, pass the model id and the provider is inferred from it:

./run-demo.sh fs:./demo-bucket -model gpt-5

Tell it something:

you> I use Vim as my editor.
  → remember_preference({"subject":"user","predicate":"prefers_editor","value":"vim"})
  ← {"stored":{"id":"m-...","value":"vim","status":"active",...}}

Numbers work as numbers:

you> Is my step goal above 8000?
  → recall({"subject":"user","predicate":"daily_step_goal","value_min":8000})
  ← {"count":1,"records":[{"value":9000,...}]}

That comparison ran in the database. The model never had to look at a paragraph and decide whether 9000 is more than 8000.

Semantic recall sits next to the filters. The demo embeds queries with a small local model (a one-time download, about 43 MB) and searches the same records. Embedding, validation, indexing, storage: all of it happens on the box.

The language model is still a cloud API in the current demo, so this is a local memory layer, not a fully local agent. But the part that has to survive never leaves the device.

What happens when the server dies?

The demo doubles as a durability test. Kill the agent process and ask again. It still knows. Then kill the database server too and restart it from the bucket. The memory is still there, and the step goal is still a number.

On a cloud VM this is a party trick. On a device that gets unplugged at closing time, it is the requirement. Losing the server process does not mean losing the memory.

Where this is useful

A few places where local typed memory beats a memory API on the other side of a WAN link.

Your personal agent

A local agent keeps your preferences, contacts, and history in a directory you control. Moving to a new machine is copying the directory. Backing up is syncing it to storage you own.

The demo still talks to a cloud model, but the memory already has the property that matters: it belongs to your machine, not to a memory service.

Why not SQLite?

SQLite is excellent at what it does, but that's not what I wanted when I meant stateless. SQLite's durable state is the database file. polign_db's durable state is an object store, and the server is just a disposable node.

That matters when the same database has to run against a local directory today and S3 tomorrow without the application changing, or when hundreds of agents connect to the same memory. The whole deployment spectrum is fs:/var/lib/polign → MinIO → S3.

Why not just use a vector database?

Vector databases or graph databases do work to some extent, but agent memory is not only semantic similarity. "What editor does the user prefer?" is a retrieval question. "Is the user's step goal above 8000?" is a database query. Agent memory needs both, plus the things similarity search has no way to express like types, cardinality, supersession, history, exact predicates, and numeric comparisons. Semantic search is good at finding memories, but it shouldn't be the source of truth while making critical decisions.

The local agent stack

The stack I expect to see more of is a pile of small, boring parts:


┌─────────────────────────────┐
│         Local LLM           │
│         conversation        │
└──────────────┬──────────────┘
               │
┌──────────────▼──────────────┐
│        Typed memory         │
│                             │
│ validation                  │
│ supersession                │
│ structured filtering        │
│ semantic recall             │
└──────────────┬──────────────┘
               │
┌──────────────▼──────────────┐
│      Durable local store    │
│        /var/lib/agent       │
└─────────────────────────────┘

The embedder and the databases are already capable of running on small hardware today. As local models improve, the whole agent will be able to move onto the device. At that point its memory does not need to be an afterthought. It is just an interface for the storage you own.

Sign up

None of this needs an account. The downloads are not gated, everything runs on your own machines, and the bucket is yours. The getting started guide covers the install.

If you want a hand getting started, a managed deployment in your own cloud, or a note when the local-agent pieces ship, leave your email on the signup page and we are happy to follow up!