Siddhant Deval
Siddhant Deval
system design5 min read

Multi-Agent Orchestration & Collaborative Swarms

A single omniscient agent prompt fails on complex cross-domain tasks because context dilution, prompt drift, and tool overloading collapse reasoning quality. This article implements a Supervisor-Worker multi-agent topology with a centralised blackboard memory and canvas concurrency locks, enabling a Researcher, Schema Engineer, and Reviewer to co-produce a technical spec on the shared Workspace canvas.

Series·Part 18 of 24

Full-Stack AI Architecture: From Streaming Foundations to Resilient Agentic Systems in Node.js & React

Multi-Agent Orchestration & Collaborative Swarms

A single omniscient agent system prompt trying to handle research, schema design, technical writing, and peer review simultaneously suffers from prompt dilution: the context fills with instructions from multiple conflicting domains, tool lists grow unwieldy, and reasoning quality degrades as the model tries to optimise for contradictory objectives simultaneously.

The solution is decomposition. Three specialised agents — a Researcher, a Schema Engineer, and a Reviewer — each with narrow system prompts and dedicated tool sets, collaborating through a shared blackboard memory. A Supervisor agent coordinates the workflow and enforces canvas concurrency locks to prevent their simultaneous canvas writes from conflicting.

Architectural Note

Who this is for: Engineers whose single-agent systems fail on complex multi-domain tasks because context dilution collapses reasoning quality. Multi-agent swarms improve quality by giving each agent a focused context budget.


1. Supervisor-Worker Topology


2. The Blackboard Memory

The blackboard is a shared fact store where agents deposit verified conclusions without access to each other's individual context windows. This prevents context pollution while enabling knowledge sharing.

TYPESCRIPT
// ✅ Blackboard memory — shared PostgreSQL facts table
// file: src/agents/Blackboard.ts

import { db } from '../../packages/db/src/drizzle'
import { pgTable, uuid, text, timestamp, jsonb } from 'drizzle-orm/pg-core'

export const blackboardFacts = pgTable('blackboard_facts', {
  id:         uuid('id').primaryKey().defaultRandom(),
  sessionId:  uuid('session_id').notNull(),
  agentId:    text('agent_id').notNull(),
  factType:   text('fact_type').notNull(),  // Typed as FactType in application layer
  content:    text('content').notNull(),
  metadata:   jsonb('metadata'),
  createdAt:  timestamp('created_at').defaultNow(),
})

// Type-safe fact type enum — keeps blackboard writes consistent across agents.
// In production, validate with z.enum([...]) before every deposit() call.
export type FactType = 'research_finding' | 'schema_decision' | 'review_comment' | 'concurrency_lock'

export class Blackboard {
  constructor(private sessionId: string) {}

  async deposit(agentId: string, factType: FactType, content: string, metadata?: unknown): Promise<void> {
    await db.insert(blackboardFacts).values({
      sessionId: this.sessionId,
      agentId,
      factType,
      content,
      metadata: metadata as any,
    })
  }

  async read(factType?: FactType): Promise<{ agentId: string; factType: FactType; content: string }[]> {
    const query = db.select({
      agentId: blackboardFacts.agentId,
      factType: blackboardFacts.factType,
      content: blackboardFacts.content,
    }).from(blackboardFacts)
    .where(eq(blackboardFacts.sessionId, this.sessionId))
    .orderBy(asc(blackboardFacts.createdAt))

    const results = await (factType
      ? query.where(eq(blackboardFacts.factType, factType))
      : query)

    return results as { agentId: string; factType: FactType; content: string }[]
  }

  async summarise(): Promise<string> {
    const facts = await this.read()
    return facts
      .map(f => `[${f.agentId}/${f.factType}]: ${f.content}`)
      .join('\n')
  }
}

3. Specialised Sub-Agents

TYPESCRIPT
// ✅ Researcher Agent — narrow focus, dedicated tools
// file: src/agents/ResearcherAgent.ts

export class ResearcherAgent {
  private runtime: FsmAgentRuntime

  constructor(orgId: string, private blackboard: Blackboard) {
    this.runtime = new FsmAgentRuntime({
      maxSteps: 8,
      maxTokensPerStep: 2048,
      systemPrompt: `You are a Technical Researcher. Your ONLY job is to search the 
knowledge vault and deposit verified findings on the blackboard. 
Do not draft documents. Do not design schemas. Only research and report.`,
      tools: [
        searchKnowledgeTool(orgId),          // Can read knowledge vault
        depositBlackboardTool(blackboard),   // Can write to blackboard
      ],
      requireHumanApproval: () => false,
    })
  }

  async research(topic: string): Promise<void> {
    await this.runtime.run(
      `Research topic: "${topic}". Search the knowledge vault thoroughly. ` +
      `Deposit each significant finding on the blackboard with factType "research_finding".`
    )
  }
}

// ✅ Schema Engineer Agent — reads blackboard, designs, writes canvas
export class SchemaEngineerAgent {
  private runtime: FsmAgentRuntime

  constructor(orgId: string, private blackboard: Blackboard) {
    this.runtime = new FsmAgentRuntime({
      maxSteps: 12,
      maxTokensPerStep: 3072,
      systemPrompt: `You are a Database Schema Engineer. You read research findings from 
the blackboard and design database schemas. Create canvas nodes for each table.
Do not perform research. Do not write prose. Only design schemas.`,
      tools: [
        readBlackboardTool(blackboard),         // Can read findings
        createCanvasNodeTool(orgId),            // Can write canvas
        depositBlackboardTool(blackboard),      // Can deposit schema decisions
      ],
      requireHumanApproval: (tool) => tool === 'deploy_schema',  // Gate deployments
    })
  }

  async design(task: string): Promise<void> {
    const findings = await this.blackboard.summarise()
    await this.runtime.run(
      `Design database schema for: "${task}".\n\nResearch findings:\n${findings}`
    )
  }
}

4. The Supervisor Orchestrator

TYPESCRIPT
// ✅ Supervisor — coordinates agents, enforces sequencing
// file: src/agents/SupervisorOrchestrator.ts

export class SupervisorOrchestrator {
  constructor(
    private orgId: string,
    private canvasStore: CanvasStore,
  ) {}

  async run(task: string, sessionId: string): Promise<OrchestratorResult> {
    const blackboard = new Blackboard(sessionId)

    // Phase 1: Research (parallel-safe — read-only canvas)
    const researcher = new ResearcherAgent(this.orgId, blackboard)
    await researcher.research(task)

    // Phase 2: Schema design (sequential — writes canvas)
    // Acquire canvas write lock before allowing SchemaEngineerAgent to proceed
    const schemaLockAcquired = this.canvasStore.acquireAgentLock('schema-engineer')
    if (!schemaLockAcquired) {
      throw new Error('Canvas is locked by another agent — retry later')
    }

    try {
      const schemaEngineer = new SchemaEngineerAgent(this.orgId, blackboard)
      await schemaEngineer.design(task)
    } finally {
      this.canvasStore.releaseAgentLock('schema-engineer')
    }

    // Phase 3: Review (read-only — can run after schema engineer releases lock)
    const reviewer = new ReviewerAgent(this.orgId, blackboard)
    const reviewComments = await reviewer.review(task)

    return {
      sessionId,
      blackboardSummary: await blackboard.summarise(),
      reviewComments,
    }
  }
}

5. Canvas Concurrency Locks for Multi-Agent Safety

TYPESCRIPT
// Extension to CanvasStore from Part 7 — agent lock management
export class CanvasStore {
  private agentLocks = new Map<string, { agentId: string; acquiredAt: number }>()
  private AGENT_LOCK_TIMEOUT = 30_000  // 30 seconds

  acquireAgentLock(agentId: string): boolean {
    const existing = this.agentLocks.get('canvas')
    if (existing) {
      const isStale = Date.now() - existing.acquiredAt > this.AGENT_LOCK_TIMEOUT
      if (!isStale) return false  // Canvas locked by another active agent
    }
    this.agentLocks.set('canvas', { agentId, acquiredAt: Date.now() })
    return true
  }

  releaseAgentLock(agentId: string): void {
    const lock = this.agentLocks.get('canvas')
    if (lock?.agentId === agentId) {
      this.agentLocks.delete('canvas')
    }
  }
}

Summary

Pattern Problem Solved
Supervisor-Worker Single-agent context dilution across multiple domains
Blackboard Memory Sub-agents share verified conclusions without polluting each other's contexts
Narrow System Prompts Each agent has one job; tool sets are limited to what that job requires
Canvas Lock Prevents simultaneous multi-agent canvas writes from producing contradictory node sets

What's Next

Part 19 addresses the cost side of production AI: deploying Redis vector semantic caching that catches semantically equivalent queries with 92–96% similarity thresholds, layered with Anthropic/OpenAI prompt prefix caching — achieving 70%+ total cost reduction on cached paths.

Research & Synthesis Note

This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.

#Multi-Agent#Orchestration#AI Agents#Node.js#TypeScript#Concurrency#System Design
Siddhant Deval

Written by Siddhant Deval

Senior Full-Stack Engineer building high-scale architectures, browser performance engineering systems, and SaaS platforms.

Series·Part 18 of 24

Full-Stack AI Architecture: From Streaming Foundations to Resilient Agentic Systems in Node.js & React

Curriculum Roadmap
01The Full-Stack AI Paradigm Shift: Beyond Toy Wrappers & Prompt Concatenation
02The Anatomy of an LLM Call: Tokens, Context Windows & Probabilistic Mechanics
03Structured Outputs & Formal Invariants: Taming Non-Determinism with Zod & TypeBox
04Full-Stack Streaming Protocols: SSE, Fetch Streams & WebSockets
05V8 Streams, Server-Side Backpressure & Cancellations in Node.js
06React 18/19 Streaming UI & Generative Components
07Generative UI & Interactive Canvas Synchronization
08High-Dimensional Vector Embeddings & Similarity Math
09Vector Storage & Indexing at Scale: HNSW vs IVF in PostgreSQL (pgvector)
10Hybrid Search: Dense Vectors Meet Sparse Lexical BM25
11Multi-Stage Retrieval & Cross-Encoder Reranking
12Advanced RAG Architectures: Contextual Retrieval & Parent-Document Splitting
13Query Transformation, Decomposition & Multi-Query Expansion
14Dynamic Context Window Management & Long-Context Packing
15Agent Loops & Deterministic State Machines
16Model Context Protocol (MCP) in Full-Stack Architecture
17Human-in-the-Loop & Durable Agent Workflows
18Multi-Agent Orchestration & Collaborative Swarms
Reading
19Semantic Caching & Provider Cost Optimization
20Multi-Provider AI Gateways: Fallbacks, Rate Limiting & Hedged Requests
21Client-Side AI & Edge Hybrid Execution: WebGPU & Transformers.js
22Production Evals & Guardrails: Measuring Retrieval and Generation
23Full-Stack AI Observability & Tracing with OpenTelemetry
24Production Capstone: Assembling The Enterprise Collaborative AI Workspace