跳转到主要内容
外部 Agent 适用于 YCloud Pro 及以上套餐。
将您自有的 AI Agent 作为 External Agent 接入 YCloud。您的 Agent 运行在您自己的基础设施中,并在收件箱中与人工客服协同工作:它可以接收客户消息、发送回复、获取对话历史记录并将对话转接给您的团队。 YCloud 会向您的 Webhook URL 发送事件。您的服务处理这些事件并调用收件箱 REST API 进行回复或管理对话。YCloud 不会调用您的 AI 推理 API,且您的 Webhook 返回的 HTTP 响应不会发送给客户。

准备工作

请准备:
  • 一个公网可访问、支持接收 JSON POST 请求的 HTTPS 端点。
  • 一个能够验证 Webhook 签名、存储事件、防止重复处理并运行 AI Agent 的服务。
  • 存储在您服务器上的普通 YCloud 账户 API 密钥。这些收件箱端点不支持开发者应用 API 密钥。
  • 已接入 YCloud 的 WhatsApp 商业号码,以及用于接收转接的人工团队或客服人员。
使用 https://api.ycloud.com 作为 API 基础 URL。

连接您的 Agent

1. 创建外部 Agent

  1. 在 YCloud 控制台中,打开 AI Agent > AI Agent。
  2. 选择 Create Agent > External Agent。
  3. 输入 Name 以及您的 Webhook URL,例如 https://agent.example.com/webhooks/ycloud。
  4. 选择 Save。
  5. 从 Agent 详情中复制生成的 Bot ID 。在 API 请求中将该值用作 botId;您无需提供来自您自有 AI 系统的 ID。
包含 Name、Webhook URL 和 Save 控件的 YCloud 创建外部 Agent 表单。

Create an External Agent with a name and a webhook URL. This example shows the form before saving.

详情页面显示了 Webhook 端点、其 Signing secret 以及四种事件类型。使用该签名密钥验证接收到的事件。选择 View delivery logs 可查看 Webhook 推送详情。同一个端点也会在 Webhooks 页面中提供。 请将端点签名密钥与您的 API 密钥分开保存。签名密钥用于验证传入的 Webhook;X-API-Key 用于验证您发出的 REST 请求。Bot ID 是标识符,而非凭据。

2. 分配 WhatsApp 号码

打开 WhatsApp Manager > WhatsApp accounts,然后打开该商业号码的设置并选择 Inbox > Assignment。在 Priority 3 Assign to 下,选择 AI Agent,然后在 Agent 选择器的 External Agent 区域选择您的 Agent。 检查规则优先级:分配给客户负责人或最后接待的客服人员可能会优先执行。参见对话分配规则。 一个外部 Agent 可以处理多个号码。每个号码一次只能关联一个机器人。您可以在号码的分配设置中管理这些关联。

3. 处理对话

  1. 客户向您的商业号码发送 WhatsApp 消息。
  2. YCloud 会打开或继续一个收件箱对话,并应用该号码的分配规则。
  3. 当您的外部 Agent 接管对话时,YCloud 会将相应的 Webhook 事件推送到其端点。
  4. 您的服务验证并存储该事件,返回 2xx,然后异步处理客户输入。
  5. 您的服务调用收件箱 API 来回复、转接或关闭对话。
  6. 您的服务通过 whatsapp.message.updated 跟踪送达情况。

了解各类标识符

ID 均为字符串。时间戳采用 UTC RFC 3339 格式。在对话 API 路径中使用 dialogId,而非 conversationId。

接收 Webhook 事件

所有四种事件类型都会发送到 同一个 Webhook URL。每次专用的 External Agent 推送都包含 id、type、apiVersion、createTime 和 externalBotContext,以及相应的业务对象。

验证、存储和确认

在解析 JSON 之前,使用端点的签名密钥和 原始 HTTP 请求体 验证 YCloud-Signature 标头。签名格式为:
使用恒定时间比较并在合理的时间容差(例如五分钟)内验证时间戳。有关签名验证示例,请参阅实现 Webhook 接收器。 持久化事件及其去重状态,然后迅速返回任意成功的 2xx 响应。异步运行 AI 推理和其他耗时任务。非 2xx 响应和超时可能会触发有限次数的重试。 在您的 YCloud 账户内按 (botId, event.id) 去重。分配变更可能会同时通知迁出和迁入的 Agent;仅使用全局事件 ID 可能会丢弃其中一个 Agent 的通知。保留处理状态,避免重复事件触发第二次回复。

示例:客户消息

从 inboxMessage.inbound 中读取客户内容。出站创建事件则使用 inboxMessage.outbound。保留未知的消息类型和字段以保持兼容性,而不是将其视为文本。

决定是否回复

externalBotContext 是生成通知时创建的快照。其 botId 标识接收 Bot;assignee 标识该快照中的实际处理者。转接后这两者可能会有所不同。 actionable=true 表示接收 Bot 当时正在处理一个进行中的对话。在出站创建事件或送达回执中它也可以为 true。
不要仅凭 actionable 触发 AI 回复。检查事件类型和消息方向。延迟到达的事件不能证明您的 Agent 仍在处理该对话。需要时请查询当前对话,并处理来自每个写入请求的归属权错误。
事件可能会多次到达或无序到达。没有事件序号字段。按 dialogId 对任务分组,避免对同一客户输入处理两次。初始分配事件不应独立触发对客户起始消息的第二次回复。

转接或关闭后会发生什么?

一旦对话转由人工处理、处于排队中、未分配或已关闭,新的客户消息和人工回复将不再投递给先前的 External Agent。 先前的 Agent 仍会收到适用于它的迁出分配或关闭通知。它已发送消息的创建通知和送达回执即使在转接或号码重新分配后,也仍与该发送 Agent 保持关联。 先前生成的通知可能会延迟到达。当归属权变更或对话关闭时,请取消挂起的工作,不要仅因为旧事件带有 actionable=true 就恢复该工作。 普通 Webhook 端点保留其现有的订阅行为和 Payload 格式。专用的 External Agent 推送会添加 externalBotContext;不要在普通 whatsapp.message.updated 推送中要求该字段。

使用 Inbox API

在每个请求中发送 X-API-Key,并在 POST 请求中发送 Content-Type: application/json。以下所有示例均使用占位符 ID。请将其替换为来自您的 Agent 和所接收事件的值。 成功的响应会直接返回业务对象,而不包含 data 包装。写入操作会检查对话是否处于打开状态并且当前正由指定的 Bot 处理。YCloud 会从对话中确定发送方和客户;请勿提交 tenantId、from、to、recipient、operatorId 或 source 来覆盖它们。

获取当前对话

请检查 status、assignee 和 canAct。bot 字段可能指向上次关联的外部 Bot,不能作为当前归属权的证明。canAct 反映了请求时被查询 Bot 的权限;后续的写入操作仍会执行自身的检查。

检索会话历史记录

响应包含 items 和 nextCursor。每个条目都包含 Inbox 消息 id、direction、type、content 和 createTime。对于文本消息,请读取 content.text.body。 历史记录包含来自 同一会话的客户可见消息,可能跨越先前的对话。消息本身的 dialogId 可能与所请求的对话不同,或者在较早的历史记录中缺失。内部备注、私密转接摘要和内部系统消息均不包含在内。 返回的消息按消息 ID 升序排列,从较早到较新。首次请求时请省略 after;在后续请求中原样传递 nextCursor 作为 after,直到其为 null。默认 limit 为 50,范围为 1–100。转接目标列表使用相同的分页模式。

发送回复

botId 和 message 为必填项。sourceEventId 和 replyToMessageId 为选填项。回复引用是 Inbox 消息 ID,例如 inboxMessage.id;它不是 wamid。
即使 HTTP 返回 200,也请检查 status。accepted 仅表示消息已进入发送管道,并不代表已送达客户。200 响应也可能报告 failed;channelMessageId 可能尚不可用。 使用 whatsapp.message.updated 跟踪后续状态,包括 sent、delivered、read、failed、expired 或 deleted。将回执的 externalBotContext.inboxMessageId 与发送响应的 inboxMessageId 进行关联。对于失败情况,请检查 whatsappMessage.errorCode 和 errorMessage。

消息类型

message 对象使用标准 WhatsApp 消息模型:type 加上匹配的内容字段。示例包括 text、image、video、audio、document、sticker、location、contacts、interactive、template 和 reaction。现有的 WhatsApp 发送和模板要求仍然适用。 例如,将 message 对象替换为:
对于媒体,请提供 id 或 link。有关消息体结构,请参阅发送 WhatsApp 消息。请通过上述 Inbox 端点发送回复,以便使用对话的处理程序检查和 Inbox 上下文。请勿在此 Inbox 消息请求中提交或依赖 filterUnsubscribed 或 filterBlocked。 客户回复可以包含 interactive.button_reply、interactive.list_reply、interactive.nfm_reply 或 button。Flow 回复的 response_json 是一个 JSON 字符串。入站 button 类型不是此端点的出站消息类型。

转接给人工

首先列出对话所属商业号码的可用团队或客服人员:
使用 type=agent 改为列出成员。每个返回的目标都会提供转接请求所需的 id 和 type。客服人员可选的 online 字段描述在线状态;它不保证会立即分配。
您必须指定带有 type(team 或 agent)和 id 的 target。没有默认的人工后备选项。可选的 reason 最多支持 256 个字符;可选的 summary 最多支持 4,000 个字符,并且不属于客户可见的消息历史记录。 响应包含 outcome 以及更新后的 dialog。结果为 assigned、queued、team_unassigned 或 unassigned。请检查实际结果,而不要假设人工已经接受了会话。 转接会保留相同的 dialogId。一旦您的 Agent 失去归属权,请停止回复或尝试关闭该对话。

关闭已解决的对话

响应包含带有 status=closed 和 canAct=false 的 dialog。您的 Agent 无法继续发送或转接该已关闭的对话。后续有效的客户消息将启动新对话,并再次运行该号码的分配规则。

处理错误与重试

错误响应使用包含 status、code、message 和 requestId 的 error 对象。联系 YCloud 支持时请保留请求 ID。
sourceEventId 和 X-Request-Id 支持链路追踪;两者均不是幂等性密钥。API 不提供跨请求去重功能。在结果不确定的情况下盲目重试可能会发送重复消息。
监控 查看送达日志 以排查 Webhook 失败。保留事件 ID、对话 ID、消息 ID、请求 ID 以及处理结果。Webhook 重试次数是有限的,因此必要时请使用对话和历史记录查询来恢复上下文。

常见问题

我可以接入使用自己的 AI 框架构建的 Agent 吗?

可以。您的服务必须能够接收并验证 YCloud Webhook,并调用 Inbox REST API。您可以完全控制服务如何生成回复、访问知识库以及调用其自有工具。

我的 Agent 会收到所有 Inbox 会话吗?

不会。其专属端点仅接收其处理范围内的事件,以及相关的分配和关闭通知、其自身发送消息的回执。转接人工后的新消息不会转发给之前的 Agent。先前生成的事件可能仍会延迟送达。

我的 Agent 可以使用来自先前对话的消息吗?

可以。历史记录端点会返回该会话中客户可见的历史记录,包括先前的对话。如果可用,请使用每条消息各自的 dialogId;切勿假定返回的每条消息都属于当前的服务会话。

如果我更改分配给号码的 Agent 会发生什么?

当前由机器人处理的打开状态对话会根据该号码的路由行为异步重新分配。迁出的 Agent 会收到 assignmentRole=outgoing 并应停止其工作。接入的 Agent 会收到带有 assignmentRole=incoming 的 reason=phone_binding_changed;在继续之前,它应先查询当前对话和历史记录。 对话 ID 和历史记录保持不变。当前由人工处理的对话不会通过此号码重新分配转移给新的机器人。在被其他机器人替换后,旧机器人无法再通过这些端点查询该对话。

我的 Webhook 响应会直接成为客户回复吗?

不会。请返回 2xx 以确认收到事件,然后通过 POST /v2/inbox/dialogs/{dialogId}/messages 发送客户回复。

为什么我的 Agent 会为其自身的回复收到 actionable=true?

该字段描述生成事件时由谁处理该打开状态的对话。因此出站创建事件和送达回执也可能带有 actionable=true。仅针对入站 inbox.message.created 事件触发常规 AI 回复,并注意处理实时对话归属权的变化。