When multiple agents collaborate on a shared task, they need access to the same memory. Spectron's scope model makes this natural: agents that share a scope dimension (such as project) can all read from and write to the same experiential memory, while retaining individual isolation where needed.
Patterns
There are two common patterns for multi-agent memory sharing:
Pattern
When to use
Shared scope
All agents contribute to and read from a common project scope
Supervisor + workers
A supervisor agent coordinates several worker agents, each with its own user/agent scope
Pattern 1: shared project scope
The simplest approach – all agents are given the same scope when creating sessions. Memory written by one agent is immediately visible to others operating in the same scope.
# Agent A: data collection agent asyncwithclient.sessions.create(scope=SHARED_SCOPE)assession_a: awaitsession_a.remember( content="Competitor X launched a new pricing tier at $299/mo targeting SMBs.", memory_category="knowledge", )
To prevent worker agents from writing to shared memory (read-only readers), issue separate API keys with restricted write capabilities. A key with principal agent and a scope_floor of {org: "acme", project: "market-research-q3"} can read and write at that scope. If you need read-only agents, add scope floor restrictions at the application layer before passing context to the agents.
Pattern 2: supervisor and workers
A supervisor agent orchestrates several workers. Each worker operates in its own user/agent scope, but the supervisor aggregates their findings into a shared project scope.
asyncdefrun_worker(topic: str,worker_id: str,client: SpectronClient) -> str: worker_scope={**SUPERVISOR_SCOPE,"agent": worker_id} asyncwithclient.sessions.create(scope=worker_scope)assession: # Worker researches its topic context=awaitsession.recall(query=topic,top_k=5) findings=awaitllm.research(topic,context.formatted) # Worker stores findings in its own scope awaitsession.remember(findings,memory_category="knowledge") # Return a summary for the supervisor reflection=awaitsession.reflect( query=f"Summarise findings about {topic}", persist=False, ) returnreflection.summary
asyncdefsupervisor(client: SpectronClient): topics=["pricing trends","competitor features","customer sentiment"] # Run workers concurrently results=awaitasyncio.gather(*[ run_worker(topic,f"worker-{i}",client) fori,topicinenumerate(topics) ]) # Supervisor aggregates findings into shared scope asyncwithclient.sessions.create(scope=SUPERVISOR_SCOPE)assession: fortopic,findinginzip(topics,results): awaitsession.remember( content=f"[{topic}] {finding}", memory_category="knowledge", ) # Supervisor synthesises and stores a final report awaitsession.reflect( query="Synthesise all research findings into a coherent executive summary.", persist=True, )
Scope hierarchy
Spectron uses subset matching for scope retrieval. A query at scope {org: "acme"} retrieves memory from all sub-scopes under that org. This creates a natural hierarchy:
{org: "acme"} ← visible to all org agents {org: "acme", project: "alpha"} ← visible to project alpha agents {org: "acme", project: "alpha", agent: "planner"} ← planner only
A supervisor querying at {org: "acme", project: "alpha"} sees:
Everything at the project scope (shared findings)
Everything at more specific scopes (individual worker findings)
A worker querying at {org: "acme", project: "alpha", agent: "worker-1"} sees only its own memory and the shared project scope.
Preventing memory pollution
When many agents write to a shared scope, unrelated facts from different tasks can accumulate. Use metadata to tag memory items with their provenance:
Use the entity type system to keep memory structured. Rather than storing flat facts, extract structured entities so that conflicts are detected and superseded correctly:
# Good: structured entity extraction will happen automatically from this awaitsession.remember( content="The enterprise customer segment prioritises SOC2 compliance over cost.", )
When two agents write conflicting facts to the same scope at roughly the same time, Spectron's reconciliation pipeline detects the conflict and creates a supersession chain. The later write wins for attribute values. You can inspect the conflict:
state=awaitsession.get_state(scope=SHARED_SCOPE) forentityinstate.entities: forattrinentity.attributes: ifattr.superseded_by: print(f"Conflict: {entity.name}.{attr.key} was {attr.value} → now {attr.superseded_by.value}")
Supervisor reflection
The supervisor pattern works best with a dedicated reflection pass that runs after all workers complete:
asyncwithclient.sessions.create(scope=SUPERVISOR_SCOPE)assupervisor_session: # Load all worker contributions full_context=awaitsupervisor_session.profile() # Synthesise synthesis=awaitsupervisor_session.reflect( query="What are the top-level conclusions across all worker findings? What is still uncertain?", persist=True, ) print(synthesis.summary)
The persisted reflection becomes part of the shared scope's long-term memory and is included in future profile() calls.
// Worker writes a finding constworkerSession=awaitclient.sessions.create({scope:{...sharedScope,agent:"worker-1"}}); awaitworkerSession.remember({ content:"Competitor X raised prices by 20% in Q2.", memoryCategory:"knowledge", }); awaitworkerSession.close();
// Supervisor reads all findings constsupervisorSession=awaitclient.sessions.create({scope:sharedScope}); constctx=awaitsupervisorSession.recall({query:"competitor pricing",topK:10}); console.log(ctx.formatted); awaitsupervisorSession.close();