Dialogflow:如何延迟触发 follow-up intent?
结论
不能让一次 Dialogflow webhook 请求等待 20 秒,再调用 agent.setFollowupEvent()。这个方法只会把 followupEventInput 写入当前 webhook 响应。请求一旦超时或响应已经结束,之后的设置就无法传给 Dialogflow。
可以按以下方式处理:
- 不需要延迟时,立即调用
agent.setFollowupEvent()并返回。 - 必须延迟 20 秒时,先结束当前 webhook 请求,再通过外部任务调度器启动后台任务。任务到期后,由后台任务调用 Dialogflow
detectIntentAPI,并将 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。 - 延迟任务应设置幂等标识,防止任务重试造成付款完成逻辑或通知被重复执行。
备注:内容仅供参考。
