🧭 全文速览
这篇文章不教你怎么用 AI 老师上课,而是把 OpenMAIC 当成一个生产级 Agent 应用的解剖样本,看它源码里有哪些能直接搬走的工程范式。
| Part | 主题 | 回答的核心问题 |
|---|---|---|
| Part 1 | 重新定义问题 | 它到底是个教育产品,还是一台"内容编译器"? |
| Part 2 | 生成与状态 | 为什么真正的复杂度不在生成,而在状态? |
| Part 3 | Skill 抽象层 | 为什么 Prompt 之后,会出现 Skill 这一层? |
| Part 4 | 调度与执行 | LangGraph 和 Playback Engine 到底各管什么? |
| Part 5 | 工程化基建 | 多模型、数据库、资源回收为什么是必需品? |
| Part 6 | 更大的图景 | AI 应用为什么正在"操作系统化"? |
| Part 7 | 泼冷水 | 上生产前必须知道的 9 个坑 |
| Part 8 | 上手路线 | 9 步阅读法 + 3 个自检问题 |
::: tips 阅读提示
全文偏长,如果你只关心结论,直接跳到 Part 6(操作系统化) 和 Part 7(9 个坑) 。 如果你在做 AI 编程 / Agent 类产品,Part 2 和 Part 3 的性价比最高。 :::
Part 1 · 重新定义问题:它不是教育产品,而是一台"内容编译器"
01|先别急着看 AI 教师:OpenMAIC 本质上在做什么?
大部分人第一次看到 OpenMAIC,注意力都会被"AI 老师 + AI 同学 + 白板辩论"这个场景吸走。这很正常,它确实酷。但如果你从这个角度看,你会把它归类为"AI 教育产品",然后错过它真正有价值的部分。
我们换个角度。
OpenMAIC 的输入是:一段自然语言意图("教我量子物理"),或者一份 PDF 文档。 它的输出是:一门可以被运行起来的课------有幻灯片、有语音讲解、有白板推导、有交互式模拟、有随堂测验、有 AI 同学插话提问。
请注意这里的动词:运行。
如果输出是一篇 Markdown,那叫"生成内容"; 如果输出是一段会被逐条执行、会操作 UI、会播放音频、会在画布上画图的指令序列,那叫"生成可执行对象"。
OpenMAIC 属于后者。拆开看,它其实是一台内容编译器,三层结构极其清晰:
text
┌─────────────────────────────────────────────────┐
│ 编译层 packages/@openmaic/generation │
│ 意图/文档 → 结构化大纲 → 场景(Slide/Quiz/ │
│ Interactive/PBL) │
├─────────────────────────────────────────────────┤
│ 调度层 lib/orchestration/ │
│ LangGraph director graph:此刻谁说话、做什么、 │
│ 上下文怎么交接 │
├─────────────────────────────────────────────────┤
│ 执行层 lib/playback/ + lib/action/ │
│ 时序状态机 + 动作引擎:TTS、聚光灯、SVG 白板 │
└─────────────────────────────────────────────────┘
如果做个类比:==模型是 CPU,那 OpenMAIC 做的就是编译器 + 操作系统 + 播放器。==
这个判断不是我硬抬。OpenMAIC 背后有一篇 JCST 2026 的论文《From MOOC to MAIC》,核心主张就是 MAIC 框架把在线课堂从"一个视频给 N 个学生"反转为"N 个 Agent 服务一个学生"。而要实现这个反转,光有模型是不够的------你需要一整套让 Agent 能够做事的基础设施。
这就是 OpenMAIC 对开发者的第一重价值:它把"一个 AI 应用该有什么"完整地摊开给你看了。
02|这不是 Chatbot,而是一个 AI 应用 Runtime
先把 "Runtime" 这个词说清楚。一个东西被称为 Runtime,通常得满足四个条件:
- 它维护状态,且状态有自己的生命周期;
- 它有执行层,能把抽象指令变成副作用;
- 它有资源模型,管理字节、句柄、引用;
- 它管理任务生命周期,任务能比一次调用活得更久。
拿这四条去量 OpenMAIC,全中:
| Runtime 要素 | OpenMAIC 的实现 |
|---|---|
| 状态 | PlaybackEngine 状态机:idle / playing / paused / live |
| 执行层 | ActionEngine:28+ 种动作类型,同步与 fire-and-forget 两种模式 |
| 资源模型 | Asset Pool:AssetId(ast_ + 128 随机位)→ registry → content-addressed bytes |
| 生命周期 | Job 系统 + Agent Session(lease / heartbeat / crash resume / cancellation) |
而 Chatbot 呢?Chatbot 是无状态的请求-响应循环。你问一句,它回一句,结束。它的输出是文本------文本是终点。
Runtime 的输出不是文本,是副作用 。OpenMAIC 的 Agent 说话不是"把字打给你看",而是真的去触发一次 TTS 播放、真的在白板上落一笔、真的把聚光灯挪到某个公式上。输出是起点,后面还有一整套执行链路。
这个区别听起来抽象,但它决定了工程复杂度的量级差异:
- 一个 Chatbot,你可以用 200 行代码写出来;
- 一个 Runtime,你需要状态机、并发控制、持久化、降级策略------OpenMAIC 用了整个 monorepo。
::: warning 一个极简的自测
判断你做的到底是 Chatbot 还是 Runtime:==关掉浏览器标签页,你的任务还在跑吗?==
如果答案是"没了",那你做的是 Chatbot,别用 Runtime 的复杂度要求自己,也别用 Runtime 的估值讲故事。 :::
Part 2 · 生成只是第一分钟,真正烧头发的是状态
03|它没有让一个大模型干完所有事情
这是我觉得 OpenMAIC 最清醒的设计决策。
现在做 AI 应用,最常见的做法是:写一个超长的 system prompt,把所有要求塞进去,然后祈祷模型照做。这条路的尽头是灾难------prompt 越写越长,模型越跑越偏,改一个需求要重调整个 prompt。
OpenMAIC 的做法是 分阶段 + 分角色 + 分权限。
① 分阶段
生成被切成两段:先生成大纲(章节、学习目标、难度曲线、场景配比),再并行填充每个场景的具体内容。
为什么要切?因为这是两个性质完全不同的问题:
| 大纲生成 | 场景生成 | |
|---|---|---|
| 问题性质 | 结构规划问题 | 内容创作问题 |
| 需要什么 | 全局视野 + 约束满足 | 局部创作 + 并行 |
| 失败处理 | 人工/规则校验 | 局部失败重试 |
| 可否并行 | 否 | 是(可摊薄延迟) |
混在一起做,模型既要顾全局又要抠细节,两头都做不好。分开做,大纲能被人或规则校验,场景能并行生成摊薄延迟,进度还能通过 SSE 流式推给用户看。
② 分角色
Agent Registry 里每个 Agent 有 AgentConfig,包含 persona、priority,以及最关键的 allowedActions。
③ 分权限(重点)
看它的权限矩阵:
teacher:完整控制权------spotlight、laser、play_video+ 全部白板工具;assistant/student:只限于WHITEBOARD_ACTIONS(画图、文字、LaTeX、代码)。
也就是说,AI 同学不能在老师讲课时抢过激光笔。这不是靠 prompt 里写一句"同学请不要随意使用激光笔"实现的,而是在工具 schema 层面就把这个动作从学生的可选集合里删掉了。
::: tips 这一点的意义远超教育场景
多智能体的价值不在于"多个大脑",而在于多个受约束的行为边界。
用代码约束模型,而不是用自然语言求模型别乱来。前者是工程,后者是许愿。 :::
顺带一提,它连 guardrail 都写了测试------tests/orchestration/tool-schemas-latex-guardrail.test.ts。不是写了就完事,是钉了测试。
04|真正复杂的不是生成课程,而是「状态」
生成,只是这门课生命周期的第一分钟。真正烧工程师头发的是后面:
- 暂停了怎么恢复?
- 用户打断了怎么接回去?
- 学到第 8 页换设备了怎么办?
- 生成到一半进程崩了怎么办?
OpenMAIC 把状态拆成了四个维度,每一层都有独立的生命周期:
1️⃣ 文档状态(Document)
DocumentStore 存的是课程本身:Stage、Scene、Action、Agent 配置、大纲快照。
两个硬约束:
- 每个文档带一个
dslVersion,读取时按迁移阶梯向前跑,缺少迁移路径就直接停止,绝不悄悄产出"半迁移文档"; - 写入时过
validateStage/validateScene闸门,schema 漂移必须 fail loud。
2️⃣ 播放状态(Playback)
idle → playing ⇄ paused,以及 playing ⇄ live。这个双状态设计非常关键,Part 4 细说。
3️⃣ 学习状态(Runtime)
RuntimeStore 存的是学习者在学的过程中产生的东西------聊天、测验提交、播放事实。两个设计细节值得抄:
- 按
(stageId, learnerKey)分区,一个 stage 可以有多个 session; - 记录是 append-only ,由存储层分配单调 seq 作为唯一重放排序键,不用时间戳。
为什么不用时间戳?
因为重放顺序不能依赖时钟。分布式环境下时钟会回拨、会漂移,而 seq 由存储层单点分配,天然全序。
这是个只有真踩过坑的人才会做的选择。
4️⃣ 会话状态(Agent Session)
Agent 会话落库,带 lease、heartbeat、checkpoint。进程崩了能从 transcript 判断"已完成 / 可继续 / 等待用户"。
还有一个细节我很喜欢:per-scene monotonic revision ,用数据库触发器维护。这意味着客户端可以精确地只重取"变化了的那一页",而不是每次都拉整门课。课程越长,这个优化越值钱。
::: info 值得记住的比例
AI 应用的复杂度,==80% 在状态,20% 在推理。==
大多数团队把 90% 的时间花在那 20% 上。 :::
05|Agent 最大的问题:生成完之后怎么改?
这是生成式 AI 产品的通病,也是最容易被低估的问题。
一次性生成的天花板是:你唯一能做的修改,是"再来一次"。而"再来一次"意味着你之前所有的微调、所有满意的部分,全部沉没。用户于是陷入一个荒诞的循环------==为了改第三页的一个公式,重新生成整门课,然后祈祷其他 19 页别变差。==
OpenMAIC v1.0.0 给出的答案,是 Pro Workbench:一个对话式的课程构建 Agent。
它的改法,任何写过 Coding Agent 的人看了都会会心一笑:
text
read_stage → 读取文档树和目标 Scene 的源码级数据
patch_stage → 通过 JSON Pointer 精确修改指定字段
str_replace → 局部替换长 HTML,避免整页重写
read_stage → 读回来验证修改结果
这就是 Coding Agent 的 read-edit-verify 循环,只不过操作对象从代码文件换成了课程 DSL。
而为了让这个循环能跑起来,它补齐了长任务的全部基础设施:
| 能力 | 解决的现实问题 |
|---|---|
| 会话落库 | 浏览器断开不会终止生成 |
| lease 协调 | 运行进程从数据库领取执行权,避免重复执行 |
| crash resume | 服务重启后根据 transcript 判断从哪里继续 |
| cancellation | 用户能取消 |
| follow-up steering | 运行中可以追加指令,不用等它跑完 |
::: success 建议抄在工位上的一句话
客户端连接从执行生命周期里被拿掉,课程生成终于可以比一个 HTTP 请求活得更久。
你的 Agent 产品从 demo 走向生产,分水岭就两条:能不能改,能不能断线续跑。 :::
Part 3 · Skill:Prompt 之后的新抽象层
06|为什么 OpenMAIC 开始出现 Skills?
README 写的是 20 built-in skills,v1.0.0 的实际目录里数量略有出入(社区统计在 22--23 个之间)。这个"官方说 20、代码里更多"的小差异本身就很有趣------说明这块还在快速演化。
先看清单:
- 教学法类:Feynman Learning(费曼学习法)、Spiral Curriculum(螺旋式课程)、K12 Core Literacy Planning、Social-Emotional Learning
- 能力类:Deep Research、Fact Check、Deep Interactive、PPTX Import、Style Clone
- 系统类 :
openmaic(环境配置与生成的引导 SOP)、stage-dsl/slide-dsl(接口手册,告诉 Agent 不同内容对应的文档路径和合法字段)
为什么需要 Skill 这一层?
因为用户的需求从来不是"调用某个工具",而是"用费曼方法给我讲量子力学"。这是两件事:
- 工具解决"能不能做";
- Skill 解决"按什么方法做"。
看几个具体例子,你就明白 Skill 到底在约束什么:
| Skill | 约束了什么 |
|---|---|
spiral-curriculum |
约束大纲的 sceneCount 和 typeMix------概念要在递增复杂度上反复出现 |
deep-interactive |
强制 interactive 类型场景的最低比例 (minRatio) |
fact-check |
两种模式:生成前 sanity check + 事后证据化审查,专门盯死精确数字、日期、因果结论 |
Skill 约束的不是单个动作,而是 Agent 的行为范式和产物的结构特征。
而且用户可以自己写 Skill,按 owner 存储,通过同一套 runtime 创建、读取、打补丁,Settings 里还能 list / download / delete / upload。
这意味着什么?意味着 ==教学专业知识变成了可插拔、可分发的数字资产==。一个好的物理老师可以把自己的教学套路写成一个 SKILL.md,别人装上就能用。
07|Skill 可能正在成为 Prompt 之后的新抽象层
我们把抽象层的演化拉一条线:
| 抽象层 | 单位 | 表达形式 | 解决什么 |
|---|---|---|---|
| Prompt | 一段话 | 自然语言 | 怎么跟模型说 |
| Function Calling | 一个动作 | JSON Schema | 模型能调用什么 |
| Skill | 一套方法 | SKILL.md(frontmatter + 指令) | 模型该怎么做事 |
Skill 凭什么能成为新的一层?三个特征:
① 声明式,不是代码。 一个 Skill 就是一个目录 + 一个 SKILL.md,frontmatter 写元数据,正文写方法论。写 Skill 不需要会 TypeScript,教学法专家就能写。这大幅扩大了"能给 AI 系统贡献能力"的人群。
② 可发现、可组合。 lib/server/agent-runtime/skills.ts 里 listSkills 扫描文件系统,availableSkillsPromptBlock 把可用 Skill 的摘要注入给 Agent。Agent 按需选择、组合。
③ 可分发、可版本化。 目录 + 文件,天然适合 git、适合打包、适合上架市场。
这里有个机制特别值得抄------渐进式披露(Progressive Disclosure):
text
第 1 层(廉价):Agent 只看到 Skill 的名字 + 描述
↓ 决定使用
第 2 层(按需):通过 read 工具加载 SKILL.md 全文
↓ 安全校验
第 3 层(受控):createNativeSkillReadTool 校验读取范围
这个设计解决的是 Agent 开发中最现实的问题:==context 是有限资源,你不能把 20 多个 Skill 的全文全塞进去。==
而加载过程有安全校验:createNativeSkillReadTool 确保 Agent 只能读取被授权的技能资源。能力可插拔,但读取范围受控------这是把"可插拔"和"安全"同时做到位的关键。
::: tips 我的判断
Prompt 是"怎么说",Skill 是"怎么做事的方法论"。前者是字符串,后者是可复用资产。
未来一年,Skill 生态的复杂度很可能会复刻 npm 走过的路:包管理、版本冲突、依赖树、供应链安全......这些问题会一个不少地重来一遍。 :::
08|OpenMAIC 与 AI Coding Agent,其实属于同一种软件
这一节是我个人最想写的。把两者的机制并排放,你会发现它们几乎是同构的:
| AI Coding Agent | OpenMAIC 课程构建 Agent |
|---|---|
| 代码仓库 | 课程 DSL 文档(Stage / Scene / Action) |
read / grep |
read_stage(detail: "text") |
edit / str_replace |
patch_stage(JSON Pointer)/ str_replace |
| 顺序写避免竞态 | 文档写工具标记为顺序执行 |
| 权限边界 = 工作目录 | Owner scope(owner ID 不在模型可填参数里) |
| LSP / 类型检查 | validateStage / validateScene + dslVersion 迁移 |
| 后台长任务 | Session + lease + heartbeat + crash resume |
| MCP 工具生态 | 能力注册(web_search / 素材 / 媒体生成) |
最精彩的细节是这个:并行工具调用的 read-modify-write 竞态。
模型经常在同一轮里并行发出多个工具调用。如果几个调用都执行"读整页 → 在内存里改 → 写回整页",它们可能都读到同一个旧版本,最后提交的那个会把前面的修改全部覆盖掉。
::: danger 经典坑,解法和 Coding Agent 一模一样
text
调用 A:读 v1 → 改 → 写 v2
调用 B:读 v1 → 改 → 写 v2' ← A 的修改被静默覆盖
OpenMAIC 的解法:把文档写工具标记为顺序执行。 牺牲一部分并行速度,换取提交顺序的确定性。 :::
再看权限:owner ID 来自当前持久化会话,不出现在模型可以填写的参数中 ;Agent 可以声明 stageId,但无法伪造另一个 owner。
这比在 prompt 里写"请勿访问他人的数据"可靠一万倍------因为模型根本没有参数能表达这个越权请求。
AI Coding Agent 在过去一年积累的工程范式,正在外溢到所有"生成结构化产物"的领域。
如果你做过 Coding Agent,你去读 OpenMAIC 会非常快;反过来,如果你在做非代码领域的生成式产品,现在就去把 Coding Agent 的工程实践学一遍,能少走一年弯路。
Part 4 · 调度与执行:谁写剧本,谁演出来
09|LangGraph 在这里到底负责什么?
LangGraph 这两年的热度有点失控,很多人以为它是"让 Agent 变聪明的框架"。不是。
LangGraph 是一个状态机框架。 它的价值在于让你用图的方式,明确地描述"在什么条件下,从哪个节点走到哪个节点"。
在 OpenMAIC 里,它的职责边界非常清楚:
DirectorGraph(createOrchestrationGraph+DirectorState)负责决定下一个说话的 Agent 是谁,或者结束这一轮;- 角色包括 teacher、assistant、moderator、多种 peer archetype;
- 它管理轮次、讨论流、上下文交接。
关键是要理解它和 PlaybackEngine 的分工:
| LangGraph(语义层) | PlaybackEngine(时序层) | |
|---|---|---|
| 管什么 | 谁在什么时候说什么、做什么动作 | 这段话怎么播、音视怎么同步、打断怎么接管 |
| 跑在哪 | 服务端 | 客户端 |
| 是否有状态 | 无状态 | 有状态机 |
| 产出 | SSE 事件流 | 副作用执行 |
一句话概括:LangGraph 负责写剧本,PlaybackEngine 负责演出来。
这里藏着一个很棒的设计哲学:用图结构的确定性,去承载模型输出的不确定性。
课堂的骨架是确定的------老师讲完一段,Director 判断是否需要 AI 同学提问,进入讨论,再回到讲解。这个流转结构由代码控制。但每一句话的具体内容、每个白板图形的具体画法,由模型生成。
==骨架确定,血肉自由。== 这大概是当前阶段做 Agent 应用最务实的架构选择。
10|另一个被低估的模块:Playback Engine
几乎所有关于 OpenMAIC 的文章都在讲生成,很少有人认真看播放引擎。但我觉得这可能是整个项目里工程含量最高的部分。
先看状态机:
text
idle ──start()──▶ playing
playing ──pause()──▶ paused
paused ──resume()─▶ playing
playing ◀──────────▶ live (讨论模式)
live ──handleEndDiscussion()──▶ idle
live 模式是灵魂。 预生成的内容按序播放(保证流畅、可预期),用户一旦打断,立刻切进 live 模式走实时推理(保证响应),讨论结束后回到 idle。
这个"预生成 + 实时"的双状态设计,解决了一个非常实际的产品问题:==实时推理的延迟会毁掉播放体验。== 你不可能让 AI 老师每讲一句都卡 3 秒。所以讲的部分预先生成好,只有互动部分才实时。
再看 ActionEngine 的执行模式分类,这个设计很讲究:
| 动作 | 执行模式 | 为什么 |
|---|---|---|
speech |
同步(await 音频播完) | 话没说完不能翻页 |
wb_draw_* |
同步 | 白板绘制要跟讲解对齐 |
play_video |
同步 | 视频播放要阻塞队列 |
discussion |
同步(回调托管) | 交给 PlaybackEngine 处理 |
spotlight |
fire-and-forget | 聚光灯只是视觉效果,不该阻塞 |
laser |
fire-and-forget | 同上 |
同步动作会 await 完成,异步动作直接丢出去不阻塞队列。这个区分看起来小,但它决定了整堂课是"行云流水"还是"一顿一顿"。
最后是这个细节------TTS 的四级降级链:
text
① 动作里带了服务端 audioUrl → 优先播放
↓ 没有
② 用 audioId 去 IndexedDB 缓存找 Blob
↓ 没有
③ 按文本长度估算 reading-time 计时器
↓ 还不行
④ 降级到浏览器原生 Web Speech API
这个降级链是工程成熟度的标志。它意味着:任何一层挂掉,课都还能上下去。 音频服务炸了,课不会卡死,只会变成没有声音的课。
播放引擎是把"剧本"演成"课堂"的地方。没有它,你生成的只是一堆躺在数据库里的 JSON。
11|从「生成内容」到「生成可执行对象」
这一节是全文的核心论点。
传统生成式应用的链路是:模型 → 文本/HTML → 人读 。 OpenMAIC 的链路是:模型 → Action 序列(DSL) → 机器执行 → 人体验。
区别在哪?内容 vs 程序。
看 OpenMAIC 定义的 Action 类型:
text
speech → 说话(触发 TTS)
spotlight → 屏幕局部聚光灯
laser → 激光笔指向
wb_draw_* → SVG 白板绘制(图形/公式/流程/代码)
play_video → 播放视频
discussion → 进入讨论
这就是一门"课堂行为的指令集"。
如果做个类比:==模型是编译器,ActionEngine 是 CPU,这套 Action DSL 就是指令集架构(ISA)。==
一旦生成物从"被阅读的文本"变成"被执行的程序",一系列 OS 级的需求就必然涌现:
| OS 级需求 | OpenMAIC 的对应实现 |
|---|---|
| 权限 | 谁能执行哪些指令?(teacher 有 laser,student 没有) |
| 调度 | 指令按什么顺序、什么节奏执行?(PlaybackEngine 状态机) |
| 持久化 | 执行产生的结果存哪?(DocumentStore / RuntimeStore) |
| 资源管理 | 音频、图片、视频这些字节怎么管?(Asset Pool) |
这四件套凑齐,就是操作系统的雏形。
不是 OpenMAIC 想做操作系统,而是当你让模型输出可执行对象时,你会==被迫重新发明操作系统已经解决过的所有问题==。
这也是为什么我认为"生成可执行对象"是比"生成更好的内容"重要得多的方向:内容会被更好的模型碾压;但一套设计良好的指令集、状态机和资源模型,不会因为模型升级而过时。
Part 5 · 工程化基建:多模型、数据库与资源回收
12|为什么它支持这么多模型?
OpenMAIC 支持的 provider 列表长得离谱:
OpenAI、Azure OpenAI、Anthropic、Amazon Bedrock、Google Gemini、DeepSeek、Qwen、Kimi、MiniMax、Grok (xAI)、OpenRouter、Doubao、腾讯混元/TokenHub、小米 MiMo、GLM(智谱)、Ollama(本地)、Lemonade(本地 LLM/图像/TTS/ASR)、FunASR(本地 ASR),以及任何 OpenAI 兼容 API。
表面原因大家都懂:让用户自选、控制成本、满足合规、国内可用性。
但深层原因更值得学,有三条:
① 能力注册,而不是供应商绑定。
服务端会先探测当前环境具备哪些能力,再决定向 Agent 注册哪些工具 。没配视频服务,generate_video 根本不会出现在工具列表里。
这一招太聪明了。它的效果是:==模型永远不会拿到一个注定报错的按钮。== 相比之下,在 prompt 里写"当前可用工具:A、B、C",模型照样会尝试调用 D。
② 工具结果抹去供应商身份。
供应商返回的任务 ID、轮询状态、媒体地址,会被整理成中性的工具结果。于是换模型的时候,Skill 和 Agent 的决策逻辑一行都不用改。
③ MODEL_ROUTES 显式路由,故意不做 fallback。
README 明确写了:必须显式把 maic-agent-driver 路由到带 provider 前缀的模型,配了 openai-completions 或 openai-responses 方言,没有 fallback。启动时校验模型配置,未解析的路由 fail loud 而不是猜一个供应商。
这个"宁可报错也不猜"的取向,是成熟工程团队的标志。==静默降级在 AI 系统里往往是灾难------你以为它在跑,其实它在用错误的模型跑。==
::: success 对开发者的启示
抽象"能力",不要抽象"供应商"。
你的代码里应该出现 generate_video,而不是 generate_video_by_replicate。 :::
13|AI Agent 应用为什么开始需要数据库?
直觉上,AI 应用 = 无状态地调用 LLM,为什么要数据库?
因为一旦 Agent 从"回答问题"变成"完成任务",数据库就从可选项变成了必需品。
OpenMAIC 给出了四个理由,环环相扣:
① 任务比请求活得久。 生成一门课要几分钟到几十分钟。HTTP 请求早就超时了,任务怎么办?必须落库,变成有状态的 job / session。
② 产物需要可寻址、可增量修改。 改第三页的一个公式,不能重跑整门课。所以文档被规范化成按实体的行,putScene 可以只写单个场景,配合 per-scene revision 做增量同步。
③ 状态需要跨进程、跨设备。 浏览器关了,服务端还在跑;服务重启了,能从持久化的 transcript 里恢复。这需要 lease + heartbeat + checkpoint。
④ 历史需要可重放。 对话、工具调用、工具结果、检查点全部进持久记录。进程异常退出后,runner 能修复缺失的 tool result,并判断当前处于"已完成 / 可继续 / 等待用户"哪种状态。
存储被清晰地切成四层:
| 层 | 存什么 | 生命周期 |
|---|---|---|
DocumentStore |
课程内容(Stage / Scene / Action / Agent / 大纲快照) | 随课程编辑而变 |
RuntimeStore |
学习过程(session + append-only 记录) | 随学习者而累积 |
AssetStore |
字节(图片/音频/视频) | 内容寻址,可去重、可回收 |
KVStore |
设备偏好(主题、语言、布局) | 分 device / account 两种 scope |
注意 KVStore 的 scope 设计:account 值是用户数据,会跨设备同步;device 值(主题、locale、布局)永远不出设备。
这个 scope 是原语的一部分,不是后端选择的副产品 ------HTTP 的 KV 契约根本不传 scope 字段,所以 HttpKVStore 在构造时就强制要求注入一个本地后端。
隐私边界应该在类型系统里表达,而不是在文档里承诺。
14|从代码仓库看,它已经不是一个简单 Demo
判断一个开源项目是不是 demo,我有三个土办法:有没有迁移机制、有没有测试护栏、有没有资源回收。 OpenMAIC 三个都有,而且都做得挺认真。
1️⃣ 迁移机制
DSL 有 dslVersion,读取时按迁移阶梯向前跑,缺少迁移路径就停止 ,明确避免产出"半迁移文档"。写入时过校验闸门。文档 DSL 和 Runtime DSL 有独立的版本线,避免两类数据误走同一套迁移逻辑。
2️⃣ 测试护栏
不是只测 happy path,专门测降级路径:
bash
tests/action/engine-latex-fallback.test.ts # LaTeX 渲染失败怎么回退
tests/orchestration/tool-schemas-latex-guardrail.test.ts # 工具 schema 的 LaTeX 护栏
tests/agent-runtime/skills.test.ts # Skill 系统的行为约束
3️⃣ 资源回收
删除 asset 只丢 registry 条目,字节由离线收集器 回收。每 ASSET_COLLECTION_INTERVAL_MS(默认 15 分钟)跑一轮,处理已无引用超过 ASSET_COLLECTION_GRACE_MS(默认 1 小时)的字节。
而且并发安全:水平扩展时每个实例都可以开着收集器,每个 blob 行在删除前会加锁并复查,并发收集器串行化而不是竞争。
其他工程化证据
- monorepo + workspace packages:
@openmaic/dsl、generation、renderer、storage、importer; - 独立的
render-service容器(Chromium + FFmpeg on Node 22)做 MP4 导出,不污染主应用; - 能力开关:
__ENABLED=false可以强制关掉任何对外能力; - 启动时校验模型配置;
- 专业的 PDF 解析走 MinerU,本地媒体抽取走 ffmpeg/ffprobe,两条路都不可用时会明确标记失败并给出可操作的提示,而不是挂起或返回空转录。
规模数据
项目 3 月开源时是数千 star,到 8 月 v1.0.0 时已有约 83 位贡献者;背后是 JCST 2026 论文;在清华 TAGI 课程的两个月部署中,319 名学生完成课程,86.3% 主动与 AI Agent 互动,近 80% 的课堂时间用于提问或发起新想法。
最后这个数据是我认为最有说服力的------它证明这套架构不只是跑得通,是真的改变了学习行为。
Part 6 · 更大的图景:AI 应用正在"操作系统化"
15|给 AI Coding 开发者最大的启发:不要只做 Agent,要做 Runtime
现在做 AI 应用的团队,90% 的时间花在 Agent 上:调 prompt、换模型、加工具、优化 few-shot。
但 OpenMAIC 展示了另一条路------把重心放在 Runtime 上。
什么是 Runtime 层面的工作?五件事:
| # | 要定义的东西 | 决定了什么 |
|---|---|---|
| 1 | 产物的数据模型(DSL) | 生成物能不能被增量修改、版本化、迁移 |
| 2 | 执行层(Action Engine) | 生成物能不能产生副作用,而不只是被阅读 |
| 3 | 状态与生命周期(Session / lease / heartbeat) | 能不能跑长任务、能不能断线续跑 |
| 4 | 权限边界(owner scope / action 白名单) | 能不能支持多用户、敢不敢把工具交给模型 |
| 5 | 能力抽象(provider-neutral) | 能不能换供应商、能不能本地化部署 |
这五件事的共同特点是:它们都不随模型升级而过时。
而 Agent 层面的工作------prompt、few-shot、模型选型------会随着模型变强而持续贬值。今天你精心调优的 prompt,明年可能被一个更好的 base model 直接抹平。
::: danger 说句狠的
==Prompt 会过时,模型会降价,DSL 和状态机不会。==
在你写第一个 prompt 之前,先回答一个问题:"我的生成物,是什么数据结构?"
- 答案是"一段 Markdown 字符串" → 你做的是一个文本生成器,护城河约等于零;
- 答案是"一棵带版本号、可校验、可增量 patch 的文档树" → 你才有可能做出一个产品。 :::
16|它和 MCP 的关系是什么?
这个问题值得单独澄清,因为很容易搞混。
首先,OpenMAIC 本身不是一个 MCP Server。 它的依赖里带的是 @modelcontextprotocol/sdk------也就是说,它是 MCP 的消费者(客户端),可以接入外部的 MCP 工具生态。
其次,社区已经把它包装成了 MCP Server。 比如开源的 open.maic-MCP,暴露了四个工具:
| 工具 | 作用 |
|---|---|
check_health |
探测连通性和服务端能力(webSearch、imageGeneration、videoGeneration、tts) |
generate_classroom |
提交一个异步课堂生成任务 |
get_job_status |
轮询直到成功或失败 |
parse_pdf |
上传本地 PDF 拿回解析后的文本 |
配好之后,你可以在 Claude Code 或 Claude Desktop 里直接说"教我量子物理",模型会自己走完 check_health → generate_classroom → get_job_status 三步,最后把课堂链接丢给你。
另外还有 OpenClaw 生态。 一条 clawhub install openmaic,就能在飞书、Slack、Discord、Telegram 等 20+ 聊天工具里直接生成课堂。
于是出现了一个很有意思的对称性:
- OpenMAIC 内部用 Skill 约束自己的 Agent;
- OpenMAIC 外部又作为一个 Skill / MCP Tool,被别的 Agent 调用。
未来的 AI 应用不是孤岛,而是"可被编排的能力节点"。
MCP 负责连接 (让别的 Agent 能调用你),Skill 负责描述(告诉调用方你擅长什么、该按什么方法用你)。两者互补,缺一不可。
如果你在做 AI 产品,现在就该问自己:==我的能力,能不能被 Claude Code 一句话调用?== 如果不能,你在未来的 Agent 网络里就是不可见的。
17|为什么它比普通 AI 项目更值得研究?
三个理由。
第一,它把一个生产级的 Agent 应用完整打开了。 不是玩具 demo,是从 DSL 到状态机到持久化到资源回收的完整链路,而且代码组织清晰,模块边界干净。这种样本在开源世界里非常稀缺。
第二,它的每个设计决策都有明确的问题来源,不是为了炫技。
随便举几个:
| 设计 | 背后的坑 |
|---|---|
| Asset 用两层间接(id → registry → bytes) | 直接存 raw URL 会把供应商和过期时间"烤进"文档,破坏可移植性;两层间接还能让相同字节只存一份 |
| object URL 按 id 单独签发,不按 contentHash 共享 | 共享 URL 会让持有两个 id 的人比较字符串推断"这两份字节相同"------侧信道泄露 |
| RuntimeStore 用单调 seq 而不是时间戳 | 重放顺序不能依赖时钟 |
| 文档写工具要顺序执行 | 并行的 read-modify-write 会静默丢修改 |
PgDocumentStore 用 forOwner(ownerId) 绑定 |
owner ID 绝不能出现在模型可以填写的参数里 |
每一个"为什么"背后,都有一个真实踩过的坑。==这种项目教你"为什么",而不只是"是什么"。==
第三,它诚实地写下了自己的局限。
README 里有这么一段:
PERSISTENCE_DEV_TOKEN和NEXT_PUBLIC_PERSISTENCE_TOKEN在任何有意义的定义上都不是秘密:NEXT_PUBLIC_前缀的 token 会被编译进公开的 JavaScript bundle,对每个访客完全可见,因此不提供任何机密性和用户隔离 ------任何能加载页面的人都能提取它,并通过选择一个x-learner-key读写每一个 学习者分区和所有 文档。它的唯一用途是挡住可信网络上的无关扫描器。这只适合 localhost 或可信网络的单用户部署。上生产前,请用真实的会话校验替换lib/persistence/server-auth.ts。
一个开源项目,在 README 最显眼的位置用最直白的语言告诉你"我的认证是玩具级的,别直接上生产"------这种诚实比一百个功能 badge 更有价值。
我建议每个做 AI 应用的人把这段读三遍。 因为它描述的正是 AI 应用最容易翻车的地方:==浏览器端的一切都不可信==,而很多团队在快速迭代中会忘掉这件事。
18|一个更值得关注的趋势:AI 应用正在出现「操作系统化」
把前面所有内容汇总,你会发现一张很有意思的对照表:
| 操作系统概念 | OpenMAIC 的对应物 |
|---|---|
| 进程 / 线程 | Agent Session(lease、heartbeat、取消、恢复) |
| 调度器 | LangGraph Director Graph |
| 指令集(ISA) | Action DSL(28+ 种动作) |
| 设备驱动 | Provider Adapters(LLM / TTS / ASR / 图像 / 视频) |
| 文件系统 | Asset Pool(id → registry → content-addressed bytes) |
| 权限 / UID | Owner Scope + 角色化 allowedActions |
| 系统调用 | 工具(read_stage / patch_stage / generate_page) |
| 库 / 包管理 | Skills(SKILL.md,可发现、可分发) |
| IPC | MCP / OpenClaw |
| 持久化 | DocumentStore / RuntimeStore / KVStore |
| 能力探测 | 按配置决定注册哪些工具 |
每一个概念,OpenMAIC 都有一个严肃的对应实现。
这不是巧合。这背后的逻辑是:
当 Agent 从"生成文本"走向"执行任务",它必然要重新发明操作系统已经解决过的所有问题------==调度、权限、资源、持久化、进程生命周期==。
我的判断:接下来 1--2 年,会出现"面向 Agent 的 Runtime 层"的标准化尝试。
不是有人要去重写 Linux,而是这批概念会以库、框架、协议的形式沉淀下来,变成 AI 应用的公共基础设施。就像 Web 开发最终沉淀出了 HTTP Server + ORM + 框架这套标准分层一样。
OpenMAIC 的价值在于,它提前把这个未来的一小块,具体地实现出来给你看了。
Part 7 · 泼冷水:上生产前必须知道的 9 个坑
前面夸了很多,这一节必须泼冷水。以下每一条都有据可查,不是我臆测。
❌ 1. Workbench 的认证是玩具级的(最严重)
上一节引过 README 原文了。NEXT_PUBLIC_PERSISTENCE_TOKEN 编译进公开 bundle,任何人可提取,可读写所有 learner 分区和所有文档。绝对不要直接暴露在公网。
上生产必须重写 lib/persistence/server-auth.ts,并重新设计文档/合并/管理的授权策略。
❌ 2. Pro Workbench 的部署门槛不低
必须配 PostgreSQL,必须显式配 MODEL_ROUTES 路由 maic-agent-driver,没有 fallback。只开 flag 不配数据库,运行时根本起不来,session 路由会报错。
❌ 3. NEXT_PUBLIC_PERSISTENCE 是 build-time 开关
它是编译进浏览器包的,不是运行时配置。配错的话,浏览器选了 HTTP 持久化但端点返回配置/认证错误。虽然有 toast 提示并会保留原有课程列表(这个降级设计还算厚道),但对新手仍是坑。
❌ 4. Agent 路径的不确定性
这是架构层面的固有代价:延迟和 token 消耗更难预测;Skill 可能互相冲突(比如同时命中 deep-interactive 和 lecture);模型可能挑错工具。OpenMAIC 用工具白名单、超时、取消栅栏、顺序写、结构检查来约束,但只能"限制在系统能处理的范围内",无法根除。
❌ 5. 顺序写的性能代价
为了正确性牺牲了并行度。编辑一门长课程时,这个代价会被放大。
❌ 6. 协议信息存在不一致
不同来源对 license 的说法不一:早期资料普遍记为 AGPL-3.0 ,而 2026 年中的资料显示已改为 MIT (v0.3.0 起)。商用前请务必以仓库根目录的 LICENSE 文件为准,不要信二手资料。
❌ 7. 成本
一次生成跨越几十分钟,图片/视频/TTS 全开的话,token 和 API 开销不小。托管实例还有每日配额限制。
❌ 8. 视频导出不是开箱即用
"Export Video" 需要额外的 render-service 容器(Chromium + FFmpeg on Node 22)。不启用这个 profile,导出会降级成下载项目 ZIP 供本地 CLI 渲染。
❌ 9. 内容质量仍需人工复核
fact-check Skill 能降低幻觉,但生成式内容在教育场景下的准确性、深度和风格,依然难以事前精确控制。这是所有生成式应用的共同局限,不是 OpenMAIC 独有。
Part 8 · 上手路线:9 步读源码 + 3 个自检问题
给一条具体的阅读路径,按这个顺序走,效率最高:
| Step | 读什么 | 重点 |
|---|---|---|
| 1 | packages/@openmaic/dsl |
先看 Action / Scene / ActionType。不看这个,后面全是天书 |
| 2 | packages/@openmaic/generation |
outline-generator.ts → scene-generator.ts → pipeline-runner.ts。理解"两阶段为什么要分开" |
| 3 | lib/orchestration/ |
registry/types.ts → tool-schemas.ts → prompt-builder.ts → director graph。看它怎么用代码约束模型 |
| 4 | lib/playback/engine.ts、lib/action/engine.ts |
状态机怎么切 playing ⇄ live,同步/异步动作怎么区分,TTS 四级降级链。工程密度最高 |
| 5 | packages/@openmaic/storage |
Document / Runtime / Asset 三层切分,Asset 的两层间接 |
| 6 | lib/server/agent-runtime/ |
skills.ts(发现与加载)、session runner(lease / heartbeat / crash resume) |
| 7 | tests/ 里那几个护栏测试 |
看他们怎么给模型输出上护栏------比读十篇"AI 应用怎么做可靠性"的文章有用 |
| 8 | 动手跑 | 先跑 classic 模式(不需要数据库),配一个 Gemini 3 Flash 或本地 Ollama;再开 PostgreSQL 玩 Pro Workbench;最后写一个自己的 SKILL.md |
| 9 | 带着三个问题读 | 见下方 |
::: warning 每读一个模块,都问自己这三个问题
- 我的生成物,能增量修改吗?
- 我的任务,能断线续跑吗?
- 我的权限边界,在类型系统里,还是在 prompt 里?
只要有一个答不上来,那就是你下一步该补的作业。 :::
写在最后:OpenMAIC 可能代表了一类新的 AI 软件
回到开头那个问题:OpenMAIC 到底是什么?
我的答案是:它是一份关于"AI 应用该怎么工程化"的、可以运行的参考答案。
AI 老师讲课很酷,但那只是 demo 效果。真正让我反复翻源码的,是它示范了一件事------
当模型足够强之后,工程的重心会从"怎么让模型说出正确的话",转移到"怎么让模型产生的东西可运行、可修改、可管理"。
这是软件工程的老问题,只不过执行者从人变成了模型。
过去两年,我们所有人都在学怎么写 prompt。这没错,那是当时的瓶颈所在。
但接下来几年,瓶颈会转移。真正稀缺的能力,会变成:
- 怎么给生成物设计一个能演进的数据模型;
- 怎么给 Agent 设计一个能跑长任务的运行时;
- 怎么给模型设计一个既开放又受控的能力边界;
- 怎么把领域专家的知识,封装成可分发的 Skill。
==换句话说:我们要从"提示词工程师",变成"为模型设计 Runtime 的人"。==
OpenMAIC 的名字里,"Open" 是开源,"MAIC" 是多智能体互动课堂。但我读完源码后,更愿意把它理解成另一层意思------
它把一类新的 AI 软件的骨架,开源了出来。
而这套骨架,跟你做不做教育,其实没什么关系。
📚 参考资料
- OpenMAIC 官方仓库(THU-MAIC/OpenMAIC,MIT 协议)
- 在线体验 Demo
- 论文:Yu J.F., Zhang-Li D., et al. From MOOC to MAIC: Reimagine Online Teaching and Learning through LLM-Driven Agents. JCST, 41(1): 394--414, Jan. 2026. DOI: 10.1007/s11390-025-6000-0
- @openmaic/storage 包文档(npm README 写得极其详细,推荐直接读)
- DeepWiki 架构解析(Skill 系统、存储层部分质量很高)
::: success 聊两句
如果这篇拆解对你有帮助,欢迎点赞收藏 🙏
也很好奇你在做的 Agent 项目里,生成物是什么数据结构?是"一段 Markdown 字符串",还是"一棵可 patch 的文档树"?评论区聊聊,我看到都会回。 :::
✍️ 每天追踪 GitHub Trending,更多内容可关注公众号「AI Agent 赛道技术拆解」。