把 LangGraph Agent 集成进 vps-monitor:Next.js 生产环境踩过的三个坑
自建 VPS 的监控面板(Next.js + PM2,3003 端口)这次加了个新功能:把 LangGraph 诊断 Agent 的结果以卡片形式显示出来。Agent 本体已经在 /opt/langgraph-agent/swarm_quick6.py 跑通,这次补的是「Agent 写 JSON → Next.js 读 JSON → 页面显示」这一段。
整体结构
- Agent:Python + LangGraph,5 个只读工具,带 tracing 和 token 预算
- 输出:结果写到 /root/vps-monitor/data/latest-diagnosis.json
- API:/api/langgraph-diagnosis/route.ts 读文件返回 JSON
- UI:LangGraphDiagnosisCard 组件放在 VPS Monitor 标签里
- 进程管理:PM2(pnpm start = next start -p 3003)
坑一:pm2 restart 不会重新 build
加完 API 路由,pm2 restart vps-monitor,然后 curl http://127.0.0.1:3003/api/langgraph-diagnosis,返回的不是 JSON,是 <!DOCTYPE html>。原因很直接:PM2 跑的是 next start(生产模式),它只服务 .next/ 里已有的构建产物,新加的 route.ts 如果没 build 过,压根不在产物里,请求就落到 fallback 的页面渲染上了。
解决就是补一步 build:
cd /root/vps-monitor
pnpm build
/root/.nvm/versions/node/v24.21.0/bin/pm2 restart vps-monitor
build 后的路由表里出现 ƒ /api/langgraph-diagnosis 就说明进产物了。以后改 API 路由或 page.tsx,必须记住 build → restart 两步走,pm2 restart 只是重启进程,不是重新编译。
坑二:401 不是代码问题,是全局 Basic Auth
curl API 返回 HTTP 401 Authentication required,但 route.ts 里一行认证代码都没有。查下来是 src/middleware.ts 里有全局 Basic Auth,matcher 写的是 /((?!_next/static|_next/image|favicon.ico).*),几乎匹配所有路径。/api/vps、/api/packages 也一样 401,所以能确定不是新 API 特有的问题。
curl 带上认证就正常:
curl -s -u admin:admin http://127.0.0.1:3003/api/langgraph-diagnosis
浏览器里弹出 Basic Auth 对话框输入账号密码也能正常看。顺带一提,这个 middleware 里 admin/admin 是硬编码的,属于后续要单独处理的弱口令问题,不在这次范围内。
坑三:空数据把整个页面搞崩了
卡片一放上去,页面直接变成「This page couldn’t load」。当时 API 还没数据,返回的是 {"status":"no_data","summary":"..."},没有 ts、token_used 这些字段。卡片组件里无条件访问这些字段,客户端直接运行时错误,整个 React 树挂掉。
排查方法很简单:把那一行 JSX 注释掉重新 build,页面恢复就说明是卡片的问题。
修复思路是换成防御式写法,data 是 null、{} 还是 status:”no_data” 都能安全渲染,所有字段用 typeof 守卫:
const noData = !data || data.status === "no_data" || (!data.summary && !data.ts);
const used = typeof data?.token_used === "number" ? data.token_used : 0;
const max = typeof data?.token_max === "number" ? data.token_max : 0;
这样没数据时显示「还没有诊断记录」的占位文案,有数据就显示摘要和 token 用量。另外加了 60 秒自动重新拉取,Agent 的 cron 跑完之后不用手动刷新页面就能看到新内容。
最终状态
- Agent 写 JSON:已验证,API 能返回真实 summary
- API 路由:返回 JSON,不再是 HTML
- 卡片组件:空 / 加载 / 错误三种状态都有兜底
- page.tsx 集成:第 9 行 import,第 246 行 JSX
- build / PM2:路由进产物,进程 online
- 浏览器显示:已确认
总结三点
- Next.js 生产模式下忘了 build,新路由就是不存在,pm2 restart 是重启不是重新编译。
- 遇到 401 先看 middleware.ts 和 nginx 的认证配置,拿现有 API 对比一下能快速定位。
- 显示外部数据的组件,先把「没有数据」的状态设计好,空数组、null、错误对象三种情况都想到,能避免上线时整个页面被一个卡片拖垮。
