将 OpenAI Agents SDK 智能体接入 WhatsApp
将号码的 Webhook 目标设置为您的 Python 应用中的一个路由。每收到一条消息,就以发送者的号码作为会话键,用 Runner.run 运行智能体,然后用该号码的访问令牌和 phone number id,通过 Cloud API 把 result.final_output 发回去。WhatsApp Business 应用在同一号码上继续可用。
SDK 内置的会话,可基于文件或存于内存,按会话 id 记住之前的对话轮次。
MCPServerStreamableHttp 在此接受 httpx.Auth 或 Authorization 标头,用于服务器验证。
Meta 当前发送消息示例所使用的 Graph API 版本。
WhatsApp 消息如何变成一次智能体运行?
通过您自己应用中的一个路由,由 Meta 来调用它。把号码的 Webhook 目标设置为该路由,并在路径中加入一段较长的随机密钥。该路由对 Meta 的验证 GET 请求,以纯文本返回 hub.challenge;对每个 POST 请求,立即响应 200 并把任务放入队列,因为 Meta 会重试响应过慢的请求,否则智能体会被运行两次。
随后由 worker 从负载中读取发送者和文本,并调用 Runner.run。传入一个会话,其 id 就是发送者的号码:SDK 会把对话存储在该 id 之下,所以同一位客户的下一条消息会接着上一条继续,而两位客户永远不会共用同一段历史。起步阶段用 SQLiteSession 就足够;若有多个 worker,SDK 也提供 Redis 和 SQLAlchemy 会话。
这里的验证靠的是 URL 中的密钥。Meta 使用 Tech Provider 的应用密钥对投递内容签名,因此客户无法验证签名标头。
import os, httpx
from agents import Agent, Runner, SQLiteSession
agent = Agent(name="Front desk", instructions="Answer briefly and politely.")
URL = f"https://graph.facebook.com/v25.0/{os.environ['PHONE_NUMBER_ID']}/messages"
async def handle(sender: str, text: str) -> None:
session = SQLiteSession(sender, "whatsapp.db")
result = await Runner.run(agent, text, session=session)
async with httpx.AsyncClient() as http:
await http.post(
URL,
headers={"Authorization": f"Bearer {os.environ['WA_TOKEN']}"},
json={"messaging_product": "whatsapp", "to": sender,
"type": "text", "text": {"body": result.final_output}},
)为什么应由您的代码而不是智能体来发送回复?
因为必须绝不违反的那条规则是时间规则,而代码比提示词更能可靠地执行它。客户发来消息时,Meta 会开启一个 24 小时的客户服务窗口。窗口内可以发送自由文本;窗口外只能发送已批准的消息模板,自由文本会返回错误 131047。
回复刚刚收到的那条消息的 worker 始终处在窗口之内,所以把 result.final_output 作为文本发送,从结构上就是安全的。稍后才发生的工作,例如次日早上的跟进或提醒,应走另一条路径,发送消息模板,绝不发送自由文本。
智能体仍然可以使用工具。给它函数工具用于查询资料,例如订单或预约,而把 WhatsApp 的发送留在 worker 中。这样还能让访问令牌远离模型可以用任意参数调用的任何东西。
智能体可以使用 EasyCoexistence MCP 服务器吗?
可以,通过 MCPServerStreamableHttp,但设置比加一个标头要多一些。SDK 接受 Authorization 标头,或 params["auth"] 中的 httpx.Auth。EasyCoexistence MCP 服务器会在浏览器登录后签发自己的 OAuth 令牌,采用动态客户端注册和 PKCE,因此不存在静态密钥。MCP Python SDK 提供 OAuthClientProvider,这是一个 HTTP 验证处理器,负责执行该流程,包括 PKCE 和令牌刷新,把它放进 params["auth"] 即可。
当您希望智能体自行决策时,这样做是值得的:检查号码的连接状态、读取消息模板状态,或根据工具描述中的规则在 send_message 和 send_template 之间做选择。如果只是做一个只负责应答的前台,上面的 worker 方案更短,出错的地方也更少。
无论哪种方式,Webhook 都要保留。MCP 是请求与响应的模式,客户的消息只能通过 Meta 调用的那个路由到达智能体。
常见错误
- 在向 Meta 返回 200 之前就运行智能体。响应过慢会被重试,客户会收到两次回答。
- 所有客户共用一个会话。请以发送者的号码作为键,否则历史会混在一起。
- 让延迟的跟进发送自由文本。超过 24 小时后会以 131047 失败,请改为发送消息模板。
在 easycoexistence.com 连接号码,把 Webhook 目标设置为您的路由,然后在控制台读取访问令牌和 phone number id。每个已连接号码每月 US$ 9 起,随号码增加降至 US$ 1,前 3 天免费。
常见问题
这需要 OpenAI Responses API 的托管 MCP 工具吗?
不需要。worker 用 Runner.run 在本地运行智能体,WhatsApp 一侧只是您的代码发起的一次普通 HTTPS 调用。
我应该使用哪个 Graph API 版本?
Meta 当前的示例使用 v25.0。请在代码中的一处固定版本号,这样升级只需改一行。
可以由真人在手机上接管吗?
可以。Coexistence 让 WhatsApp Business 应用保持可用,您在手机上的回复会以 echo 事件的形式到达您的路由,worker 可据此暂停智能体。
EasyCoexistence 会存储对话吗?
不会。Meta 直接投递到您的路由。会话所存储的内容都保存在您自己的数据库中。
继续阅读
准备开始了吗?
几分钟即可设置 WhatsApp Coexistence,而不是几个月。应用会继续在手机上运行。
开始免费试用验证时间