从“生成内容”到“生成可执行对象”:我把 OpenMAIC 源码翻了一遍,发现 AI Agent 正在变成应用操作系统

🧭 全文速览

这篇文章不教你怎么用 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,通常得满足四个条件:

  1. 它维护状态,且状态有自己的生命周期;
  2. 它有执行层,能把抽象指令变成副作用;
  3. 它有资源模型,管理字节、句柄、引用;
  4. 它管理任务生命周期,任务能比一次调用活得更久。

拿这四条去量 OpenMAIC,全中:

Runtime 要素 OpenMAIC 的实现
状态 PlaybackEngine 状态机:idle / playing / paused / live
执行层 ActionEngine:28+ 种动作类型,同步与 fire-and-forget 两种模式
资源模型 Asset Pool:AssetIdast_ + 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:完整控制权------spotlightlaserplay_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 约束大纲的 sceneCounttypeMix------概念要在递增复杂度上反复出现
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.tslistSkills 扫描文件系统,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_stagedetail: "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 里,它的职责边界非常清楚:

  • DirectorGraphcreateOrchestrationGraph + 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-completionsopenai-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/dslgenerationrendererstorageimporter
  • 独立的 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 会静默丢修改
PgDocumentStoreforOwner(ownerId) 绑定 owner ID 绝不能出现在模型可以填写的参数里

每一个"为什么"背后,都有一个真实踩过的坑。==这种项目教你"为什么",而不只是"是什么"。==

第三,它诚实地写下了自己的局限。

README 里有这么一段:

PERSISTENCE_DEV_TOKENNEXT_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-interactivelecture);模型可能挑错工具。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.tsscene-generator.tspipeline-runner.ts。理解"两阶段为什么要分开"
3 lib/orchestration/ registry/types.tstool-schemas.tsprompt-builder.ts → director graph。看它怎么用代码约束模型
4 lib/playback/engine.tslib/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 每读一个模块,都问自己这三个问题

  1. 我的生成物,能增量修改吗?
  2. 我的任务,能断线续跑吗?
  3. 我的权限边界,在类型系统里,还是在 prompt 里?

只要有一个答不上来,那就是你下一步该补的作业。 :::


写在最后:OpenMAIC 可能代表了一类新的 AI 软件

回到开头那个问题:OpenMAIC 到底是什么?

我的答案是:它是一份关于"AI 应用该怎么工程化"的、可以运行的参考答案。

AI 老师讲课很酷,但那只是 demo 效果。真正让我反复翻源码的,是它示范了一件事------

当模型足够强之后,工程的重心会从"怎么让模型说出正确的话",转移到"怎么让模型产生的东西可运行、可修改、可管理"。

这是软件工程的老问题,只不过执行者从人变成了模型。

过去两年,我们所有人都在学怎么写 prompt。这没错,那是当时的瓶颈所在。

但接下来几年,瓶颈会转移。真正稀缺的能力,会变成:

  • 怎么给生成物设计一个能演进的数据模型
  • 怎么给 Agent 设计一个能跑长任务的运行时
  • 怎么给模型设计一个既开放又受控的能力边界
  • 怎么把领域专家的知识,封装成可分发的 Skill

==换句话说:我们要从"提示词工程师",变成"为模型设计 Runtime 的人"。==

OpenMAIC 的名字里,"Open" 是开源,"MAIC" 是多智能体互动课堂。但我读完源码后,更愿意把它理解成另一层意思------

它把一类新的 AI 软件的骨架,开源了出来。

而这套骨架,跟你做不做教育,其实没什么关系。


📚 参考资料

::: success 聊两句

如果这篇拆解对你有帮助,欢迎点赞收藏 🙏

也很好奇你在做的 Agent 项目里,生成物是什么数据结构?是"一段 Markdown 字符串",还是"一棵可 patch 的文档树"?评论区聊聊,我看到都会回。 :::


✍️ 每天追踪 GitHub Trending,更多内容可关注公众号「AI Agent 赛道技术拆解」。

相关推荐
邀星月为媒14 分钟前
从零打造一个属于自己的 AI Agent:Core Hive Agent 开源项目解析
人工智能·hive·开源
一次旅行17 分钟前
2026‑09‑01 AI产业深度解读|AI安全体系化升级、算力转租模式兴起、多模态模型持续开源
人工智能·安全·开源
苏灵凯24 分钟前
Codex 官网前端可以抄吗?从设计到实现的技术拆解
笔记·ai·agent·codex·deepseek
BreezeJiang10 小时前
大模型为什么能记住上一轮?从 InMemoryChatMessageHistory 看对话 Memory
llm·agent
Flynt10 小时前
OpenJDK禁AI代码半年了,现在执行得怎么样?答案是:全靠自觉
java·开源·ai编程
冬奇Lab10 小时前
开源项目第204期:LoopX — 长周期 Agent 控制平面,跑在 Codex/Claude Code 之上的状态管理层
人工智能·开源
stormzhangV12 小时前
AI 视频迎来了奇点时刻
开源·ai编程
孟健13 小时前
Uber:70% 的 PR 来自 Agent,AI 账单却没涨
ai编程
ovO14 小时前
DeepSeek Harness 源码解读(五):工具明明并发执行,结果为什么还按顺序写入
开源·agent·deepseek