Shared Memory and Blackboard Patterns
Two approaches coexist in 2026 multi-agent systems: the **message pool** (everyone sees everyone's messages, as in AutoGen GroupChat or MetaGPT) and the **blackboard with subscription** (agents subscribe to relevant events, as in Context-Aware MCP or the Matrix framework). Both hold the stateful part of a multi-agent system, so both are where the interesting bugs live. The reference failure mode is **memory poisoning**: one agent hallucinates a "fact," other agents treat it as verified, and accuracy decays gradually, which is much harder to debug than an immediate crash. This lesson builds both structures from stdlib, injects a poisoning attack, and walks through the three mitigations that work in production, two of them in code.
Learning Objectives
- Choose between a full message pool and a blackboard with subscription for a given agent count, role mix, and conversation length.
- Trace how one hallucinated figure becomes a "fact" in downstream agents' reasoning through shared state.
- Implement provenance-tagged writes, topic-keyed pub/sub, and a read-only verifier in stdlib Python.
- Apply the three mitigations (provenance, append-only writes, an unwritable verifier) to a shared-memory design.
- Pick a write-contention pattern: sequential writer, optimistic concurrency, or topic partitioning.
Problem
Multi-agent systems need a place for agents to share facts. You can pass everything in messages, but that reinvents shared state with extra copying. You can give everyone a global log, but global logs grow without bound and poison easily. You can project a view per agent, which scales but costs you a schema.
When one agent hallucinates and writes the hallucination to shared state, the agents downstream read it as fact. By the time a human notices, the reasoning chain is five steps deep and the root cause is the third message ever written. Accuracy decay is harder to debug than a crash.
This is memory poisoning. It is the second-most-documented failure family in the MAST taxonomy (Cemri et al., arXiv:2503.13657), and it is structural: a shared-memory design without provenance and an unwritable verifier will eventually exhibit it.
Concept
The two main topologies
Full message pool. Each agent reads the whole pool. AutoGen GroupChat and MetaGPT use this. It is simple and easy to inspect, but it stops scaling around 10 agents because each agent's context fills with other agents' work.
agent-A ──write──▶ ┌────────────────┐ ◀──read── agent-D
│ message pool │
agent-B ──write──▶ │ │ ◀──read── agent-E
│ (global log) │
agent-C ──write──▶ └────────────────┘ ◀──read── agent-F
Blackboard with subscription. Agents declare interest in topics; the substrate routes only relevant messages. CA-MCP (arXiv:2601.11595) and the Matrix decentralized framework (arXiv:2511.21686) use this. It scales further, at the price of designing a schema up front so the subscriptions mean something.
┌─ topic: prices ──┐
agent-A ──pub────▶ │ │ ──▶ agent-D (subscribed)
├─ topic: orders ──┤
agent-B ──pub────▶ │ │ ──▶ agent-E (subscribed)
├─ topic: alerts ──┤
agent-C ──pub────▶ │ │ ──▶ agent-F (subscribed)
└──────────────────┘
When each wins
- Full pool wins with few agents (< 10), mixed roles, and a short conversation. Working out who said what is easy when everyone sees the same log.
- Blackboard wins with many agents of the same role (swarms) and a long-running conversation. Routing cuts token cost and keeps unrelated work out of each context.
Production systems often mix: a small full pool at the top (planning layer), blackboards below (worker layer).
Memory poisoning, in one scenario
Three agents work on a research task: A retrieves, B summarizes, C analyzes.
- A fetches a page and writes a message to shared state: "The study reports a 42% accuracy improvement."
- The fetched page actually said "4.2% improvement." A hallucinated a decimal.
- B, reading shared state, writes: "Large 42% accuracy gain reported (source: A)."
- C, reading shared state, writes: "Recommend adoption. A 42% lift is transformative."
- The final report cites a 42% number that never existed.
Nothing crashed and no test failed. The system "worked," and the hallucination still crossed from A's context into the reasoning of each downstream agent through shared state.
Why this is structural
Without shared state, agent A's hallucination stays in A's context. Downstream agents would re-fetch or re-derive and might catch the error. With naive shared state, A's context becomes everyone's context, and the hallucination is laundered into fact.
The problem is not shared state itself. It is shared state without provenance and without an independent verifier. Three mitigations address this:
- Attribute provenance on every write. Each entry in shared state records who wrote it, when, under what prompt, and (if applicable) what source the agent cited. Downstream agents read with skepticism keyed to provenance.
- Version writes; treat them as append-only. A correction is a new entry that supersedes the old, not an in-place update. The audit trail is preserved.
- Keep at least one agent that cannot write to shared state. A read-only verifier agent samples entries, re-fetches sources, and flags inconsistencies. Because it cannot write to the pool, it cannot be poisoned by the pool.
Blackboard precedent (Hayes-Roth, 1985)
The blackboard pattern predates LLM agents by four decades. Hayes-Roth (1985, "A Blackboard Architecture for Control") described specialist Knowledge Sources that observe a global blackboard, contribute partial solutions, and trigger other sources. The 2026 blackboard (CA-MCP, Matrix) is the same pattern with LLM agents as Knowledge Sources and JSON blobs as partial solutions. The old literature already documents solutions to write contention, opportunistic control, and consistency, which modern systems rediscover.
Projection vs full view
A pure blackboard gives every subscriber the same projection (topic-scoped). A more aggressive design is per-agent projection: each agent gets a view customized to its role. LangGraph's state reducers are the canonical 2026 implementation: the reducer function folds global state into a role-specific slice.
Per-agent projection scales further but needs a schema. Without one, you rebuild ad-hoc projection in each agent's prompt.
Write-contention patterns
Multiple agents writing simultaneously is a concurrency problem, not just an LLM problem. Three patterns work:
- Sequential writer (single producer). All writes go through one coordinator agent that serializes. Simple, but a bottleneck.
- Optimistic concurrency with versioning. Each entry has a version; writers fail on version mismatch and retry. Classic database technique.
- Topic partitioning. Different agents own different topics. No cross-topic contention. Requires designed partition boundaries.
Most 2026 frameworks default to sequential writer because LLM calls are slow enough that contention is rare and the bottleneck does not hurt.
The unwritable verifier
The most load-bearing mitigation is the read-only verifier. Implementation rules:
- Verifier shares state with the team (reads the blackboard or pool).
- Verifier has no write handle to shared state, only to a separate verification channel.
- Verifier independently fetches sources cited in writes. Flags disagreement.
- Verifier's own outputs are routed to a human or a separate decision agent, never fed back into the pool.
Without this separation, the verifier's outputs become new entries in the pool, which means a poisoned pool poisons the verifier, which poisons its verifications.
Build It
code/main.py implements both topologies in stdlib Python, plus a toy poisoning attack and a read-only verifier.
MessagePool: a thread-safe, append-only log of entries with full read-out and aflagmethod for annotating an entry after the fact.Blackboard: topic-keyed pub/sub. Subscribers register a callback per topic, andpublishcalls only the callbacks for that topic.ProvenanceEntry: each write records writer, timestamp, prompt hash, and source URI, with optionalsupersedesandflagsfields.retrieval_agent,summarizer_agent,analyst_agent: a three-agent research task where the retriever turns "4.2%" into "42%" and the other two build on it.verifier_agent: a read-only agent that re-fetches each cited source and returns findings. It does not write to the pool, so the caller decides what to do with them.
Run:
python3 code/main.py
Expected output:
- Run 1 (no verifier): the hallucinated 42% propagates through the summary into the analyst's verdict, and no alarm is raised.
- Run 2 (with verifier): the verifier flags the retriever's entry as a mismatch with its source, and the printout marks it
FLAGGED. The toy analyst does not read flags, so its verdict is unchanged. The flag is there for a downstream agent or a human to act on. - Blackboard demo: price subscribers receive entries 0 and 1, alert subscribers receive entry 2, and neither sees the other's topic.
Use It
outputs/skill-memory-auditor.md is a skill that audits any multi-agent system's shared-memory design for provenance, versioning, and verifier separation. Run it on new multi-agent architectures before production.
Ship It
For any shared-memory design:
- Record provenance on each write:
(writer, timestamp, prompt_hash, tool_calls_cited, source_uri). - Make the log append-only. Corrections are new entries that reference the superseded one.
- Deploy at least one read-only verifier agent with independent source access.
- Route verifier output to a separate channel, not back into the shared pool.
- Log the ratio of writes that are supersessions, since a rising ratio is early evidence of hallucination patterns.
I'd run main.py and read the two runs side by side before touching the theory. The retriever flips 4.2% to 42% with a single replace call, and you can watch that string get copied into the summarizer's entry and then into the analyst's verdict. The summarizer and analyst do nothing wrong given their input, which is the whole problem.
One thing to notice in run 2: the verifier only checks entries that carry a source_uri. It flags the retriever's entry and leaves the summarizer's copy of the number unflagged. The analyst doesn't read flags either, so its verdict still says adopt. A flag only helps if something downstream acts on it, and in the toy nothing does. I'd treat that as the real exercise: make the analyst refuse to build on flagged entries.
I also left supersedes unused in the demo. ProvenanceEntry has the field and MessagePool.write accepts it, but nothing calls it with a value, so append-only corrections are yours to wire up. The blackboard demo is only about routing, with no poisoning or verifier in it. My bet is that the full pool is the right place to start with a handful of agents, and I'd move to topics once context bloat shows up.
Exercises
- Run
code/main.py. Confirm run 1 propagates the hallucination and run 2 catches it. - Add a second hallucination: agent B invents a dataset size. The verifier should catch both without being hand-tuned for either.
- Switch the full pool to a blackboard with topic partitions (
prices,summaries,analyses). Which poisoning scenarios does topic partitioning make harder to pull off, and which does it not help with? - Read Hayes-Roth (1985, "A Blackboard Architecture for Control"). Identify two control patterns from the paper not discussed in this lesson that 2026 systems would benefit from.
- Read CA-MCP (arXiv:2601.11595). Map its Shared Context Store to either the MessagePool or Blackboard class in
code/main.py. Which primitives does CA-MCP add on top?
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Message pool | "Shared chat history" | Append-only log that every agent reads. Full transparency, poor scaling. |
| Blackboard | "Shared workspace" | Topic-keyed pub/sub. Agents subscribe to relevant topics. Scales farther. |
| Provenance | "Who wrote what" | Metadata on each write: writer, timestamp, prompt, sources. |
| Memory poisoning | "Hallucinations spreading" | One agent's error enters shared state, downstream agents adopt it as fact. |
| Append-only | "No in-place updates" | Corrections are new entries that supersede. Preserves audit trail. |
| Unwritable verifier | "Independent auditor" | Read-only agent that re-fetches sources and flags inconsistencies. |
| Projection | "Scoped view" | Per-agent view computed from global state. LangGraph reducers are the canonical case. |
| Knowledge Source | "Specialist agent" | Hayes-Roth's 1985 term for a blackboard participant. |
Further Reading
- Cemri et al.: Why Do Multi-Agent LLM Systems Fail?: MAST taxonomy; memory poisoning is a coordination-failure sub-family
- CA-MCP: Context-Aware Multi-Server MCP: Shared Context Store for coordinated MCP servers
- Matrix: decentralized multi-agent framework: message-queue-based blackboard without a central orchestrator
- LangGraph state and reducers: the per-agent projection pattern in production
- Anthropic: How we built our multi-agent research system: provenance and verification notes from a production deployment
Code
- main.py (python, 227 lines)