# LangGraph Multi-Agent Orchestration in OpenMAIC: How the Director Graph Coordinates AI Agents

> Discover how LangGraph orchestrates multi-agent systems in OpenMAIC. Learn how its StateGraph and Director node enable deterministic coordination and real-time event streaming for AI agents.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: deep-dive
- Published: 2026-08-31

---

**OpenMAIC leverages LangGraph's StateGraph to create a deterministic state machine that separates orchestration logic from agent generation, enabling real-time multi-agent coordination through a Director node that decides who speaks next and streams events via Server-Sent Events.**

The OpenMAIC repository implements sophisticated multi-agent conversations using the `@langchain/langgraph` package. At the heart of this system lies a carefully architected graph structure defined in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts) that treats agent coordination as a typed state machine, making complex interactions predictable and debuggable.

## StateGraph Architecture and State Management

The foundation of OpenMAIC's orchestration is a **StateGraph** built using LangGraph's `Annotation.Root` system. This architecture enforces clear boundaries between immutable inputs and mutable state fields that evolve as the conversation progresses.

### Defining the Orchestrator State

In [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts) (lines 48-78), the `OrchestratorState` is defined as a LangGraph `Annotation.Root` containing:

- **Immutable inputs**: Messages, model configuration, agent list, and user profile
- **Mutable fields**: Current agent selection, turn count, accumulated responses, and whiteboard ledger

This strict typing ensures that each node in the graph receives a well-defined state shape while allowing specific fields to update as the workflow executes.

### Graph Topology and Linear Execution Flow

The `createOrchestrationGraph` function (lines 84-95) constructs a deliberately linear flow:

```

START → director → agent_generate → END
          │
          └─(end)→ END

```

Unlike typical LangGraph implementations that might loop indefinitely, OpenMAIC executes **at most one director → agent cycle per request**. The client serializes multiple requests to continue dialogue, meaning the graph itself never loops. This design eliminates the need for a `maxTurns` counter—the graph's topology itself bounds execution, preventing runaway agent conversations.

## The Director Node: Orchestration Decision Logic

The `directorNode` function serves as the brain of the operation, determining which agent should speak next or whether to cue the user for input.

### Single-Agent Fast Path

When a conversation involves only one agent, the director uses pure code logic without LLM calls (lines 91-128). It dispatches the agent on turn 0 and automatically cues the user afterwards, avoiding unnecessary model latency for trivial routing decisions.

### Multi-Agent LLM-Driven Planning

For multi-agent scenarios, the director can employ sophisticated LLM-based reasoning (lines 149-190):

1. **Fast-path triggering**: Optionally trigger a specific agent on the first turn
2. **LLM deliberation**: Call the model through `AISdkLangGraphAdapter` with a system prompt built via `buildDirectorPrompt`
3. **Decision parsing**: Extract routing decisions using `parseDirectorDecision` to determine `nextAgentId` or `shouldEnd`

The prompt includes the conversation summary, available agent personas, whiteboard state, and user profile, allowing the LLM to make context-aware routing decisions.

### Real-Time Event Streaming

Throughout execution, the node streams `StatelessEvent` objects via `config.writer`, enabling real-time Server-Sent Events (SSE) to the front-end. Event types include `thinking`, `cue_user`, `agent_start`, and `agent_end`, providing granular visibility into the orchestration process.

## Agent Generation and Structured Output Parsing

Once the Director selects an agent, the `runAgentGeneration` function (lines 235-277) handles the actual response generation and action execution.

### System Prompt Construction

The node builds a comprehensive system prompt combining:
- The selected agent's persona and allowed actions
- Current store state and conversation history
- Whiteboard context from previous turns

### Streaming and Parsing

While streaming the model output, the system:
1. Parses structured JSON chunks for actions
2. Emits `text_delta` events for incremental text display
3. Triggers `action` events for whiteboard or slide modifications
4. Records whiteboard actions for persistence via the ledger

## Stateless Runtime Execution

The entry point [`lib/orchestration/stateless-generate.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/stateless-generate.ts) contains the `statelessGenerate` function (lines 5-13) that bridges the graph with the API layer.

```typescript
import { createOrchestrationGraph, buildInitialState } from '@/lib/orchestration/director-graph';
import { statelessGenerate } from '@/lib/orchestration/stateless-generate';
import { OpenAI } from '@langchain/openai';

// Build the graph state from the incoming request
const llm = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const initialState = buildInitialState(chatRequest, llm);

// Stream events from the graph execution
for await (const ev of statelessGenerate(chatRequest, abortSignal, llm)) {
  switch (ev.type) {
    case 'thinking':
      // Display loading indicator
      break;
    case 'agent_start':
      // Initialize new message bubble
      break;
    case 'text_delta':
      // Append streamed text
      break;
    case 'action':
      // Execute whiteboard updates
      break;
    case 'cue_user':
      // Enable user input
      break;
  }
}

```

This implementation creates the graph, initializes state from the `StatelessChatRequest`, and yields events that the front-end consumes for UI updates, action execution, and turn bookkeeping.

## Summary

- **LangGraph StateGraph** provides the backbone for OpenMAIC's multi-agent coordination, offering type-safe state management through `Annotation.Root` defined in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts)
- **Deterministic execution** is enforced through a linear graph topology that executes at most one cycle per request, eliminating infinite loops without requiring turn counters
- **Dual-mode Director** blends fast code paths for single-agent scenarios with LLM-driven deliberation for complex multi-agent routing using `buildDirectorPrompt` and `parseDirectorDecision`
- **Event-driven architecture** streams `StatelessEvent` objects via `config.writer`, enabling real-time UI updates through Server-Sent Events
- **Clean separation of concerns** isolates orchestration logic (Director node) from generation logic (Agent-generate node), making the system extensible and maintainable

## Frequently Asked Questions

### What is LangGraph and why did OpenMAIC choose it for multi-agent orchestration?

LangGraph is a framework for building stateful, multi-actor applications with LLMs. OpenMAIC selected it because it provides a **deterministic, typed state machine** that cleanly separates decision-making from generation. Unlike ad-hoc orchestration scripts, LangGraph's `StateGraph` enforces explicit state definitions and transition logic, making complex multi-agent systems predictable and debuggable.

### How does the Director node decide which agent speaks next?

The Director node employs a **two-tier decision strategy**. For single-agent conversations, it uses pure code logic to dispatch the agent and cue the user without LLM latency. For multi-agent scenarios, it constructs a system prompt via `buildDirectorPrompt` containing conversation summaries and agent personas, then calls an LLM through `AISdkLangGraphAdapter` to analyze context and return the `nextAgentId` via `parseDirectorDecision`.

### What are StatelessEvents and how does the front-end consume them?

`StatelessEvent` objects are typed events streamed from the graph execution via `config.writer`. They include variants like `thinking`, `agent_start`, `text_delta`, `action`, and `cue_user`. The front-end receives these via Server-Sent Events and maps them to UI behaviors—showing loading indicators, appending text to message bubbles, executing whiteboard actions, or enabling user input fields.

### Why doesn't the OpenMAIC graph use loops for continuing conversations?

OpenMAIC uses a **linear graph topology** (`START → director → agent_generate → END`) intentionally to prevent runaway agent loops. The architecture treats each turn as a discrete request; the client serializes multiple requests to continue dialogue. This eliminates the need for `maxTurns` counters and makes the system more predictable, as the graph's shape itself bounds execution rather than runtime logic.