Multi-Agent Memory Architecture: How to Share Context Across AI Agents
Multiple AI agents sharing a single memory store need namespace isolation, typed edges for cross-agent context, and concurrent-safe read patterns. Feather DB handles all three with its entity system, graph traversal API, and in-process concurrency model.
Multi-agent systems where multiple AI agents share context require three things from their memory layer: namespace isolation (agent A's private memories cannot bleed into agent B's context), typed edges for cross-agent knowledge sharing (agent A can link its findings to agent B's context explicitly), and concurrent-safe read access. Feather DB handles all three through its entity system, graph traversal API, and in-process concurrency model — multiple agents share a single .feather file with strong isolation guarantees and sub-millisecond concurrent reads.
Why Multi-Agent Memory Is Different
Single-agent memory is a solved problem: one agent, one memory store, query by entity ID. Multi-agent memory introduces three challenges that single-agent architectures don't face:
- Isolation: Agent A's memories about User X must not appear in Agent B's context for the same user. A support agent's private diagnostic notes should not surface in the sales agent's context.
- Controlled sharing: Some information should cross agent boundaries. A research agent's findings should be accessible to the report-writing agent. A data-gathering agent should be able to share context with an analysis agent.
- Concurrency: Multiple agents querying the same store simultaneously must not corrupt each other's reads or produce inconsistent results.
Feather DB addresses all three with a namespace + entity model, typed edges for explicit sharing, and a reader-writer locking model that allows concurrent reads with exclusive writes.
Namespace Model: Isolation by Design
Every memory in Feather DB has metadata fields. The multi-agent isolation pattern uses two fields: user_id (who this memory is about) and agent_id (which agent owns this memory). Shared memories omit the agent_id field and are accessible to all agents.
import feather_db as fdb
from datetime import datetime
# Shared memory store for all agents
db = fdb.FeatherDB("multi_agent_memory.feather")
def store_agent_memory(
text: str,
user_id: str,
agent_id: str,
importance: float = 0.6,
shared: bool = False
) -> str:
"""
Store a memory with namespace isolation.
If shared=True, omit agent_id so all agents can access it.
"""
metadata = {
"user_id": user_id,
"stored_at": datetime.now().isoformat(),
"shared": shared
}
if not shared:
metadata["agent_id"] = agent_id
return db.add(
text=text,
metadata=metadata,
importance=importance
)
def recall_agent_context(
query: str,
user_id: str,
agent_id: str,
include_shared: bool = True,
top_k: int = 8
) -> list:
"""
Retrieve memories for a specific agent.
By default, includes both private agent memories and shared memories.
"""
results = []
# Agent-private memories
private_memories = db.search(
query=query,
top_k=top_k // 2,
filters={"user_id": user_id, "agent_id": agent_id}
)
results.extend(private_memories)
if include_shared:
# Shared memories (accessible to all agents)
shared_memories = db.search(
query=query,
top_k=top_k // 2,
filters={"user_id": user_id, "shared": True}
)
results.extend(shared_memories)
# Re-rank combined results by score
results.sort(key=lambda x: x['score'], reverse=True)
return results[:top_k]
Cross-Agent Context Sharing with Typed Edges
When Agent A discovers something that Agent B should know, the recommended pattern is not to copy the memory — it is to link it. Feather DB's typed edges allow Agent A to create a link from its memory to Agent B's context namespace, without duplicating data:
# Agent A (research agent) stores a finding
research_agent_id = "research_agent"
analysis_agent_id = "analysis_agent"
research_finding_id = store_agent_memory(
text="User's customer churn rate increased 23% in Q2 2026, primarily from the enterprise tier",
user_id="client_acme",
agent_id=research_agent_id,
importance=0.9,
shared=False # Private to research agent initially
)
# Research agent decides to share this with the analysis agent
shared_context_id = store_agent_memory(
text="[Shared from research] ACME churn: +23% Q2 2026, enterprise tier dominant driver",
user_id="client_acme",
agent_id=analysis_agent_id,
importance=0.9,
shared=False # Private to analysis agent
)
# Link the shared copy to the original for provenance
db.link_nodes(
source_id=shared_context_id,
target_id=research_finding_id,
edge_type="derived_from",
weight=1.0
)
# When analysis agent retrieves context, it gets the shared copy
# and can traverse to the original finding via context_chain()
analysis_context = db.context_chain(
query="ACME churn data",
hops=2,
filters={"user_id": "client_acme", "agent_id": analysis_agent_id}
)
# Returns: the shared copy + the original finding (via derived_from edge)
Complete Multi-Agent System: 4-Agent Pipeline
Here is a complete 4-agent system where agents specialize in research, analysis, writing, and review — sharing context through Feather DB:
import feather_db as fdb
from openai import OpenAI
from dataclasses import dataclass
from typing import Optional
db = fdb.FeatherDB("pipeline_memory.feather")
client = OpenAI()
@dataclass
class Agent:
agent_id: str
role: str
system_prompt: str
def recall(self, query: str, user_id: str, top_k: int = 8) -> list:
"""Recall memories accessible to this agent."""
private = db.search(
query=query,
top_k=top_k // 2,
filters={"user_id": user_id, "agent_id": self.agent_id}
)
shared = db.search(
query=query,
top_k=top_k // 2,
filters={"user_id": user_id, "shared": True}
)
combined = sorted(private + shared, key=lambda x: x['score'], reverse=True)
return combined[:top_k]
def remember(self, text: str, user_id: str, importance: float = 0.7,
shared: bool = False) -> str:
"""Store a memory from this agent."""
metadata = {
"user_id": user_id,
"agent_id": self.agent_id,
"stored_at": __import__('datetime').datetime.now().isoformat(),
"shared": shared
}
return db.add(text=text, metadata=metadata, importance=importance)
def run(self, task: str, user_id: str) -> str:
"""Execute the agent's task with memory context."""
memories = self.recall(task, user_id)
context = "\n".join([f"- {m['text']}" for m in memories])
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": f"{self.system_prompt}\n\nContext:\n{context}"},
{"role": "user", "content": task}
]
)
return response.choices[0].message.content
# Define the 4-agent pipeline
research_agent = Agent(
agent_id="research",
role="Researcher",
system_prompt="You are a research agent. Gather and synthesize factual information."
)
analysis_agent = Agent(
agent_id="analysis",
role="Analyst",
system_prompt="You are an analysis agent. Identify patterns and insights from research."
)
writing_agent = Agent(
agent_id="writing",
role="Writer",
system_prompt="You are a writing agent. Produce clear, structured content from analysis."
)
review_agent = Agent(
agent_id="review",
role="Reviewer",
system_prompt="You are a review agent. Check accuracy, clarity, and completeness."
)
def run_pipeline(task: str, user_id: str) -> dict:
"""Run the 4-agent pipeline with shared memory."""
# Step 1: Research
print("Research agent running...")
research_output = research_agent.run(task, user_id)
research_id = research_agent.remember(
f"Research findings: {research_output[:500]}",
user_id=user_id,
importance=0.85,
shared=True # Make available to downstream agents
)
# Step 2: Analysis (has access to research via shared memory)
print("Analysis agent running...")
analysis_output = analysis_agent.run(
f"Analyze this research and extract key insights: {research_output}",
user_id=user_id
)
analysis_id = analysis_agent.remember(
f"Analysis insights: {analysis_output[:500]}",
user_id=user_id,
importance=0.85,
shared=True # Make available to writing agent
)
# Link analysis to research for provenance
db.link_nodes(analysis_id, research_id, "derived_from", 1.0)
# Step 3: Writing (has access to both research and analysis)
print("Writing agent running...")
writing_output = writing_agent.run(
f"Write a comprehensive report based on this analysis: {analysis_output}",
user_id=user_id
)
# Step 4: Review
print("Review agent running...")
review_output = review_agent.run(
f"Review this draft for accuracy and completeness: {writing_output}",
user_id=user_id
)
return {
"research": research_output,
"analysis": analysis_output,
"draft": writing_output,
"review": review_output
}
Concurrency Model: Concurrent Reads, Safe Writes
Multiple agents running simultaneously will query Feather DB concurrently. Feather DB uses a reader-writer locking model:
- Concurrent reads: Any number of agents can read simultaneously without blocking each other. A 0.19ms read does not block while 10 other reads are in progress.
- Exclusive writes: Writes acquire an exclusive lock. If Agent A and Agent B both write simultaneously, one waits (typically under 1ms). For most agent pipelines, this is not a bottleneck — reads dominate writes by 10:1 or more.
- Single-file model: For extremely high write concurrency (thousands of agents writing simultaneously), use Feather DB Cloud (Q3 2026) which distributes write load across nodes.
import threading
import feather_db as fdb
db = fdb.FeatherDB("concurrent_memory.feather")
def simulate_concurrent_reads(n_agents: int, query: str, user_id: str):
"""Simulate concurrent reads from multiple agents."""
results = {}
errors = []
def agent_read(agent_id: int):
try:
memories = db.search(
query=query,
top_k=5,
filters={"user_id": user_id}
)
results[agent_id] = len(memories)
except Exception as e:
errors.append(str(e))
# Launch n_agents concurrent reads
threads = [threading.Thread(target=agent_read, args=(i,)) for i in range(n_agents)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"{n_agents} concurrent reads: {len(results)} succeeded, {len(errors)} errors")
# Output: 50 concurrent reads: 50 succeeded, 0 errors
simulate_concurrent_reads(n_agents=50, query="user background", user_id="user_001")
Deployment Patterns for Multi-Agent Systems
Pattern 1: Shared file, multiple processes
All agents access the same .feather file. Works well for up to ~100 concurrent agents. Deploy agents as separate Python processes, point them all to the same file path.
Pattern 2: Shard by user
Each user has their own .feather file. All agents for user X use memory_X.feather. This eliminates cross-user contention entirely and makes user data deletion trivial (delete the file).
Pattern 3: Shard by agent type
Research agents use research.feather; writing agents use writing.feather. Shared context is explicitly copied (with a derived_from link) between files. Higher complexity, maximum isolation.
FAQ
How do I prevent one agent's memories from contaminating another agent's context?
Always include "agent_id": agent_id in the filter for every db.search() call. Shared memories use "shared": True in their metadata and are queried with a separate "shared": True filter. The two filter conditions never overlap with agent-private queries.
Can two agents write to the same memory at the same time?
Feather DB's write lock ensures only one write proceeds at a time. The waiting write queues and completes as soon as the lock is released — typically under 1ms wait time. There is no data corruption from concurrent writes.
How do agents discover what other agents have found?
Two mechanisms: (1) shared memories ("shared": True) are accessible to all agents; (2) explicit edges (db.link_nodes()) allow one agent's memory to surface in another's context_chain() traversal via a derived_from or refines edge.
What is the maximum number of agents that can share a single Feather DB file?
Read concurrency is unlimited — any number of agents can read simultaneously. Write concurrency serializes, but with typical agent write rates (a few writes per minute per agent), a single file handles hundreds of concurrent agents without bottleneck.
Can I use Feather DB for a supervisor/worker multi-agent pattern?
Yes. The supervisor agent writes task assignments and overall context to shared memory. Worker agents read their assigned context, execute tasks, and write results back with their agent_id. The supervisor reads all worker outputs via shared memory queries. This is a natural fit for Feather DB's namespace model.