跳到主要内容

部署 Mastra TypeScript Agent 到 CloudBase 云托管

一句话定义:用 Mastra 1.x 在 TypeScript 里声明 agent + tool,mastra build 产出一个标准 Hono server,再写多阶段 Dockerfile,通过 tcb cloudrun deploy 把整套 agent 后端跑在 CloudBase 云托管上,并演示一个查询云数据库的 tool。

预计耗时:40 分钟 | 难度:进阶

适用场景

这一篇覆盖的场景:团队是 TypeScript 栈,想用一个有 tool calling、多 step、memory 的 agent 框架,但又不想被 Vercel / Cloudflare 这类平台 deployer 锁死,想把 agent 后端跑在自己可控的容器里。Mastra 提供了"自建 Hono server"的部署模式,正好和 CloudBase 云托管的 Docker 模型对得上。

  • 适用:TS 团队 + 需要 agent 框架(tool / memory / multi-step)
  • 适用:希望和 CloudBase 云数据库、对象存储、云函数走内网,避免跨云带宽
  • 适用:要自定义域名、要在境内备案、不想出海部署
  • 不适用:纯前端流式 chatbot 不带 tool,用 add-vercel-ai-sdk-streaming-chatbot 路径 C 即可
  • 不适用:只跑一次 LLM 调用、没有任何工具,用云函数足够,不必上 agent 框架

Mastra 和 Vercel AI SDK 的关系一句话:Vercel AI SDK 是底层 SDK(流式 + provider 抽象),Mastra 是在它之上封装的 agent 框架(agent / tool / workflow / memory),运行时还是 Hono。所以本篇思路也适用于"任何输出 Hono / Node server 的 agent 框架"。

环境要求

依赖版本
Node.js(本地开发 + 镜像内运行时)≥ 20(Mastra 1.x 要求)
@mastra/core^1.0(撰写时 stable 1.31.x,2026-01-20 起 GA)
mastra CLIlatest(全局或 npx)
@ai-sdk/openai^1.x(或换成混元/DeepSeek 兼容 provider)
@cloudbase/node-sdk^3.x(演示 tool 用,可选)
Docker(本地构建验证)latest
@cloudbase/clitcblatest
一个已开通的 CloudBase 环境含云托管能力

第一步:mastra create 初始化项目

npm install -g mastra
mastra create my-agent
cd my-agent

CLI 会问几个问题(选 TypeScript、选 OpenAI provider、是否带 example agent),都用默认即可。生成出来的目录大致是:

my-agent/
├── src/
│ └── mastra/
│ ├── index.ts # Mastra 实例入口,注册 agents / tools
│ └── agents/
│ └── my-agent.ts # agent 定义
├── package.json
├── tsconfig.json
└── .env # OPENAI_API_KEY 写这里

本地跑起来确认默认 agent 通:

mastra dev
# 默认 http://localhost:4111 起一个 playground,可以直接调 agent

mastra dev 是开发态服务器,热重载 + UI playground;线上用的是 mastra build 产物,两者不混用。

第二步:写 agent + tool

agent 定义(src/mastra/agents/my-agent.ts):

import { Agent } from "@mastra/core/agent";
import { openai } from "@ai-sdk/openai";
import { queryDb } from "../tools/query-db";

export const myAgent = new Agent({
name: "myAgent",
instructions: `你是 CloudBase 助手。当用户问数据库里的数据时,调用 queryDb tool 查询,然后用中文回答。不要编造数据。`,
model: openai("gpt-4o-mini"),
tools: { queryDb },
});

tool 定义(src/mastra/tools/query-db.ts)——演示一个查 CloudBase 云数据库的 tool:

import { createTool } from "@mastra/core/tools";
import { z } from "zod";
import tcb from "@cloudbase/node-sdk";

const app = tcb.init({
env: process.env.CLOUDBASE_ENV,
});

export const queryDb = createTool({
id: "query-db",
description: "查询 CloudBase 云数据库,返回某 collection 的前 N 条记录",
inputSchema: z.object({
collection: z.string().describe("collection 名,例如 users / orders"),
limit: z.number().int().min(1).max(50).default(10),
}),
outputSchema: z.object({
data: z.array(z.record(z.unknown())),
count: z.number(),
}),
execute: async ({ context }) => {
const { collection, limit } = context;
const res = await app.database()
.collection(collection)
.limit(limit)
.get();
return {
data: res.data ?? [],
count: res.data?.length ?? 0,
};
},
});

Mastra 实例入口(src/mastra/index.ts):

import { Mastra } from "@mastra/core";
import { myAgent } from "./agents/my-agent";

export const mastra = new Mastra({
agents: { myAgent },
});

几个写法上的硬约束:

  • inputSchema 必须用 zod,Mastra 把它转成 LLM tool calling 协议里的 JSON Schema,缺一个 description LLM 调用质量会肉眼可见地下降
  • execute 拿到的 context 就是 inputSchema 推导出来的 typed object,不需要再手动 parse
  • agent 的 instructions显式声明工具的使用条件,比 LLM 自己猜要稳得多
  • 不要把 tcb.init 写在 execute 里——每次调用都重新建连接,云数据库连接数会被打爆

第三步:mastra build 验证产物

mastra build

产物默认在 .mastra/output/ 下(具体路径以 CLI 输出为准),结构大致是:

.mastra/output/
├── index.mjs # Hono server 入口
├── package.json # 运行时依赖(已 prune)
└── ...

mastra build 干的事:把你的 agent 编译成一个 Hono ^4.6 + @hono/node-server ^1.13 的 production server,自动挂上 /api/agents/:agentId/generate 之类的 REST 路由。换句话说,build 完之后这就是个标准 Node.js HTTP server,不依赖 Mastra CLI 也能跑。

本地起一下确认产物能跑:

node .mastra/output/index.mjs
# 默认监听 4111,curl http://localhost:4111/api/agents/myAgent/generate 应有响应

如果这一步不通,后面 Docker 也跑不通,先在本地把这步搞定。

第四步:写多阶段 Dockerfile

项目根目录新建 Dockerfile

# ===== Stage 1: deps =====
FROM node:20-alpine AS deps
WORKDIR /app

COPY package.json package-lock.json* ./
RUN npm ci

# ===== Stage 2: builder =====
FROM node:20-alpine AS builder
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

RUN npx mastra build

# ===== Stage 3: runner =====
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0

# 创建非 root 用户运行
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 mastra

# 只复制 build 产物 + 运行时 node_modules
COPY --from=builder --chown=mastra:nodejs /app/.mastra/output ./
COPY --from=builder --chown=mastra:nodejs /app/node_modules ./node_modules

USER mastra

EXPOSE 3000

CMD ["node", "index.mjs"]

几个关键细节:

  • HOSTNAME=0.0.0.0 必须加,@hono/node-server 默认监听 127.0.0.1,容器外打不进来——和 Next.js standalone 一样的坑
  • PORT=3000 要和云托管创建服务时填的端口一致;Mastra 默认 4111,这里我们统一改 3000 对齐其他 recipe
  • Mastra 的运行入口只是个 Node.js 进程,所以 base image 用 node:20-alpine(≈ 50MB)就够,不要拉 node:20(≈ 400MB)
  • 没用 output: 'standalone' 那一套(这是 Next.js 的产物),Mastra 自己 prune 过依赖,直接 COPY node_modules 就行

本地验一下镜像:

docker build -t my-mastra-agent:local .
docker run -p 3000:3000 \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e CLOUDBASE_ENV=$CLOUDBASE_ENV \
my-mastra-agent:local
# 浏览器或 curl 打 http://localhost:3000/api/agents/myAgent/generate 测试

第五步:tcb cloudrun deploy 部署

tcb login
tcb cloudrun deploy --port 3000

CLI 会问:

  1. 选环境 ID
  2. 服务名(建议 my-agent / mastra-agent 之类)
  3. 是否启用公网访问(Web 端要调就选是)

CLI 把当前目录上传,云端用你的 Dockerfile build 镜像并部署,几分钟出结果。完成后控制台「云托管 → 服务 → 你的服务名」会显示默认域名 https://my-agent-xxxx.ap-shanghai.app.tcloudbase.com

环境变量必须在云托管控制台单独配,本地 .env 不会被打进镜像(也不应该)。在「服务设置 → 版本管理 → 新建版本」的环境变量区域加:

Key必填说明
OPENAI_API_KEYLLM 调用 key
CLOUDBASE_ENVtool 用到才填云数据库所在环境 ID
TENCENTCLOUD_SECRETID公网容器查私有云数据库时填走 secret 鉴权
TENCENTCLOUD_SECRETKEY同上不要写死在镜像里

云托管和云函数同环境时,@cloudbase/node-sdk 通常能直接走内网拿临时凭证,省掉 secret 配置;详见 secure-secrets-in-cloud-function

运行验证

# 1. 健康检查
curl -I https://my-agent-xxxx.ap-shanghai.app.tcloudbase.com/
# 预期: HTTP/2 200 或 404(Mastra 默认根路径不一定有响应,但 TCP 通即可)

# 2. 列出已注册的 agents
curl https://my-agent-xxxx.ap-shanghai.app.tcloudbase.com/api/agents

# 3. 调用 agent(无 tool)
curl -X POST https://my-agent-xxxx.ap-shanghai.app.tcloudbase.com/api/agents/myAgent/generate \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"你好,介绍一下你自己"}]}'

# 4. 调用 agent 触发 tool calling
curl -X POST https://my-agent-xxxx.ap-shanghai.app.tcloudbase.com/api/agents/myAgent/generate \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"查一下 users 集合里前 5 条数据"}]}'

第 4 步的返回里应该能看到 toolCalls 字段,记录了 LLM 触发了 queryDb tool 以及具体参数;如果没有,要么是 instructions 写得不够明确,要么模型选得不行(用 gpt-4o-mini 触发 tool 没问题,更小的模型可能力不从心)。

常见错误

错误信息原因修复
部署成功但访问 503 / 容器启动失败Hono server 监听了 127.0.0.1,端口暴露不出来Dockerfile 加 ENV HOSTNAME=0.0.0.0,与 --port 对齐
调用 agent 报 OpenAI API key not found401镜像里没 key,或 OPENAI_API_KEY 没注入云托管「服务设置 → 环境变量」加 OPENAI_API_KEY;不要写进 Dockerfile,那样会被打进镜像层
LibSQL 报 unable to open database file 或重启后历史对话全没了Mastra 默认存储是 LibSQL file:./mastra.db,容器内是临时文件,实例重启即丢生产换 PostgreSQL / MySQL:new Mastra({ storage: new PostgresStore({ connectionString: ... }) })
tool 调用报 tcb is not a functiondatabase is undefined@cloudbase/node-sdk 没装进 runtime,或 CLOUDBASE_ENV 没注入package.json 里把 @cloudbase/node-sdkdependencies 不是 devDependencies;环境变量加 CLOUDBASE_ENV
mastra buildCannot find module '@mastra/core'全局装了 mastra CLI 但项目内没装 @mastra/corenpm install @mastra/core 把它放本地依赖,mastra CLI 只是触发器
部署后调用 agent 一直 timeoutLLM 调用时长超过云托管默认请求超时(一般 60s)控制台「服务设置 → 高级配置」把请求超时调到 300s+;或在前端用流式(/stream endpoint 而不是 /generate
tool 触发了但参数全是 undefinedinputSchema 字段没写 .describe(),LLM 不知道怎么填每个 zod 字段加 .describe("中文说明"),命中率会显著提高
镜像体积巨大(>500MB)runner stage 里把整个 builder 都 COPY 过来了,没只 COPY .mastra/output严格按上面的 Dockerfile 写,runner 只 COPY 产物 + node_modules
mastra dev 本地能跑、mastra build 后跑不起来代码里写了 import.meta.url / 顶层 await 等只在 dev 工作的写法把这些移到 execute 函数体内或异步初始化里
多个实例间 agent memory 不一致默认 in-memory memory 是单实例的,水平扩容后每个实例独立配置 Mastra memory backend 到 PG / Redis,详见官方文档;或者强制单实例

构建阶段报错可以在云托管「服务详情 → 部署历史 → 查看日志」看到完整 npm / Docker build 输出。运行时报错在「服务详情 → 实时日志」里看 stdout/stderr。错误码对照见 https://docs.cloudbase.net/error-code/

相关文档