OpenMAIC 的 README 還寫著 LangGraph,課堂早就換了引擎
OpenMAIC 的 README 在 Key Architecture 那段寫的是 LangGraph state machine。原始碼寫的是另一回事:從 2026 年 9 月 24 日的 v1.1.0 開始,課堂聊天預設就不走那台狀態機了。
文件落後程式碼,在開源專案裡一點都不稀奇。讓我想把它寫下來的,是落後的那一段剛好是整個專案最核心的東西:AI 老師跟 AI 同學在課堂上怎麼輪流講話。
它是什麼,一句話交代。OpenMAIC 全名 Open Multi-Agent Interactive Classroom,GitHub 組織是 THU-MAIC。照 README 的說法,你丟一個主題或一份教材進去,它生出投影片、測驗、互動模擬和 PBL 專題,再由 AI 老師和 AI 同學上課、寫白板、討論。2026-10-04 用 GitHub API 查,THU-MAIC/OpenMAIC 有 39,918 顆星、6,176 個 fork、111 位貢獻者,MIT 授權(v0.3.0 之前是 AGPL-3.0)。repo 是 2026 年 3 月 11 日建的,到今天還不到七個月。
這篇沒有安裝,也沒有跑過任何一堂課。下面的內容出自它的 README、CHANGELOG 和原始碼,寫「文件說」「原始碼寫」的地方就是字面意思。
以前:一次只點一個人
預設的課堂有 6 個 agent,定義在 lib/orchestration/registry/store.ts:1 位老師、1 位助教、4 位學生。學生各有個性,開心果、好奇寶寶、筆記員,還有一位專門唱反調的思考者(原始碼裡學生和助教的名字是簡體中文)。
舊版讓這六個人講話的方式,是一張很短的圖:START → director → END,或者 START → director → agent_generate → END。
翻成白話:你送出一句話,director 決定這次輪到誰,被點到的那位講完,結束。下一句話進來,重來一次。每個 request 只有一個人發言。
這像一堂老師很強勢的課。同學舉手,老師點一個人回答,答完就換下一題,沒有人能接著別人的話講第二輪。程式碼註解把這個設計講得很直白:”There is no maxTurns cap — the topology is the bound”。不需要回合上限,因為圖本身就只長那樣,它沒有能繞回去的邊。
我滿欣賞這個寫法。煞車不是一個數字,是形狀。你不用擔心哪天參數設錯,讓 agent 互相講到天荒地老,因為根本沒有那條路。
代價也很明顯:一次就一個人,講完就收。
現在:一個回答裡可以做很多事
v1.1.0 之後,課堂聊天預設走 lib/chat/pi/ 底下的 Pi agent loop,用的是 @earendil-works/pi-agent-core 0.78.0。
同樣是你在課堂上丟一句話,現在發生的事多了很多。單次回答裡,agent 可以讀投影片、web_search,也可以呼叫教室工具:call_agent、cue_user、close_session、read_scene、spotlight,加上一組 wb_* 開頭的白板工具。
回到那堂課。被點到的同學可以起身翻投影片、上網查資料;看 call_agent、cue_user 這兩個名字,大概還能轉頭叫別的同學出來,最後把問題丟回給你(這兩個工具實際怎麼運作,我是照名字推的)。課堂活了,但多了一件以前不用想的事:這樣下去,什麼時候停?
答案從形狀搬到了數字。lib/chat/pi/config.ts 規定,一次最多 6 個 agent 回合,每個 agent 最多 8 個動作。
| 舊:director graph | 新:Pi agent loop | |
|---|---|---|
| 一次 request 的發言 | director 點一個人 | 最多 6 個 agent 回合(工具裡有 call_agent) |
| agent 能做的事 | agent_generate 節點產生回應 |
讀投影片、web_search、教室工具、wb_* 白板工具 |
| 讓它停下來的東西 | 圖的拓撲,沒有 maxTurns | config.ts:最多 6 個 agent 回合、每 agent 最多 8 個動作 |
| v1.1.0 起的預設 | 要 NEXT_PUBLIC_PI_CHAT_ENABLED=false 再建置才回得來 |
預設開啟 |
轉折點:開關翻了,文件沒翻
切換靠一個環境變數 NEXT_PUBLIC_PI_CHAT_ENABLED,讀取時用的是 readDefaultOnBoolean,沒設就當作開。要舊行為,得明確設成 false 再建置。
CHANGELOG 寫了這次切換,程式碼也一致。落後的只有 README 的 Key Architecture 那一段,還寫著 LangGraph state machine。
這種落後麻煩的地方,在於它看起來很可信。README 是你第一個打開的檔案,架構段寫得有條有理,你沒有理由懷疑。假如你要查的是「agent 為什麼一次講了好幾輪」,照 README 去翻 LangGraph 的節點,會整個找錯地方。
再往下一層:v1.1.2 跟 main 也不是同一個東西
README 跟程式碼對不上,還算好處理。比較容易踩坑的是另一層:你讀的是哪個分支的 README。
最新正式版是 v1.1.2,2026-09-28 發布。到 10-04 為止,main 跟 v1.1.2 已經分歧,main 領先 22 個 commit、落後 3 個,而 main 的 package.json 版號還停在 1.1.1。
main 的 CHANGELOG [Unreleased] 段堆了幾件會改變安裝方式的事。9 月 29 日起 PostgreSQL 變成必要,沒有 DATABASE_URL 程式一啟動就退出。10 月 2 日加了 openmaic.yml 和 capability slots,使用者在網頁上設定的 key 改存進 PostgreSQL,用 OPENMAIC_SECRET_KEY 加密。另外,web search 開著的時候,課程生成一律先上網研究。
v1.1.2 tag 上的 README,系統需求只寫了 Node.js 和 pnpm。main 的 README 要的是 Node 22.19 以上、pnpm 10 以上、PostgreSQL 16。
所以 GitHub 首頁那份安裝說明,描述的是一個還沒發版的東西。照首頁 README 準備環境、卻 clone 下 v1.1.2 的 tag,你會多準備一個那一版還不強制的資料庫;反過來,照 tag 的 README 去跑 main,沒設 DATABASE_URL 就是啟動即退出。
我的立場:要自架,checkout 正式版的 tag,照那個 tag 裡的 README 走,首頁的先別信。什麼時候我會改口?等下一個正式版把 [Unreleased] 那一包發出去、README 架構段的 LangGraph 那句也一起改掉,首頁 README 就重新等於你裝到的東西了。
金鑰換了住址
兩個版本差多遠,看金鑰放在哪裡最清楚。依文件和原始碼,OpenMAIC 的模型金鑰有三層。
A 層是伺服器端設定的 provider,README 寫 “credentials never reach the browser”。B 層是使用者自己在網頁上填的 key,到 v1.1.2 為止存在瀏覽器,main 改成存 PostgreSQL 並加密。C 層是已經 deprecated 的 x-api-key/x-base-url header,程式仍然會讀,只有 allowWorkspaceProviders: false 時才不收。
同一個「使用者填 key」的動作,在 tag 上跟在 main 上落腳的地方完全不同。三層之間怎麼互動,我手上的資料沒有逐一追完,相關討論在 open issue #441。
main 的 openmaic.yml 有一個設計我很喜歡:寫進檔案的 slot,網頁上會被鎖住不能改;解析不到模型路由時,原文是 “fail loudly instead of guessing a vendor”。寧可炸掉也不幫你猜廠商,這個我投贊成票。
七個月的版本線
OpenMAIC 版本演進(2026 年;標「約」者為約略日期)
03-11
repo 建立。
03-26
v0.1.0。
約 04-20
v0.2.0,Deep Interactive Mode。
06-02
v0.2.2:Editor Pro、離線匯出、zh-TW。
約 06-28
v0.3.0:PBL v2、@openmaic/* 套件上 npm,授權從 AGPL-3.0 改成 MIT。
07-21
v0.3.1:一鍵 MP4、Postgres 參考實作、SSRF 強化。
08-27
v1.0.0:Agent workbench、durable session、24 個內建 skill。
09-06~09-15
v1.0.1~v1.0.3 連三版安全修補,包括升級 Next.js 修一個 critical RCE。
09-24
v1.1.0:Pi agent loop 成為課堂聊天預設。
約 09-27/09-28
v1.1.1 與 v1.1.2(v1.1.2 的 GitHub 發布時間是 09-28)。
09-29~10-02(main,未發版)
PostgreSQL 變必要;openmaic.yml 與 capability slots。
標「約」的幾個日期我沒有逐版重查,GitHub release 用 UTC,跟 CHANGELOG 的日期會差一天。
題外話,以 10-03 往回數的最近 50 個 commit 裡,有 15 個帶 Co-Authored-By: Claude 的 trailer,CONTRIBUTING.md 也寫了 AI-Assisted PRs 的規範:要標註,送審前要先跑過 AI code review。做 AI 教室的專案,自己的程式碼也有一部分是跟 AI 一起寫的,這倒是很一致。
安全公告集中在 v1.0.0 之後
版本線上 9 月那一段特別密。2026 年 9 月 6 日到 9 月 28 日之間,這個 repo 公開了 12 筆安全公告(10-04 用 GitHub API 查到的筆數),公開日期全都晚於 v1.0.0,也都已經修補。v1.0.1 到 v1.1.2 這六版,有 5 版的 release 標題帶 Security,唯一例外是 v1.1.0。
公告的類型包括雲端 metadata 位址的 SSRF(其中一筆是 /api/proxy-media 沒擋阿里雲的 metadata 位址)、路徑穿越造成任意寫檔、經 dangerouslySetInnerHTML 的 stored XSS、DNS rebinding。每一筆的嚴重度我沒有重新逐筆核對,所以這裡不列分布。近四萬顆星和公告擠在 9 月那二十幾天,兩者有沒有因果,我沒查到能說明的資料。
公告都修補了,自架時真正會碰到的是預設值。README 自己寫,ACCESS_CODE 沒設的時候是 fail-open,所有 route 連 API 在內都不檢查憑證。設了 ACCESS_CODE,但 TRUST_PROXY_HEADERS 不是 true,就不限流(有開的話是每個 client 60 秒 10 次)。課程只要知道 stage id 就能讀,沒有 owner 檢查,只有寫入和刪除才比對 owner。匿名身分靠一個 anonymous_id cookie,內建沒有帳號系統;要求多使用者和後台管理的 issue #23 從 2026 年 3 月開到現在。
main 的 README(也就是含未發版行為的那份)給 Docker Compose 的預設是 single-user,綁在 127.0.0.1:3000。要對外開放,它給的順序是:先設 ACCESS_CODE,再改掉 PERSISTENCE_POSTGRES_PASSWORD 的預設值 openmaic-dev,**最後才把 OPENMAIC_PUBLISH_ADDRESS 設成 0.0.0.0**。這個順序不要顛倒。
煞車搬家了
前面講的課堂聊天和自架,其實是同一個變化:煞車從形狀搬到了設定。
舊的課堂聊天靠圖的形狀停下來,新的靠 config.ts 裡的 6 和 8。Docker Compose 預設綁 127.0.0.1,擋人的是網路位置;一旦你把位址改成 0.0.0.0,擋人的就只剩 ACCESS_CODE、資料庫密碼這幾個你自己填的值。
形狀式的煞車不用讀,它就在那裡,想繞也繞不過去。設定式的煞車要你去讀,而且要讀對版本。這也是 README 落後比表面上嚴重的原因:安全邊界從結構變成數字之後,文件就是你手上唯一的地圖,而首頁那張地圖畫的,是另一個版本的地形。










