> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# deepagents

> A Synap memory backend, query-conditioned recall middleware, and tools for LangChain's deepagents harness.

Give a [deepagents](https://docs.langchain.com/oss/python/deepagents/overview) agent persistent, searchable memory through Synap. deepagents reaches storage through `BackendProtocol` — a filesystem interface — and reaches memory through `MemoryMiddleware`, which pastes whole `AGENTS.md` files into the system prompt. This package plugs Synap into both, and adds a retrieval path the stock middleware cannot express.

<Note>
  Requires Python 3.11+ and `deepagents>=0.7.4`.

  This covers the **`deepagents` library** — agents you build with `create_deep_agent(...)`. The separate `deepagents-code` terminal agent constructs its memory backend internally and does not accept a custom one, so it cannot use this package.
</Note>

<Warning>
  Mount `SynapBackend` on a **route**, never as `backend=` on its own. As the default backend it would route the agent's source-code reads and writes through a memory API, and the agent would lose its working tree.
</Warning>

## Overview

Three surfaces, mapped onto deepagents' own extension points. Adopt only the ones you need.

| Surface                              | deepagents extension point                              | Purpose                                                                            |
| ------------------------------------ | ------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `SynapBackend`                       | `create_deep_agent(backend=...)` via `CompositeBackend` | Memory reachable through the agent's own `read_file` / `grep` / `write_file` tools |
| `SynapMemoryMiddleware`              | `create_deep_agent(middleware=[...])`                   | Recall scoped to the user's actual question                                        |
| `SynapShortTermMiddleware`           | `create_deep_agent(middleware=[...])`                   | Compacted conversation history, refreshed each turn                                |
| `synap_st_instructions`              | `create_deep_agent(system_prompt=...)`                  | Short-term context folded into a static system prompt                              |
| `SynapSearchTool` / `SynapStoreTool` | `create_deep_agent(tools=[...])`                        | Explicit `search_memory` / `store_memory` the model can call                       |

All of them take an already-constructed `MaximemSynapSDK` — your app owns the SDK and its credentials.

### When this is worth it

The payoff scales with how much the agent remembers, and it is worth being concrete about that.

In our own testing, an agent answering a question against a 200-memory scope needed **96 characters** of retrieved context to get it right. The same task with a plain-file memory backend pasted **24,853 characters** — the entire corpus — into the prompt on every turn to reach the same answer. That gap is the reason this integration exists, and it widens as memory grows.

The trade is latency and small-corpus behaviour: a retrieval call is a network round trip, so expect a couple of extra seconds per turn versus reading a local file, and on a handful of memories a file backend is both faster and more reliable. Use Synap where memory outgrows a file — long-lived agents, many sessions, memory shared across projects — and leave a small static `AGENTS.md` on `FilesystemBackend`. `CompositeBackend` lets you do both at once.

## Setup

<CodeGroup>
  ```bash pip theme={null}
  pip install maximem-synap-deepagents deepagents
  ```

  ```bash uv theme={null}
  uv add maximem-synap-deepagents deepagents
  ```
</CodeGroup>

## Basic integration

Mount Synap at `/memories/` and leave the repository on a normal filesystem backend:

```python theme={null}
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend
from deepagents.backends.filesystem import FilesystemBackend
from maximem_synap import MaximemSynapSDK
from synap_deepagents import SynapBackend

sdk = MaximemSynapSDK(api_key="sk-...")

backend = CompositeBackend(
    default=FilesystemBackend(root_dir="/path/to/repo"),
    routes={"/memories/": SynapBackend(sdk, user_id="alice")},
)

agent = create_deep_agent(
    model="anthropic:claude-sonnet-5",
    backend=backend,
    memory=["/memories/AGENTS.md"],
)

agent.invoke({"messages": [{"role": "user", "content": "What do I prefer?"}]})
```

`CompositeBackend` routes by longest path prefix. Anything under `/memories/` reaches Synap; everything else goes to the repository, untouched.

## Core concepts

### `grep` becomes a semantic search

This is the part worth knowing. On a Synap route, the agent's `grep` tool is **not** a regex match over file bytes. The pattern is passed to Synap as a natural-language query:

```python theme={null}
# The agent runs this:
grep("what deployment process does the user follow", path="/memories/")

# It becomes this:
sdk.fetch(search_query=["what deployment process does the user follow"], mode="accurate")
```

Stock deepagents `grep` can only find what is literally written in a file. This searches by meaning instead, so a memory can be phrased differently from the query and still match. Matches are attributed to `/memories/AGENTS.md`, so the agent can read that file for more context.

If your agent tends to write regexes, say so in its system prompt. A model that assumes literal matching will write `^prefer.*` and misread the misses.

<Note>
  Retrieval is a relevance search, not a guarantee. Some phrasings return nothing even when a matching memory is in scope, so treat an empty `grep` as "no strong match for this wording" rather than "not in memory". Where it matters, have the agent try a second phrasing, or read `/memories/AGENTS.md` directly for the unfiltered recall block.
</Note>

### A new scope needs a corpus before recall is useful

Recall is only as good as what is in the scope, and a nearly-empty scope returns nothing at all rather than a little. In testing, a scope holding three memories returned an empty context block on every setting, while the same scope at a dozen memories returned content consistently.

That is worth knowing because of how it looks from the outside: the agent reads `/memories/AGENTS.md`, gets `file_not_found`, and behaves exactly as if the integration were broken. It isn't — there is genuinely nothing to return yet. Seed a real corpus, or let the agent accumulate one over several sessions, before judging recall quality.

### The recall file is synthesized, not stored

`/memories/AGENTS.md` does not exist anywhere. Reading it calls `sdk.fetch()` and returns the formatted context as the file body. That is what makes `create_deep_agent(memory=["/memories/AGENTS.md"])` work with no other changes.

### Queued writes and read-after-write

Synap ingestion is asynchronous — `create` returns `{ingestion_id, document_id, status: QUEUED}`. A memory written a moment ago is not yet retrievable, so an agent that writes a file and reads it back in the same turn would otherwise get nothing, which reads as data loss.

`SynapBackend` keeps a short-TTL, per-process write-through cache so your own writes are always readable back. It is a read-after-write guarantee, **not** semantic dedup, and it does not survive a restart.

The package never polls `wait_for_completion` on the agent's path. Waiting would cost turn latency without improving the answer: Synap may split one submitted document into several memories, or merge it with existing ones, so there is no count to wait for.

### There is no `delete`

`sdk.memories.create()` returns an `ingestion_id`; `sdk.memories.delete()` needs a `memory_id`. They are different identifiers, and the memory does not exist yet at write time. So a path written through this backend cannot be resolved back to a durable memory.

`delete` is optional in `BackendProtocol`, so `SynapBackend` inherits the default that raises `NotImplementedError`, and `CompositeBackend` reports it cleanly. Remove memories through the Synap API or dashboard with a memory id.

## Complete example: query-conditioned recall

`create_deep_agent(memory=[...])` installs deepagents' own `MemoryMiddleware`, which calls `backend.download_files(paths)` — paths, and nothing else. Even with `SynapBackend` underneath, that is one unqueried digest per run.

`SynapMemoryMiddleware` reads the pending user message first and passes it as the search query, so what lands in the prompt is scoped to what was actually asked:

```python theme={null}
from deepagents import create_deep_agent
from maximem_synap import MaximemSynapSDK
from synap_deepagents import SynapMemoryMiddleware, SynapSearchTool

sdk = MaximemSynapSDK(api_key="sk-...")

agent = create_deep_agent(
    model="anthropic:claude-sonnet-5",
    middleware=[
        SynapMemoryMiddleware(
            sdk=sdk,
            user_id="alice",
            customer_id="acme",
            max_results=20,
            mode="fast",
        )
    ],
    tools=[SynapSearchTool(sdk=sdk, user_id="alice")],
)

agent.invoke({
    "messages": [{"role": "user", "content": "How do I usually deploy?"}]
})
```

Use `memory=[...]` **or** `SynapMemoryMiddleware`, not both — they write to the same part of the system prompt, and you would pay for two retrievals to say the same thing twice.

## Advanced patterns

### Short-term context

Long-term memory and short-term context are different things. Short-term is the compacted history of the *current* conversation:

```python theme={null}
from synap_deepagents import synap_st_instructions

system_prompt = await synap_st_instructions(
    sdk, "conv_abc", system="You are a helpful coding agent."
)
agent = create_deep_agent(model="...", system_prompt=system_prompt)
```

That is a snapshot taken once, at construction. For a long-running agent, use `SynapShortTermMiddleware` instead — it refreshes each turn:

```python theme={null}
from synap_deepagents import SynapShortTermMiddleware

agent = create_deep_agent(
    model="anthropic:claude-sonnet-5",
    middleware=[SynapShortTermMiddleware(sdk=sdk, conversation_id="conv_abc")],
)
```

### Tuning retrieval

Reads sit on the agent's startup path; `grep` is a deliberate question. They get different defaults, and both are configurable:

```python theme={null}
SynapBackend(
    sdk,
    user_id="alice",
    mode="fast",          # reads — on the startup path
    grep_mode="accurate", # grep — worth the latency
    precision_level="high",
    max_results=20,
    cache_ttl_seconds=300,
)
```

### Error policy

| Operation                                              | Behaviour                                                   |
| ------------------------------------------------------ | ----------------------------------------------------------- |
| Reads (`read`, `grep`, `ls`, `glob`, `download_files`) | Degrade — log at `ERROR`, return empty or `file_not_found`  |
| Writes (`write`, `edit`, `upload_files`)               | Raise `SynapIntegrationError`                               |
| Recall in middleware                                   | Degrades to an empty block — an outage must not end the run |
| `delete`                                               | Raises `NotImplementedError`                                |

Read failures are always reported as `file_not_found`, never any other code. That is deliberate: deepagents' `MemoryMiddleware` raises `ValueError` on any download error code *except* `file_not_found`, so returning anything else during a Synap outage would end the agent run instead of degrading to an empty memory block.

## Going further

* Mount the backend **and** the tools together. The backend covers automatic recall through `AGENTS.md`; the tools cover deliberate lookups with names the model already understands.
* Set `document_type` and `ingest_mode` on writes when the agent is storing something other than notes — for example `document_type="meeting-transcript"`.
* Scope with `customer_id` alone for shared team memory, or `user_id` + `customer_id` for per-person memory inside an organisation.

## Next steps

<CardGroup cols={2}>
  <Card title="LangGraph" icon="https://github.com/langchain-ai.png" href="/integrations/langgraph">
    deepagents runs on LangGraph — the checkpointer integration composes with this one.
  </Card>

  <Card title="LangChain" icon="https://github.com/langchain-ai.png" href="/integrations/langchain">
    Retrievers, tools, and short-term context for LangChain itself.
  </Card>
</CardGroup>
