Skip to content

第 17 章 · 集成 React 与 Next.js

本章目标:把 Mastra Agent 接入 React/Next.js 应用:API 路由暴露流式接口,前端实现打字机效果对话 UI。

17.1 架构总览

text
React 组件 ──fetch──▶ Next.js API Route ──▶ Mastra Agent
   ▲                        │                  │
   └────── SSE 流式渲染 ◀───┴──────────────────┘

Mastra 不绑定特定前端库——你可以用 Vercel AI SDK 的 useChat、CopilotKit,或直接消费 Mastra Client SDK。

17.2 Next.js API 路由

typescript
// app/api/chat/route.ts(App Router)
import { mastra } from '@/mastra'
import { createUIMessageStreamResponse } from 'ai'

export async function POST(req: Request) {
  const { messages, threadId, resourceId } = await req.json()

  const agent = mastra.getAgent('support')
  // streamVNext 输出与 AI SDK 兼容的 UI 消息流
  const stream = await agent.streamVNext(messages, {
    format: 'aisdk',
    memory: {
      resource: resourceId,   // 用户标识
      thread: threadId,       // 会话标识
    },
  })

  return createUIMessageStreamResponse({ stream: stream.toUIMessageStream() })
}

17.3 前端 useChat 对接

tsx
// app/page.tsx
'use client'
import { useChat } from '@ai-sdk/react'

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit, status } = useChat({
    api: '/api/chat',
    // 携带会话信息以便服务端记忆
    body: { resourceId: 'user-123', threadId: 'thread-a' },
  })

  return (
    <div className="chat">
      {messages.map((m) => (
        <div key={m.id} className={m.role}>
          {/* 流式时 content 增量到达,天然打字机效果 */}
          {m.parts.map((p, i) =>
            p.type === 'text' ? <span key={i}>{p.text}</span> : null,
          )}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} disabled={status !== 'ready'} />
        <button type="submit" disabled={status !== 'ready'}>发送</button>
      </form>
    </div>
  )
}

17.4 工具调用可视化

AI SDK 消息的 parts 里包含工具事件,可渲染"正在查询订单…"这类中间状态:

tsx
{m.parts.map((p, i) => {
  if (p.type === 'tool-queryOrder') {
    return p.state === 'output-available'
      ? <Tag key={i}>✅ 已查到订单</Tag>
      : <Tag key={i}>⏳ 正在查询订单…</Tag>
  }
  if (p.type === 'text') return <span key={i}>{p.text}</span>
  return null
})}

17.5 其他集成路径

  • Mastra Client SDK——@mastra/client-js 直接从浏览器调用部署好的 Mastra server,无需自建路由;
  • CopilotKit——把 Mastra agent 注册为 Copilot 后端,获得现成聊天侧边栏与人工接管能力;
  • 任意框架——只要能发 fetch 并读 SSE,Vue/Svelte 同样适用。

CopilotKit 快速接入

若想要现成的聊天侧边栏与人工接管能力:

tsx
// app/layout.tsx —— 包一层 Provider 即可
'use client'
import { CopilotKit } from '@copilotkit/react-core'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <CopilotKit runtimeUrl="/api/copilotkit">
      {/* runtimeUrl 指向桥接 Mastra agent 的服务端路由 */}
      {children}
    </CopilotKit>
  )
}

本章小结

本章小结

  • 服务端用 streamVNext({ format: 'aisdk' }) 输出 AI SDK 兼容流;
  • 前端 useChat + body 透传 resource/thread 即可获得带记忆的多轮对话;
  • 工具调用以消息 parts 形式下发,可用于展示执行状态;
  • 浏览器直连可选 Mastra Client SDK 或 CopilotKit。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. streamVNext 的 format: "aisdk" 选项的作用是?

2. useChat 中 body 参数的作用是?

3. 前端如何得知工具已执行完毕?

4. 不想自建 Next.js API 路由时,浏览器直连 Mastra 的推荐方式是?

🛠️ 动手实践

  1. 完成 Next.js 示例并接入第 10 章的工具,让页面显示"正在查询订单…"中间态。
  2. 用两个浏览器标签模拟两个用户,验证 resource 隔离后互不串话。
  3. 把 API 路由换成 Mastra Client SDK 直连方案,对比两种方式的代码量。