Действия в Google: похожие намерения? - PullRequest
0 голосов
/ 02 мая 2018

Я работаю с Действиями в Google с DialogFlow + Firebase. Идея состоит в том, чтобы создавать пользовательские IoT-устройства, не предусмотренные AoG по умолчанию.

В случае радио и телевидения в настоящее время существует 2 намерения:

1) Канал: принимает 3 параметра: имя_устройства (пользовательский объект), действие_устройства (пользовательский объект) и значение.

Например: измените радиоканал на 22,3
ИЛИ измените канал телевизора на 22 * ​​1008 *

2) Объем: принимает 3 параметра: имя_устройства (пользовательский объект), действие_устройства (пользовательский объект) и значение.

Например: пожалуйста, измените громкость радио на 99 ИЛИ измените громкость телевизора на 22 * ​​1012 *

Проблема в том, что агент, по-видимому, не способен различать две скважины.

Это неправильная реализация?

enter image description here

enter image description here

enter image description here

enter image description here

// Изменить 9 мая 2018 года:

index.js


// Copyright 2016, Google, Inc.
// Licensed under the Apache License, Version 2.0 (the 'License');
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

const util = require('util');
const functions = require('firebase-functions');
const {
  dialogflow,
  Suggestions,
  BasicCard,
  Button,
  SimpleResponse,
} = require('actions-on-google');

const {values, concat, random, randomPop} = require('./util');
const responses = require('./responses');

/** Dialogflow Contexts {@link https://dialogflow.com/docs/contexts} */
const AppContexts = {
  FACT: 'choose_fact-followup',
  CATS: 'choose_cats-followup',
  XXX: 'choose_xxx-followup',

};

/** Dialogflow Context Lifespans {@link https://dialogflow.com/docs/contexts#lifespan} */
const Lifespans = {
  DEFAULT: 5,
};

const app = dialogflow({
  debug: true,
  init: () => ({
    data: {
      // Convert array of facts to map
      facts: responses.categories.reduce((o, c) => {
        o[c.category] = c.facts.slice();
        return o;
      }, {}),
      cats: responses.cats.facts.slice(), // copy cat facts
    },
  }),
});

/**
 * Greet the user and direct them to next turn
 * @param {DialogflowConversation} conv DialogflowConversation instance
 * @return {void}
 */
app.intent('Unrecognized Deep Link Fallback', (conv) => {
  const response = util.format(responses.general.unhandled, conv.query);
  const suggestions = responses.categories.map((c) => c.suggestion);
  conv.ask(response, new Suggestions(suggestions));
});

// redirect to the intent handler for tell_fact
app.intent('choose_fact', 'tell_fact');

// Say a fact
app.intent('tell_fact', (conv, {category}) => {
  const {facts, cats, xxx} = conv.data;
  if (values(facts).every((c) => !c.length)) {
    // If every fact category facts stored in conv.data is empty,
    // close the conversation
    return conv.close(responses.general.heardItAll);
  }
  const categoryResponse =
    responses.categories.find((c) => c.category === category);
  const fact = randomPop(facts[categoryResponse.category]);
  if (!fact) {
    const otherCategory =
      responses.categories.find((other) => other !== categoryResponse);
    const redirect = otherCategory.category;
    const parameters = {
      category: redirect,
    };
    // Add facts context to outgoing context list
    conv.contexts.set(AppContexts.FACT, Lifespans.DEFAULT, parameters);
    const response = [
      util.format(responses.transitions.content.heardItAll, category, redirect),
    ];
    // If cat facts not loaded or there still are cat facts left
    if (cats.length) {
      response.push(responses.transitions.content.alsoCats);
    }
    response.push(responses.general.wantWhat);
    conv.ask(concat(...response));
    conv.ask(new Suggestions(otherCategory.suggestion));
    if (cats.length) {
      conv.ask(new Suggestions(responses.cats.suggestion));
    }
    return;
  }
  const {factPrefix} = categoryResponse;
  // conv.ask can be called multiple times to have the library construct
  // a single response itself the response will get sent at the end of
  // the function or if the function returns a promise, after the promise
  // is resolved.
  conv.ask(new SimpleResponse({
    speech: concat(factPrefix, fact),
    text: factPrefix,
  }));
  conv.ask(responses.general.nextFact);
  conv.ask(new BasicCard({
    title: fact,
    image: random(responses.content.images),
    buttons: new Button({
      title: responses.general.linkOut,
      url: responses.content.link,
    }),
  }));
  console.log('hiwwxxxxxww thi is aaron');
  conv.ask(responses.general.suggestions.confirmation);
});

// Redirect to the intent handler for tell_cat_fact
app.intent('choose_cats', 'tell_cat_fact');

// Say a cat fact
app.intent('tell_cat_fact', (conv) => {
  const {cats} = conv.data;
  console.log('this is cats data' + {cats});
  const fact = randomPop(cats);
  if (!fact) {
    conv.contexts.delete(AppContexts.FACT);
    conv.contexts.delete(AppContexts.CATS);
    conv.ask(responses.transitions.cats.heardItAll);
    return conv.ask(responses.general.suggestions.confirmation);
  }
  const {factPrefix, audio} = responses.cats;
  // conv.ask can be called multiple times to have the library construct
  // a single response itself. The response will get sent at the end of
  // the function or if the function returns a promise, after the promise
  // is resolved.
  const sound = util.format(audio, random(responses.cats.sounds));
  conv.ask(new SimpleResponse({
    // <speak></speak> is needed here since factPrefix is a SSML string
    // and contains audio.
    speech: `<speak>${concat(factPrefix, sound, fact)}</speak>`,
    text: factPrefix,
  }));
  conv.ask(responses.general.nextFact);
  conv.ask(new BasicCard({
    title: fact,
    image: random(responses.cats.images),
    buttons: new Button({
      title: responses.general.linkOut,
      url: responses.cats.link,
    }),
  }));
  console.log('hiwwxxxxxww thi is aaron');
  conv.ask(responses.general.suggestions.confirmation);
});



//say a tv channel
app.intent('volume', (conv, {device_name,device_action, value}) => {

  var no_device_name = device_name;
  var no_value = value;
  var no_device_action = device_action;

  var this_device_value = util.inspect(value, false, null);
  var this_device_name = util.inspect(device_name, false, null);
  var this_device_action = util.inspect(device_action, false, null);
  console.log(this_device_action[0]);

  if (no_device_name[0] == 'channel'){
    console.log('inside tv but CHANNEL');



  }

  else{
    console.log('VOLUME');
  }
  console.log('THIS IS VOL');
  console.log(no_device_action[0]);
   conv.ask(`Alright, ${device_name} VOLUM is now ${value}! `);
  console.log('inside volume ' + value[0]);
  console.log('inside volume' + device_name[0]);
  console.log('inside volume' + device_action[0]);
  console.log('hiwwxxxxxww thi is aaron');


});

//say a tv channel
app.intent('channel', (conv, {device_channel_name, device_channel_action, channel_value}) => {


  console.log('THIS IS CHANNEL');



  conv.ask(`Alright, ${device_channel_name} CHANNEL is now ${channel_value}! `);
  console.log('inside CHANNEL ' + channel_value[0]);
  console.log('inside CHANNEL' + device_channel_name[0]);
  console.log('inside CHANNEL' + device_channel_action[0]);
  console.log('hiwwxxxxxww thi is aaron');


});



app.intent('no_input', (conv) => {
  const repromptCount = parseInt(conv.arguments.get('REPROMPT_COUNT'));
  if (repromptCount === 0) {
    conv.ask(`What was that?`);
  } else if (repromptCount === 1) {
    conv.ask(`Sorry I didn't catch that. Could you repeat yourself?`);
  } else if (conv.arguments.get('IS_FINAL_REPROMPT')) {
    conv.close(`Okay let's try this again later.`);
  }
});



// The entry point to handle a http request
exports.factsAboutGoogle = functions.https.onRequest(app);

1 Ответ

0 голосов
/ 03 мая 2018

Если у вас есть образец фразы с частью этой фразы, заданной для представления параметра, эта часть фразы может принимать любое значение , для которого определен параметр. Столбец «разрешенное значение» просто показывает, к чему он был разрешен в вашей типовой фразе.

Таким образом, в вашем намерении "channel", фраза "tv channel to 3" может также соответствовать "radio volume to 4", так как "tv" и "radio" относятся к сущности device_name, а также к "channel" и «объем» относится к сущности device_action.

У вас есть несколько решений:

  1. Вы можете превратить их в одно намерение, которое принимает все фразы, и в своем веб-крюке проверьте значение device_action, чтобы увидеть, что вам следует делать.

  2. Вы можете сохранить их как отдельные Intents и полностью удалить параметр device_action. Вы не нуждаетесь в них. Просто используйте синонимы в различных типовых фразах, чтобы система обучения была обучена тому, какие синонимы работают с каким намерением.

  3. Если вы все еще беспокоитесь о синонимах, или если это все еще имеет смысл, сделайте их различными типами сущностей.

    Например, вы можете захотеть иметь «канал» и «предустановку» в качестве разных сущностей для одного и того же типа сущности. Вы заботитесь о разнице, потому что она меняет ваше отношение к номеру, но вы все равно меняете канал. «Объем» будет другой сущностью, поскольку он представляет нечто совершенно иное.

В ваших комментариях вы спрашивали о персонализированных псевдонимах для устройств. Вы не спрашивали о более чем одном типе устройства с разными именами (поэтому вы хотите обрабатывать «tv1» и «tv2» с одним и тем же намерением, но иметь возможность обнаружить разницу. Оба из них лучше всего обрабатываются с опцией (3 ) выше, но немного по-другому.

Если вам нужно более одного «tv», каждый из них будет иметь один и тот же тип объекта (например, «device tv»), но каждый из них будет иметь другое значение объекта. Это может выглядеть примерно так:

enter image description here

"Но подожди!" Я слышал, как вы говорили: «Что, если у пользователя есть три телевизора? Или девять? Нужно ли мне все это настраивать? А что, если они захотят назвать одно« спальней », а другое« ден ТВ »?»

Это говорит о том, что вы должны будете хранить количество телевизоров и их псевдонимов в какой-то базе данных (что вам нужно сделать в любом случае - вам, вероятно, нужно сопоставить их телевизор с каким-то уникальным идентификатором устройства, чтобы фактически включить их) и когда пользователь говорит с вашим агентом, обновите его с помощью User Entity .

Пользовательские объекты имеют пользовательские имена и псевдонимы, установленные только для этого пользователя. Вы будете устанавливать их с помощью Dialogflow API, когда пользователь впервые общается с вашим агентом. Если вы используете V1, вы будете использовать конечную точку / userEntities . Если вы используете V2, вы будете использовать ресурс projects.agent.sessions.entityTypes .

...