Iris Dai f1239f3e0a feat(pbl): support written decisions and learner rationales (#2060)
Related #1997

PBL v3 currently accepts only predefined decision option IDs. Courses
cannot collect a learner-authored engineering rule or preserve an
explanation alongside a choice when another role prepares a plan or
reviews code.

This adds optional `responseType: "text"` and `requiresRationale`
fields. Existing decisions retain their immediate-submit option buttons.
New forms preserve unsent drafts per study and carry submitted responses
and rationales through study state, versioned events, role artifact
projections, production summaries, and report evidence. Discussion
Agents receive saved responses both in their initial prompt and in
authoritative live context refreshed before each model call. Text
answers remain literal in summaries and reports even when they match an
authored option ID. All successful decision commands, including explicit
retries, acknowledge only the matching local draft and preserve newer
edits. The server validates response shape, required fields, and option
membership; saving a response does not certify its engineering
correctness.

Each submitted answer and rationale is capped at 64,000 UTF-16 code
units, matching existing chat input. The shared size rule is enforced at
the command boundary before any study activity or command receipt is
written, and again by domain decision validation. Forms use the same
validation and HTML maxlength; oversized restored drafts remain editable
with an inline error and are not silently truncated. Existing stored
records are not rewritten. Their combined JSON-encoded strings must also
fit 192,000 UTF-8 bytes, reserving room for command metadata beneath the
route-level 256,000-byte cap. The shared browser/domain/server rule
accounts for Unicode and JSON escaping; invalid drafts remain editable.
NUL and unpaired UTF-16 surrogates, which PostgreSQL JSONB cannot store,
are also rejected before writes; valid Unicode and literal escape
sequences remain accepted without rewriting learner text.

The shared teaching prompts also explain how to use recorded rationales
and how to handle a failed learner proposal during repair. The repair
assistant is instructed to distinguish an implementation mistake from a
problem in the learner's proposed rule, explain the evidence, and obtain
a real learner reply before changing that rule. This happens in the
existing Coder repair conversation during the test step: it does not
return to decision submission, overwrite the original formal decision,
unlock the approved plan, or add another workflow step. Courses can
preserve the confirmed amendment in their permitted artifacts. This is
teaching guidance, not a new server-enforced semantic approval gate.

Archives using either new response format declare
`pbl-v3-decision-responses@1` alongside `pbl-v3-projects@1`. Export adds
the requirement automatically; import rejects archives that omit it
before uploading project resources. Older readers reject the unsupported
capability. Capability inspection reuses the project schema and rejects
malformed descriptors through validation errors before installation.
Existing course packages remain compatible. No database migration or
provider change is required.

Private course materials, project-specific Skills, tests, reference
implementations, activity copy, and local configuration are excluded
from this PR.

The final beta scope also fixes the idle-restore editor blocker reported
in [the hosted acceptance
update](https://github.com/CogEvol/MAIC-Mono/issues/3734#issuecomment-6461748519).
That reproduction used an earlier integration commit without #2060.
Dirty buffers retained their released environment ID and were skipped by
reload protection, leaving the restored editor stale and Save/Retry
disabled. Environment-unavailable/changed save errors also failed to
refresh the displayed environment state.

On recovery, all dirty tabs are checked against their recovered files.
Only a matching base hash or an already-observed submitted value
reconnects a draft; a conflicting file remains protected without erasing
local edits. Verified drafts resume saving with fresh request
identities. Unknown outcomes in the same environment still replay the
exact original request; late outcomes from the released environment
cannot freeze its replacement. Existing permissions, file-hash fencing
and learning progress gates remain enforced. The inactive-environment
hint now explicitly points to the restore button in all 12 locales. A
recovered-base mismatch now shows an explicit conflict and a Copy
retained draft action, clears obsolete old-environment save failures and
keeps the protected draft read-only. Copying does not write or rebase
server files. This is the requested pre-beta fix, not a broader editor
redesign.


Report projection retains independent, bounded excerpts of both the
literal decision answer and rationale even when the original event
exceeds the generic 32 KiB JSON guard. Long learner chat also receives
bounded excerpts. Each retained decision revision gets an equal field
allowance calculated before processing, within the existing history and
text limits. That evidence budget includes display text, literal
quote-provenance fields and separately retained question/option context,
so newer long answers cannot consume all of the older retained
revisions' space. Durable history and quantitative totals remain
unchanged.

Report sources retain the same question/option-label context as UI
evidence, alongside literal answer/rationale excerpts in model input.
New report evidence keeps bounded question and selected-option fields
separately; model projection reserves independent space for each so a
long question cannot crowd out the option meaning. Older saved reports
fall back to their retained combined context. UTF-16 clipping preserves
surrogate pairs. Quotes now require a literal substring match, including
whitespace: `a part` cannot validate `apart`. Every quote field,
including journey/next findings, must match literal learner text visible
in the actual bounded model input; omitted source IDs and clipped-away
quotes are rejected. Validation checks the exact bounded learner
segments used to construct model input, so matching course context
cannot rescue a phrase clipped from the learner excerpt. Unquoted
journey/next facts may still cite Agent actions or test results. Raw
tool output remains excluded.

Initial and live teaching contexts separate submitted decisions into
explicitly quoted, untrusted learner data. They remain proposals rather
than host instructions or artifact approval; existing role artifact
access and deterministic workflow gates remain unchanged. The shared
quoted-decision block has a cumulative 64 KiB UTF-8 cap in initial role
prompts and live dialogue refreshes. Oversized data becomes an
explicitly incomplete, Unicode-safe excerpt with instructions to ask for
concise clarification rather than infer omitted behavior. This does not
rewrite stored proposals or limit teaching duration/tool calls, and it
is not a claim of complete model-context governance.

Validation at `60acf357`:
- Complete local PBL v3 suite: 597 passed, 13 skipped, including 227
isolated PostgreSQL runtime cases. Fixtures use controlled
model/transport behavior and local test execution.
- Reproduced the new review findings before fixing them: choice
question/label loss, journey/next Agent-quote acceptance, citations to
clipped-away/omitted model sources, and an empty oldest answer in an
18-revision PostgreSQL report. Also reproduced the latest 200-record
long-question/option-label loss and both answer/rationale context-only
quote acceptance failures before the fix. These three additional
regressions now pass.
- The final regressions reproduce a 2.88 MB cumulative decision block
before bounding and whitespace-altered quotes being accepted before
literal validation; both now pass. Tests cover initial prompts for all
four roles, actual Planner/Coder and live-dialogue paths, Unicode/JSON
escaping, preserved saved decisions, literal whitespace
acceptance/rejection and unchanged short proposals.
- Report checks cover context for ordinary choice IDs, literal
answer/reason attribution across all finding categories, unquoted
contextual facts, retained excerpts for all 18 long revisions,
surrogate-pair boundaries, and aggregate evidence size including
quote-provenance and separate context fields. The crowded-report
regression verifies the actual model-text budget and excludes unbounded
metadata copies from model input. Existing chronology, complete
quantitative totals and historical bounds still pass.
- Decision validation covers NUL/unpaired-surrogate input in either
field, valid emoji/Chinese text and literal escape sequences, unchanged
progress/activity/receipt/event counts on rejection, and retained
editable UI drafts. Earlier field/aggregate-byte limits and
retry/role-isolation coverage remain passing.
- Native workspace E2E: the previously failing test now passes against
the actual local classroom/workbench/Monaco UI with controlled PBL
transport. Its old assertion treated every environment-ID change as
permanently stale even when the recovered base was unchanged. The
fixture now models a genuinely different recovered base for the
read-only/no-write assertions, checks copying the retained draft, and
separately verifies ENVIRONMENT_UNAVAILABLE -> Restore -> fresh
hash-fenced save -> editable/Saved. Completion/report assertions retain
their no-compute contract with an updated counter baseline.
- Editor recovery: all 23 draft-hook cases pass, including eight
recovery regressions for replacement and same-environment recovery, page
reentry, background tabs, current permissions, real conflicts, uncertain
receipts, and late old-environment failures. A separate Chromium check
uses the production hook with an isolated HTTP fixture: rejected save,
actual full-page reload retaining sessionStorage draft, restore, fresh
save, editable/Saved state and no page errors. It does not call the real
cloud provider or prove deployed sandbox recovery.
- TypeScript, changed-file ESLint/formatting, all 12 locale-key
alignment and diff whitespace checks pass. Test-fixture cleanup keeps
the existing maintenance concurrency checks isolated.
- Real local HTTP probes return 400 for invalid NUL/surrogate responses;
both existing course pages return 200 and all seven saved study
states/revisions remain unchanged after the idle-server update. No model
call or sandbox allocation was needed.
- GitHub checks rerun for this commit. Earlier complete
real-model/real-sandbox teaching acceptance remains evidence for its
stated earlier revision; it was not rerun for this report/input update.

UI comparison (production component, native application styles, public
English fixture data; no live model/provider calls):

Existing immediate-submit choice mode:

![Existing choice
interaction](https://raw.githubusercontent.com/THU-MAIC/OpenMAIC/a76d0fa77ccdea6eae9d35b9012dc67cbb6e2405/lib/pbl/v3/screenshots/decision-choice-before.png)

Choice/text responses with rationales, explicit submission, and
saved/unsaved status:

![Decision response
forms](https://raw.githubusercontent.com/THU-MAIC/OpenMAIC/a76d0fa77ccdea6eae9d35b9012dc67cbb6e2405/lib/pbl/v3/screenshots/decision-responses-after.png)

Targets `integration/shared-sandbox` and includes #2052 in its base.

Beta priority: validating the introductory course content and teaching
setup. Further report quality improvements remain separate follow-up
work.

Deferred report follow-ups (separate PRs; not introductory-course beta
blockers):
- Reuse unused report-input text capacity or reserve more space for
learner decisions, including long option labels whose distinguishing
suffix may be clipped.
- Align literal raw-choice-ID quotations with the label displayed by the
UI citation.
- Resolve unusually long option IDs before the report SQL excerpt is
clipped at 4,000 characters.

Further report refinements found during beta should be handled
separately unless they expose a concrete release-blocking defect.






Other non-blocking hardening follow-up: an aggregate
stored-decision-state budget for unusually many maximum-size responses.
This is separate from the already bounded model-input projection and
should define its compatibility and authoring/input contract in a
focused follow-up rather than add an unreviewed course constraint during
beta finalization.
2026-10-28 14:36:42 +08:00
2026-10-11 16:01:29 +08:00
2026-06-17 04:00:00 -04:00

OpenMAIC Banner

Get an immersive, multi-agent learning experience in just one click

v1.0.0 User Guide (English)    v1.0.0 体验指南(中文)

Paper License: MIT Live Demo OpenClaw Integration Lemonade Local AI Stars
Discord   Feishu Community
Next.js React TypeScript LangGraph Tailwind CSS

English | Simplified Chinese
Live Demo · Quick Start · Lemonade · FunASR · Features · Use Cases · OpenClaw

🗞️ News

  • 2026-10-16 — v1.2.0: server-first is stable. Generation runs on the server and survives closed tabs and restarts, models are configured once in openmaic.yml (defaults, locks, and an option to forbid user keys), materials are parsed as soon as they are attached, and the web app and the API share one pipeline. New: classroom export, as a PPTX that keeps every scene or as one offline HTML file that plays the lesson. It needs PostgreSQL and a long-running server; Vercel deployments stay on 1.1.x. Read Upgrading from 1.1.x before upgrading.
  • 2026-09-28 — v1.1.2: security — provider requests connect only to validated addresses and refuse redirects (GHSA-g87c-cm4q-cw5x).
  • 2026-09-27 — v1.1.1: security — hardened MinerU Cloud parsing (GHSA-cpjc-vgjh-c5jp).
  • 2026-09-24 — v1.1.0: agentic classroom chat (ask about any slide element, interactive, or whiteboard drawing); settings rebuilt around the course workflow; Token Plan connections.
  • 2026-09-15 — v1.0.3: security — access-code tokens expire, render-service network policy, pinned audio connections, Next.js RCE patch.
  • 2026-09-14 — v1.0.2: security — SSRF and DNS-rebinding fixes, classroom overwrite fix.
  • 2026-09-06 — v1.0.1: security and stability; tightens two defaults.
  • 2026-08-27 — v1.0.0: agent workbench, durable sessions, reusable skills, session materials, provider-neutral capabilities.
Earlier releases
  • 2026-08-14 — v0.3.2: video export hardening, server-backed persistence and asset registry, @openmaic/generation, four new locales, FunASR.
  • 2026-07-21 — v0.3.1: one-click MP4 export, direct slide editing, smarter "Edit with AI", expanded document parsing.
  • 2026-06-28 — v0.3.0: PBL v2, "Edit with AI" editor agent, @openmaic/* SDKs on npm, relicensed to MIT.
  • 2026-06-02 — v0.2.2: MAIC Editor Pro Mode, editable outlines, offline classroom export.
  • 2026-04-26 — v0.2.1: VoxCPM2 TTS with voice cloning, per-model thinking config, course completion page.
  • 2026-04-20 — v0.2.0: Deep Interactive Mode — 3D, simulations, games, mind maps, online programming.
  • 2026-04-14 — v0.1.1: language inference, ACCESS_CODE, classroom ZIP export/import, Ollama.
  • 2026-03-26 — v0.1.0: discussion TTS, immersive mode, keyboard shortcuts.

Full history in the changelog.

📖 Overview

OpenMAIC (Open Multi-Agent Interactive Classroom) is an open-source AI platform that turns any topic or document into a rich, interactive classroom experience. Powered by multi-agent orchestration, it generates slides, quizzes, interactive simulations, and project-based learning activities — all delivered by AI teachers and AI classmates who can speak, draw on a whiteboard, and engage in real-time discussions with you. The built-in OpenMAIC Skill works with OpenClaw as well as agent workbenches such as Codex, DeepSeek, and WorkBuddy, so you can generate classrooms from messaging apps like Feishu, Slack, or Telegram, or right inside your IDE.

https://github.com/user-attachments/assets/8f3f1e5f-1468-4e93-8054-afeeea683a61

Highlights

  • One-click lesson generation — Describe a topic or attach your materials; the AI builds a full lesson in minutes
  • Multi-agent classroom — AI teachers and peers lecture, discuss, and interact with you in real time
  • Rich scene types — Slides, quizzes, interactive HTML simulations, and project-based learning (PBL)
  • Whiteboard & TTS — Agents draw diagrams, write formulas, and explain out loud
  • Export anywhere — A .pptx that keeps every scene, one offline .html file that plays the whole lesson, or a classroom ZIP to move it to another deployment
  • Agent workbench integration — The OpenMAIC Skill supports OpenClaw, Codex, DeepSeek, WorkBuddy, and more — generate classrooms from Feishu, Slack, Telegram, 20+ messaging apps, or your IDE

🚀 Quick Start

Prerequisites

  • Node.js >= 22.19
  • pnpm >= 10
  • PostgreSQL 16 — courses are stored on the server. For local development pnpm db:up starts one in Docker for you.

1. Clone & Install

git clone https://github.com/THU-MAIC/OpenMAIC.git
cd OpenMAIC
pnpm install

2. Configure

cp .env.example .env.local            # API keys and server options
cp openmaic.example.yml openmaic.yml  # which models the server uses

The copied example needs only OPENAI_API_KEY in .env.local (or change its provider to one you have a key for); everything else in it is commented out until you want it. openmaic.yml declares providers (accounts the server can call) and gives each slot (a use of AI: the outline, slide content, text to speech, web search, …) a model. Keys stay in .env.local and are referenced as ${VAR}:

providers:
  openai:
    preset: openai
    apiKey: ${OPENAI_API_KEY}      # OPENAI_API_KEY=sk-... in .env.local
  anthropic:
    preset: anthropic
    apiKey: ${ANTHROPIC_API_KEY}

slots:
  llm: openai:gpt-5.5              # the default chat model
  course.outline: anthropic:claude-sonnet-4-6
  video: null                      # turn a capability off

The slots you write are the server's defaults; users can change them, or connect services of their own, in the app's model settings unless you lock them or set allowUserKeys: false. The server validates the file at startup and names every mistake by its field (or, for broken YAML, its line).

To learn about Read
Slots, presets, fallbacks, lock, allowUserKeys, OPENMAIC_SECRET_KEY Configuration
Every provider preset and model ID Supported models
Upgrading: provider variables, server-providers.yml, DEFAULT_MODEL and MODEL_FALLBACK still work without openmaic.yml; MODEL_ROUTES must become slots Migrating from the legacy configuration
Which models users may pick: a provider whose key the server holds and that selects an upstream model offers only the models in its models list (*_MODELS), plus the slot, default and fallback models Model authorization

Providers: OpenAI, Azure OpenAI, Anthropic, Amazon Bedrock, Google Gemini, DeepSeek, Qwen, Kimi, MiniMax, Grok (xAI), OpenRouter, TokenDance, Doubao, Tencent Hunyuan/TokenHub, Xiaomi MiMo, GLM (Zhipu), Ollama, Lemonade and FunASR (local), and any OpenAI-compatible API.

Tip

Recommended setup: OpenMAIC is at its best with every modality turned on — generated illustrations, narration, video clips, and web-grounded research. The least friction is a single key that covers all of them (see the token plan examples below), with a fast long-context model such as deepseek-v4.1-flash as the default.

More examples: token plans, Xiaomi MiMo and GLM, Amazon Bedrock

Token plan quick example (one key for chat, image, video, TTS and web search; tokendance works the same way):

providers:
  minimax:
    preset: minimax
    apiKey: ${MINIMAX_API_KEY}

slots:
  llm: minimax:MiniMax-M3
  image: minimax                   # a provider id alone: the plan's default model
  video: minimax
  tts: minimax
  webSearch: minimax

TokenDance quick example with a fast long-context default model:

providers:
  tokendance:
    preset: tokendance
    apiKey: ${TOKENDANCE_API_KEY}

slots:
  llm: tokendance:deepseek-v4.1-flash
  image: tokendance
  video: tokendance
  tts: tokendance
  webSearch: tokendance

Xiaomi MiMo Token Plan and GLM (Zhipu) quick example:

providers:
  mimo:
    preset: xiaomi
    apiKey: ${MIMO_API_KEY}
    baseUrl: https://token-plan-cn.xiaomimimo.com/v1
  glm:
    preset: glm
    apiKey: ${GLM_API_KEY}
    baseUrl: https://open.bigmodel.cn/api/paas/v4   # or https://api.z.ai/api/paas/v4

slots:
  llm: mimo:mimo-v2.5-pro
  course.content: glm:glm-5.1

Use https://token-plan-sgp.xiaomimimo.com/v1 or https://token-plan-ams.xiaomimimo.com/v1 for the Singapore or Europe Token Plan clusters.

Amazon Bedrock quick example:

providers:
  bedrock:
    preset: bedrock
    models: [us.anthropic.claude-sonnet-5, us.anthropic.claude-opus-4-8]

slots:
  llm: bedrock:us.anthropic.claude-sonnet-5

Bedrock uses AWS environment credentials or the AWS SDK credential provider chain, with the region from BEDROCK_REGION (for example BEDROCK_REGION=us-east-1 in .env.local). For temporary credentials, set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN, or use an AWS profile / role available to the runtime.

3. Start the database

pnpm db:up

Then uncomment the local DATABASE_URL line in .env.local:

DATABASE_URL=postgres://openmaic:openmaic-dev@127.0.0.1:5432/openmaic

pnpm db:up starts a development PostgreSQL on 127.0.0.1:5432 (set OPENMAIC_DB_PORT for another port). It is its own Compose project, openmaic-dev-db, shared by every checkout on this machine, with its own container and volume, so it never touches the database of a docker compose up stack. pnpm db:down stops it and keeps the data. Any other PostgreSQL works too.

4. Run

pnpm dev

Open http://localhost:3000 and start learning! Without a DATABASE_URL the server refuses to start and tells you how to provide one (see Server-backed persistence).

5. Build for Production

pnpm build && DATABASE_URL=postgres://... pnpm start

Keep the server running as a long-running process (a process manager or a container): course generation runs inside it and continues after the browser leaves the page. See the Deployment guide for the required configuration and for upgrading from 1.1.x.

Docker Deployment

cp .env.example .env.local
# Edit .env.local with your API keys, then:
docker compose up --build

Open http://localhost:3000. The stack is two containers, the app and PostgreSQL. Courses, generated media and runtime sessions live in the named volumes openmaic-postgres and openmaic-data, which survive docker compose down and rebuilds; docker compose down -v deletes them. To configure models with openmaic.yml, create it from openmaic.example.yml and uncomment its mount in docker-compose.yml; otherwise connect a model service in the model settings once the app is running.

The Compose file is a personal installation: it runs in single-user mode (every browser sees one shared library) and listens on 127.0.0.1:3000 only. To serve other machines, protect it first:

  1. Set a long random ACCESS_CODE in .env.local (details). Without it, anyone who can reach the port owns, edits and can delete the whole library.

  2. Before the first start, set PERSISTENCE_POSTGRES_PASSWORD to a random value of letters and digits.

  3. Publish on the network address:

    OPENMAIC_PUBLISH_ADDRESS=0.0.0.0 docker compose up -d --build
    

OPENMAIC_PUBLISH_ADDRESS, OPENMAIC_PORT and PERSISTENCE_POSTGRES_PASSWORD come from your shell or a .env file next to docker-compose.yml, not from .env.local. Overriding the Compose defaults, rotating the database password and the full upgrade notes are in Hosting and identity.

Important

Upgrading a Compose deployment? PostgreSQL now always starts, the app is published on 127.0.0.1 only, and every visitor is the same single owner. If .env.local sets PERSISTENCE_SHARED_OWNER_ID, also set OWNER_SINGLE_USER=false, or the app refuses to start. Read Upgrading a Compose deployment first.

Slow-network / China build acceleration

Docker builds support two optional build arguments. Both are empty by default, so the standard command above keeps using the upstream Alpine and npm registries.

  • ALPINE_MIRROR is an Alpine mirror hostname without https://.
  • NPM_REGISTRY is a complete npm registry URL.

Use public mirror endpoints only. Do not embed usernames, passwords, or access tokens in these build arguments because Docker may record them in image metadata or build provenance.

With Docker Compose:

ALPINE_MIRROR=mirrors.tuna.tsinghua.edu.cn \
NPM_REGISTRY=https://registry.npmmirror.com \
docker compose up --build

For a direct image build:

docker build \
  --build-arg ALPINE_MIRROR=mirrors.tuna.tsinghua.edu.cn \
  --build-arg NPM_REGISTRY=https://registry.npmmirror.com \
  -t openmaic:local .

These arguments do not accelerate Docker Hub pulls, including the Dockerfile frontend and the node:22-alpine base image. Configure a Docker daemon registry mirror separately if those pulls are slow. The pnpm store cache is reused by the same BuildKit builder across builds, subject to normal cache garbage collection; the cache only improves performance and is not required for a correct build.

Vercel Deployment (up to 1.1.x)

Serverless deployment is supported up to OpenMAIC 1.1.x. From 1.2.0, course generation runs on the server in a process that outlives requests, so OpenMAIC needs a long-running Node.js process with PostgreSQL (the Docker deployment or pnpm start); Vercel and other serverless hosts are not supported, and the repository no longer ships a vercel.json. To deploy 1.1.x on Vercel:

  1. Fork this repository on GitHub, unchecking Copy the main branch only.
  2. In the fork, set the default branch to release/1.1.x (Settings → General → Default branch).
  3. In Vercel, Add New → Project and import the fork. Vercel builds its default branch; configure at least one LLM provider key as described in that branch's .env.example.

Such a deployment can move to a long-running host later without losing data: point the new host's DATABASE_URL at the database it used, if it used one, and serve it at the same address, since browsers keep their data per site; the courses visitors kept in their browsers are imported the first time each browser opens the upgraded app. See Upgrading from 1.1.x for every kind of deployment.

Server-backed persistence (PostgreSQL)

OpenMAIC keeps courses on the server. Course documents, folders, chat history, learner sessions and generated media live in PostgreSQL (media optionally in S3), served by the app itself at /api/persistence. The browser keeps only what belongs to the device, such as settings, playback position and caches; Settings → Clear Local Cache never touches the server.

  • DATABASE_URL is required. Without it the server prints the fix and exits with code 1. Compose sets it for you; for development, pnpm db:up starts a database.
  • Courses an older build kept in the browser are imported once per browser, automatically, the first time it opens the upgraded app. The browser copy is left untouched.
  • Invalid settings stop the server at startup with a one-line [boot] reason instead of a server that answers every request with 500.

Who owns a course. Every request resolves to an owner, and each owner has its own library. Pick one identity mode:

Mode Set Library
Single user (Compose default) OWNER_SINGLE_USER=true One library for everyone who can reach the server; guard it with ACCESS_CODE or loopback
Shared team PERSISTENCE_SHARED_OWNER_ID=<id> together with ACCESS_CODE One library for the team behind the access code
Anonymous (default without either) nothing One library per browser cookie; a second browser sees an empty one
Your own accounts owner auth methods registered in instrumentation.ts One library per signed-in account

Set the mode before the first start of 1.2.0: classrooms that 1.1.x saved as files are imported for the owner configured then. See Upgrading from 1.1.x for every kind of deployment.

Running OpenMAIC for other people, or embedding it in your own product? Hosting and identity covers:

Optional: ACCESS_CODE (Shared Deployments)

Protect a deployment with a site-level password in .env.local:

ACCESS_CODE=your-secret-code

Use a long random value, at least 16 characters from a random generator: it is the only secret guarding the deployment. Visitors then see a password prompt, and every API route is gated too. When it is unset (the default in .env.example), middleware.ts checks no credential and every route, the API included, is reachable: an unconfigured deployment is not gated, and there is no second enforcement point. The code is remembered for 7 days in a signed HTTP-only cookie; attempts are rate limited only behind a trusted proxy with TRUST_PROXY_HEADERS=true. See Configuration → ACCESS_CODE.

Optional: Agent workbench and runtime

The Pro workbench, a course-building surface entered from the home page, is off by default. Turn on its build-time entry point and the server runtime; it uses the same PostgreSQL as the rest of the app:

NEXT_PUBLIC_PRO_WORKBENCH_ENABLED=true
OPENMAIC_AGENT_RUNTIME_ENABLED=true
DATABASE_URL=postgres://openmaic:openmaic-dev@postgres:5432/openmaic

The agent runs on the agent slot, which follows the default chat model unless assigned and needs a model with tool calling:

slots:
  llm: openai:gpt-5.5
  agent:
    model: openai:gpt-5.5
    api: openai-completions        # or openai-responses

The agent slot must not set thinking.effort. Its routes, the DEFAULT_MODEL caveat and runner tuning are in Hosting and identity.

Optional: Lemonade (Local AI Provider)

OpenMAIC supports Lemonade as a local, OpenAI-compatible provider for LLMs, image generation, TTS, and ASR. No API key is required.

Run Lemonade locally, then point OpenMAIC to it:

providers:
  lemonade:
    preset: lemonade
    baseUrl: http://localhost:13305/v1
  lemonade-tts:
    preset: lemonade-tts
    baseUrl: http://localhost:13305/v1
  lemonade-asr:
    preset: lemonade-asr
    baseUrl: http://localhost:13305/v1
  lemonade-image:
    preset: lemonade-image
    baseUrl: http://localhost:13305/v1

slots:
  llm: lemonade:Gemma-4-26B-A4B-it-GGUF
  tts: lemonade-tts
  asr: lemonade-asr
  image: lemonade-image

With the legacy variables, the same is LEMONADE_BASE_URL, TTS_LEMONADE_BASE_URL, ASR_LEMONADE_BASE_URL and IMAGE_LEMONADE_BASE_URL.

Optional: FunASR (Local Speech Recognition)

OpenMAIC can transcribe locally through FunASR's OpenAI-compatible server. The built-in provider supports SenseVoiceSmall, Paraformer, and Fun-ASR-Nano and requires no API key.

python -m pip install torch torchaudio
python -m pip install "funasr==1.4.0" fastapi uvicorn python-multipart
# Add vLLM for Fun-ASR-Nano on NVIDIA GPUs
python -m pip install vllm
funasr-server --device cuda --model fun-asr-nano

Point OpenMAIC at the server:

providers:
  funasr:
    preset: funasr-asr
    baseUrl: http://localhost:8000/v1

slots:
  asr: funasr

(Legacy variable: ASR_FUNASR_BASE_URL=http://localhost:8000/v1.)

Use funasr-server --device cpu --model sensevoice for a CPU-only setup. See the FunASR deployment guide for production options.

Optional: Local Audio and Video Extraction

OpenMAIC can extract timestamped transcripts and prepared video keyframes locally. Install the system ffmpeg package so both ffmpeg and ffprobe are executable on PATH, then assign the asr slot (for example FunASR, Lemonade, or OpenAI) as above. The application resolves the executables at extraction time; ffmpeg is not an npm dependency and is not required to start or use OpenMAIC.

If the executables are unavailable, the local extractor is skipped. A configured AliDocMind provider remains available as the cloud extraction path. When neither local ffmpeg extraction nor AliDocMind is available, audio/video materials are marked failed with an actionable setup message instead of hanging or completing with an empty transcript.

Optional: MP4 Video Export (Render Service)

The "Export Video" menu builds a self-contained Hyperframes project entirely in the browser. Turning that into an MP4 needs Chromium + FFmpeg on Node 22, so it runs in an isolated render-service container rather than the app.

It's opt-in. Start it with the video-export compose profile:

docker compose --profile video-export up --build

The app auto-detects the service via RENDER_SERVICE_URL (preset in docker-compose.yml) and enables one-click MP4 rendering. Without the profile — or when RENDER_SERVICE_URL is unset — export degrades to downloading the project ZIP for local CLI rendering. See render-service/README.md for standalone setup and tuning (RENDER_MAX_CONCURRENCY, etc.). When upgrading, upgrade the render-service together with the app or before it: render jobs are bound to their owner, and a mix of old and new versions fails closed.

Optional: MinerU (Advanced Document Parsing)

MinerU provides enhanced parsing for complex tables, formulas, and OCR. You can use the MinerU official API or self-host your own instance.

Declare it in openmaic.yml and assign the document slot to it: mineru-cloud for the official API, or mineru with the baseUrl of your own instance.

providers:
  mineru:
    preset: mineru-cloud
    apiKey: ${PDF_MINERU_CLOUD_API_KEY}

slots:
  document: mineru

Without openmaic.yml, the legacy variables PDF_MINERU_CLOUD_API_KEY or PDF_MINERU_BASE_URL in .env.local still work.

Optional: VoxCPM2 (Self-Hosted TTS with Voice Cloning)

VoxCPM2 is an open-source TTS model from OpenBMB with voice cloning. OpenMAIC ships an adapter; run VoxCPM on your own hardware and OpenMAIC will talk to it.

1. Run a VoxCPM backend. Three deployment styles, all behind the same OpenMAIC adapter. You pick which one with options.backend in openmaic.yml (step 2).

Backend Endpoint When to use
vLLM-Omni /v1/audio/speech OpenAI-compatible speech endpoint, ideal for GPU servers
Python API /tts/upload Official VoxCPM Python runtime via FastAPI
Nano-vLLM /generate Lightweight Nano-vLLM FastAPI deployment

See the VoxCPM repo for backend setup.

2. Point OpenMAIC at it. VoxCPM2 runs on your own network, so the deployment configures it in openmaic.yml (no API key required); workspaces cannot add it in Settings → Model Services:

providers:
  voxcpm:
    preset: voxcpm-tts
    baseUrl: http://localhost:8000/v1
    options:
      backend: vllm-omni          # vllm-omni (default) | python-api | nano-vllm

slots:
  tts: voxcpm

Voice registration works only on the vllm-omni backend; python-api and nano-vllm send the voice prompt with each request.

Without openmaic.yml, the legacy TTS_VOXCPM_BASE_URL=http://localhost:8000/v1 sets the endpoint but cannot choose a backend.

3. Manage voices. With VoxCPM2 assigned to the tts slot, open Settings → Model Services → Text-to-Speech → VoxCPM2 (the voice manager appears only when tts resolves to a voxcpm-tts provider). Three voice modes:

VoxCPM2 VoxCPM Voices section with Auto, Prompt and Clone modes
  • Auto Voice (default): OpenMAIC generates a voice prompt from each agent's persona at synthesis time. No setup required.
  • Prompt voice: describe the voice in natural language, e.g. "warm female teacher voice, calm and encouraging, mid-pitch".
  • Clone voice: upload a short reference audio clip or record one in the browser. The clip is stored in this browser (IndexedDB) and sent to your VoxCPM backend on each synthesis.

✨ Features

Agent Workbench and Pro Mode (v1.0.0)

The workbench adds a conversational course-building agent to OpenMAIC. Its durable sessions can be resumed after a worker restart, accept follow-up instructions while running, and stream a replayable event history to the chat surface.

Open it from the Pro control on the home page. The workspace combines a transient, collapsible folders/conversations rail with a chat pane and a classroom pane whose open courses stay in tabs. Workspace controls return to classic mode, and either entry remains gated by the public workbench flag plus the configured server runtime.

The agent works through explicit, validated tools rather than editing opaque blobs:

Area Capabilities
Plan and organize Plan multi-lesson curricula; create courses and folders; rename and move courses
Build and edit Read/search the stage DSL; atomically patch one scene; generate, duplicate, insert, delete, and reorder pages; edit narration and deck structure
Use materials Upload files; extract documents, audio, and video; search extracted text; fetch trusted web URLs; reuse material media
Create media Generate images and videos through configured server providers; generate narration audio
Import and inspect Import .pptx slides with their layout preserved; render scene previews for visual inspection when available
Configure the classroom List available voices, set the agent roster, and clone/register a voice when a pluggable registration adapter is configured

Twenty-four built-in skills cover curriculum planning, deep research, interactive, lecture, workshop, vocational, and other teaching styles, slide/stage craft, PPTX import, editing, and style reuse. User-authored skills are stored per owner and can be created, read, and patched through the same runtime.

The server-backed workbench also exposes owner-scoped folder routes and a per-viewer stage metadata sidecar for ownership, publication, and generation-complete state. A stage ID acts as the capability for reading a non-deleted course, but stage mutations remain restricted to its owner. The material upload contract stores supported source bytes before lease-fenced document or media extraction records derived text and images; media extraction can select AliDocMind or the optional local ffmpeg/ffprobe provider.

Under the hood, agent sessions are database-backed with leases, heartbeats, crash resume, cancellation, and follow-up steering, and database-maintained revision counters keep per-stage and per-scene freshness monotonic so the workbench refetches only the scenes that changed. Server routes resolve LLM, media, ASR/TTS, and search configuration provider-neutrally: credentials never reach the browser, uniform <CAP>_<PREFIX>_ENABLED=false switches can force off any served capability, startup validation warns about bad model configuration, and unresolved model routes fail loudly instead of guessing a vendor.

Pluggable Storage

OpenMAIC stores course documents, learner runtime records and assets on the server, in PostgreSQL. The @openmaic/storage package defines swappable stores for those primitives — PostgreSQL-backed documents, learner runtime, assets, durable agent sessions, session materials, and user skills, plus browser implementations for embedders — and HTTP clients connect the browser to the embedded persistence endpoint, while the server asset layer can keep bytes in PostgreSQL or S3. Device-scoped KV values (settings, playback position) stay in the browser.

Deep Interactive Mode (New!)

Passive listening? ❌ Hands-on exploration! ✅

As Einstein said: "Play is the highest form of research."

While Standard Mode focuses on quickly generating classroom content, Deep Interactive Mode goes further — creating interactive, explorable, hands-on learning experiences. Students don't just watch knowledge; they adjust experiments, observe simulations, and actively explore how things work.

Five Types of Interactive UI

🌐 3D Visualization

Three-dimensional visual representations that make abstract structures more intuitive.

⚙️ Simulation

Process simulations and experimental environments for observing dynamic changes and outcomes.

🎮 Game

Knowledge-based mini-games that reinforce understanding and memory through interactive challenges.

🧭 Mind Map

Structured knowledge organization to help learners build an overall conceptual framework.

💻 Online Programming

In-browser coding and instant execution for learning by writing, testing, and iterating.

AI Teacher Guidance

The AI teacher can actively operate the UI to guide students — highlighting key areas, setting conditions, providing hints, and directing attention at the right moments.

Available on Any Device

All generated interactive UI is fully responsive — desktop, tablet, or mobile.

Desktop

Mobile

iPad

Need a More Complete and Professional UI Generation Experience?

If you are looking for a version with richer functionality, stronger interactivity, and deeper optimization for high-quality educational UI production, please visit MAIC-UI.

Lesson Generation

Describe what you want to learn or attach reference materials. PDF, Word, PowerPoint, spreadsheet, text, image, audio, and video inputs can enter the material pipeline; configured extractors turn supported sources into content for generation. OpenMAIC's classic two-stage pipeline handles the rest:

Stage What Happens
Outline AI analyzes your input and generates a structured lesson outline
Scenes Each outline item becomes a rich scene — slides, quizzes, interactive modules, or PBL activities

Classroom Components

🎓 Slides

AI teachers deliver lectures with voice narration, spotlight effects, and laser pointer animations — just like a real classroom.

🧪 Quiz

Interactive quizzes (single / multiple choice, short answer) with real-time AI grading and feedback.

🔬 Interactive Simulation

HTML-based interactive experiments for visual, hands-on learning — physics simulators, flowcharts, and more.

🏗️ Project-Based Learning (PBL)

Choose a role and collaborate with AI agents on structured projects with milestones and deliverables.

Multi-Agent Interaction

  • Classroom Discussion — Agents proactively initiate discussions; you can jump in anytime or get called on
  • Roundtable Debate — Multiple agents with different personas discuss a topic, with whiteboard illustrations
  • Q&A Mode — Ask questions freely; the AI teacher responds with slides, diagrams, or whiteboard drawings
  • Whiteboard — AI agents draw on a shared whiteboard in real time — solving equations step by step, sketching flowcharts, or illustrating concepts visually.

Agent Workbench Integration

The OpenMAIC skill package (skills/openmaic/) uses the standard SKILL.md format and can be loaded by various agent workbenches — besides OpenClaw, this includes Codex, DeepSeek, WorkBuddy, and others. It is a guided SOP covering the live demo, local setup, classroom generation, and secondary development on top of the @openmaic/* SDK.

OpenClaw is a personal AI assistant that connects to the messaging platforms you already use (Feishu, Slack, Discord, Telegram, WhatsApp, etc.). With this integration, you can generate and view interactive classrooms directly from your chat app without ever touching a terminal.

Just tell your agent assistant what you want to learn — it handles everything else:

  • Hosted mode — Grab an access code from open.maic.chat, save it in your config, and generate classrooms instantly — no local setup required
  • Self-hosted mode — Clone, install dependencies, configure API keys, and start the server — the skill guides you through each step
  • Track progress — Poll the async generation job and send you the link when ready
  • Secondary development — Guide you through building on top of OpenMAIC: create your own app with the @openmaic/* SDK (see the extend docs inside the skill)

Every step asks for your confirmation first. No black-box automation.

Available on ClawHub — Install with one command:

clawhub install openmaic

Or, in other agent workbenches such as Codex, DeepSeek, or WorkBuddy, import the skills/openmaic/ folder from the repo (or its zipped archive) into the workbench to use it:

Configuration & details
Phase What the skill does
Clone Detect an existing checkout or ask before cloning/installing
Startup Choose between pnpm dev, pnpm build && pnpm start, or Docker
Provider Keys Recommend a provider path; you edit .env.local yourself
Generation Submit an async generation job and poll until it completes

Optional config in ~/.openclaw/openclaw.json:

{
  "skills": {
    "entries": {
      "openmaic": {
        "config": {
          // Hosted mode: paste your access code from open.maic.chat
          "accessCode": "sk-xxx",
          // Self-hosted mode: local repo path and URL
          "repoDir": "/path/to/OpenMAIC",
          "url": "http://localhost:3000"
        }
      }
    }
  }
}

Export

Format Description
PowerPoint (.pptx) Editable slides with images, charts, and LaTeX formulas. Interactive, quiz, and PBL scenes can stay in place as placeholder slides with an Open online button and a QR code
Resource Pack The PPTX plus each interactive scene as a self-contained web page
Offline HTML One file that plays the classroom without a network connection: narration with captions, spotlight and laser, slide video, quizzes and interactive scenes; with or without narration
Classroom ZIP Full classroom export (course structure + media) for backup or sharing

See Exporting a classroom for the options, the ZIP that classrooms over 400 MB are saved as, and opening an exported file on a phone.

Importing a classroom ZIP stores its embedded audio, images, video, and posters in the server asset pool before saving the course, so other browsers resolve those assets without the importing browser's cache. A ZIP is also a way to move a course between deployments: export it from one and import it on the other.

Offline / intranet classrooms: When you export a classroom (.maic.zip) or a Resource Pack, OpenMAIC inlines the external assets referenced by interactive scenes (KaTeX, Three.js incl. three/addons, Tailwind CDN, Google Fonts, images) into the exported HTML as data: URIs. The exported course then plays fully offline after import into an air-gapped/intranet instance — no public CDN is contacted at playback time. Assets that can't be fetched at export time (e.g. CORS-restricted image hosts) are reported and left as URLs. Classrooms exported before this feature still reference CDNs and must be re-exported to gain offline support.

And More

  • Text-to-Speech — Multiple voice providers with customizable voices
  • Speech Recognition — Talk to your AI teacher using your microphone
  • Web Search — Agents search the web for up-to-date information during class
  • Provider controls — Server-side capability discovery, model resolution, force-off switches, and fail-loud routing keep deployments explicit
  • Course freshness — Database-triggered per-scene revision counters, freshness events, and targeted scene fetches keep workbench views synchronized
  • i18n — Interface supports 12 locales across 11 languages: Simplified Chinese, Traditional Chinese, English, Japanese, Korean, Russian, Arabic, Portuguese (Brazil), Spanish (Mexico), French, Vietnamese, and German
  • Dark Mode — Easy on the eyes for late-night study sessions

💡 Use Cases

"Teach me Python from scratch in 30 min"

"How to play the board game Avalon"

"Analyze the stock prices of Zhipu and MiniMax"

"Break down the latest DeepSeek paper"


🤝 Contributing

We welcome contributions from the community! Whether it's bug reports, feature ideas, or pull requests — every bit helps.

Project Structure

OpenMAIC/
├── app/                        # Next.js App Router
│   ├── api/                    #   Generation, media, persistence, and agent APIs
│   │   ├── agent/              #     Durable session, event, material, and skill control plane
│   │   ├── stages/             #     Owner-scoped course reads, writes, manifests, and scene fetches
│   │   ├── generate/           #     Images, video, TTS and voice registration
│   │   ├── generate-classroom/ #     Async classroom job submission + polling
│   │   ├── chat/               #     Multi-agent discussion (SSE streaming)
│   │   ├── pbl/                #     Project-Based Learning endpoints
│   │   ├── persistence/        #     Embedded persistence service (Runtime/Document Store HTTP contracts)
│   │   ├── export-video/       #     MP4 video export (backs onto render-service)
│   │   └── ...                 #     generation-runs, materials, quiz-grade, parse-pdf, transcription, etc.
│   ├── classroom/[id]/         #   Classroom playback page
│   └── page.tsx                #   Home page (generation input)
│
├── lib/                        # Core business logic
│   ├── generation/             #   Two-stage lesson generation pipeline
│   ├── orchestration/          #   LangGraph multi-agent orchestration (director graph)
│   ├── playback/               #   Playback state machine (idle → playing → live)
│   ├── action/                 #   Action execution engine (speech, whiteboard, effects)
│   ├── ai/                     #   LLM provider abstraction
│   ├── api/                    #   Stage API facade (slide/canvas/scene manipulation)
│   ├── store/                  #   Zustand state stores
│   ├── types/                  #   Centralized TypeScript type definitions
│   ├── audio/                  #   TTS & ASR providers
│   ├── media/                  #   Image & video generation providers
│   ├── persistence/            #   Browser/server persistence wiring and PostgreSQL provider
│   ├── server/agent-runtime/   #   Durable runner, skills, materials, and course-building tools
│   ├── export/                 #   PPTX & HTML export
│   ├── hooks/                  #   React custom hooks (55+)
│   ├── i18n/                   #   Internationalization (zh-CN, zh-TW, en-US, ja-JP, ko-KR, ru-RU, ar-SA, pt-BR, es-MX, fr-FR, vi-VN, de-DE)
│   └── ...                     #   prosemirror, storage, pdf, web-search, utils
│
├── components/                 # React UI components
│   ├── slide-renderer/         #   Canvas-based slide editor & renderer
│   │   ├── Editor/Canvas/      #     Interactive editing canvas
│   │   └── components/element/ #     Element renderers (text, image, shape, table, chart …)
│   ├── scene-renderers/        #   Quiz, Interactive, PBL scene renderers
│   ├── generation/             #   Lesson generation toolbar & progress
│   ├── workbench/              #   Pro workbench conversation and course-reference UI
│   ├── chat/                   #   Chat area & session management
│   ├── settings/               #   Settings panel (providers, TTS, ASR, media …)
│   ├── whiteboard/             #   SVG-based whiteboard drawing
│   ├── agent/                  #   Agent avatar, config, info bar
│   ├── ui/                     #   Base UI primitives (shadcn/ui + Radix)
│   └── ...                     #   audio, roundtable, stage, ai-elements
│
├── packages/                   # Workspace packages
│   ├── @openmaic/dsl/          #   Versioned course/slide data contract and validators
│   ├── @openmaic/renderer/     #   React renderer for the slide DSL
│   ├── @openmaic/editor/       #   Composable slide editing core and React surface
│   ├── @openmaic/importer/     #   PPTX → OpenMAIC slide importer
│   ├── @openmaic/generation/   #   Generation contracts, pipeline, and prompt assets
│   ├── @openmaic/storage/      #   Browser, HTTP, PostgreSQL, and S3 persistence primitives
│   ├── pptxgenjs/              #   Customized PowerPoint generation
│   └── mathml2omml/            #   MathML → Office Math conversion
│
├── render-service/             # MP4 video export render service (Chromium + FFmpeg, standalone container)
│
├── skills/                     # OpenClaw / ClawHub skills
│   └── openmaic/               #   Guided OpenMAIC setup & generation SOP
│       ├── SKILL.md            #   Thin router with confirmation rules
│       └── references/         #   On-demand SOP sections (generation, deployment, extending, …)
│
├── configs/                    # Shared constants (shapes, fonts, hotkeys, themes …)
└── public/                     # Static assets (logos, avatars)

Key Architecture

  • Generation Pipeline (@openmaic/generation) — Two-stage: outline generation → scene content generation
  • Agent Runtime (lib/server/agent-runtime/) — PostgreSQL-backed sessions with leased execution, resume/steer semantics, skills, materials, and validated course tools
  • Persistence Layer (@openmaic/storage) — Swappable document, runtime, KV, asset, agent-session, material, and user-skill stores
  • Multi-Agent Orchestration (lib/orchestration/) — LangGraph state machine managing agent turns and discussions
  • Playback Engine (lib/playback/) — State machine driving classroom playback and live interaction
  • Action Engine (lib/action/) — Executes 21 action types (speech, whiteboard draw/text/shape/chart, spotlight, laser …)
  • Storage Layer (@openmaic/storage) — Runtime/Document/asset storage abstraction with a Postgres reference implementation; its HTTP contracts let you plug in any external storage service

How to Contribute

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

💼 Partnerships

This project is licensed under the MIT License, so commercial use is permitted free of charge. For partnership or collaboration inquiries, please contact: thu_maic@mail.tsinghua.edu.cn


📝 Citation

If you find OpenMAIC useful in your research, please consider citing:

@Article{JCST-2509-16000,
  title = {From MOOC to MAIC: Reimagine Online Teaching and Learning through LLM-driven Agents},
  journal = {Journal of Computer Science and Technology},
  volume = {},
  number = {},
  pages = {},
  year = {2026},
  issn = {1000-9000(Print) /1860-4749(Online)},
  doi = {10.1007/s11390-025-6000-0},
  url = {https://jcst.ict.ac.cn/en/article/doi/10.1007/s11390-025-6000-0},
  author = {Ji-Fan Yu and Daniel Zhang-Li and Zhe-Yuan Zhang and Yu-Cheng Wang and Hao-Xuan Li and Joy Jia Yin Lim and Zhan-Xin Hao and Shang-Qing Tu and Lu Zhang and Xu-Sheng Dai and Jian-Xiao Jiang and Shen Yang and Fei Qin and Ze-Kun Li and Xin Cong and Bin Xu and Lei Hou and Man-Li Li and Juan-Zi Li and Hui-Qin Liu and Yu Zhang and Zhi-Yuan Liu and Mao-Song Sun}
}

⭐ Star History

Star History Chart


📄 License

This project is licensed under the MIT License.

Third-Party Components

The repository bundles workspace packages that are not covered by the root MIT license and keep their own terms:

When redistributing the repository as a whole, the terms of each bundled package above apply to that package's files.

Languages
TypeScript 93.6%
MDX 3.2%
JavaScript 2.6%
CSS 0.5%