# How the PBL v2 System Implements Classroom UI and Project Workflows

> Explore how the PBL v2 system's React workspace and runtime agents create an interactive classroom UI and project workflows powered by LLM streams.

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

---

**The PBL v2 subsystem combines a three-column React workspace shell with orchestrated runtime agents to deliver an interactive Project-Based Learning environment driven by LLM streams and scenario-based roleplay.**

The **PBL v2** (Project-Based Learning) implementation in the OpenMAIC repository provides a comprehensive classroom interface that manages entire project lifecycles—from initial planning through scenario-based roleplay to final evaluation. This React-based architecture normalizes project state across specialized UI panels while coordinating multiple AI agents to guide learners through structured milestones.

## Three-Column Workspace Architecture

The core classroom interface resides in **`PBLV2Workspace`** at [`/components/scene-renderers/pbl/v2/workspace.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main//components/scene-renderers/pbl/v2/workspace.tsx)【/components/scene-renderers/pbl/v2/workspace.tsx#L1-L15】, which initializes a persistent three-column layout that remains mounted throughout the learning session.

The workspace allocates screen real estate as follows:

- **Left Sidebar (~22%):** Hosts the milestone roadmap and scenario-specific navigation controls, implemented in [`/components/scene-renderers/pbl/v2/sidebar.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main//components/scene-renderers/pbl/v2/sidebar.tsx)【/components/scene-renderers/pbl/v2/sidebar.tsx#L4-L10】
- **Center Agent Chat (~52%):** Manages the Instructor LLM thread and learner input interface via `PBLV2Chat`【/components/scene-renderers/pbl/v2/chat.tsx#L4-L11】
- **Right Submission Panel (~26%):** Contains task submission forms, evaluation cards, and scenario briefing tabs【/components/scene-renderers/pbl/v2/submission.tsx#L27-L31】

The shell monitors `uiPhase` state to determine visibility; when `uiPhase` is not `"workspace"`, the parent renderer swaps the shell for Hero or Completion screens, though the workspace itself never handles navigation logic【/components/scene-renderers/pbl/v2/workspace.tsx#L8-L11】.

## Sidebar Logic and Scenario Support

The **sidebar** builds a hierarchical tree of milestones and microtasks, automatically marking completed items and expanding the active milestone based on the current project state【/components/scene-renderers/pbl/v2/sidebar.tsx#L6-L9】【/components/scene-renderers/pbl/v2/sidebar.tsx#L33-L40】.

For **scenario projects** (when `project.scenario` is true), the sidebar enforces a three-act narrative structure:

1. **Prep Stage:** Displays the **Enter scenario** button (`enter_scenario` action) when the learner reaches the preparation milestone【/components/scene-renderers/pbl/v2/sidebar.tsx#L73-L86】
2. **Role-Play Acts:** Shows **Complete act** (`complete_act`) only after the learner sends a message in the simulator thread, preventing premature advancement【/components/scene-renderers/pbl/v2/sidebar.tsx#L86-L97】
3. **Handover Points:** Renders **Continue handover** (`continue_handover`) when a staged transition between milestones is pending【/components/scene-renderers/pbl/v2/sidebar.tsx#L73-L80】

All scene-action buttons respect the `sceneBusy` state, disabling interaction while server-side task updates are in flight【/components/scene-renderers/pbl/v2/workspace.tsx#L44-L48】.

## Chat Interface and Instructor Streaming

The **Agent Chat** panel connects learner input to the **Instructor** LLM via the `useInstructorStream` hook. When a learner submits a message, the system executes a three-step sequence:

1. **Message Persistence:** `appendUserMessage` adds the text to the project clone, creates a runtime event, and updates timestamps【/components/scene-renderers/pbl/v2/chat.tsx#L87-L16】
2. **Stream Initialization:** The system POSTs to either `/api/pbl/v2/open-task` for standard Instructor turns or `/api/pbl/v2/evaluate` for evaluation requests, depending on the current microtask status【/components/scene-renderers/pbl/v2/chat.tsx#L81-L87】
3. **UI Feedback:** A "thinking…" indicator displays during `instructorStreaming` states, persisting across Hero-to-Workspace remounts【/components/scene-renderers/pbl/v2/chat.tsx#L69-L76】

The chat interface also enforces **handover states**—when no active microtask exists (pending handover), the input field disables until the learner clicks the sidebar's Continue button【/components/scene-renderers/pbl/v2/chat.tsx#L88-L92】.

## Right-Panel Evaluation and Submission System

The right column dynamically switches between context-aware tabs implemented in [`/components/scene-renderers/pbl/v2/right-panel-tabs.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main//components/scene-renderers/pbl/v2/right-panel-tabs.tsx):

- **Scenario Briefing:** Available exclusively for scenario projects, displaying narrative context and role information【/components/scene-renderers/pbl/v2/scenario-briefing-gate.tsx】
- **Evaluation Cards:** Render star ratings and feedback for task, milestone, or project completion via specialized components like [`task-evaluation-card.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/task-evaluation-card.tsx), [`milestone-card.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/milestone-card.tsx), and [`completion-cta-card.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/completion-cta-card.tsx)【/components/scene-renderers/pbl/v2/eval-cards/task-evaluation-card.tsx】

These components consume data from the **evaluation pipeline**, which generates `PBLEvaluation` objects after each task submission.

## Runtime Agents and Workflow Orchestration

The PBL v2 system coordinates five specialized agents that share a **single normalized project instance** (`normalizeProjectRuntime` ensures consistent state across LLM calls)【/lib/pbl/v2/agents/instructor.ts#L41-L44】:

| Agent | Function | Source Location |
|-------|----------|-----------------|
| **Planner** | Generates complete `PBLProjectV2` structures from outlines, including scenario-specific cast and scene fields【/lib/pbl/v2/agents/planner.ts#L4-L27】 | [`lib/pbl/v2/agents/planner.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/agents/planner.ts) |
| **Instructor** | Builds system prompts containing project context, streams LLM tokens, and processes teaching tools like `record_observation` and `adjust_difficulty` | [`lib/pbl/v2/agents/instructor.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/agents/instructor.ts) |
| **Evaluator** | Parses evaluation results and creates `PBLEvaluation` events that populate the right-panel cards | [`lib/pbl/v2/agents/evaluator.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/agents/evaluator.ts) |
| **Progress Tracker** | Updates microtask completion and applies tier-based difficulty adjustments | [`lib/pbl/v2/operations/kernel/progress.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/operations/kernel/progress.ts) |
| **Event Runtime** | Manages SSE streaming (`PBLProjectPatch`) and state transitions via `transitionProjectUiPhase` | [`lib/pbl/v2/operations/kernel/runtime-events.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/operations/kernel/runtime-events.ts) |

### Project Lifecycle Flow

1. **Planning:** `generatePBLV2Project` creates the initial structure
2. **Launch:** `prepareWorkspaceLaunchProject` sets `uiPhase: 'workspace'` and transitions from the Hero screen【/lib/pbl/v2/operations/runtime/workspace-launch.ts#L23-L33】
3. **Interaction:** Learner messages trigger `appendUserMessage` and `message_created` runtime events
4. **Task Completion:** The Submit button calls `/api/pbl/v2/task/update`, potentially generating `pendingHandover` states for scenario transitions
5. **Evaluation:** The Evaluator agent runs post-submission, storing results as `PBLEvaluation` objects
6. **Persistence:** `preparePBLScenesForDocumentPersistence` strips runtime-only data, preserving only the design template for durability【/lib/pbl/v2/runtime/document-persistence.ts#L6-L14】

## Technical Implementation Examples

### Workspace Shell Component

The following implementation demonstrates the three-panel layout structure:

```tsx
import { PBLV2Sidebar } from './sidebar';
import { PBLV2AgentTabs } from './agent-tabs';
import { PBLV2SubmissionPanel } from './submission';
import { PBLV2RightPanelTabs } from './right-panel-tabs';

export function PBLV2Workspace({ project, onProjectChange, … }: Props) {
  const [panelWidths, setPanelWidths] = useState(DEFAULT_PANEL_WIDTHS);
  const activeMilestoneIndex = useMemo(() => workspaceActiveMilestoneIndex(project), [project]);

  return (
    <div className="flex h-full" style={PBL_WORKSPACE_THEME}>
      <PBLV2Sidebar … />
      <PBLV2AgentTabs … />
      <PBLV2SubmissionPanel … />
      <PBLV2RightPanelTabs … />
    </div>
  );
}

```

*Source:* [`components/scene-renderers/pbl/v2/workspace.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/scene-renderers/pbl/v2/workspace.tsx)【/components/scene-renderers/pbl/v2/workspace.tsx#L1-L15】

### Scenario Action Dispatch

The sidebar triggers scene transitions through a centralized action handler:

```tsx
const runSceneAction = useCallback(
  async (action: 'enter_scenario' | 'continue_handover' | 'complete_act') => {
    setSceneBusy(true);
    const res = await fetch('/api/pbl/v2/task/update', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ project, action }),
    });
    if (res.ok) {
      const { project: nextProject } = await res.json();
      onProjectChange(nextProject);
    }
    setSceneBusy(false);
  },
  [project, onProjectChange],
);

```

*Source:* [`components/scene-renderers/pbl/v2/workspace.tsx`](https://github.com/THU-MAIC/OpenMAIC/blob/main/components/scene-renderers/pbl/v2/workspace.tsx)【/components/scene-renderers/pbl/v2/workspace.tsx#L44-L51】

### Instructor Streaming Implementation

The Instructor agent handles LLM streaming for educational guidance:

```tsx
export async function runInstructorTurn(project: PBLProjectV2) {
  const system = await loadPBLV2Prompt('instructor-system');
  await streamLLM({
    model,
    system,
    prompt: '',
    onToken: (t) => setDraftAssistant((s) => s + t),
    onFinish: (patch) => applyPatch(patch),
  });
}

```

*Conceptual flow derived from* `PBLV2Chat` and [`lib/pbl/v2/agents/instructor.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/agents/instructor.ts)【/components/scene-renderers/pbl/v2/chat.tsx#L61-L69】【/lib/pbl/v2/agents/instructor.ts#L4-L12】

## Summary

- The **PBLV2Workspace** component provides a stable three-column React shell that persists across navigation changes while delegating actual routing to parent renderers.
- **Scenario projects** enforce narrative structure through the sidebar's action buttons (`enter_scenario`, `complete_act`, `continue_handover`), which POST to `/api/pbl/v2/task/update` and advance milestones only after learner engagement.
- The **Instructor agent** streams LLM responses through the central chat panel, maintaining state via `instructorStreaming` flags that survive component remounts.
- **Project state normalization** occurs before every agent turn, ensuring consistent data shapes across Planner, Instructor, and Evaluator operations.
- **Document persistence** strips runtime-only fields via `preparePBLScenesForDocumentPersistence`, storing only the design template for long-term durability.

## Frequently Asked Questions

### How does the PBL v2 system handle transitions between scenario acts?

The system uses the **handover mechanism** managed by the sidebar's `runSceneAction` dispatcher. When a learner completes a role-play act, the `complete_act` action validates that the learner actually participated in the simulator thread before marking the milestone complete. The `continue_handover` action then advances to the next milestone (either subsequent role-play acts or the wrap-up stage), with the chat interface disabling input until the learner explicitly clicks Continue【/components/scene-renderers/pbl/v2/sidebar.tsx#L86-L97】【/components/scene-renderers/pbl/v2/chat.tsx#L88-L92】.

### What triggers the Instructor agent to stream responses to the learner?

The Instructor agent initiates streaming when the learner submits a message via the chat interface. The `appendUserMessage` function updates the project state and emits a `message_created` event, then the system POSTs to `/api/pbl/v2/open-task` (or `/api/pbl/v2/evaluate` for evaluation turns). The Instructor builds a system prompt containing the current project, milestone, and microtask context, then streams tokens back to the UI while processing teaching tools like `record_observation`【/components/scene-renderers/pbl/v2/chat.tsx#L81-L87】【/lib/pbl/v2/agents/instructor.ts#L4-L12】.

### How does OpenMAIC persist PBL v2 project state between sessions?

The system uses `preparePBLScenesForDocumentPersistence` in [`lib/pbl/v2/runtime/document-persistence.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/runtime/document-persistence.ts) to strip transient runtime data—such as streaming states, temporary events, and UI phase flags—before saving. Only the design template and completed milestone structures persist to the document store. When reloading, `prepareWorkspaceLaunchProject` rehydrates the workspace state and resets `uiPhase` to `"workspace"`【/lib/pbl/v2/runtime/document-persistence.ts#L6-L14】【/lib/pbl/v2/operations/runtime/workspace-launch.ts#L23-L33】.

### What is the difference between the Planner and Evaluator agents in PBL v2?

The **Planner** ([`lib/pbl/v2/agents/planner.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/agents/planner.ts)) operates once at project creation, generating the complete `PBLProjectV2` structure from an outline using tool-calling loops, including scenario-specific cast and scene fields【/lib/pbl/v2/agents/planner.ts#L4-L27】. The **Evaluator** ([`lib/pbl/v2/agents/evaluator.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/lib/pbl/v2/agents/evaluator.ts)) runs repeatedly after task submissions to assess learner work, create `PBLEvaluation` objects, and populate the right-panel evaluation cards with feedback and star ratings.