我正在使用Dialogflow创建一个代理/机器人,该代理/机器人使用诸如“我需要从HR来信来证明地址”之类的操作项来响应不同类型的用户查询。这需要机器人从公司数据库中获取一些信息,并通过在人力资源部提供的模板化信函文件中填充检索到的信息来生成文档/信函。执行此操作的逻辑已经编写在python文件中。数据库集成是使用Webhooks完成的。
问题在于,解释用户请求,打开数据库和检索所需信息的完整过程需要5秒钟以上,这恰好是Dialogflow代理的响应超时限制。我对此进行了一些研究,发现我们不能增加此限制,但可以通过异步调用使会话保持活动状态。我找不到合适的资源来提供答案。
所以,我的问题是-
我们可以在dialogflow中进行异步调用吗?
如果是,那么我们如何通过json将异步数据发送到Dailogflow代理?
还有其他方法可以解决5秒钟的响应超时限制吗?
谢谢!
答案 0 :(得分:7)
我刚刚检查了Actions on Google documentation和Fulfillment documentation页面,确实有5秒的超时限制。
这可能不是最好的解决方案,可能不适合您的情况,但请考虑给定的严格5秒钟窗口(我们希望确保动态对话,而不会冒用户等待时间过长的风险)
您首先以第一个意图异步开始计算,然后返回到用户,并告诉他们在完成计算的同时几秒钟内请求结果。它将被保存在用户的专用空间中,这时用户将触发第二个意图,该意图将要求结果同时已被预先计算,因此您只需获取并返回它们即可。
答案 1 :(得分:2)
通过设置多个跟进事件,您可以将5秒的Intent限制延长到15秒。 目前,您只能一个接一个地设置3个跟进事件(最多可以将超时时间延长到15秒)。
以下是您如何在配送中心执行此操作的示例:
function function1(agent){
//This function handles your intent fulfillment
//you can initialize your db query here.
//When data is found, store it in a separate table for quick search
//get current date
var currentTime = new Date().getTime();
while (currentTime + 4500 >= new Date().getTime()) {
/*waits for 4.5 seconds
You can check every second if data is available in the database
if not, call the next follow up event and do the
same while loop in the next follow-up event
(up to 3 follow up events)
*/
/*
if(date.found){
agent.add('your data here');//Returns response to user
}
*/
}
//add a follow-up event
agent.setFollowupEvent('customEvent1');
//add a default response (in case there's a problem with the follow-up event)
agent.add("This is function1");
}
let intentMap = new Map();
intentMap.set('Your intent name here', function1);;
agent.handleRequest(intentMap);
要了解有关自定义事件的更多信息,请访问以下页面:https://dialogflow.com/docs/events/custom-events
答案 2 :(得分:2)
减少代码的复杂性以使其更快;您中的一部分正在使用诸如Firebase函数,AWS Lambda或Kubernetes之类的微服务或纳米服务架构,试图通过初始化函数内部而不是全局范围的库来减少无效启动和冷启动,
如果您有多个API调用,请尝试并行执行,而不是一个接一个地减少。例如Promise.all方法
您还可以通过数据库或上下文解决问题。
例如用户问:我的余额是多少
Bot:我正在检查您的余额。再过几秒钟问
然后获取在后台花费时间的API,并将数据保存在诸如MongoDB(相对慢的Web服务API相对较高)的高速数据库中,并在上下文菜单或数据库中标记一个标记。
当用户在几秒钟后再次询问时,请检查该标志是否为正,并从高速数据库中获取数据并将其提供给用户
提示:如果您使用的是Google助手,则可以在从API提取数据完成后发送推送通知
回复评论:“您能解释一下“在函数内部而不是全局范围内初始化库”是什么意思吗?”
例如,在firebase函数中,它实际上是在容器化环境中执行的,当您一段时间不调用该函数时,它只是从内存中释放了函数的容器,而当您再次调用它时,它将初始化在实际执行之前再次调用容器,该初始化称为冷启动,因此第一次调用花费的时间少一点,而后续调用花费的时间更少,即使第一次调用的执行时间是相同的,但函数不能分层执行直到容器初始化完成后,容器的初始化包括所有库和数据库连接的初始化以及所有。没关系,您无法摆脱微/纳米服务体系结构中的冷启动,但有时会花费越来越多的时间,并给用户带来挫败感和糟糕的体验,并且诸如dialogflow首次调用之类的服务完全失败了,每次都不好,这里还有更多:firebase之类的服务实际上为每个函数创建了单独的容器,例如,如果您有多个函数,firebase实际上将每个函数部署在一个单独的容器中,因此调用每个函数仅初始化该函数的容器,而不初始化所有其他函数的容器,此处是真正的问题来了,无论您是否使用函数,都调用一个函数并在全局范围内初始化所有函数,大多数开发人员都会犯错,他们在全局范围内初始化数据库,这意味着每个函数都必须在冷启动时对其进行初始化,但并非全部您实际上是使用数据库连接运行的,所以我们需要分别在每个函数的主体中初始化数据库,不在函数之外,实际上我要做的是制作一个可重用的函数,该函数检查数据库是否尚未连接,否则将其连接,否则不执行任何操作,此检查是为了避免在每个函数调用中初始化数据库,这可能会导致执行时间增加。 / p>
稍后我将尝试添加Firebase函数代码示例。
这是代码示例
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import * as _cors from 'cors';
import firestore from './../db'
import * as mongoose from "mongoose";
const defaultApp = admin.initializeApp(functions.config().firebase)
const dbURI = `mongodb://xxxxxx:xxxxxx@ds00000.mlab.com:123456/mydb`;
// const dbURI = `mongodb://localhost:27017/mydb`;
mongoose.connect(dbURI, {
useNewUrlParser: true, useUnifiedTopology: true
}).catch(e => {
console.log("mongo connection failed for reason: ", e);
})
var cors = _cors({ origin: true });// set these options appropriately According to your case,
// see document: https://www.npmjs.com/package/cors#configuration-options
// true means allow everything
// http example
export const addMessage = functions.https.onRequest((req, res) => {
const original = req.query.text;
admin.database().ref('/messages').push({ original: original }).then(snapshot => {
res.redirect(303, snapshot.ref);
});
});
export const signup = functions.https.onRequest(async (req, res) => {
... signup stuff using mongodb
res.send("user signed up");
})
//databse trigger example
export const makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite(event => {
const original = event.data.val();
console.log('Uppercasing', event.params.pushId, original);
const uppercase = original.toUpperCase();
return event.data.ref.parent.child('uppercase').set(uppercase);
});
//cors example
export const ping = functions.https.onRequest(async (req, res) => {
cors(req, res, () => {
res.send("this is a function");
})
})
在上面的代码中,您可能会注意到4个功能
您可能还会注意到两个数据库初始化,firebase和MongoDB
假设您在一段时间后第一次调用某个函数并且该函数处于冷状态,那么它将不仅初始化一次这两个数据库,而且还将对这四个函数分别进行初始化,假设每个数据库初始化需要400毫秒,因此这两个过程将花费800英里,因此当您调用第一个函数添加一条消息时,它将初始化两个db(800ms),然后它将实际上执行该函数(假设为150ms),因此800ms + 150ms因此将花费大约950ms第一次,无论它是否未使用mongodb都将对其进行初始化,因为初始化是在全局范围内编写的
如果在addMessage函数之后立即调用signup函数,它将对db init执行相同的800ms,然后执行signup函数使它说需要200ms,因此总计800 + 200 = 1000ms,您可能会认为db已被初始化,因此再次为什么呢,正如我在最初的回答中已经提到的那样,每个函数可能都生活在单独的容器中(并非总是如此,这是真的),这意味着注册函数可能不知道addMessage函数中正在发生什么,因此它将为其初始化容器的db。也是如此,因此第一个通话要比随后的通话花费更多时间
function 3是一个数据库触发器,它不使用数据库,但是当它被调用时,它接收到数据库的句柄,并使用该句柄在数据库中进行更改,但是在这种情况下,当函数变冷并且您执行在db条目中,它实际上像其他任何函数一样初始化了该函数,这意味着800ms的开销仍然是第一次出现,这就是大多数人讨厌db触发器的原因,但他们不知道它为什么发生(这时我想要提及的是,除了冷启动设计之外,还有其他一些事情,而github上还有一些问题,但是相信我优化冷启动将解决您的问题50%)
功能4只是一个ping功能,但它也会初始化数据库,什么也没花800毫秒
您可能会注意到,不是在全局范围内直接初始化db,而是在全局范围内注册了一个包含db初始化逻辑的名为initMongodb的子例程函数,因此,当您调用firebase函数时,它不会在冷启动期间初始化数据库,而只会注册此子例程函数在全局范围内,因此您可以使用任何firebase函数来访问它,
现在,如果您观察到第二个函数是注册,那么您可能已经注意到我使db初始化成为进一步的条件,因为如果函数未收到执行该注册的适当数据,那么初始化数据库的意义何在?要提到的是,如果一次完成数据库初始化,那么在随后的调用中它实际上不会再次初始化数据库,实际上,当firebase函数执行完成时,它将销毁该firebase函数作用域中的所有变量,但会保留全局变量(直到下一个冷启动),您可能会注意到我在全局范围内需要mongodb作为变量名mongoose
和firebase作为变量名admin
,并且初始化对这些变量以及所有这些变量进行了某些更改,这就是为什么初始化逻辑是条件是,如果未初始化db,则初始化否则,则什么也不做。
这里要注意的另一点是“不要”尝试将所有内容保留在firebase函数本地范围内(例如,导入猫鼬以及初始化猫鼬和其他数据库),这将使开销永久化,并将导入和从头开始初始化数据库的每一次调用,因为执行完成后所有本地变量都将被销毁,因此比冷启动本身更加危险
最后,如果您观察到函数3和4,将不会进行数据库初始化,但这并不意味着在冷启动和随后的调用中将花费相同的时间,在导入过程中仍然会发生一些事情它将库文件从磁盘加载到内存,并且所有操作都花了很长时间,与数据库初始化(向互联网上的其他计算机发出https / socket请求)相比,导入操作都是在同一台计算机上进行的,但还是更好为了避免生产中不必要的进口。
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import * as _cors from 'cors';
import firestore from './../db'
import * as mongoose from "mongoose";
const dbURI = `mongodb://xxxxxx:xxxxxx@ds00000.mlab.com:123456/mydb`;
// const dbURI = `mongodb://localhost:27017/mydb`;
export functions initFirebase(){
if (admin.apps.length === 0) {
console.log("initializing firebase database");
admin.initializeApp(functions.config().firebase)
}else{
console.log("firebase is already initialized");
}
}
export function initMongoDb() {
if (mongoose.connection.readyState !== mongoose.STATES.connected
&& mongoose.connection.readyState !== mongoose.STATES.connecting) {
console.log("initializing mongoose");
mongoose.connect(dbURI, {
useNewUrlParser: true, useUnifiedTopology: true
}).catch(e => {
console.log("mongo connection failed for reason: ", e);
})
} else {
console.log("mongoose already connected: ", mongoose.STATES[mongoose.connection.readyState]);
}
}
var cors = _cors({ origin: true });// set these options appropriately According to your case,
// see document: https://www.npmjs.com/package/cors#configuration-options
// true means allow everything
// http example
export const addMessage = functions.https.onRequest((req, res) => {
initFirebase()
const original = req.query.text;
admin.database().ref('/messages').push({ original: original }).then(snapshot => {
res.redirect(303, snapshot.ref);
});
});
export const signup = functions.https.onRequest(async (req, res) => {
if(req.body.name && req.body.email && req.body.password){
initMongoDb();
... signup stuff using mongodb
res.send("user signed up");
}else{
res.status(400).send("parameter missing");
}
})
//database trigger example
export const makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite(event => {
const original = event.data.val();
console.log('Uppercasing', event.params.pushId, original);
const uppercase = original.toUpperCase();
return event.data.ref.parent.child('uppercase').set(uppercase);
});
//cors example
export const function3 = functions.https.onRequest(async (req, res) => {
cors(req, res, () => {
res.send("this is a function");
})
})
Update: a ping call to function on start of mobile app or on page load in web also works well
Inzamam Malik,
Web & Chatbot developer.
malikasinger@gmail.com