Solución:
Acabo de revisar la documentación de Actions on Google y las páginas de documentación de cumplimiento y, de hecho, hay un límite de tiempo de espera de 5 segundos.
Puede que esta no sea la mejor de las soluciones y puede que no se ajuste a su caso, pero teniendo en cuenta la estricta ventana de 5 segundos (queremos garantizar una conversación dinámica sin arriesgar al usuario a esperar demasiado)
Comienzas el cálculo con tu primer intento de forma asincrónica y regresas al usuario y le dices que solicite los resultados en unos segundos, mientras tanto, cuando se completa el cálculo. Se guardará en un espacio privado para el usuario, momento en el que el usuario activará una segunda intención que solicitará los resultados que, mientras tanto, se habrán calculado previamente, para que pueda recuperarlos y devolverlos.
Puede extender el límite de intención de 5 segundos hasta 15 segundos configurando múltiples eventos de seguimiento. Actualmente, solo puede configurar 3 eventos de seguimiento uno tras otro (lo que puede extender el tiempo de espera hasta 15 segundos).
A continuación, se muestra un ejemplo de cómo puede hacerlo en el centro logístico:
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);
Para obtener más información sobre eventos personalizados, visite esta página: https://dialogflow.com/docs/events/custom-events
Reduzca la complejidad de su código para hacerlo más rápido; de ustedes están usando una arquitectura de microservicio o nano servicio, como la función firebase, AWS lambda o Kubernetes, intente reducir el inicio muerto y el inicio en frío inicializando bibliotecas dentro de la función en lugar del alcance global,
Si tiene varias llamadas a la API, intente hacerlo en paralelo en lugar de una tras otra para reducir. p. ej., promesa.todos los enfoques
También puede resolver el problema a través de la base de datos o el contexto.
por ejemplo, el usuario pregunta: ¿cuál es mi saldo?
Bot: Estoy revisando tu saldo. Pregunte en unos segundos de nuevo
Y obtenga el tiempo que toma la API en segundo plano y guarde los datos en una base de datos de alta velocidad como MongoDB (relativamente más alta que las API de servicio web lentas), y marque una bandera en el menú contextual o la base de datos.
Cuando el usuario vuelva a preguntar en unos segundos, verifique la bandera si es positiva, obtenga los datos de la base de datos de alta velocidad y entréguelos al usuario.
Sugerencia: si está en el asistente de Google, puede enviar notificaciones automáticas cuando se complete la recuperación de datos de la API
actualizar:
Responder al comentario: “¿Puede explicar qué quiere decir con” inicializar bibliotecas dentro de la función en lugar del alcance global “?
Por ejemplo, en el caso de funciones de base de fuego, en realidad se ejecutó en un entorno en contenedor, y cuando no llama a la función por un tiempo, simplemente libera el contenedor de su función de la memoria, y cuando lo llama nuevamente, inicializa el contenedor nuevamente antes la ejecución real, esa inicialización se llama inicio en frío, por lo que toma un poco más de tiempo para la primera llamada y la llamada subsiguiente toma menos, incluso el tiempo de ejecución para la primera llamada es el mismo pero la función no puede iniciar la ejecución hasta que se complete la inicialización del contenedor, inicialización del contenedor incluye toda la inicialización de la conexión de la biblioteca y la base de datos y todo. Todo esto está bien, no puede deshacerse del arranque en frío en la arquitectura de micro / nano-servicio, pero a veces lleva más y más tiempo y causa frustración y mala experiencia para el usuario, y servicios como la primera llamada de dialogflow simplemente falla Cada vez, lo cual no es bueno , aquí hay más: servicios como firebase en realidad crean un contenedor separado para cada función, por ejemplo, si tiene varias funciones, firebase implementa cada función en un contenedor separado, por lo que llamar a cada función inicializa solo el contenedor de esa función, no todas las demás funciones, y aquí el contenedor real viene el problema, llama a una función e inicializa todo en el alcance global, independientemente de que su función la esté usando o no, la mayoría de los desarrolladores cometen el error de inicializar la base de datos en el alcance global, lo que significa que todas las funciones deben inicializarlas en su inicio en frío, pero no todas de ustedes funcionan realmente usando la conexión de la base de datos, así que lo que necesitamos es inicializar la base de datos en el cuerpo de cada función por separado y no fuera de la función, de hecho, lo que hago es crear una función reutilizable que verifica si la base de datos aún no está conectada, conectarla; de lo contrario, no hacer nada, esta verificación es para evitar la inicialización de la base de datos en cada llamada de función, lo que puede causar un aumento del tiempo de ejecución.
Intentaré agregar un ejemplo de código de funciones de base de fuego más adelante.
Actualización 2:
aquí está el ejemplo de código
forma tradicional:
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:[email protected]: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");
)
)
En el código anterior, puede notar 4 funciones
- HTTP trigger addMessage para agregar un mensaje en firebase DB
- Función de registro HTTP, usa MongoDB
- activador de base de datos para hacer una entrada en mayúscula, sin usar ninguna base de datos
- Función de ping de activación HTTP, que no utiliza ninguna base de datos
también puede notar dos inicialización de base de datos, firebase y MongoDB
digamos que cuando llama a una función por primera vez después de un tiempo y la función está fría, por lo que inicializará estas dos bases de datos, no solo una vez, sino para las cuatro funciones por separado, digamos que cada inicialización de la base de datos toma 400 milisegundos, entonces estos dos tomaría 800 millas, por lo que cuando llame a la primera función para agregar un mensaje, inicializará ambos db (800ms) y luego ejecutará la función (digamos 150ms), por lo que 800ms + 150ms, por lo que tomará aproximadamente 950ms para la primera tiempo, independientemente de que no esté usando mongodb, lo inicializará porque la inicialización está escrita en el alcance global
si llama a la función de registro justo después de la función addMessage, hará lo mismo 800ms para db init y luego la ejecución de la función de registro, digamos que toma 200ms, por lo que un total de 800 + 200 = 1000ms, es posible que esté pensando que db ya está inicializado, así que ¿por qué otra vez? como ya mencioné en mi respuesta inicial que cada función podría vivir en un contenedor separado (no siempre, pero eso es cierto) significa que la función de registro puede no saber lo que está sucediendo en la función addMessage, por lo que también inicializará la base de datos para su contenedor, así que primero la llamada tomaría más tiempo que las llamadas posteriores
la función 3 es un disparador de base de datos y no está usando la base de datos, pero cuando se llama recibe el identificador de la base de datos y usa ese identificador para hacer cambios en la base de datos, pero en este caso cuando la función está fría y usted hace una entrada en el db, en realidad inicializa la función como cualquier otra función, lo que significa que la sobrecarga de 800 ms sigue ahí por primera vez y esta es la razón por la que la mayoría de las personas odian los disparadores de db pero no saben por qué está sucediendo (en este punto me gustaría mencione que hay pocas cosas además del arranque en frío en su diseño y hay problemas en github, pero créanme que optimizar el arranque en frío resolverá su problema al 50%)
la función 4 no es más que una función de ping, pero también inicializará la base de datos, 800 ms de sobrecarga por nada
ahora eche un vistazo al siguiente código con algunas optimizaciones:
puede notar que en lugar de inicializar la base de datos directamente en el alcance global, registré una función de subrutina en el alcance global llamada initMongodb que contiene la lógica de inicialización de la base de datos, por lo que cuando llama a una función de base de fuego, no inicializará la base de datos durante el arranque en frío, sino que solo registrará esta función de subrutina en alcance global para que pueda acceder a él desde cualquier función de base de fuego,
ahora, si observa la segunda función que es el registro, es posible que haya notado que hice la inicialización de la base de datos aún más condicional, porque si la función no recibe los datos adecuados para realizar el registro, ¿cuál es el punto de inicializar la base de datos? En este punto, me gustaría mencionar que si la inicialización de la base de datos se realiza una vez, en llamadas posteriores, en realidad no inicializaría la base de datos nuevamente, de hecho, cuando la ejecución de la función de base de fuego se completa, destruye todas las variables en el alcance de las funciones de base de fuego, pero mantiene las variables globales (hasta el próximo inicio en frío) y puedes notar que necesitaba el mongodb como nombre varibale mongoose
y firebase como varibale nombrado admin
en el alcance global y la inicialización realiza algunos cambios en esas variables y todo eso, y es por eso que la lógica de inicialización está condicionada a que si db no se inicializa, se inicializa; de lo contrario, no se hace nada.
Otro punto que debe tenerse en cuenta aquí es “no” intente mantener todas las cosas dentro del alcance local de la función de base de fuego (como la importación de mangosta y la inicialización de mangosta y otras bases de datos), hará que la sobrecarga sea permanente e importará e inicializará la base de datos cada llamar desde cero, ya que toda la variable local se destruye después de que se completa la ejecución, por lo que es aún más peligroso que el inicio en frío
y finalmente, si observa la función 3 y 4, no habrá inicialización de la base de datos, pero esto no significa que tomaría el mismo tiempo en el arranque en frío y la llamada posterior, aún hay algunas cosas que suceden durante, como las importaciones que cargan la biblioteca archivos desde el disco a la memoria y todo, pero esto no lleva tanto tiempo en comparación con la inicialización de db (hace una solicitud https / socket a otra computadora en Internet) la importación se realiza en la misma computadora, aún así es mejor evitar innecesaria importaciones en producción.
modo optimizado de arranque en frío (recomendado)
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:[email protected]: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.
[email protected]
Si estás de acuerdo, tienes la opción de dejar un post acerca de qué te ha gustado de este post.