跳到主要内容

WhatsApp 聊天 bot

本指南演示如何使用 Mastra Agent 和 Workflow 创建 WhatsApp 聊天 bot。该 bot 通过 webhook 接收传入的 WhatsApp 消息,使用 AI Agent 进行处理,将回答拆分成自然的文本消息,再通过 WhatsApp Business API 发回。

前提条件
前提条件的直接链接

本示例需要设置 WhatsApp Business API,并使用 anthropic 模型。请将以下环境变量添加到 .env 文件:

.env
ANTHROPIC_API_KEY=<your-anthropic-api-key>
WHATSAPP_VERIFY_TOKEN=<your-verify-token>
WHATSAPP_ACCESS_TOKEN=<your-whatsapp-access-token>
WHATSAPP_BUSINESS_PHONE_NUMBER_ID=<your-phone-number-id>
WHATSAPP_API_VERSION=v22.0

创建 WhatsApp client
创建 WhatsApp client的直接链接

此 client 负责通过 WhatsApp Business API 向用户发送消息。

src/whatsapp-client.ts
// Simple WhatsApp Business API client for sending messages

interface SendMessageParams {
to: string
message: string
}

export async function sendWhatsAppMessage({ to, message }: SendMessageParams) {
// Get environment variables for WhatsApp API
const apiVersion = process.env.WHATSAPP_API_VERSION || 'v22.0'
const phoneNumberId = process.env.WHATSAPP_BUSINESS_PHONE_NUMBER_ID
const accessToken = process.env.WHATSAPP_ACCESS_TOKEN

// Check if required environment variables are set
if (!phoneNumberId || !accessToken) {
return false
}

// WhatsApp Business API endpoint
const url = `https://graph.facebook.com/${apiVersion}/${phoneNumberId}/messages`

// Message payload following WhatsApp API format
const payload = {
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: to,
type: 'text',
text: {
body: message,
},
}

try {
// Send message via WhatsApp Business API
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(payload),
})

const result = await response.json()

if (response.ok) {
console.log(`✅ WhatsApp message sent to ${to}: "${message}"`)
return true
} else {
console.error('❌ Failed to send WhatsApp message:', result)
return false
}
} catch (error) {
console.error('❌ Error sending WhatsApp message:', error)
return false
}
}

创建聊天 Agent
创建聊天 Agent的直接链接

此 Agent 以友好、自然的交流风格处理主要对话逻辑。

src/mastra/agents/chat-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { LibSQLStore } from '@mastra/libsql'

export const chatAgent = new Agent({
id: 'chat-agent',
name: 'Chat Agent',
instructions: `
You are a helpful, friendly, and knowledgeable AI assistant that loves to chat with users via WhatsApp.

Your personality:
- Warm, approachable, and conversational
- Enthusiastic about helping with any topic
- Use a casual, friendly tone like you're chatting with a friend
- Be concise but informative
- Show genuine interest in the user's questions

Your capabilities:
- Answer questions on a wide variety of topics
- Provide helpful advice and suggestions
- Engage in casual conversation
- Help with problem-solving and creative tasks
- Explain complex topics in simple terms

Guidelines:
- Keep responses informative but not overwhelming
- Ask follow-up questions when appropriate
- Be encouraging and positive
- If you don't know something, admit it honestly
- Adapt your communication style to match the user's tone
- Remember this is WhatsApp, so keep it conversational and natural

Always aim to be helpful while maintaining a friendly, approachable conversation style.
`,
model: 'openai/gpt-5.6-sol',
memory: new Memory({
storage: new LibSQLStore({
id: 'agent-storage',
url: 'file:../mastra.db',
}),
}),
})

创建文本消息 Agent
创建文本消息 Agent的直接链接

此 Agent 会将较长的回答转换成适合 WhatsApp 的自然、短小文本消息。

src/mastra/agents/text-message-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { LibSQLStore } from '@mastra/libsql'

export const textMessageAgent = new Agent({
id: 'text-message-agent',
name: 'Text Message Agent',
instructions: `
You are a text message converter that takes formal or lengthy text and breaks it down into natural, casual text messages.

Your job is to:
- Convert any input text into 5-8 short, casual text messages
- Each message should be 1-2 sentences maximum
- Use natural, friendly texting language (contractions, casual tone)
- Maintain all the important information from the original text
- Make it feel like you're texting a friend
- Use appropriate emojis sparingly to add personality
- Keep the conversational flow logical and easy to follow

Think of it like you're explaining something exciting to a friend via text - break it into bite-sized, engaging messages that don't overwhelm them with a long paragraph.

Always return exactly 5-8 messages in the messages array.
`,
model: 'openai/gpt-5.6-sol',
memory: new Memory({
storage: new LibSQLStore({
id: 'agent-storage',
url: 'file:../mastra.db',
}),
}),
})

创建聊天 Workflow
创建聊天 Workflow的直接链接

此 Workflow 会编排完整的聊天过程:生成回答、将其拆分成多条消息,并通过 WhatsApp 发送。

src/mastra/workflows/chat-workflow.ts
import { createStep, createWorkflow } from '@mastra/core/workflows'
import { z } from 'zod'
import { sendWhatsAppMessage } from '../../whatsapp-client'

const respondToMessage = createStep({
id: 'respond-to-message',
description: 'Generate response to user message',
inputSchema: z.object({ userMessage: z.string() }),
outputSchema: z.object({ response: z.string() }),
execute: async ({ inputData, mastra }) => {
const agent = mastra?.getAgent('chatAgent')
if (!agent) {
throw new Error('Chat agent not found')
}

const response = await agent.generate([{ role: 'user', content: inputData.userMessage }])

return { response: response.text }
},
})

const breakIntoMessages = createStep({
id: 'break-into-messages',
description: 'Breaks response into text messages',
inputSchema: z.object({ prompt: z.string() }),
outputSchema: z.object({ messages: z.array(z.string()) }),
execute: async ({ inputData, mastra }) => {
const agent = mastra?.getAgent('textMessageAgent')
if (!agent) {
throw new Error('Text Message agent not found')
}

const response = await agent.generate([{ role: 'user', content: inputData.prompt }], {
structuredOutput: {
schema: z.object({
messages: z.array(z.string()),
}),
},
})

if (!response.object) throw new Error('Error generating messages')

return response.object
},
})

const sendMessages = createStep({
id: 'send-messages',
description: 'Sends text messages via WhatsApp',
inputSchema: z.object({
messages: z.array(z.string()),
userPhone: z.string(),
}),
outputSchema: z.object({ sentCount: z.number() }),
execute: async ({ inputData }) => {
const { messages, userPhone } = inputData

console.log(`\n🔥 Sending ${messages.length} WhatsApp messages to ${userPhone}...`)

let sentCount = 0

// Send each message with a small delay for natural flow
for (let i = 0; i < messages.length; i++) {
const success = await sendWhatsAppMessage({
to: userPhone,
message: messages[i],
})

if (success) {
sentCount++
}

// Add delay between messages for natural texting rhythm
if (i < messages.length - 1) {
await new Promise(resolve => setTimeout(resolve, 1000))
}
}

console.log(`\n✅ Successfully sent ${sentCount}/${messages.length} WhatsApp messages\n`)

return { sentCount }
},
})

export const chatWorkflow = createWorkflow({
id: 'chat-workflow',
inputSchema: z.object({ userMessage: z.string() }),
outputSchema: z.object({ sentCount: z.number() }),
})
.then(respondToMessage)
.map(async ({ inputData }) => ({
prompt: `Break this AI response into 3-8 casual, friendly text messages that feel natural for WhatsApp conversation:\n\n${inputData.response}`,
}))
.then(breakIntoMessages)
.map(async ({ inputData, getInitData }) => {
// Parse the original stringified input to get user phone
const initData = getInitData<typeof chatWorkflow>()
const webhookData = JSON.parse(initData.userMessage)
const userPhone = webhookData.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.from || 'unknown'

return {
messages: inputData.messages,
userPhone,
}
})
.then(sendMessages)

chatWorkflow.commit()

设置 Mastra 配置
设置 Mastra 配置的直接链接

使用 Agent、Workflow 和 WhatsApp webhook 端点配置 Mastra 实例。

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { registerApiRoute } from '@mastra/core/server'
import { PinoLogger } from '@mastra/loggers'
import { LibSQLStore } from '@mastra/libsql'

import { chatWorkflow } from './workflows/chat-workflow'
import { textMessageAgent } from './agents/text-message-agent'
import { chatAgent } from './agents/chat-agent'

export const mastra = new Mastra({
workflows: { chatWorkflow },
agents: { textMessageAgent, chatAgent },
storage: new LibSQLStore({
id: 'agent-storage',
url: ':memory:',
}),
logger: new PinoLogger({
name: 'Mastra',
level: 'info',
}),
server: {
apiRoutes: [
registerApiRoute('/whatsapp', {
method: 'GET',
handler: async c => {
const verifyToken = process.env.WHATSAPP_VERIFY_TOKEN
const {
'hub.mode': mode,
'hub.challenge': challenge,
'hub.verify_token': token,
} = c.req.query()

if (mode === 'subscribe' && token === verifyToken) {
return c.text(challenge, 200)
} else {
return c.status(403)
}
},
}),
registerApiRoute('/whatsapp', {
method: 'POST',
handler: async c => {
const mastra = c.get('mastra')
const chatWorkflow = mastra.getWorkflow('chatWorkflow')

const body = await c.req.json()

const workflowRun = await chatWorkflow.createRun()
const runResult = await workflowRun.start({
inputData: { userMessage: JSON.stringify(body) },
})

return c.json(runResult)
},
}),
],
},
})

测试聊天 bot
测试聊天 bot的直接链接

你可以通过模拟 WhatsApp webhook payload,在本地测试聊天 bot。

src/test-whatsapp-bot.ts
import 'dotenv/config'

import { mastra } from './mastra'

// Simulate a WhatsApp webhook payload
const mockWebhookData = {
entry: [
{
changes: [
{
value: {
messages: [
{
from: '1234567890', // Test phone number
text: {
body: 'Hello! How are you today?',
},
},
],
},
},
],
},
],
}

const workflow = mastra.getWorkflow('chatWorkflow')
const workflowRun = await workflow.createRun()

const result = await workflowRun.start({
inputData: { userMessage: JSON.stringify(mockWebhookData) },
})

console.log('Workflow completed:', result)

示例输出
示例输出的直接链接

当用户向 WhatsApp bot 发送“你好!今天过得怎么样?”时,它可能会返回多条类似下面的消息:

Hey there! 👋 I'm doing great, thanks for asking!

How's your day going so far?

I'm here and ready to chat about whatever's on your mind

Whether you need help with something or just want to talk, I'm all ears! 😊

What's new with you?

bot 会通过 Memory 保留对话上下文,并提供符合 WhatsApp 消息交流习惯的自然回复。