OpenMAIC 运行踩坑与经验
本文记录把 OpenMAIC(Next.js 16 多智能体互动课堂)从源码跑到可用、以及打包分发、部署过程中实际遇到的问题、根因和解决办法。 
一、环境与版本
| 项目 | 版本 / 要求 |
|---|---|
| 操作系统 | Windows(开发),Linux + 宝塔(Nginx / Docker 部署) |
| Node.js | v22.19.0(engines 要求 >= 22.19.0) |
| 包管理器 | pnpm 10.28.0(packageManager 声明,不要用 npm 安装) |
| Next.js | 16.2.11(Turbopack) |
| React | 19.2.3 |
| TypeScript | 5.9.3 |
| 关键依赖 | tailwindcss 4.3.3、@langchain/core 1.2.9、@types/node 22.20.1 |
启动命令:
powershell
pnpm install # 首次安装,会执行 postinstall 构建全部 workspace 包
pnpm dev # 开发模式,默认 3000 端口被占用时自动改用 3001
pnpm build # 生产构建(会做完整 TypeScript 类型检查)
pnpm start # 生产模式启动
!warning 用 pnpm,不要用 npm 项目是 pnpm workspace,
postinstall里用的是pnpm run build,而且锁文件是pnpm-lock.yaml。npm install不会正确构建@openmaic/*子包,后续必然报模块找不到。npm run build只是执行脚本本身没问题,但依赖必须由 pnpm 安装。
二、问题速查表
| 症状 | 根因 | 解决 |
|---|---|---|
启动后访问页面 500,Missing field 'negated' on ScannerOptions.sources |
锁文件把 tailwindcss 4.0.0 与 4.3.3 的 oxide 扫描器混装 | pnpm update -D tailwindcss @tailwindcss/postcss 统一到 4.3.3 |
CSS 错误修完仍然报旧路径 @tailwindcss+postcss@4.0.0 |
旧版本目录被运行中的 dev server 锁住未清理 + Turbopack 缓存 | 停服务 → 删 .next 和残留 .pnpm 目录 → 重启 |
Module not found: Can't resolve '@langchain/core/utils/uuid' |
@langchain/core 1.1.16 太旧,langgraph-checkpoint@1.1.5 需要 ^1.1.48 |
pnpm update @langchain/core(升到 1.2.9) |
npm run build 报 Type 'Buffer' is not assignable to 'BinaryLike' |
@types/node 锁在 20.0.0,与 TypeScript 5.9 不兼容 |
package.json 改 "@types/node": "^22" 后 pnpm update @types/node |
找不到 @openmaic/renderer / @openmaic/editor 的模块 |
两个 workspace 包缺少 dist 构建产物 |
pnpm --filter @openmaic/renderer run build、pnpm --filter @openmaic/editor run build |
| 端口 3000 打不开,日志提示改用 3001 | 3000 被本机其他程序(grafana)占用 | 用日志里的端口访问;部署时让应用只监听 127.0.0.1:3000 |
| 页面能打开,但生成课程/语音失败 | .env.local 没有配置任何 LLM 服务商 Key |
从 .env.example 复制并至少填一个 *_API_KEY |
| 想用小米 MiMo TTS,填自定义 TTS 却调不通 | MiMo TTS 走 /v1/chat/completions + audio 参数,不是 /v1/audio/speech |
源码内新增原生 xiaomi-tts provider(见第四节) |
日志里 instrumentation.ts: A Node.js API is used 警告 |
Edge Runtime 检查提示,非致命 | 忽略,不影响启动和功能 |
三、问题详解
3.1 工作区包缺少 dist 产物
现象 :启动或构建时报找不到 @openmaic/renderer、@openmaic/editor 的模块(它们的 main 指向 dist/...)。
原因 :这两个包在 postinstall 里构建,如果安装过程被中断(或只跑了部分脚本),dist 就不存在。
解决:
powershell
pnpm --filter @openmaic/renderer run build
pnpm --filter @openmaic/editor run build
随后确认 packages/@openmaic/<包名>/dist 存在即可。之后正常 pnpm install 会自动完成这一步。
3.2 Tailwind 版本错配导致页面 500
现象:首页首次编译直接 500,日志:
typescript
Error evaluating Node.js code
Error: Missing field `negated` on ScannerOptions.sources
at .../@tailwindcss+postcss@4.0.0/.../dist/index.js
根因 :锁文件里 @tailwindcss/postcss@4.0.0 被解析成依赖 @tailwindcss/node@4.3.3、@tailwindcss/oxide@4.3.3,4.0.0 的 JS 与 4.3.3 的原生扫描器 API 不匹配。
解决:
powershell
pnpm update -D tailwindcss @tailwindcss/postcss # 统一到 4.3.3
后续坑 :更新完仍然报 @tailwindcss+postcss@4.0.0 的路径。原因是 dev server 还在运行,Windows 下旧目录被文件锁住删不掉,加上 Turbopack 的 .next 缓存仍指向旧路径。
powershell
# 先停掉 pnpm dev,再执行:
node -e "const fs=require('fs'); for (const p of ['.next','node_modules/.pnpm/@tailwindcss+postcss@4.0.0']) { if (fs.existsSync(p)) { fs.rmSync(p,{recursive:true,force:true}); console.log('removed',p); } }"
pnpm dev
经验 :更新/删除依赖前先停 dev server;改完依赖后清一次 .next 最省事。
3.3 @langchain/core/utils/uuid 找不到
现象 :访问 /api/chat 或执行 next build 时:
sql
Module not found: Can't resolve '@langchain/core/utils/uuid'
./node_modules/.pnpm/@langchain+langgraph-checkpoint@1.1.5_@langchain+core@1.1.16/...
根因 :langgraph-checkpoint@1.1.5 的 peer 依赖是 @langchain/core@^1.1.48,但锁文件把 core 固定在 1.1.16,该版本没有导出 utils/uuid。
解决:
powershell
pnpm update @langchain/core # 1.1.16 -> 1.2.9
验证:
powershell
node -e "import('@langchain/core/utils/uuid').then(m=>console.log('ok',typeof m.v5,typeof m.v6))"
再清一次 .next 缓存并重启。修复后 POST /api/chat 返回 400(参数校验)而不是 500(模块缺失),说明路由编译通过。
3.4 npm run build 的 Buffer 类型错误(重点)
现象 :npm run build 在 TypeScript 阶段失败:
python
./app/api/materials/route.ts:341:48
Type error: Argument of type 'Buffer' is not assignable to parameter of type 'BinaryLike'.
Type 'Buffer' is not assignable to type 'Uint8Array<ArrayBufferLike> | DataView<ArrayBufferLike>'.
Type 'SharedArrayBuffer' is missing the following properties from type 'ArrayBuffer': ...
类似错误全项目有几百处(凡是把 Buffer 传给 createHash、fs.writeFile、Blob、Response 的地方)。
根因 :@types/node 被锁在 20.0.0(很旧的版本),与 TypeScript 5.9 的 TypedArray/Buffer 类型定义不兼容。next dev 不做类型检查所以察觉不到,next build 会做全量类型检查直接失败。
解决:
package.json开发依赖改为:
json
"@types/node": "^22"
- 升级依赖:
powershell
pnpm update @types/node # 20.0.0 -> 22.20.1
- 验证(全量类型检查 0 错误 + 生产构建成功):
powershell
pnpm exec tsc --noEmit -p tsconfig.json
pnpm build
结果 :修复前 tsc 有 370+ 处 Buffer 相关错误,修复后 0 错误;pnpm build 完整通过(编译成功、TypeScript 通过、52 个页面/接口全部生成)。
!tip 判断技巧 只要看到
Type 'Buffer' is not assignable to ...且伴随SharedArrayBuffer is missing ...,基本就是@types/node与 TypeScript 版本不匹配,而不是业务代码写错。
3.5 端口占用
本机 3000 端口被 grafana 常驻占用,Next.js 会自动改用 3001,日志里会明确写:
arduino
⚠ Port 3000 is in use by process xxxx, using available port 3001 instead.
排查与验证:
powershell
Get-NetTCPConnection -LocalPort 3000 -State Listen | Select-Object LocalPort,OwningProcess
(Invoke-WebRequest -Uri http://localhost:3001 -UseBasicParsing).StatusCode
部署到服务器时建议把容器/进程端口绑定为 127.0.0.1:3000,对外只通过 Nginx 暴露 80/443。
3.6 .env.local 配置
powershell
copy .env.example .env.local
- 所有变量都"可选",但至少要有一个 LLM 服务商 Key,否则界面能开、生成课程必失败。
- 建议同时指定
DEFAULT_MODEL=provider:model,例如openai:gpt-5.5。 - 共享部署建议设置
ACCESS_CODE做站点级密码保护。 - 改完
.env.local建议重启pnpm dev/pnpm build后再访问。
各服务商 Key 与 TTS/ASR 是分开的变量族:
| 能力 | 变量前缀示例 |
|---|---|
| LLM 对话 | OPENAI_API_KEY、DEEPSEEK_API_KEY、XIAOMI_API_KEY ... |
| TTS 语音合成 | TTS_XIAOMI_API_KEY、TTS_MINIMAX_API_KEY ... |
| ASR 语音识别 | ASR_OPENAI_API_KEY、ASR_FUNASR_BASE_URL ... |
四、小米 MiMo TTS 接入经验
4.1 关键结论:MiMo TTS 不是 OpenAI 的 /audio/speech
官方接口形态(mimo.mi.com/docs):
- 请求:
POST https://api.xiaomimimo.com/v1/chat/completions - 认证头:
api-key: $MIMO_API_KEY(OpenAI SDK 的Authorization: Bearer同样可用) - 待合成文本放在
role: "assistant"的 message 里(不是 user) - 音频参数:
"audio": { "format": "wav", "voice": "mimo_default" } - 返回:base64 音频在
choices[0].message.audio.data
模型:mimo-v2.5-tts(预置音色)、mimo-v2.5-tts-voicedesign(文本设计音色)、mimo-v2.5-tts-voiceclone(音色复刻)。
预置音色: mimo_default、冰糖、茉莉、苏打、白桦、Mia、Chloe、Milo、Dean。
因此 :OpenMAIC 里"添加自定义语音合成(OpenAI 兼容)"按钮只支持 /audio/speech,对 MiMo 无效,必须写原生 provider。
4.2 新增原生 provider 的改动点
| 文件 | 改动 |
|---|---|
lib/audio/types.ts |
BuiltInTTSProviderId 增加 'xiaomi-tts' |
lib/audio/constants.ts |
注册 provider(模型 mimo-v2.5-tts、9 个音色、默认值、图标) |
lib/audio/tts-providers.ts |
新增 generateXiaomiMiMoTTS():chat/completions + audio 参数 + base64 解码 |
lib/server/provider-config.ts |
环境变量映射 TTS_XIAOMI / TTS_MIMO → xiaomi-tts |
lib/audio/provider-display.ts、lib/store/settings.ts、components/settings/tts-settings.tsx |
显示名、默认配置、接口路径提示 |
lib/i18n/locales/*.json(12 个) |
新增 providerXiaomiTTS 文案 |
.env.example / .env.local |
新增 TTS_XIAOMI_API_KEY、TTS_XIAOMI_BASE_URL 说明 |
4.3 配置与验证
env
TTS_XIAOMI_API_KEY=你的MiMo密钥
TTS_XIAOMI_BASE_URL=https://api.xiaomimimo.com/v1
# Token Plan 区域端点(按需替换):
# TTS_XIAOMI_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1
# TTS_XIAOMI_BASE_URL=https://token-plan-sgp.xiaomimimo.com/v1
# TTS_XIAOMI_BASE_URL=https://token-plan-ams.xiaomimimo.com/v1
TTS_MIMO_* 是 TTS_XIAOMI_* 的别名,两者等价。
验证:重启 → 设置 → 语音合成 → 选 "Xiaomi MiMo TTS" → 选音色 → 点试听。
!note 注意 已为对话模型配置的
XIAOMI_API_KEY不会 自动用于语音合成,必须单独配置TTS_XIAOMI_API_KEY。
五、复制项目给别人(不带密钥)
复制时排除个人密钥与运行期数据:
powershell
robocopy "G:\vibing\OpenMAIC-main" "G:\vibing\OpenMAIC-copy" /E `
/XD node_modules .next .codegraph data dist `
/XF .env.local .env.*.local *.tsbuildinfo
| 排除项 | 原因 |
|---|---|
.env.local |
含 API Key |
data/ |
运行期产生的本地数据 |
node_modules、.next、dist、*.tsbuildinfo |
构建产物/缓存,对方重新安装即可 |
复制后校验没有残留密钥(把 $key 换成你的 key 值,或从 .env.local 读取):
powershell
rg -l -F --hidden --no-ignore $key "G:\vibing\OpenMAIC-copy"
无输出即安全;最后打包 Compress-Archive 成 zip,并附上一份安装说明(.env.example 必须保留)。
六、部署要点(宝塔 + Nginx / Docker)
两套方案共用同一个架构:应用只监听 127.0.0.1:3000,Nginx 负责 80/443 与 SSL。
6.1 Docker 方案
bash
cd /www/wwwroot/openmaic
cp .env.example .env.local
# 国内网络加速
ALPINE_MIRROR=mirrors.tuna.tsinghua.edu.cn \
NPM_REGISTRY=https://registry.npmmirror.com \
docker compose up -d --build
curl http://127.0.0.1:3000/api/health
- 建议把
docker-compose.yml的端口改为'127.0.0.1:3000:3000',避免 3000 直接暴露公网。 - 需要服务端持久化(课程/会话入库)时加
--profile server-persistence,并在构建时 传入NEXT_PUBLIC_PERSISTENCE=1与一致的NEXT_PUBLIC_PERSISTENCE_TOKEN(编译期开关,写进浏览器 bundle)。 - 需要 MP4 导出时加
--profile video-export,该服务含 Chromium + FFmpeg,内存需要 ≥ 8GB,否则会自动降级为下载 ZIP。
!warning 持久化鉴权 项目内置的是开发级 token 认证,
NEXT_PUBLIC_PERSISTENCE_TOKEN会暴露在前端 JS 中,拿到即可读写所有学习者数据。只适合可信网络 +ACCESS_CODE的单用户部署;公开多用户必须自行替换为真正的会话鉴权。
6.2 Nginx + Node(不用 Docker)
bash
cd /www/wwwroot/openmaic
node -v # 必须 >= 22.19
npm i -g pnpm@10
pnpm install
NODE_OPTIONS=--max-old-space-size=3072 pnpm build
pnpm start
用 pm2 守护:
bash
npm i -g pm2
pm2 start pnpm --name openmaic -- start
pm2 save && pm2 startup
6.3 Nginx 反代配置要点
nginx
server_name maic.example.com;
client_max_body_size 200m; # 上传课程材料
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; # WebSocket / SSE 必需
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_buffering off; # 流式输出必需
}
安全组/防火墙只放行 80、443。
七、常用命令速查
powershell
# 启动 / 构建
pnpm dev
pnpm build
pnpm start
# 类型检查与 i18n 校验
pnpm exec tsc --noEmit -p tsconfig.json
node scripts/check-i18n-keys.mjs
# 单独构建 workspace 包
pnpm --filter @openmaic/renderer run build
pnpm --filter @openmaic/editor run build
# 清理缓存(先停 dev server)
node -e "require('fs').rmSync('.next',{recursive:true,force:true})"
# 健康检查
(Invoke-WebRequest http://localhost:3001/api/health -UseBasicParsing).Content

八、经验总结
- 先 pnpm install 再启动 ;
postinstall会构建全部子包,中断了就用pnpm --filter <包> run build补。 - pnpm-lock 是唯一依赖真相;不要用 npm 安装,不要手工改 node_modules 里的版本号。
- 改依赖前先停 dev server ,改完清一次
.next,能避开 Windows 文件锁和 Turbopack 陈旧缓存这两类"假故障"。 - dev 不查类型、build 才查类型 ;本地能跑不代表
pnpm build能过,交付前一定跑一次完整构建。 - 报错先看进程与端口(Port ... in use),再看依赖树,最后才怀疑业务代码。
- 区分编译期与运行时环境变量 :
NEXT_PUBLIC_*在构建时写进前端 bundle,改完必须重新 build,只改.env.local不生效。 - 第三方能力要核对真实协议 :MiMo TTS 表面是"OpenAI 兼容",实际走 chat/completions 的 audio 通道,套用通用 TTS 适配器必然失败。
