Composite Backend Example¶
Combine multiple backends with path-based routing.
Full Documentation
For complete backend documentation, see pydantic-ai-backend docs.
Source Code¶
examples/composite_backend.py
Overview¶
This example demonstrates:
- Combining multiple backends (memory + local)
- Routing operations by path prefix
- Using persistent and ephemeral storage together
When to Use CompositeBackend¶
CompositeBackend is useful when:
- Some files should persist (project files) while others are temporary (scratch)
- Different paths need different storage characteristics
- You want to isolate certain operations
Full Example¶
Python
"""Example using CompositeBackend for mixed storage."""
import asyncio
from pydantic_deep import (
CompositeBackend,
DeepAgentDeps,
LocalBackend,
StateBackend,
create_deep_agent,
)
async def main():
# Create backends:
# - StateBackend for temporary/scratch files
# - LocalBackend for persistent project files
memory_backend = StateBackend()
local_backend = LocalBackend(root_dir="./workspace")
# Create composite backend with routing rules
backend = CompositeBackend(
default=memory_backend, # Default to memory for unmatched paths
routes={
"/project/": local_backend, # Project files go to disk
"/workspace/": local_backend, # Workspace files go to disk
},
)
# Create the agent
agent = create_deep_agent(
model="openai:gpt-4.1",
instructions="""
You are a project assistant.
File organization:
- /project/ - Persistent project files (saved to disk)
- /workspace/ - Working files (saved to disk)
- /temp/ or /scratch/ - Temporary files (in memory only)
Use the appropriate location based on whether files should persist.
""",
)
deps = DeepAgentDeps(backend=backend)
# Run the agent
result = await agent.run(
"""Create a small Python project:
1. Create /project/src/app.py with a simple Flask app
2. Create /project/requirements.txt with dependencies
3. Create /scratch/notes.txt with implementation notes (temporary)
4. Create /project/README.md with project description
""",
deps=deps,
)
print("Agent output:")
print(result.output)
# Show what's in memory (temporary files)
print("\nTemporary files (in memory):")
for path in sorted(memory_backend.files.keys()):
print(f" {path}")
if __name__ == "__main__":
asyncio.run(main())
Running the Example¶
Key Concepts¶
Route Configuration¶
Python
backend = CompositeBackend(
default=memory_backend, # Fallback for unmatched paths
routes={
"/project/": local_backend, # Paths starting with /project/
"/workspace/": local_backend, # Paths starting with /workspace/
},
)
Routes are matched by prefix. The first matching route wins.
How Routing Works¶
Python
# These go to local_backend (match /project/ prefix)
backend.write("/project/src/main.py", "...")
backend.read("/project/config.json")
# These go to memory_backend (default, no route matches)
backend.write("/scratch/temp.txt", "...")
backend.read("/notes.md")
Variations¶
Docker + Local Hybrid¶
Python
from pydantic_deep import DockerSandbox, LocalBackend
backend = CompositeBackend(
default=DockerSandbox(runtime="python-datascience"), # Isolated execution
routes={
"/local/": LocalBackend(root_dir="./local"), # Local access
},
)
Read-Only Source + Writable Workspace¶
Python
# Source files are read-only
source_backend = LocalBackend(
root_dir="./src",
enable_execute=False,
)
# Workspace for modifications
workspace_backend = StateBackend()
backend = CompositeBackend(
default=workspace_backend,
routes={
"/src/": source_backend, # Read-only source
},
)
Best Practices¶
- Clear path conventions - Document which paths go where
- Default to memory - Safe fallback for unexpected paths
- Consider persistence needs - What should survive restarts?
Next Steps¶
- Filesystem Example - Single backend usage
- Docker Sandbox - Isolated execution
- pydantic-ai-backend docs - Full reference