Understanding the Role of `buildDirectorPrompt` and `parseDirectorDecision` in OpenMAIC

The buildDirectorPrompt and parseDirectorDecision functions in OpenMAIC work together to power the Director agent, a lightweight LLM-based controller that dynamically routes conversation turns between multiple specialized agents and decides when a round should end.

OpenMAIC is a multi-agent orchestration framework developed by THU-MAIC that coordinates specialized agents (teacher, student, whiteboard, and others) through structured conversations. The Director sits at the heart of this system, replacing hard-coded routing rules with data-driven LLM decisions. These two functions—buildDirectorPrompt and parseDirectorDecision—form the complete request-response cycle that enables this flexible control mechanism.

What buildDirectorPrompt Does

buildDirectorPrompt generates the system prompt that instructs the LLM acting as the Director. Located in lib/orchestration/director-prompt.ts (lines 23-89), this function assembles all runtime context into a structured string using the DIRECTOR template.

The function accepts these key parameters:

  • agents — Array of available AgentConfig objects defining who can participate
  • conversationSummary — Current state of the dialogue
  • agentResponses — What each agent has already said in this round
  • turnCount — How many exchanges have occurred
  • discussionContext — Optional student-initiated discussion mode settings
  • triggerAgentId — Which agent started the current flow
  • whiteboardLedger — Whiteboard action history as WhiteboardActionRecord[]
  • userProfile — Optional learner characteristics for personalization
  • whiteboardOpen — Whether the whiteboard UI is currently active

The prompt construction handles multiple conversation modes. In discussion mode, it applies special routing rules that prioritize student-driven exploration. It also calls buildWhiteboardStateForDirector to inject a concise whiteboard summary, ensuring the Director understands any collaborative annotations or tool usage.

What parseDirectorDecision Does

parseDirectorDecision processes the LLM's response and extracts the routing decision. Found in the same file at lines 110-140, this function serves as the defensive parser that guards against malformed outputs.

The function expects a JSON response from the Director LLM, typically:

{ "next_agent": "teacher" }

or

{ "next_agent": "END" }

The parser handles three outcomes:

  • Valid agent ID — Returns { nextAgentId: string, shouldEnd: false } and routing continues
  • Explicit "END" value — Returns { nextAgentId: null, shouldEnd: true } to terminate the round
  • Missing or malformed JSON — Falls back to { nextAgentId: null, shouldEnd: true } as a safe default

This normalization ensures the orchestration layer never crashes on unexpected LLM outputs.

How the Functions Work Together

The complete Director control flow follows this sequence:

  1. State gathering — The workbench collects conversation snapshot, agent configurations, response history, and whiteboard data
  2. Prompt generation — buildDirectorPrompt formats this into the DIRECTOR system prompt
  3. LLM inference — The Director LLM receives the prompt and returns a JSON decision
  4. Decision parsing — parseDirectorDecision safely extracts and validates the next_agent field
  5. Action execution — The orchestrator either dispatches to the named agent or ends the round

This loop repeats until the Director signals termination or a maximum turn limit is reached.

Implementation Example

Here's how these functions integrate into the orchestration loop:

import {
  buildDirectorPrompt,
  parseDirectorDecision,
} from '@/lib/orchestration/director-prompt';

// Assemble current conversation state
const systemPrompt = buildDirectorPrompt(
  agents,                      // AgentConfig[]
  summary,                     // string
  respondedAgents,             // AgentTurnSummary[]
  turnCount,                   // number
  discussionCtx,               // optional discussion info
  triggerAgentId,              // optional initiator
  whiteboardLedger,            // WhiteboardActionRecord[]
  userProfile,                 // optional user profile
  whiteboardOpen,              // boolean
);

// Query the Director LLM
const directorResponse = await llm.chat({ 
  system: systemPrompt, 
  user: '' 
});

// Extract routing decision
const { nextAgentId, shouldEnd } = parseDirectorDecision(
  directorResponse.content
);

// Execute routing or terminate
if (!shouldEnd && nextAgentId) {
  await dispatchToAgent(nextAgentId);
} else {
  endCurrentRound();
}

Key Source Files and Type Definitions

File Purpose
lib/orchestration/director-prompt.ts Implements both functions plus buildWhiteboardStateForDirector helper
lib/orchestration/registry/types.ts Defines AgentConfig interface used by the prompt builder
lib/prompts.ts Contains the DIRECTOR template string referenced by buildPrompt
lib/orchestration/types.ts Declares WhiteboardActionRecord and AgentTurnSummary structures

The type system ensures AgentTurnSummary captures each agent's contribution timestamp, content, and metadata, while WhiteboardActionRecord tracks collaborative edits with operation types and authors.

Why This Design Matters

The separation of buildDirectorPrompt and parseDirectorDecision creates a clean interface boundary between orchestration logic and LLM interaction. Teams can modify prompt engineering strategies—adding new context fields, adjusting formatting, or switching discussion modes—without changing the parsing contract. Conversely, parser improvements for edge cases (newlines in JSON, markdown code fences, truncated responses) don't affect prompt construction.

This architecture also enables deterministic reproducibility. Given the same conversation state, the prompt builder produces identical inputs, making Director behavior testable and debuggable.

Summary

  • buildDirectorPrompt constructs the complete system prompt that informs the Director LLM about available agents, conversation history, whiteboard state, and routing constraints
  • parseDirectorDecision safely extracts the next_agent JSON field and determines whether to continue routing or end the round
  • Both functions reside in lib/orchestration/director-prompt.ts and operate on shared type definitions from lib/orchestration/types.ts
  • The prompt builder supports dynamic conversation modes including student-initiated discussion and whiteboard-aware routing
  • The parser implements defensive fallback logic that treats malformed responses as end-of-round signals

Frequently Asked Questions

What happens if the Director LLM returns invalid JSON?

parseDirectorDecision catches parsing errors and returns shouldEnd: true with a null next agent. This conservative fallback prevents the conversation from entering undefined states when LLM outputs are malformed.

Can the Director route to the same agent multiple times in one round?

Yes. The agentResponses parameter tracks which agents have already spoken, but the prompt does not enforce uniqueness. The Director LLM can theoretically select any available agent based on conversation needs, including revisiting previous speakers.

How does discussion mode change the Director's behavior?

When discussionContext is provided, buildDirectorPrompt injects special instructions that prioritize student agency and exploratory dialogue over direct instructional flow. This modifies the routing incentives without changing the underlying parsing logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →