# How OpenMAIC Handles Multi-Turn Discussions With Its Director Graph

> Discover how OpenMAIC manages multi-turn discussions using its Director Graph. Learn how stateless, single-round execution enables clients to serialize turns efficiently.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: internals
- Published: 2026-09-05

---

**OpenMAIC orchestrates multi-turn conversations through a stateless, single-round Director Graph where the client serializes turns by feeding updated directorState back into sequential requests.**

The THU-MAIC/OpenMAIC repository implements a unique approach to multi-agent conversation management. Rather than maintaining long-running server-side loops, its **Director Graph** treats each turn as an isolated execution, pushing state persistence to the client layer. This design enables scalable, stateless deployment while supporting complex multi-turn dialogues across multiple AI agents.

## Understanding the Single-Round Director Graph Architecture

The Director Graph in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts) follows a deliberately constrained topology: `START → director → (agent_generate) → END` [^1^]. This structure contains **no internal `maxTurns` limit**—the boundary is the graph execution itself, not a counter within the graph logic.

```typescript
// Simplified topology from lib/orchestration/director-graph.ts
// Lines 4-12 define this fixed structure

```

Because the graph terminates after each agent response, **multi-turn discussions are achieved externally**. The client receives a complete `directorState` object after every request, including:
- `turnCount`: Current turn number
- `agentResponses`: Accumulated outputs from all agents
- `whiteboard`: Shared ledger of actions and decisions
- `shouldEnd`: Termination flag

The client then initiates a new request with this updated state, creating the illusion of continuous conversation through serialized state transfer.

## Director Node Decision Logic

The director node determines the next speaker through three distinct pathways, implemented in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts) [^2^][^3^][^4^].

### Single-Agent Mode: Code-Only Dispatch

When only one agent is configured, the director operates without LLM invocation:

- **Turn 0**: Automatically dispatches the sole available agent
- **Subsequent turns**: Returns `USER` cue, prompting client input

This fast-path avoids unnecessary model calls for trivial routing decisions [^2^].

### Multi-Agent Mode: LLM-Powered Arbitration

With multiple agents, the director constructs a comprehensive prompt via [`lib/orchestration/director-prompt.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-prompt.ts) containing:
- Agent descriptions and capabilities
- Conversation summary
- Prior agent responses
- Whiteboard action history

The LLM returns one of: an `agentId`, `USER`, or `END` [^3^][^4^].

### Trigger Agent Fast-Path

If `triggerAgentId` is specified in the request configuration, the director skips LLM deliberation for turn 0 and immediately dispatches the designated agent. This optimizes known-start scenarios like scripted introductions [^3^].

## Agent Execution and State Propagation

Once an agent is selected, the director emits a `thinking` event and transitions to the `agent_generate` node. This node:
1. Streams the agent's response generation
2. Captures tool actions and whiteboard updates
3. Updates `turnCount` and response history
4. Returns the complete `directorState` to the client [^5^]

The graph then terminates, handing control back to the client layer.

## Client-Side Turn Serialization

The actual multi-turn loop resides in [`lib/chat/pi/director-loop.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/chat/pi/director-loop.ts). This client component:

- Maintains conversation continuity across HTTP requests
- Manages `directorState` lifecycle between turns
- Detects termination conditions (`shouldEnd`, `END` response, or error states)
- Handles user input integration when `USER` is cued

## Practical Implementation Example

```typescript
// Build the graph once at server initialization
import { createOrchestrationGraph, buildInitialState } from '@/lib/orchestration/director-graph';
import { myLanguageModel } from '@/lib/ai-models';

const graph = createOrchestrationGraph();

// First request: initialize new discussion
let request = {
  messages: [],
  storeState: initialStoreState,
  config: {
    agentIds: ['researcher', 'critic', 'synthesizer'],
    triggerAgentId: 'researcher',  // Optional: bypass LLM for turn 0
  },
};
let state = buildInitialState(request, myLanguageModel);
let result = await graph.invoke(state);

// Subsequent requests: feed back directorState
while (!result.directorState.shouldEnd) {
  request = {
    ...request,
    directorState: result.directorState,  // Carry forward accumulated state
    messages: result.messages,
  };
  state = buildInitialState(request, myLanguageModel);
  result = await graph.invoke(state);
  // Client handles USER cues or termination
}

```

## Key Files in the Multi-Turn Pipeline

| File | Responsibility |
|------|----------------|
| [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts) | LangGraph definition, director routing logic, graph construction [^1^][^2^][^3^][^4^][^5^] |
| [`lib/orchestration/director-prompt.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-prompt.ts) | LLM prompt construction for multi-agent arbitration |
| [`lib/chat/pi/director-loop.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/chat/pi/director-loop.ts) | Client-side turn serialization and conversation loop |
| [`lib/orchestration/stateless-generate.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/stateless-generate.ts) | Stream parsing and event structuring for agent outputs |

## Summary

- **OpenMAIC's Director Graph** executes exactly one turn per request with a fixed topology that intentionally excludes internal looping
- **Multi-turn discussions** are achieved through client-managed state serialization, where `directorState` is passed between sequential requests
- **Routing decisions** use code-only paths for single-agent or triggered scenarios, falling back to LLM arbitration for complex multi-agent selections
- **Stateless architecture** enables horizontal scaling and simplifies server deployment at the cost of client-side complexity

## Frequently Asked Questions

### What is the Director Graph in OpenMAIC?

The Director Graph is a LangGraph-based orchestration layer defined in [`lib/orchestration/director-graph.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/orchestration/director-graph.ts) that manages which agent speaks next in a multi-agent conversation. It uses a fixed three-node topology (START → director → agent_generate → END) with no internal iteration capability [^1^].

### Why doesn't OpenMAIC support server-side multi-turn loops?

The stateless, single-round design eliminates long-running server processes, enabling easier scaling and fault tolerance. By pushing turn management to the client via `directorState` serialization, the server treats each request as independent and idempotent [^2^][^5^].

### How does the director choose between agents?

The director checks agent count first: with one agent, it uses deterministic code; with multiple agents, it either fast-paths a `triggerAgentId` or constructs an LLM prompt describing all agents and conversation context, then parses the model's selection [^2^][^3^].

### What happens when the director returns `USER`?

A `USER` response signals that no agent should speak next—the conversation requires human input. The graph terminates, returning control to the client with `directorState` intact; the client collects user input and initiates the next request to continue the dialogue [^4^].