A shared Google Doc “blackboard” feels collaborative until you cannot prove who wrote which step or why the Implementer ignored the Critic. For production agents, prefer a message bus (SQS / EventBridge) with typed envelopes — and reserve blackboards for read-mostly scratch that is still versioned.
⚡ TL;DR: Use EventBridge/SQS for agent handoffs with
run_id,role,schema_version, and payload hash. Keep a small append-only artifact store for plans/diffs. Prefer bus over shared mutable docs when you need auditability.
When a blackboard is enough
Blackboards work for short, single-tenant research loops where humans watch live. They fail when multiple writers mutate the same plan section without optimistic locking.
# blackboard/scratch.py — OK for demos, not for prod apply
class Scratch:
def __init__(self):
self.notes: list[dict] = []
def append(self, role: str, text: str):
self.notes.append({"role": role, "text": text}) # ❌ no hash, no schema
Message bus envelopes
{
"run_id": "run_7f3a",
"role": "planner",
"type": "PlanReady",
"schema_version": 3,
"payload_s3": "s3://agent-artifacts/run_7f3a/plan.json",
"payload_sha256": "ab12…",
"ts": "2026-09-11T15:00:00Z"
}
# bus/emit.py
import boto3, hashlib, json
eb = boto3.client("events")
s3 = boto3.client("s3")
def emit_plan(run_id: str, plan: dict, bucket: str):
body = json.dumps(plan, sort_keys=True).encode()
key = f"{run_id}/plan.json"
s3.put_object(Bucket=bucket, Key=key, Body=body, ContentType="application/json")
eb.put_events(Entries=[{
"Source": "agents.planner",
"DetailType": "PlanReady",
"Detail": json.dumps({
"run_id": run_id,
"payload_s3": f"s3://{bucket}/{key}",
"payload_sha256": hashlib.sha256(body).hexdigest(),
"schema_version": 3,
}),
"EventBusName": "agent-orchestration",
}])
✅ Consumers verify SHA before trusting the plan — bus messages stay small and auditable.
SQS vs EventBridge
| Need | Prefer |
|---|---|
| Competing consumers / work queues | SQS (+ DLQ) |
| Fan-out (critic + metrics + audit) | EventBridge |
| Exactly-once apply | SQS FIFO + idempotency keys |
| Cross-account notify | EventBridge |
// consumer/idempotency.ts
const seen = new Set<string>();
export function once(key: string, fn: () => Promise<void>) {
if (seen.has(key)) return; // ✅ at-least-once bus, exactly-once side effect
seen.add(key);
return fn();
}
Closing checklist
- [ ] Typed envelopes with schema_version
- [ ] Artifacts in object storage with content hash
- [ ] DLQ + alarm on poison messages
- [ ] Idempotency keys on apply tools
- [ ] Prefer bus when multiple roles write sequentially
Series navigation
Day 51: Multi-Agent Roles: Planner, Implementer, Critic · Day 53: Avoiding Agent Ping-Pong
Last updated September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
