Home / Articles

Your Agent's Filesystem Is a Decision: Deep Agents Backends Explained

LangChain Deep Agents backends decide which files survive: five built-ins, CompositeBackend routing, the namespace trap in multi-user stores and sandboxes.

Publisher: REACTAPP.TOP9 min read2174 words

This article is published in English.

Sources: LangChain Deep Agents: Your Agent’s Filesystem Is a Decision. Most People Make It by Accident. IV

The most frightening agent bug doesn't crash. It quietly forgets yesterday, exposes someone else's memory or writes valuable knowledge into scratch space you never meant to lose. In LangChain's Deep Agents, the place where those outcomes get decided is the backend, and the aim here is to stop treating the filesystem as magic and decide on purpose what survives, what disappears and who can see it.

Every Deep Agents agent gets a filesystem, but where that filesystem actually lives is up to you: in memory, on disk, in durable storage that spans threads, or a mix. A wrong pick is how an agent silently forgets everything between conversations.

The bug behind the vanishing notes

Say you build an agent with the filesystem tools switched on and see it save notes to /memories/findings.md in the middle of a chat. It reads them back later in the same conversation. All fine, so you ship. The next day a user returns, mentions something the agent supposedly knew yesterday, and the agent has no idea. The notes are gone. No error, no log line. The agent simply began fresh and didn't say so.

Nobody writes that bug deliberately. It comes from a default: the agent's files go into thread-scoped scratch space that ends together with the conversation. Skip the decision about where files live and the framework decides for you. For a scratchpad that's fine, and for memory it's the wrong choice.

Permissions, memory, sandboxes and deployment all build on this one choice. With the backend right, the rest falls into place, and with it wrong you chase disappearing notes for afternoons.

The filesystem is virtual and the backend is where it lands

The agent always sees an ordinary filesystem with ordinary paths. The backend decides what that filesystem is made of, and you can swap backends without changing how the agent behaves.

Six filesystem tools are provided: ls, read_file, write_file, edit_file, glob and grep. To the model they act on a plain filesystem. It writes /workspace/plan.md, reads it later and greps through /repo as though it had a disk, and none of that changes whichever backend is underneath.

The backend is the implementation behind that abstraction. It answers something the agent never wonders about. If the model writes /workspace/plan.md, where do the bytes go? Into Python memory that disappears when the process exits? Onto a real disk? Into a database that lasts across every conversation the user will ever have? Each answer corresponds to a different backend.

The choice is made by handing a backend to create_deep_agent(model=..., backend=...). Hand it nothing and the default applies, which explains the vanishing notes above.

One tip so your code matches current releases: give the agent a ready instance, such as StateBackend(). Older samples used a lambda rt: wrapper, which is deprecated and unnecessary now.

Five backends, ordered by what survives

The plain way to separate the five built-ins is by lifespan and not by internals: how long does a file written through each one remain?

StateBackend is the default and is thread-scoped scratch space. Files live in the agent's state and persist across the turns of one conversation (a thread) but aren't shared between threads. It's the agent's notepad, ideal for intermediate work that gets read back two steps later and gone when the conversation ends. It's the right default, and also exactly the backend that causes the vanishing-notes surprise when mistaken for memory.

StoreBackend is durable storage across threads. Files go into a LangGraph store and survive across conversations, which is what long-term memory needs. The catch, a significant one, is that you have to tell it whose data it holds. That comes up below.

FilesystemBackend and LocalShellBackend use the host machine's real disk. They read and write actual files in a directory you choose, and LocalShellBackend also gives the agent an execute tool that runs shell commands straight on your machine with no sandbox. They suit a local coding assistant you're developing at your desk and are dangerous everywhere else, which is why the documentation warns bluntly against using them in deployed agents. Raw host access combined with dynamic user input is a security incident waiting to happen.

ContextHubBackend is LangSmith's native durable option. Files sit in a LangSmith Hub repo and are saved as commits, so you get persistence across runs plus a record of each filesystem change, without hosting a store of your own. It makes sense when you're already in the LangSmith ecosystem and want persistence without wiring up a database.

Seen together, the choices lie along a single axis: nothing survives, the conversation survives, everything survives, or everything survives with history. Choose the point your feature needs.

CompositeBackend: different paths, different lifetimes

The idea that makes backends click is that you seldom want one persistence policy for the whole filesystem. Working files should be cheap, fast and disposable, while memory should be durable. Those are two backends, and CompositeBackend gives you both inside one filesystem by routing on path prefix.

Give it one default backend for most paths and a routing table that diverts selected prefixes. The usual layout is scratch for everything, with durable storage behind /memories/:

from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
from langgraph.store.memory import InMemoryStore

agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-6",
    backend=CompositeBackend(  # ①
        default=StateBackend(),  # ②
        routes={  # ③
            "/memories/": StoreBackend(namespace=lambda _rt: ("memories",)),  # ④
        },
    ),
    store=InMemoryStore(),  # passed to create_deep_agent, not to the backend  ⑤
)

Walking through the numbered markers in that listing:

  1. CompositeBackend presents one filesystem to the agent and forwards every operation to the sub-backend whose route matches the path.
  2. default takes whatever no route claims. In this example that's the thread-scoped StateBackend.
  3. routes pairs path prefixes with backends. If two prefixes match, the longer one wins, so /memories/projects/ beats /memories/.
  4. Writes under /memories/ go to a durable StoreBackend, whose namespace callable returns the tuple that identifies the owner and scopes the data.
  5. The LangGraph store is handed to create_deep_agent, not to the backend, a wiring detail people miss the first time.

The effect is that anything written under /memories/ lands in the durable store and survives across conversations, while everything else, whether a plan in /workspace, a scratch file or a cached read, goes to the default StateBackend and vanishes when the thread ends. The agent neither knows nor cares. It writes paths.

A filesystem strategy for a real agent

Take a code-maintenance agent, say one called buggy-shop. When it works on a repository, the repo's files are working state. They get loaded, edited and tested, and there's no reason for them to leak into the next conversation. What the agent learns along the way is another matter, and that's what it ought to remember on the next request: flaky tests in one module, a fixture shared by several suites, an off-by-one pagination bug found previously.

So buggy-shop uses the composite arrangement above. The repo sits under the default StateBackend as thread-scoped scratch, and the accumulated notes sit under /memories/ in a persistent StoreBackend. That's one agent with a deliberate answer to what should outlive the conversation. Separating throwaway working state from lasting knowledge is the move, and path-prefix routing is how you express it.

Where code runs versus where files live

So far this is all about files, and many agents never need more. But an agent whose job is to prove a fix by running pytest needs a shell, because running commands isn't a filesystem operation. A sandbox backend adds that: besides the filesystem it exposes the execute tool for running commands, all inside an isolated container so the agent can't damage the host.

Agents can be wired to a sandbox in two ways. One puts the agent inside the sandbox, working on its filesystem. That keeps local development close to production, but forces your API keys into the sandbox. The other treats the sandbox as a tool: the agent stays on your server and calls into the sandbox just to run code. Credentials remain outside, you can throw the sandbox away, and if it crashes the agent's state is safe. Deep Agents handles either, and that's a clear contrast with harnesses that only allow the first.

Keep in mind that executing code and storing files are two separate questions. A filesystem backend answers one, a sandbox backend the other, and many serious agents need both.

The configuration you can't skip

The moment StoreBackend serves more than one user, a namespace factory is required. If it's missing you won't get a crash, you'll get a leak. The backend needs to know whose files it's holding, and it learns that from a namespace: a tiny function that takes the runtime and returns a tuple naming the owner. Scoping by user gives each person separate storage:

from deepagents.backends import StoreBackend
backend = StoreBackend(
    namespace=lambda rt: (rt.server_info.user.identity,),
)

Leave the namespace out and the legacy default scopes by assistant, so every user of the same agent shares a single pile of storage and can read each other's supposedly private memory.

That bug passes every test on your own machine, since you're the only user. It shows up as a privacy incident the day a second person signs in. Treat the namespace factory as mandatory for anything beyond a single-user local toy. The documentation says it's becoming required, so write it now and skip the migration later.

A smaller trap comes with the default backend too. StateBackend checkpoints the agent's entire state at every step, which is what lets a conversation resume cleanly. The price is that large files stored in state get serialised again and again, step after step, so a big artefact there quietly drains performance and storage. The remedy is the same composite pattern: send large or durable items to a store and keep StateBackend for small, transient scratch.

A checklist for today

  • Find out which backend your agent uses. Without a backend= argument to create_deep_agent, you're on StateBackend and the agent forgets everything between conversations. Check that this is what you intended.
  • Decide what should outlive a conversation. List working files and durable knowledge separately. Anything in the durable column belongs in a StoreBackend.
  • Use the composite pattern. Build a CompositeBackend that defaults to StateBackend and routes /memories/ to a StoreBackend. The store itself goes to create_deep_agent, never to the backend.
  • Give every multi-user StoreBackend a namespace factory. Key it to the user's identity, and do it before the second person logs in.
  • Audit large files in state. If the agent writes big artefacts, route them out of StateBackend so they aren't re-serialised on every step.

The backend becomes a decision

Treat the backend as a choice and you've already avoided the afternoon-eating bug from the start. You can describe the five built-ins by their lifespan, send different paths to different lifetimes with CompositeBackend and you know multi-user storage can't go without a namespace factory. An agent designed like this has a plan for its files: scratch for the working repo, lasting memory for what it picks up.

Durable storage the agent writes into itself should give you pause, and that's healthy. An agent able to write files can write the wrong one, remove the right one or run a command you never wanted. A filesystem is like giving it hands. What comes next is the leash, meaning declarative permissions that restrict which files it can touch and human approval that makes it pause and ask first. A leash only helps if you've already settled, on purpose, where the files live.