How MemoryMiddleware Populates the System Prompt from AGENTS.md in DeepAgents
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.
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, 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.
# 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.
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.
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, 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:
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:
memory.sources.append("/etc/custom/AGENTS.md")
Summary
- MemoryMiddleware uses a configurable backend to load AGENTS.md files specified in the
sourceslist during thebefore_agentphase. - 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_memorymethod wraps file contents in the MEMORY_SYSTEM_PROMPT template, producing structured XML-like blocks with<agent_memory>tags. - The
modify_requestmethod injects the formatted memory into the system prompt usingappend_to_system_messagefromlibs/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. This method relies on append_to_system_message from 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →