在Google Action中“跨会话”存储数据

时间:2018-10-12 12:11:28

标签: actions-on-google

我对Google Actions documentation about storing data有点困惑,希望有人可以帮助澄清...

文档指出conv.user.storage对象中的数据将“跨会话”保存。我的意思是,如果用户退出对话,则这些值将在下一次与我的操作进行交互时保持不变并可用。这种理解正确吗?

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

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

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

/* 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):

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写入会话存储):

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对象始终为空。

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

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

STORAGE_WRITE请求:

{
  "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"
}

STORAGE_WRITE响应:

{
  "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请求:

{
    "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响应:

    {
        "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"
            }
        }
    }

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

谢谢!

1 个答案:

答案 0 :(得分:1)

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

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

诸如智能扬声器(家用,迷你)和智能显示器之类的共享设备的个人搜索结果默认为禁用。选中this documentation启用它。

  1. 在您的Android手机上打开设置
  2. 在“辅助设备”下,选择您的设备(例如Mini)
  3. 打开个人搜索结果

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

要检查userStorage是否将持续存在,可以使用GUEST / VERIFIED标志,请参阅documentation here