PAYATHON 2026

Dialogflow:如何延迟触发 follow-up intent?

支付老李

结论

不能让一次 Dialogflow webhook 请求等待 20 秒,再调用 agent.setFollowupEvent()。这个方法只会把 followupEventInput 写入当前 webhook 响应。请求一旦超时或响应已经结束,之后的设置就无法传给 Dialogflow。

可以按以下方式处理:

  1. 不需要延迟时,立即调用 agent.setFollowupEvent() 并返回。
  2. 必须延迟 20 秒时,先结束当前 webhook 请求,再通过外部任务调度器启动后台任务。任务到期后,由后台任务调用 Dialogflow detectIntent API,并将 event 作为输入。

为什么有日志,event 却没有触发

console.log('Hello') 能输出,只能说明 JavaScript 定时器执行了,不能说明 Dialogflow 还在等待 webhook 响应。

agent.setFollowupEvent('payment_completed');

这段代码不会主动向 Dialogflow 发起新的网络请求。它只是修改当前 fulfillment 响应对象,让最终响应包含类似下面的内容:

{
  "followupEventInput": {
    "name": "payment_completed"
  }
}

如果 webhook 在等待期间已经超时,或者响应已经发出,这次修改就不会生效。即使示例只等待 5 秒,也可能接近或超过当前环境允许的响应时间。具体超时限制取决于 Dialogflow 版本、集成渠道和 webhook 托管环境。

不需要延迟时的写法

如果 event 可以立即触发,直接设置 follow-up event,并返回一个已经完成的 Promise:

function payment(agent) {
  agent.setFollowupEvent('payment_completed');
  return Promise.resolve();
}

还要确认目标 intent 配置了名为 payment_completed 的 Event,而不只是建立了 follow-up intent 关系。Event 名称必须完全一致,包括大小写。

延迟 20 秒的推荐方案

整个流程需要拆成两次独立调用:

用户请求
   ↓
Dialogflow 调用 webhook
   ↓
webhook 创建一个延迟 20 秒的任务,并立即返回
   ↓
后台任务到期
   ↓
调用 Dialogflow detectIntent API,输入 payment_completed event
   ↓
匹配配置了该 Event 的 intent

不要依靠 Cloud Functions、Serverless Function 等环境中的 setTimeout() 执行后台延迟。响应结束后,运行实例可能暂停或销毁,定时器不一定能继续执行。延迟任务可以交给 Cloud Tasks、消息队列或其他支持延迟执行的任务系统。

后台任务触发 Event 的示例

下面是使用 Dialogflow ES Node.js 客户端的示例。后台任务必须保存原会话的 sessionId。否则,event 会进入新会话,原有 contexts 和参数可能无法延续。

const dialogflow = require('@google-cloud/dialogflow');

async function triggerPaymentCompleted({
  projectId,
  sessionId,
  languageCode = 'zh-CN'
}) {
  const sessionsClient = new dialogflow.SessionsClient();
  const sessionPath = sessionsClient.projectAgentSessionPath(
    projectId,
    sessionId
  );

  const request = {
    session: sessionPath,
    queryInput: {
      event: {
        name: 'payment_completed',
        languageCode
      }
    }
  };

  const [response] = await sessionsClient.detectIntent(request);
  return response.queryResult;
}

Webhook 只负责创建延迟任务,不等待任务执行:

async function payment(agent) {
  const sessionId = getCurrentSessionId(agent);

  await createDelayedTask({
    delaySeconds: 20,
    payload: {
      projectId: process.env.GOOGLE_CLOUD_PROJECT,
      sessionId,
      languageCode: 'zh-CN'
    }
  });

  agent.add('正在处理付款,完成后会继续。');
}

其中,getCurrentSessionId() 和 createDelayedTask() 是示意函数,需要根据实际 webhook 请求结构和所用的任务系统实现。不要自行生成新的 sessionId,而要从当前 Dialogflow session 路径中提取并传递。

注意事项

  • setFollowupEvent() 只能修改当前 webhook 响应,不能用于延迟调用 API。
  • Event 会触发配置了对应 Event 的 intent,并不是按名称直接调用某个 intent。
  • 延迟任务执行时,需要沿用原来的 projectId、sessionId 和 languageCode。
  • Dialogflow 的 session context 有生命周期限制。20 秒后仍要使用的参数,最好也保存在任务 payload 或持久化存储中。
  • 后台调用 detectIntent 后,回复只会返回给后台调用者,不一定会自动推送到用户当前使用的聊天渠道。如果需要在 20 秒后主动发送消息,还要调用该渠道的主动消息 API。
  • 延迟任务应设置幂等标识,防止任务重试造成付款完成逻辑或通知被重复执行。

备注:内容仅供参考。