Mastra 开发(11):部署 TS Agent 服务

QuibblerAgentQuibblerAgent 2026-11-11 约 20 分钟 5 次阅读

前 10 篇的 Agent 都活在 mastra dev 里:改代码热重载,Studio 里点一点就能验证。可 Agent 迟早要见真实用户——挂到公网、接上前端、扛住流量,这一步在框架教程里往往是空白。Python 版 Agent 系列第 29 篇讲过那边的部署运维,换到 TypeScript 阵营,问题换了一副面孔:Node 进程怎么守护、Docker 镜像怎么打、Serverless 能不能直接上,答案都带着 TS 生态自己的印记。

先给结论:Mastra 的生产形态是 mastra build 打出的一个自包含 Hono 服务器——一个目录、一份 node_modules,拷到任何装着 Node 的机器上 node index.mjs 就能跑。本文按"服务形态 → 构建产物 → 进程守护 → Docker 化 → 前端 API → Serverless 取舍 → 密钥管理 → 总结"组织,版本事实基于 2026 年 10 月官方文档逐一核实。


1、服务形态总览

第 1 篇核实过:Mastra 的服务端底座是 Hono。落到部署上,mastra build 会为你的 Mastra 实例生成一整套 REST 端点——每个 Agent 自动获得生成回复、流式输出、读写会话一组接口,Studio 的 playground 也由同一个服务器托管。这套自动生成的 API 面向通用客户端,联调够用,但直接暴露到公网有两个硬伤:默认没有鉴权,路径与业务语义也对不上。

所以生产形态有两条路。路线 A:直接部署生成的服务器,鉴权与限流交给网关层(Nginx、API 网关)做;路线 B:自己包一层 Hono,把 Mastra 实例嵌进去,鉴权、流式格式、路由语义全部自己定义——官方参考里为这种场景留了 MastraServer 适配器抽象,Hono 与 Express 之外还能扩展其他框架。整体形态如下:

flowchart TD A["前端调用方"] -->|"HTTPS"| B["Nginx / API 网关<br>TLS 终结、限流"] B --> C["Node 常驻进程<br>pm2 或 systemd 守护"] C --> D["index.mjs 入口<br>Hono 服务 :4111"] D --> E["/api/agents<br>自动生成 REST 端点"] D --> F["/api/chat<br>自包业务路由"] D --> G["storage / memory<br>LibSQL 或 Postgres"]

图里最关键的分界线是中间那层 Node 常驻进程:Serverless 平台打不破这层(第 6 节展开),而存储落在哪里决定了能不能水平扩容(第 4 节展开)。官方对运行时要求宽松,Node.js、Bun、Deno 都支持,Node 建议 22.13 及以上;产物可以落到任意 VM、容器或 PaaS 平台,不绑定特定云厂商,细节见部署总览。

注意:自动生成的端点默认无鉴权。哪怕只是测试环境,也别把 4111 端口直接暴露到公网。

2、构建与运行

开发态用 mastra dev(热重载、贴着 Studio),生产换成 mastra build:把 src/mastra 下的 agents、workflows、tools 连同依赖一起打成 Hono 服务器。产物结构节选如下:

.mastra/output/
├── index.mjs       # Hono 服务器入口,node 直接运行它
├── mastra.mjs      # 你的 Mastra 实例(agents / workflows)
├── tools.mjs       # 工具注册表
├── package.json    # 依赖清单
├── node_modules/   # 自带依赖
├── public/
└── playground/     # Studio 界面(可选打包)

两个要点。其一,产物自包含:output 目录带了自己的 node_modules,整个目录拷到目标机即可运行,不需要重新 npm install。其二,index.mjs 本身就是可独立运行的 Hono 服务器,启动只要一条命令:

npx mastra build                # 打包,产物在 .mastra/output/
npx mastra start                # 官方启动器,自动读 .env.production / .env
node .mastra/output/index.mjs   # 或不依赖 CLI,直接用 node 启动

mastra start 与裸 node 的差别在细节:前者会优先加载 .env.production 再兜底 .env,并统一处理进程信号;后者胜在零依赖,适合塞进极简容器。端口用 PORT 环境变量控制(默认 4111),也可以在 Mastra 配置的 server: { port } 里构建期写死,见服务器部署文档。

优雅退出是内置的:收到 SIGINT 或 SIGTERM,进程先排空在途请求再退出,宽限期 drainTimeout 默认只有 5 秒——Agent 请求动辄几十秒,建议像 server: { drainTimeout: 240_000 } 这样调大。等不及的话再发一次信号会立即强杀,部署脚本里"停不住就补一刀"是安全的。想让 Studio 跟服务一起上线,构建时加 --studio 即可,命令细节可查 CLI 参考。

3、进程守护

裸起的 node 进程挂了就没了,生产必须配守护。TS 生态两条主流路:pm2 与 systemd。

3.1、pm2 守护

pm2 是 Node 圈的事实标准:进程崩溃自动拉起、内存超限自动重启、日志管理、多实例 cluster 一把梭。推荐用配置文件而非命令行参数,跟着代码进仓库:

module.exports = {
  apps: [
    {
      name: 'mastra-api',
      script: '.mastra/output/index.mjs', // 直接跑构建产物
      env: { NODE_ENV: 'production', PORT: 4111 },
      max_memory_restart: '512M',        // 内存超限自动重启
      instances: 1,                      // LibSQL 文件存储别开多实例
    },
  ],
}

pm2 start ecosystem.config.js 起服务,pm2 logs mastra-api 看日志,pm2 save && pm2 startup 做开机自启。注释里那句提醒是存储耦合问题:LibSQL 是嵌入式文件库(第 5 篇),多个进程写同一个文件会锁冲突——要么单实例,要么把 storage 换成 Postgres 再谈扩容。这也是为什么配置里 instances 明确写 1 而不是 "max":cluster 模式开出来的每个副本都会抢同一个 mastra.db 文件,症状是随机报错与记忆丢失,非常难排查。

3.2、systemd 守护

只有一台服务器、不想多装任何东西,systemd 是零依赖方案——Linux 发行版自带,一个 unit 文件解决:

[Unit]
Description=Mastra API Server
After=network.target

[Service]
User=mastra
WorkingDirectory=/srv/mastra
ExecStart=/usr/bin/node .mastra/output/index.mjs
Environment=PORT=4111
EnvironmentFile=/etc/mastra/env        # 密钥集中放这里,权限 600
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

systemctl enable --now mastra 启用,日志走 journalctl -u mastra -f,不需要额外配置日志轮转。怎么选:多语言项目混布、要用 cluster 吃满多核,选 pm2;追求最少依赖、运维只认系统标准工具,选 systemd。两者效果等价——挂了拉起、开机自启、日志可查。

4、Docker 化

容器化的价值在可复制:镜像锁死 Node 版本与依赖,从测试机到云主机行为一致。产物自包含让 Dockerfile 可以写得很薄——多阶段构建,构建阶段跑 mastra build,运行阶段只带走 output:

FROM node:22-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx mastra build               # 产物在 .mastra/output/

FROM node:22-slim
WORKDIR /app
ENV NODE_ENV=production PORT=4111
COPY --from=build /app/.mastra/output ./
VOLUME /app/data                   # LibSQL 数据文件落这里
EXPOSE 4111
CMD ["node", "index.mjs"]

镜像里刻意不放任何密钥文件,.dockerignore 至少排除三样:.env、node_modules、.mastra——前一个是密钥泄漏,后两个是让构建阶段自己生成,避免把本机旧产物带进镜像。启动时挂卷、注入密钥:docker run -d -p 4111:4111 -v mastra-data:/app/data -e ANTHROPIC_API_KEY=... mastra-api。

LibSQL 场景务必把数据库文件指到 /app/data——在 mastra/index.ts 里让 LibSQLStore 的 url 读 DATABASE_URL 环境变量,容器里传 file:/app/data/mastra.db。落在容器可写层的话,容器一删、记忆清零。用 Postgres 反而更省心:不挂卷,DATABASE_URL 换成连接串即可,存储与容器生命周期天然解耦,也是后面扩多实例的前提。

5、前端 API 设计

自动生成的端点是"框架视角":路径带 agentId、入参是标准消息体,适合通用客户端联调。给真实前端用,建议按"业务视角"自己包一层(第 1 节路线 B):鉴权中间件挡在最外层,路由里绑定会话线程,流式响应用 SSE。核心实现:

import { Hono } from 'hono'
import { mastra } from './mastra/index'

export const app = new Hono()

// Bearer 鉴权:没带 key 或 key 不对,一律 401
app.use('/api/*', async (c, next) => {
  const token = c.req.header('Authorization')
  if (token !== `Bearer ${process.env.API_KEY}`) {
    return c.json({ error: 'unauthorized' }, 401)
  }
  await next()
})

// 流式对话:threadId 由前端生成并在每次请求带上
app.post('/api/chat', async (c) => {
  const { threadId, message } = await c.req.json()
  const agent = mastra.getAgent('ragAgent')
  const result = agent.stream(message, {
    threadId,                         // 同一 threadId = 同一段多轮记忆
    resourceId: 'app-user',           // 用户 / 租户级隔离
  })
  return result.toTextStreamResponse() // SSE 流,前端边收边渲染
})

三个设计点。其一,threadId 就是会话管理的全部:第 5 篇讲过,Mastra 的 memory 挂在线程上,前端首条消息生成一个 UUID 存本地,之后每轮带上,服务端保持零状态、多轮记忆自动续接。其二,toTextStreamResponse() 把 Agent 输出直接转成 SSE 响应;要更细的事件粒度(工具调用开始与结束),改用 UIMessage 流,或自己遍历 fullStream 组装事件。其三,示例里的 Bearer 是演示级,生产应换成 JWT 或网关层鉴权——官方 2026 年发布了 @mastra/core/auth 插拔式鉴权组件,要把鉴权做进生成的服务器时可以直接复用。

还有个部署侧的坑要提前踩:SSE 经过 Nginx 反代时默认会被缓冲,表现是前端收不到逐字流、憋到最后一次性吐完。给这层路由加 proxy_buffering off;(或让响应头带 X-Accel-Buffering: no),流式才真正逐块到达。函数平台同理,不少网关对流式响应有自己的缓冲策略,上线前用 curl 实测一遍首字延迟。

6、Serverless 取舍

Serverless 对 Agent 服务诱惑不小:免运维、按量付费、自动缩容。但长任务 Agent 与函数模型天生不合,三个硬约束先看清,时长上限是第一个:

平台 时长限制 对 Agent 的含义
AWS Lambda 单次调用最长 15 分钟 多步工具循环容易被掐断
Vercel Functions maxDuration 最高 800 秒 比 Lambda 更紧,长回复有风险
Cloudflare Workers 墙上时钟不限,CPU 时间默认 30 秒、可调高 等 LLM 响应不算 CPU,但重计算会撞墙

冷启动是第二重税:Node 依赖树加载,加上你的工具代码与知识库索引,冷启动从几百毫秒到数秒不等,交互式 Agent 的首字延迟直接翻倍。第三重是无状态:函数的文件系统是临时的,本地 LibSQL 文件根本不成立,storage 必须整体换成 Postgres 这类外部服务。Workers 的具体限额以官方限制文档为准。

Python 系列第 29 篇的结论在 TS 侧原样成立:短任务可以上,长 Agent 别硬塞。单轮问答、一次检索增强回答、批量 embedding 这类秒级任务,Vercel 与 Cloudflare 都合适,Mastra 也内置了这两家的 deployer 支持一键发布。反过来,几十步的 workflow、human-in-the-loop、长时间 SSE 连接,请回到第 3、4 节的常驻进程加容器路线;要弹性伸缩,Kubernetes 上还有官方 Helm Chart。真有长任务又想享受弹性,正路是改架构——队列加常驻 worker,或用工作流引擎做步进恢复——而不是指望函数平台放宽时限。

经验法则:一次调用撑不过 1 分钟的才考虑 Serverless;要挂长连接、要长记忆、要跑几十步的,直接常驻。

7、密钥与环境管理

开发期一切密钥在 .env:模型 API key、存储连接串、自设的 API_KEY。生产的第一条纪律是密钥只经环境变量注入,具体三条规矩:

  • .env 进 .gitignore 与 .dockerignore,仓库与镜像里都不出现
  • 服务器上密钥文件权限收到 600,用 mastra start 时靠 .env.production 加载
  • 容器与云平台用各自的 secret 机制:docker run -e、systemd 的 EnvironmentFile、云厂商 secret 管理服务

按环境拆 key 是低成本高收益的动作:开发、预发、生产各一套模型 key,泄漏时影响面小,轮换也不用全量停机。镜像不含密钥还有个连带好处:制品仓库可以放心共享,密钥只在编排层注入——compose 的 env_file、Kubernetes 的 Secret、云平台的 secret 管理各归其位。最后一条日志纪律:别指望平台兜底——你代码里 console.log 出去的明文 key,日志采集会原样带走。

8、总结

回顾全文路线:形态上,Mastra 生产端是 mastra build 打出的自包含 Hono 服务器,默认 4111 端口,直接部署或自己包一层 Hono 皆可;运行上,pm2 与 systemd 二选一守护,Docker 多阶段构建十几行搞定,存储用 LibSQL 要挂卷、用 Postgres 只需连接串;接口上,鉴权中间件加 threadId 会话加 SSE 流式,是给前端的最小完备组合;Serverless 只留给短任务,长 Agent 老实常驻;密钥只经环境变量注入,按环境隔离与轮换。

练习(承接第 10 篇的 RAG Agent):

  1. 本地跑 npx mastra build,用 node .mastra/output/index.mjs 起服务,curl 打通一个端点
  2. 写出第 4 节的 Dockerfile,挂数据卷启动,验证重启容器后 thread 记忆仍在
  3. 加分项:把 storage 换成 Postgres 连接串重跑,体会存储外置后多实例才可行

对于已经用 Mastra 写完 Agent 的团队,建议本周就把 build → Docker → 守护这条链路走通——它不改任何业务代码,却是从 demo 到产品的最后一块板子。

相关推荐

获取系统SDK版本、判断手机ROM
精选Code

获取系统SDK版本、判断手机ROM

Build获取系统SDK版本Android中部分API的使用,需要在特定的SDK版本之后才能使用,因此在兼容老版本SDK的时候,经常需要判断API的版本。各种Android版本的对应关系参考《Android各版本对应的SDK版本》判断手机ROM有时候需要判断手机系统的ROM,检测ROM是MIUI、EMUI还是Flyme,可以使用getprop命令,去系统build.prop文件(关于build.p

4.2k
AndroidStudio中各种中文乱码问题
精选Code

AndroidStudio中各种中文乱码问题

AndroidStudio中各种中文乱码问题1、编译Java错误信息乱码在出现这个Annotation processors must be explicitly declared now...问题的时候,正好也发现AndroidStudio Build Output错误信息都乱码。经常遇到各种问题,习惯了,现在遇到的问题,都是以后的答案。 1.1、修改项目build.gradle(无效)在整个p

2.8k
Multi-Agent 详解:多智能体协作架构与实践
精选Agent

Multi-Agent 详解:多智能体协作架构与实践

一个智能体配上一套工具,已经能完成相当多任务。但让它同时“当产品经理、写后端、跑测试、做代码评审”,很快就会露馅:上下文里塞满了互相干扰的指令,十几件工具的清单来回切换,任务做到一半忘了开头。解法看起来很自然——像人类团队一样分工:让多个智能体各司其职,由一个协调者调度,这就是 Multi-Agent(多智能体)系统。它也是 2026 年智能体工程里讨论度最高、误用率也最高的架构。本文按“架构模式…

122