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 availableAgentConfigobjects defining who can participateconversationSummary— Current state of the dialogueagentResponses— What each agent has already said in this roundturnCount— How many exchanges have occurreddiscussionContext— Optional student-initiated discussion mode settingstriggerAgentId— Which agent started the current flowwhiteboardLedger— Whiteboard action history asWhiteboardActionRecord[]userProfile— Optional learner characteristics for personalizationwhiteboardOpen— 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:
- State gathering — The workbench collects conversation snapshot, agent configurations, response history, and whiteboard data
- Prompt generation —
buildDirectorPromptformats this into theDIRECTORsystem prompt - LLM inference — The Director LLM receives the prompt and returns a JSON decision
- Decision parsing —
parseDirectorDecisionsafely extracts and validates thenext_agentfield - 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
buildDirectorPromptconstructs the complete system prompt that informs the Director LLM about available agents, conversation history, whiteboard state, and routing constraintsparseDirectorDecisionsafely extracts thenext_agentJSON field and determines whether to continue routing or end the round- Both functions reside in
lib/orchestration/director-prompt.tsand operate on shared type definitions fromlib/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →