Dialogflow Agent无法在HTTP请求回调内输出文本的问题
解决Dialogflow Fulfillment异步回调中agent.add()失效的问题
我完全懂你碰到的这个坑——用Dialogflow默认欢迎意图触发外部GET请求时,回调里的agent.add()总是时灵时不灵,看日志才发现函数居然在请求完成前就结束了,导致回调里的输出根本没机会传给Agent。
先看看你当前的代码实现:
'use strict'; const functions = require('firebase-functions'); const {WebhookClient} = require('dialogflow-fulfillment'); const {Card, Suggestion} = require('dialogflow-fulfillment'); process.env.DEBUG = 'dialogflow:debug'; exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); function search(agent){ var request = require("request"); var options = { method: 'GET', url: 'http://google.com' }; console.log("Before request"); agent.add("Before request"); request(options, function (error, response, body){ console.log("Request completed"); agent.add("Request completed"); //<- 这行有时不显示在Agent里 console.log("finished"); //<- 这行能正常出现在日志里 }); console.log("Request sent"); agent.add(`Request sent`); } let intentMap = new Map(); intentMap.set('Default Welcome Intent', search); agent.handleRequest(intentMap); });
问题根源
核心问题出在异步操作的执行时机上:request是回调式的异步函数,当search函数执行到request(options, ...)这一行时,它会立刻发起请求,然后直接往下跑完剩余代码,函数就结束了。而Firebase Functions会在你的函数执行完毕后,立刻终止进程并返回响应——这时候你的HTTP请求回调还没触发呢!所以回调里的agent.add()要么没机会执行,要么执行时进程已经被关闭,自然就失效了。
解决方案:让函数等待异步操作完成
要解决这个问题,我们需要把异步操作改成Promise风格,让search函数等待异步请求完成后再结束进程。这里有两种常用的实现方式:
方式1:用async/await + axios(推荐,代码更简洁)
先安装axios依赖:npm install axios,然后修改代码:
'use strict'; const functions = require('firebase-functions'); const {WebhookClient} = require('dialogflow-fulfillment'); const {Card, Suggestion} = require('dialogflow-fulfillment'); const axios = require('axios'); // 引入axios process.env.DEBUG = 'dialogflow:debug'; exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); // 把search改成async函数 async function search(agent){ console.log("Before request"); agent.add("Before request"); try { // 用await等待请求完成 const response = await axios.get('http://google.com'); console.log("Request completed"); agent.add("Request completed"); console.log("finished"); } catch (error) { // 别忘了处理请求错误 console.error("Request failed:", error); agent.add("请求出错了,请稍后再试"); } console.log("Request sent"); agent.add(`Request sent`); } let intentMap = new Map(); intentMap.set('Default Welcome Intent', search); agent.handleRequest(intentMap); });
方式2:把request包装成Promise
如果不想更换HTTP请求库,可以手动把request的回调逻辑封装成Promise:
'use strict'; const functions = require('firebase-functions'); const {WebhookClient} = require('dialogflow-fulfillment'); const {Card, Suggestion} = require('dialogflow-fulfillment'); const request = require("request"); // 移到顶部统一引入 process.env.DEBUG = 'dialogflow:debug'; // 封装request为Promise风格的函数 function requestPromise(options) { return new Promise((resolve, reject) => { request(options, (error, response, body) => { if (error) reject(error); else resolve({response, body}); }); }); } exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { const agent = new WebhookClient({ request, response }); async function search(agent){ console.log("Before request"); agent.add("Before request"); const options = { method: 'GET', url: 'http://google.com' }; try { // 等待Promise完成后再继续执行 const {response, body} = await requestPromise(options); console.log("Request completed"); agent.add("Request completed"); console.log("finished"); } catch (error) { console.error("Request failed:", error); agent.add("请求出错了,请稍后再试"); } console.log("Request sent"); agent.add(`Request sent`); } let intentMap = new Map(); intentMap.set('Default Welcome Intent', search); agent.handleRequest(intentMap); });
为什么这样能解决问题?
把search改成async函数后,用await关键字等待异步请求完成,函数会暂停执行直到Promise状态改变,这样Firebase Functions就会等到整个异步流程跑完才终止进程,回调里的agent.add()就能正常生效了。
内容的提问来源于stack exchange,提问作者Raschid JFR
相关产品推荐
相关产品推荐

