# How MemoryMiddleware Populates the System Prompt from AGENTS.md in DeepAgents

> Learn how MemoryMiddleware populates the system prompt from AGENTS.md files, structuring and appending agent memory to LLM requests for enhanced context and performance.

- Repository: [LangChain/deepagents](https://github.com/langchain-ai/deepagents)
- Tags: deep-dive
- Published: 2026-03-17

---

**MemoryMiddleware reads external AGENTS.md files, formats their contents into a structured `<agent_memory>` block, and appends it to the LLM's system prompt via the `modify_request` method.**

The DeepAgents SDK provides a mechanism to inject persistent project knowledge into agent conversations through the **MemoryMiddleware** class. This middleware automatically loads external documentation files—specifically AGENTS.md—and populates the system prompt with contextual information before each LLM request, ensuring the agent maintains awareness of project-specific guidelines without explicit tool calls.

## Configuring MemoryMiddleware with External Sources

When instantiating **MemoryMiddleware**, you provide a **backend** (such as `FilesystemBackend`) and a list of file paths via the `sources` parameter. These paths point to AGENTS.md files containing project-specific knowledge. The constructor stores these values for later use during the agent lifecycle.

```python
from deepagents.middleware.memory import MemoryMiddleware
from deepagents.backends.filesystem import FilesystemBackend

middleware = MemoryMiddleware(
    backend=FilesystemBackend(root_dir="/"),
    sources=["~/.deepagents/AGENTS.md", "./.deepagents/AGENTS.md"],
)

```

In [`libs/deepagents/deepagents/middleware/memory.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/middleware/memory.py), the `__init__` method simply stores the backend and source list, deferring actual file operations until the agent execution begins.

## Loading AGENTS.md Files via the Backend

Before the agent runs, the middleware invokes `before_agent` (synchronous) or `abefore_agent` (asynchronous) to load the external knowledge. This process follows a specific sequence to optimize performance and handle errors gracefully.

First, the method checks whether `memory_contents` already exists in the agent state to avoid redundant loading. If absent, it resolves the backend via `_get_backend` (supporting both direct instances and factory functions), then calls `download_files` for every path in `self.sources`.

```python

# Excerpt from libs/deepagents/deepagents/middleware/memory.py

results = backend.download_files(list(self.sources))
for path, response in zip(self.sources, results, strict=True):
    if response.error is None and response.content is not None:
        contents[path] = response.content.decode("utf-8")
return MemoryStateUpdate(memory_contents=contents)

```

Each successful download yields raw bytes that are decoded to UTF-8 and stored in a dictionary keyed by the source path. This dictionary becomes the `memory_contents` entry in the agent state.

## Formatting Knowledge into Structured Memory Blocks

The helper method `_format_agent_memory` transforms the loaded file contents into a formatted string suitable for LLM consumption. It constructs a block containing each file's path followed by its content, separated by blank lines, then wraps the entire body in the **MEMORY_SYSTEM_PROMPT** template.

```python
def _format_agent_memory(self, contents: dict[str, str]) -> str:
    if not contents:
        return MEMORY_SYSTEM_PROMPT.format(agent_memory="(No memory loaded)")
    sections = [f"{path}\n{contents[path]}" for path in self.sources if contents.get(path)]
    memory_body = "\n\n".join(sections)
    return MEMORY_SYSTEM_PROMPT.format(agent_memory=memory_body)

```

If no files load successfully, the method injects a placeholder message. The resulting string contains XML-like tags such as `<agent_memory>` and `<memory_guidelines>`, providing clear structural boundaries for the LLM to parse.

## Injecting Memory into the System Prompt

When assembling the LLM request, `modify_request` extracts the `memory_contents` from the request state, invokes `_format_agent_memory` to render the memory block, and appends it to the existing system message using the utility function `append_to_system_message`.

```python
def modify_request(self, request):
    contents = request.state.get("memory_contents", {})
    agent_memory = self._format_agent_memory(contents)
    new_system_message = append_to_system_message(request.system_message, agent_memory)
    return request.override(system_message=new_system_message)

```

The `append_to_system_message` function, defined in [`libs/deepagents/deepagents/middleware/_utils.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/middleware/_utils.py), creates a fresh **SystemMessage** whose `content_blocks` contain the original blocks plus the new text, inserting a blank line separator when necessary to maintain clean formatting.

## Complete Implementation Example

The following example demonstrates a complete setup that verifies the memory injection:

```python
from deepagents import create_deep_agent, MemoryMiddleware
from deepagents.backends.filesystem import FilesystemBackend
from deepagents.middleware._utils import append_to_system_message

# Configure the middleware with local AGENTS.md files

backend = FilesystemBackend(root_dir="/")
memory = MemoryMiddleware(
    backend=backend,
    sources=["./AGENTS.md", "~/.deepagents/AGENTS.md"],
)

# Create agent with memory middleware

agent = create_deep_agent(middleware=[memory])

# Runtime inspection of prompt injection

def inspect_system_prompt(request):
    contents = request.state.get("memory_contents", {})
    if contents:
        formatted = memory._format_agent_memory(contents)
        print(f"Injected memory length: {len(formatted)} characters")
        return True
    return False

```

To add sources dynamically after initialization, append new paths to the `sources` list:

```python
memory.sources.append("/etc/custom/AGENTS.md")

```

## Summary

- **MemoryMiddleware** uses a configurable backend to load AGENTS.md files specified in the `sources` list during the `before_agent` phase.
- The middleware stores decoded file contents in the agent state as `memory_contents`, skipping redundant loads if the state already contains this key.
- The `_format_agent_memory` method wraps file contents in the **MEMORY_SYSTEM_PROMPT** template, producing structured XML-like blocks with `<agent_memory>` tags.
- The `modify_request` method injects the formatted memory into the system prompt using `append_to_system_message` from [`libs/deepagents/deepagents/middleware/_utils.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/middleware/_utils.py).
- This architecture allows DeepAgents to maintain persistent, project-specific context across conversation turns without requiring explicit file read operations in the agent logic.

## Frequently Asked Questions

### What file formats does MemoryMiddleware support for external knowledge?

MemoryMiddleware supports any text-based files decodable as UTF-8, though AGENTS.md is the conventional filename. The middleware reads raw bytes via the backend's `download_files` method and decodes them to strings, making it compatible with markdown, plain text, or code files. Binary files will fail the UTF-8 decode step and be excluded from the memory block.

### Can I use MemoryMiddleware with cloud storage instead of local files?

Yes. While the examples use **FilesystemBackend**, you can implement a custom backend by providing an object with `download_files` and `adownload_files` methods that return response objects containing `.content` (bytes) and `.error` attributes. The middleware resolves the backend via `_get_backend`, supporting both direct instances and callable factories that return backend instances.

### How does MemoryMiddleware prevent duplicate loading on every turn?

The `before_agent` method checks if `memory_contents` already exists in the agent state dictionary. If present, it skips the `download_files` call entirely, returning early without modification. This ensures AGENTS.md contents load only once per session unless the state is explicitly cleared or the agent instance is recreated.

### Where is the system prompt modification logic implemented?

The injection occurs in the `modify_request` method within [`libs/deepagents/deepagents/middleware/memory.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/middleware/memory.py). This method relies on `append_to_system_message` from [`libs/deepagents/deepagents/middleware/_utils.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/middleware/_utils.py) to safely concatenate the memory block with existing system message content while preserving the `content_blocks` structure required by the DeepAgents message format.