在“跨会话”中存储数据。在Google Action中 [英] Storing data "across conversations" in Google Action

查看:87
本文介绍了在“跨会话”中存储数据。在Google Action中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有关存储数据的Google Actions文档,并希望有人可以帮助澄清...

I'm a bit confused by the Google Actions documentation about storing data and hoped someone can help clarify...

文档指出 conv.user.storage 对象将跨对话保存。我的意思是,如果用户退出对话,则这些值将在下一次与我的操作进行交互时保持不变并可用。

The docs state that data in the conv.user.storage object will be saved "across conversations". I took this to mean that if the user exited the conversation these values would be persisted and available the next time they interact with my action. Is that understanding correct?

我问的原因是我无法让这种行为在我的行动中发挥作用。

The reason I ask is that I can't get this behaviour to work in my action.

我已经构建了一个简单的动作执行服务(使用Google NodeJS库v2.4.0和Koa v2.5.3上的Actions)。兑现是由Dialogflow中定义的意图触发的(在帐户已与Google登录链接后),并将其值存储在对话存储中。代码如下:

I have built a simple action fulfilment service (using Actions on Google NodeJS library v2.4.0 and Koa v2.5.3). The fulfilment is triggered from an intent defined in Dialogflow (after an account has been linked with Google Sign In) and stores a value in conversation storage. The code is as follows:

server.js(基本服务器-从本地./actions/ dir动态加载操作)

server.js (base server - loads actions dynamically from the local ./actions/ dir)

/* Load the environment */
const dotenv = require('dotenv');
const path = require('path');
const packageJson = require('./package.json');
dotenv.config({
    silent: true,
    path: process.env.ENV_FILE!=undefined && process.env.ENV_FILE.trim()!='' ? path.normalize(process.env.ENV_FILE) : path.join(__dirname, './.env')
});

const SERVER_NAME = process.env.NAME || packageJson.name;
const SERVER_PORT = process.env.PORT||'8080';
const SERVER_HOST = process.env.HOST||'0.0.0.0';
const HANDLERS_PATH = './actions/';


/* Load the dependencies */
const logger = require('utils-general').logger('google-server');
const Koa = require('koa');
const KoaBody = require('koa-body');
const KoaActionsOnGoogle = require('koa-aog');
const fs = require('fs');
const { dialogflow } = require('actions-on-google');


/* Load and initialise the Google Assistant actions */
//Initialise DialogFlow
const action = dialogflow({ debug: process.env.ACTIONS_DEBUG==='true', clientId: process.env.GOOGLE_CLIENT_ID });

//Load the action intent handlers
const handlers = [];
let handlerFiles = fs.readdirSync(HANDLERS_PATH);
handlerFiles.forEach(function loadHandlers(file) {
    let handlerImpl = require(HANDLERS_PATH+file);
    let handler = {};
    handler[handlerImpl.intent] = handlerImpl.action;
    handlers.push(handler);
});

//Add the actions intent handlers to DialogFlow
handlers.forEach(item => {
    let key = Object.keys(item)[0];
    logger.info(`Adding handler for action intent ${key}`);
    action.intent(key, item[key]);
});


/* Create the application server to handle fulfilment requests */
logger.info(`Initialising the ${SERVER_NAME} server (port: ${SERVER_PORT}, host: ${SERVER_HOST})`);

//Create the server
const app = new Koa();

//Add default error handler middleware
app.on('error', function handleAppError(err) {
    logger.error(`Unhandled ${err.name||'Error'}: ${err.message || JSON.stringify(err)}`);
});

//Add body parsing middleware
app.use(KoaBody({ jsonLimit: '50kb' }));

//Log the request/ response
app.use(async (ctx, next) => {
    logger.trace(`REQUEST ${ctx.method} ${ctx.path} ${JSON.stringify(ctx.request.body)}`);
    await next();
    logger.trace(`RESPONSE (${ctx.response.status}) ${ctx.response.body ? JSON.stringify(ctx.response.body) : ''}`);
});

//Make the action fulfilment endpoint available on the server
app.use(KoaActionsOnGoogle({ action: action }));


/* Start server on the specified port */
app.listen(SERVER_PORT, SERVER_HOST, function () {
    logger.info(`${SERVER_NAME} server started at ${new Date().toISOString()} and listening for requests on port ${SERVER_PORT}`);
});

module.exports = app;

storage-read.js(实现 STORAGE_READ意图-从会话存储中读取存储的uuid) :

storage-read.js (fulfilment for the "STORAGE_READ" intent - reads stored uuid from conversation storage):

const logger = require('utils-general').logger('google-action-storage-read');
const { SimpleResponse } = require('actions-on-google');
const { getUserId } = require('../utils/assistant-util');
const _get = require('lodash.get');

module.exports = {
    intent: 'STORAGE_READ',
    action: async function (conv, input) {
        logger.debug(`Processing STORAGE_READ intent request: ${JSON.stringify(conv)}`, { traceid: getUserId(conv) });
        let storedId = _get(conv, 'user.storage.uuid', undefined);
        logger.debug(`User storage UUID is ${storedId}`);
        conv.close(new SimpleResponse((storedId!=undefined ? `This conversation contains stored data` : `There is no stored data for this conversation`)));
    }
}

storage-write.js(完成 STORAGE_WRITE目的-将UUID写入会话存储):

storage-write.js (fulfils the "STORAGE_WRITE" intent - writes a UUID to conversation storage):

const logger = require('utils-general').logger('google-action-storage-read');
const { SimpleResponse } = require('actions-on-google');
const { getUserId } = require('../utils/assistant-util');
const _set = require('lodash.set');
const uuid = require('uuid/v4');

module.exports = {
    intent: 'STORAGE_WRITE',
    action: async function (conv, input) {
        logger.debug(`Processing STORAGE_WRITE intent request`, { traceid: getUserId(conv) });
        let newId = uuid();
        logger.debug(`Writing new UUID to conversation storage: ${newId}`);
        _set(conv, 'user.storage.uuid', newId);
        conv.close(new SimpleResponse(`OK, I've written a new UUID to conversation storage`));
    }
}

此 STORAGE_WRITE实现会存储数据并使之在同一对话中各回合之间可用(即,在同一对话中触发的另一个意图可以读取存储的数据)。但是,当对话关闭时,与同一用户的后续(新)对话无法读取数据(即,当 STORAGE_READ意图得到满足时)- conv.user.storage 对象始终为空。

This "STORAGE_WRITE" fulfilment stores the data and makes it available between turns in the same conversation (i.e. another intent triggered in the same conversation can read the stored data). However, when the conversation is closed, subsequent (new) conversations with the same user are unable to read the data (i.e. when the "STORAGE_READ" intent is fulfilled) - the conv.user.storage object is always empty.

我在我使用的Google帐户/ Home Mini上设置了语音匹配,但看不到如何确定操作中的声音是否匹配(尽管好像是在我开始新对话时使用的是我的关联帐户)。我在模拟器上也得到了相同的行为。

I have voice match set up on the Google account/ Home Mini I'm using, but I can't see how I determine in the action if the voice is matched (although it seems to be as when I start a new conversation my linked account is used). I'm also getting the same behaviour on the simulator.

示例请求/响应(使用模拟器时)如下:

Sample request/ responses (when using the simulator) are as follows:

STORAGE_WRITE请求:

STORAGE_WRITE request:

{
  "user": {
    "userId": "AB_Hidden_EWVzx3q",
    "locale": "en-US",
    "lastSeen": "2018-10-18T12:52:01Z",
    "idToken": "eyMyHiddenTokenId"
  },
  "conversation": {
    "conversationId": "ABwppHFrP5DIKzykGIfK5mNS42yVzuunzOfFUhyPctG0h0xM8p6u0E9suX8OIvaaGdlYydTl60ih-WJ5kkqV4acS5Zd1OkRJ5pnE",
    "type": "NEW"
  },
  "inputs": [
    {
      "intent": "actions.intent.MAIN",
      "rawInputs": [
        {
          "inputType": "KEYBOARD",
          "query": "ask my pathfinder to write something to conversation storage"
        }
      ],
      "arguments": [
        {
          "name": "trigger_query",
          "rawText": "write something to conversation storage",
          "textValue": "write something to conversation storage"
        }
      ]
    }
  ],
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.WEB_BROWSER"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      }
    ]
  },
  "isInSandbox": true,
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.WEB_BROWSER"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        }
      ]
    }
  ],
  "requestType": "SIMULATOR"
}

存储_WRITE响应:

STORAGE_WRITE response:

{
  "conversationToken": "[]",
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "simpleResponse": {
            "textToSpeech": "OK, I've written a new UUID to conversation storage"
          }
        }
      ]
    }
  },
  "responseMetadata": {
    "status": {
      "message": "Success (200)"
    },
    "queryMatchInfo": {
      "queryMatched": true,
      "intent": "a7e54fcf-8ff1-4690-a311-e4c6a8d1bfd7"
    }
  },
  "userStorage": "{\"data\":{\"uuid\":\"7dc835fa-0470-4028-b8ed-3374ed65ac7c\"}}"
}

后续的STORAGE_READ请求:

Subsequent STORAGE_READ request:

{
    "user": {
        "userId": "AB_Hidden_EWVzx3q",
        "locale": "en-US",
        "lastSeen": "2018-10-18T12:52:47Z",
        "idToken": "eyMyHiddenTokenId"
    },
    "conversation": {
        "conversationId": "ABwppHHVvp810VEfa4BhBJPf1NIfKUGzyvw9JCw7kKq9YBd_F8w0VYjJiSuzGLrHcXHGc9pC6ukuMB62XVkzkZOaC24pEbXWLQX5",
        "type": "NEW"
    },
    "inputs": [
        {
            "intent": "STORAGE_READ",
            "rawInputs": [
                {
                    "inputType": "KEYBOARD",
                    "query": "ask my pathfinder what is in conversation storage"
                }
            ],
            "arguments": [
                {
                    "name": "trigger_query",
                    "rawText": "what is in conversation storage",
                    "textValue": "what is in conversation storage"
                }
            ]
        }
    ],
    "surface": {
        "capabilities": [
            {
                "name": "actions.capability.WEB_BROWSER"
            },
            {
                "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
                "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
                "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
            }
        ]
    },
    "isInSandbox": true,
    "availableSurfaces": [
        {
            "capabilities": [
                {
                    "name": "actions.capability.WEB_BROWSER"
                },
                {
                    "name": "actions.capability.AUDIO_OUTPUT"
                },
                {
                    "name": "actions.capability.SCREEN_OUTPUT"
                }
            ]
        }
    ],
    "requestType": "SIMULATOR"
}

STORAGE_READ响应:

STORAGE_READ response:

    {
        "conversationToken": "[]",
        "finalResponse": {
            "richResponse": {
                "items": [
                    {
                        "simpleResponse": {
                            "textToSpeech": "There is no stored data for this conversation"
                        }
                    }
                ]
            }
        },
        "responseMetadata": {
            "status": {
                "message": "Success (200)"
            },
            "queryMatchInfo": {
                "queryMatched": true,
                "intent": "368d08d3-fe0c-4481-aa8e-b0bdfa659eeb"
            }
        }
    }

有人可以让我弄清楚我是在误解文档还是在某个地方出现错误?

Can someone set me straighten me out on whether I'm misinterpreting the docs or maybe I have a bug somewhere?

谢谢!

推荐答案

我怀疑您的个人搜索结果已关闭

您提到您正在Home Mini上进行测试,并且Prisoner能够在设备上进行复制(在评论中)。

You mentioned you're testing on Home Mini and Prisoner was able reproduce on device (in the comments).

智能扬声器(家用,迷你)和智能显示器等共享设备的个性化搜索结果默认情况下处于禁用状态。查看此文档以启用它。

Shared devices like Smart Speakers (Home, Mini) and Smart Displays have personal results disabled by default. Check this documentation to enable it.


  1. 在Android手机上打开设置

  2. 在辅助设备下,选择您的设备(例如Mini)

  3. 打开个人结果

请注意,这意味着可以通过设备访问诸如日历条目之类的个人结果。

Beware that this means personal results like Calendar entries can be accessed through the device.

要检查userStorage是否将持续存在,可以使用GUEST / VERIFIED标志,请参见此处的文档

To check if userStorage will persist, you can use the GUEST/VERIFIED flag, see documentation here.

这篇关于在“跨会话”中存储数据。在Google Action中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆