Multi-Agent Collaboration: Coordinating Teams with Supervisors
For complex, multi-domain tasks (such as writing an research paper and then compiling a code demo), a single agent with dozens of tools becomes slow and error-prone. The Multi-Agent Collaboration pattern resolves this by grouping specialized worker agents under a coordinating Supervisor Node.
1. Supervisor Dispatch Workflow
The supervisor node acts as a manager. It evaluates the current state, selects the appropriate worker agent to execute the next task, receives the worker's output, and decides whether to dispatch to another worker or finish the task.
graph TD
A[Start: Complex User Task] --> B[Node: Supervisor Manager]
B -->|Conditional Routing| C{Who should work next?}
C -->|Choose Research| D[Node: Research Agent]
C -->|Choose Coder| E[Node: Coding Agent]
C -->|Choose Finish| F[__end__]
D --> B
E --> B2. Implementing the Supervisor Graph in Node.js
Define the supervisor agent that chooses the next node:
// src/services/multiAgentSupervisor.ts
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, Annotation } from "@langchain/langgraph";
import { z } from "zod";
// 1. Declare Shared State Annotation
const TeamStateAnnotation = Annotation.Root({
taskInput: Annotation<string>(),
researchData: Annotation<string>(),
codeSnippet: Annotation<string>(),
nextWorker: Annotation<string>(), // Tracks which node executes next
});
// 2. Define specialized Worker Nodes
async function researcherNode(state: typeof TeamStateAnnotation.State) {
console.log("Researcher working...");
return {
researchData: "Retrieved sales data reports indicating 20% annual growth.",
nextWorker: "supervisor", // Pass control back to manager
};
}
async function coderNode(state: typeof TeamStateAnnotation.State) {
console.log("Coder writing scripts...");
return {
codeSnippet: "console.log('Growth metrics calculated');",
nextWorker: "supervisor", // Pass control back to manager
};
}
// 3. Define the Supervisor Node using Structured Outputs
async function supervisorNode(state: typeof TeamStateAnnotation.State) {
const model = new ChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0 }).withStructuredOutput(
z.object({
nextAgent: z.enum(["researcher", "coder", "FINISH"]),
})
);
const prompt = `Evaluate the task status.
Task Input: ${state.taskInput}
Research Data: ${state.researchData || "empty"}
Code Snippet: ${state.codeSnippet || "empty"}
Select the next agent to execute or choose FINISH if the task is complete.`;
const decisionObj = await model.invoke(prompt);
return {
nextWorker: decisionObj.nextAgent,
};
}
// 4. Compose the collaborative StateGraph
const workflow = new StateGraph(TeamStateAnnotation)
.addNode("supervisor", supervisorNode)
.addNode("researcher", researcherNode)
.addNode("coder", coderNode)
.setEntryPoint("supervisor")
// Normal worker edge outputs return to supervisor
.addEdge("researcher", "supervisor")
.addEdge("coder", "supervisor")
// Define conditional routes departing from the supervisor manager node
.addConditionalEdges("supervisor", (state) => state.nextWorker, {
researcher: "researcher",
coder: "coder",
FINISH: "__end__",
});
export const teamApp = workflow.compile();3. Advantages of Multi-Agent Hierarchies
- Separation of Concerns: Each worker agent uses a specialized system prompt and holds access only to its specific subset of tools.
- Easier Debugging: You can isolate, run, and unit-test the coder agent or researcher agent independently from the supervisor orchestrator.
Published on Last updated: