Réponses enrichies (Dialogflow)

Explorer dans Dialogflow

Cliquez sur Continuer pour importer l'exemple de réponses dans Dialogflow. Ensuite, procédez comme suit pour déployer et tester l'exemple:

  1. Saisissez un nom d'agent et créez-en un pour l'exemple.
  2. Une fois l'agent importé, cliquez sur Accéder à l'agent.
  3. Dans le menu de navigation principal, accédez à Fulfillment.
  4. Activez l'éditeur intégré, puis cliquez sur Déployer. L'éditeur contient l'exemple de code.
  5. Dans le menu de navigation principal, accédez à Integrations (Intégrations), puis cliquez sur Google Assistant (Assistant Google).
  6. Dans la fenêtre modale qui s'affiche, activez l'option Modifications de l'aperçu automatique, puis cliquez sur Tester pour ouvrir le simulateur Actions.
  7. Dans le simulateur, saisissez Talk to my test app pour tester l'exemple.
Continuer

Utilisez une réponse enrichie si vous souhaitez afficher des éléments visuels pour améliorer les interactions des utilisateurs avec votre action. Ces éléments visuels peuvent fournir des indications sur la façon de poursuivre une conversation.

Les réponses enrichies peuvent s'afficher sur l'écran uniquement, ou sur l'audio et l'écran. Ils peuvent contenir les composants suivants:

Vous pouvez également consulter nos consignes de conception des conversations pour découvrir comment intégrer ces éléments visuels à votre action.

Propriétés

Les réponses enrichies nécessitent les propriétés suivantes et des propriétés facultatives que vous pouvez configurer:

  • Compatible avec les surfaces disposant de la capacité actions.capability.SCREEN_OUTPUT.
  • Le premier élément d'une réponse enrichie doit être une réponse simple.
  • Deux réponses simples au maximum.
  • Une seule carte de base ou StructuredResponse au maximum.
  • Huit chips de suggestion au maximum.
  • Les chips de suggestion ne sont pas autorisés dans FinalResponse
  • Il n'est actuellement pas possible de se connecter au Web depuis un écran connecté.

Les sections suivantes expliquent comment créer différents types de réponses enrichies.

Carte de base

Figure 1. Exemple de carte de base (smartphone)

Une fiche de base affiche les informations suivantes:

  • Image
  • Titre
  • Sous-titre
  • Corps du texte
  • Bouton "Associer"
  • Border

Utilisez les cartes de base principalement à des fins d'affichage. Ils sont conçus pour être concis, présenter des informations clés (ou résumées) aux utilisateurs et leur permettre d'en savoir plus si vous le souhaitez (via un lien Web).

Dans la plupart des cas, vous devez ajouter des chips de suggestion sous les fiches pour poursuivre ou orienter la conversation.

Évitez de répéter les informations présentées dans la fiche dans l'info-bulle de chat, à tout prix.

Propriétés

Le type de réponse de base de la fiche présente les exigences suivantes et les propriétés facultatives que vous pouvez configurer:

  • Compatible avec les surfaces disposant de la capacité actions.capability.SCREEN_OUTPUT.
  • Texte formaté (obligatoire s'il n'y a pas d'image)
    • Texte brut par défaut.
    • Ne doit pas contenir de lien.
    • 10 lignes avec une image, 15 lignes sans image. Il s'agit d'environ 500 (avec image) ou 750 (sans image). Les téléphones à petit écran tronque également le texte plus tôt que les téléphones à grand écran. Si le texte contient trop de lignes, il est tronqué lors de la dernière coupure au niveau d'un point avec des points de suspension.
    • Un sous-ensemble limité de Markdown est accepté :
      • Nouvelle ligne avec une espace double suivie de \n
      • **bold**
      • *italics*
  • Image (obligatoire en l'absence de mise en forme)
    • Toutes les images doivent avoir une hauteur de 192 dp.
    • Si le format de l'image est différent de celui de l'écran, l'image est centrée avec des barres grises verticales ou horizontales.
    • La source de l'image est une URL.
    • Les GIF animés sont autorisés.

Facultatif

  • Titre
    • Texte brut.
    • Police et taille fixes.
    • Une seule ligne au maximum. Les caractères supplémentaires sont tronqués.
    • La hauteur de la fiche se réduit si le titre n'est pas indiqué.
  • Sous-titre
    • Texte brut.
    • Police et taille de police fixes.
    • Une seule ligne au maximum. Les caractères supplémentaires sont tronqués.
    • La hauteur de la fiche se réduit si aucun sous-titre n'est spécifié.
  • Bouton "Associer"
    • Veuillez indiquer le titre du lien
    • Un seul lien au maximum
    • Les liens vers des sites extérieurs au domaine du développeur sont autorisés.
    • Le texte du lien ne doit pas être trompeur. Cette étape est effectuée lors du processus d'approbation.
    • Une carte de base ne permet pas d'interagir sans lien. Lorsque l'utilisateur appuie sur le lien, l'utilisateur est redirigé vers le lien, tandis que le corps principal de la carte reste inactif.
  • Bordure
    • Vous pouvez ajuster la bordure entre la carte et le conteneur de l'image afin de personnaliser la présentation de votre fiche de base.
    • Configuré en définissant la propriété de chaîne JSON imageDisplayOptions
Figure 2 : Exemple de carte de base (écran connecté)

Exemple de code

Node.js

app.intent('Basic Card', (conv) => {
  if (!conv.screen) {
    conv.ask('Sorry, try this on a screen device or select the ' +
      'phone surface in the simulator.');
    conv.ask('Which response would you like to see next?');
    return;
  }

  conv.ask(`Here's an example of a basic card.`);
  conv.ask(new BasicCard({
    text: `This is a basic card.  Text in a basic card can include "quotes" and
    most other unicode characters including emojis.  Basic cards also support
    some markdown formatting like *emphasis* or _italics_, **strong** or
    __bold__, and ***bold itallic*** or ___strong emphasis___ as well as other
    things like line  \nbreaks`, // Note the two spaces before '\n' required for
                                 // a line break to be rendered in the card.
    subtitle: 'This is a subtitle',
    title: 'Title: this is a title',
    buttons: new Button({
      title: 'This is a button',
      url: 'https://assistant.google.com/',
    }),
    image: new Image({
      url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
      alt: 'Image alternate text',
    }),
    display: 'CROPPED',
  }));
  conv.ask('Which response would you like to see next?');
});

Java

@ForIntent("Basic Card")
public ActionResponse basicCard(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
    return responseBuilder
        .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
        .add("Which response would you like to see next?")
        .build();
  }

  // Prepare formatted text for card
  String text =
      "This is a basic card.  Text in a basic card can include \"quotes\" and\n"
          + "  most other unicode characters including emoji \uD83D\uDCF1. Basic cards also support\n"
          + "  some markdown formatting like *emphasis* or _italics_, **strong** or\n"
          + "  __bold__, and ***bold itallic*** or ___strong emphasis___ as well as other\n"
          + "  things like line  \\nbreaks"; // Note the two spaces before '\n' required for
  // a line break to be rendered in the card.
  responseBuilder
      .add("Here's an example of a basic card.")
      .add(
          new BasicCard()
              .setTitle("Title: this is a title")
              .setSubtitle("This is a subtitle")
              .setFormattedText(text)
              .setImage(
                  new Image()
                      .setUrl(
                          "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                      .setAccessibilityText("Image alternate text"))
              .setImageDisplayOptions("CROPPED")
              .setButtons(
                  new ArrayList<Button>(
                      Arrays.asList(
                          new Button()
                              .setTitle("This is a Button")
                              .setOpenUrlAction(
                                  new OpenUrlAction().setUrl("https://assistant.google.com"))))))
      .add("Which response would you like to see next?");

  return responseBuilder.build();
}

Node.js

if (!conv.screen) {
  conv.ask('Sorry, try this on a screen device or select the ' +
    'phone surface in the simulator.');
  conv.ask('Which response would you like to see next?');
  return;
}

conv.ask(`Here's an example of a basic card.`);
conv.ask(new BasicCard({
  text: `This is a basic card.  Text in a basic card can include "quotes" and
  most other unicode characters including emojis.  Basic cards also support
  some markdown formatting like *emphasis* or _italics_, **strong** or
  __bold__, and ***bold itallic*** or ___strong emphasis___ as well as other
  things like line  \nbreaks`, // Note the two spaces before '\n' required for
                               // a line break to be rendered in the card.
  subtitle: 'This is a subtitle',
  title: 'Title: this is a title',
  buttons: new Button({
    title: 'This is a button',
    url: 'https://assistant.google.com/',
  }),
  image: new Image({
    url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
    alt: 'Image alternate text',
  }),
  display: 'CROPPED',
}));
conv.ask('Which response would you like to see next?');

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
  return responseBuilder
      .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
      .add("Which response would you like to see next?")
      .build();
}

// Prepare formatted text for card
String text =
    "This is a basic card.  Text in a basic card can include \"quotes\" and\n"
        + "  most other unicode characters including emoji \uD83D\uDCF1. Basic cards also support\n"
        + "  some markdown formatting like *emphasis* or _italics_, **strong** or\n"
        + "  __bold__, and ***bold itallic*** or ___strong emphasis___ as well as other\n"
        + "  things like line  \\nbreaks"; // Note the two spaces before '\n' required for
// a line break to be rendered in the card.
responseBuilder
    .add("Here's an example of a basic card.")
    .add(
        new BasicCard()
            .setTitle("Title: this is a title")
            .setSubtitle("This is a subtitle")
            .setFormattedText(text)
            .setImage(
                new Image()
                    .setUrl(
                        "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                    .setAccessibilityText("Image alternate text"))
            .setImageDisplayOptions("CROPPED")
            .setButtons(
                new ArrayList<Button>(
                    Arrays.asList(
                        new Button()
                            .setTitle("This is a Button")
                            .setOpenUrlAction(
                                new OpenUrlAction().setUrl("https://assistant.google.com"))))))
    .add("Which response would you like to see next?");

return responseBuilder.build();

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Here's an example of a basic card."
            }
          },
          {
            "basicCard": {
              "title": "Title: this is a title",
              "subtitle": "This is a subtitle",
              "formattedText": "This is a basic card.  Text in a basic card can include \"quotes\" and\n    most other unicode characters including emojis.  Basic cards also support\n    some markdown formatting like *emphasis* or _italics_, **strong** or\n    __bold__, and ***bold itallic*** or ___strong emphasis___ as well as other\n    things like line  \nbreaks",
              "image": {
                "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
                "accessibilityText": "Image alternate text"
              },
              "buttons": [
                {
                  "title": "This is a button",
                  "openUrlAction": {
                    "url": "https://assistant.google.com/"
                  }
                }
              ],
              "imageDisplayOptions": "CROPPED"
            }
          },
          {
            "simpleResponse": {
              "textToSpeech": "Which response would you like to see next?"
            }
          }
        ]
      }
    }
  }
}

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Here's an example of a basic card."
              }
            },
            {
              "basicCard": {
                "title": "Title: this is a title",
                "subtitle": "This is a subtitle",
                "formattedText": "This is a basic card.  Text in a basic card can include \"quotes\" and\n    most other unicode characters including emojis.  Basic cards also support\n    some markdown formatting like *emphasis* or _italics_, **strong** or\n    __bold__, and ***bold itallic*** or ___strong emphasis___ as well as other\n    things like line  \nbreaks",
                "image": {
                  "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
                  "accessibilityText": "Image alternate text"
                },
                "buttons": [
                  {
                    "title": "This is a button",
                    "openUrlAction": {
                      "url": "https://assistant.google.com/"
                    }
                  }
                ],
                "imageDisplayOptions": "CROPPED"
              }
            },
            {
              "simpleResponse": {
                "textToSpeech": "Which response would you like to see next?"
              }
            }
          ]
        }
      }
    }
  ]
}
Figure 3. Exemple de carrousel de navigation (smartphone)

Un carrousel de navigation est une réponse enrichie qui permet aux utilisateurs de faire défiler la page verticalement et de sélectionner une carte dans une collection. Les carrousels de navigation sont conçus spécifiquement pour le contenu Web en ouvrant la tuile sélectionnée dans un navigateur Web (ou dans un navigateur AMP si toutes les tuiles sont compatibles avec le format AMP). Le carrousel de navigation persiste également sur la surface de l'Assistant de l'utilisateur pour une navigation ultérieure.

Propriétés

Le type de réponse du carrousel de navigation présente les exigences suivantes et les propriétés facultatives que vous pouvez configurer:

  • Compatible avec les surfaces disposant des fonctionnalités actions.capability.SCREEN_OUTPUT et actions.capability.WEB_BROWSER. Ce type de réponse n'est actuellement pas disponible sur les écrans connectés.
  • Carrousel de navigation
    • Dix tuiles maximum.
    • Au moins deux vignettes.
    • Les tuiles du carrousel doivent toutes rediriger vers un contenu Web (contenu AMP recommandé).
      • Pour que l'utilisateur soit redirigé vers un lecteur AMP, le urlHintType des tuiles de contenu AMP doit être défini sur "AMP_CONTENT".
  • Parcourir les tuiles du carrousel
    • Cohérence des cartes (obligatoire) :
      • Toutes les tuiles d'un carrousel de navigation doivent avoir les mêmes composants. Par exemple, si une carte comporte un champ d'image, les autres tuiles du carrousel doivent également comporter des champs d'image.
      • Si toutes les cartes du carrousel de navigation renvoient à du contenu compatible AMP, l'utilisateur est redirigé vers un navigateur AMP doté de fonctionnalités supplémentaires. Si une carte renvoie vers du contenu standard, toutes les cartes redirigent les utilisateurs vers un navigateur Web.
    • Image (facultatif)
      • Les dimensions de l'image doivent être de 128 dp de haut x 232 dp de large.
      • Si le format de l'image ne correspond pas au cadre de délimitation, l'image est centrée avec des barres de chaque côté. Sur les smartphones, l'image est centrée dans un carré aux angles arrondis.
      • Si un lien image n'est pas fonctionnel, une image d'espace réservé est utilisée.
      • Veuillez saisir un texte alternatif sur une image.
    • Titre (obligatoire)
      • Mêmes options de mise en forme que la fiche textuelle de base
      • Les titres doivent être uniques (pour permettre la sélection vocale).
      • Deux lignes de texte maximum.
      • Taille de la police : 16 sp
    • Description (facultatif)
      • Mêmes options de mise en forme que la fiche textuelle de base
      • Quatre lignes au maximum de texte.
      • Tronqué à l'aide de points de suspension (...)
      • Taille de police 14 sp, gris.
    • Pied de page (facultatif)
      • Police et taille de police fixes.
      • Une ligne de texte au maximum.
      • Tronqué à l'aide de points de suspension (...)
      • Ancrées en bas, de sorte que les tuiles avec moins de lignes de corps de texte peuvent avoir un espace blanc au-dessus du sous-texte.
      • Taille de police 14 sp, gris.
  • Interaction
    • L'utilisateur peut faire défiler l'écran verticalement pour afficher les éléments.
    • Fiche tactile: lorsque l'utilisateur appuie sur un élément, il accède à un navigateur qui affiche la page liée.
  • Saisie vocale
    • Comportement du micro
      • Le micro ne se réactive pas lorsqu'un carrousel de navigation est envoyé à l'utilisateur.
      • L'utilisateur peut toujours appuyer sur le micro ou appeler l'Assistant ("Ok Google") pour le rouvrir.

Conseils

Par défaut, le micro reste fermé après l'envoi du carrousel de navigation. Si vous souhaitez poursuivre la conversation, nous vous recommandons vivement d'ajouter des chips de suggestion sous le carrousel.

Ne répétez jamais les options de la liste sous forme de chips de suggestion. Dans ce contexte, les chips sont utilisés pour réorienter la conversation (et non pour la sélection).

Comme pour les listes, la bulle de chat qui accompagne la fiche de carrousel est un sous-ensemble de l'audio (TTS/SSML). Ici, l'audio (TTS/SSML) intègre la première tuile du carrousel, et nous vous déconseillons vivement de lire tous les éléments du carrousel. Il est préférable de mentionner le premier élément et la raison pour laquelle il se trouve là (par exemple, le plus populaire, le plus récemment acheté, le plus parlé).

Exemple de code

app.intent('Browsing Carousel', (conv) => {
  if (!conv.screen
    || !conv.surface.capabilities.has('actions.capability.WEB_BROWSER')) {
    conv.ask('Sorry, try this on a phone or select the ' +
      'phone surface in the simulator.');
      conv.ask('Which response would you like to see next?');
    return;
  }

  conv.ask(`Here's an example of a browsing carousel.`);
  conv.ask(new BrowseCarousel({
    items: [
      new BrowseCarouselItem({
        title: 'Title of item 1',
        url: 'https://example.com',
        description: 'Description of item 1',
        image: new Image({
          url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
          alt: 'Image alternate text',
        }),
        footer: 'Item 1 footer',
      }),
      new BrowseCarouselItem({
        title: 'Title of item 2',
        url: 'https://example.com',
        description: 'Description of item 2',
        image: new Image({
          url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
          alt: 'Image alternate text',
        }),
        footer: 'Item 2 footer',
      }),
    ],
  }));
});
@ForIntent("Browsing Carousel")
public ActionResponse browseCarousel(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())
      || !request.hasCapability(Capability.WEB_BROWSER.getValue())) {
    return responseBuilder
        .add("Sorry, try this on a phone or select the phone surface in the simulator.")
        .add("Which response would you like to see next?")
        .build();
  }

  responseBuilder
      .add("Here's an example of a browsing carousel.")
      .add(
          new CarouselBrowse()
              .setItems(
                  new ArrayList<CarouselBrowseItem>(
                      Arrays.asList(
                          new CarouselBrowseItem()
                              .setTitle("Title of item 1")
                              .setDescription("Description of item 1")
                              .setOpenUrlAction(new OpenUrlAction().setUrl("https://example.com"))
                              .setImage(
                                  new Image()
                                      .setUrl(
                                          "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                                      .setAccessibilityText("Image alternate text"))
                              .setFooter("Item 1 footer"),
                          new CarouselBrowseItem()
                              .setTitle("Title of item 2")
                              .setDescription("Description of item 2")
                              .setOpenUrlAction(new OpenUrlAction().setUrl("https://example.com"))
                              .setImage(
                                  new Image()
                                      .setUrl(
                                          "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                                      .setAccessibilityText("Image alternate text"))
                              .setFooter("Item 2 footer")))));

  return responseBuilder.build();
}
if (!conv.screen
  || !conv.surface.capabilities.has('actions.capability.WEB_BROWSER')) {
  conv.ask('Sorry, try this on a phone or select the ' +
    'phone surface in the simulator.');
    conv.ask('Which response would you like to see next?');
  return;
}

conv.ask(`Here's an example of a browsing carousel.`);
conv.ask(new BrowseCarousel({
  items: [
    new BrowseCarouselItem({
      title: 'Title of item 1',
      url: 'https://example.com',
      description: 'Description of item 1',
      image: new Image({
        url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
        alt: 'Image alternate text',
      }),
      footer: 'Item 1 footer',
    }),
    new BrowseCarouselItem({
      title: 'Title of item 2',
      url: 'https://example.com',
      description: 'Description of item 2',
      image: new Image({
        url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
        alt: 'Image alternate text',
      }),
      footer: 'Item 2 footer',
    }),
  ],
}));
ResponseBuilder responseBuilder = getResponseBuilder(request);
if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())
    || !request.hasCapability(Capability.WEB_BROWSER.getValue())) {
  return responseBuilder
      .add("Sorry, try this on a phone or select the phone surface in the simulator.")
      .add("Which response would you like to see next?")
      .build();
}

responseBuilder
    .add("Here's an example of a browsing carousel.")
    .add(
        new CarouselBrowse()
            .setItems(
                new ArrayList<CarouselBrowseItem>(
                    Arrays.asList(
                        new CarouselBrowseItem()
                            .setTitle("Title of item 1")
                            .setDescription("Description of item 1")
                            .setOpenUrlAction(new OpenUrlAction().setUrl("https://example.com"))
                            .setImage(
                                new Image()
                                    .setUrl(
                                        "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                                    .setAccessibilityText("Image alternate text"))
                            .setFooter("Item 1 footer"),
                        new CarouselBrowseItem()
                            .setTitle("Title of item 2")
                            .setDescription("Description of item 2")
                            .setOpenUrlAction(new OpenUrlAction().setUrl("https://example.com"))
                            .setImage(
                                new Image()
                                    .setUrl(
                                        "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                                    .setAccessibilityText("Image alternate text"))
                            .setFooter("Item 2 footer")))));

return responseBuilder.build();

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Here's an example of a browsing carousel."
            }
          },
          {
            "carouselBrowse": {
              "items": [
                {
                  "title": "Title of item 1",
                  "openUrlAction": {
                    "url": "https://example.com"
                  },
                  "description": "Description of item 1",
                  "footer": "Item 1 footer",
                  "image": {
                    "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
                    "accessibilityText": "Image alternate text"
                  }
                },
                {
                  "title": "Title of item 2",
                  "openUrlAction": {
                    "url": "https://example.com"
                  },
                  "description": "Description of item 2",
                  "footer": "Item 2 footer",
                  "image": {
                    "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
                    "accessibilityText": "Image alternate text"
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Here's an example of a browsing carousel."
              }
            },
            {
              "carouselBrowse": {
                "items": [
                  {
                    "description": "Description of item 1",
                    "footer": "Item 1 footer",
                    "image": {
                      "accessibilityText": "Image alternate text",
                      "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png"
                    },
                    "openUrlAction": {
                      "url": "https://example.com"
                    },
                    "title": "Title of item 1"
                  },
                  {
                    "description": "Description of item 2",
                    "footer": "Item 2 footer",
                    "image": {
                      "accessibilityText": "Image alternate text",
                      "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png"
                    },
                    "openUrlAction": {
                      "url": "https://example.com"
                    },
                    "title": "Title of item 2"
                  }
                ]
              }
            }
          ]
        }
      },
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ]
    }
  ]
}

Traitement de l'élément sélectionné...

Aucun traitement de suivi n'est nécessaire pour les interactions des utilisateurs avec les éléments du carrousel de navigation, car le carrousel gère le transfert du navigateur. N'oubliez pas que le micro ne se rouvrira pas après que l'utilisateur aura interagi avec un élément du carrousel de navigation. Vous devez donc mettre fin à la conversation ou inclure des chips de suggestion dans votre réponse, conformément aux conseils ci-dessus.

Chips de suggestion

Figure 4. Exemple de chips de suggestion (smartphone)

Utilisez les chips de suggestion pour suggérer des réponses afin de poursuivre ou d'orienter la conversation. Si, au cours de la conversation, il existe une incitation à l'action principale, envisagez de l'afficher comme premier chip de suggestion.

Dans la mesure du possible, vous devez intégrer une suggestion clé dans la bulle de chat, mais uniquement si la réponse ou la conversation par chat semble naturelle.

Propriétés

Les chips de suggestion présentent les exigences et les propriétés facultatives que vous pouvez configurer:

  • Compatible avec les surfaces disposant de la capacité actions.capability.SCREEN_OUTPUT.
  • Pour associer des chips de suggestion au Web, les surfaces doivent également disposer de la capacité actions.capability.WEB_BROWSER. Cette fonctionnalité n'est actuellement pas disponible sur les écrans connectés.
  • Huit chips maximum.
  • Il ne doit pas dépasser 25 caractères.
  • N'accepte que le texte brut.

Figure 5. Exemple de chips de suggestion (écrans connectés)

Exemple de code

Node.js

app.intent('Suggestion Chips', (conv) => {
  if (!conv.screen) {
    conv.ask('Chips can be demonstrated on screen devices.');
    conv.ask('Which response would you like to see next?');
    return;
  }

  conv.ask('These are suggestion chips.');
  conv.ask(new Suggestions('Suggestion 1'));
  conv.ask(new Suggestions(['Suggestion 2', 'Suggestion 3']));
  conv.ask(new LinkOutSuggestion({
    name: 'Suggestion Link',
    url: 'https://assistant.google.com/',
  }));
  conv.ask('Which type of response would you like to see next?'); ;
});

Java

@ForIntent("Suggestion Chips")
public ActionResponse suggestionChips(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
    return responseBuilder
        .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
        .add("Which response would you like to see next?")
        .build();
  }

  responseBuilder
      .add("These are suggestion chips.")
      .addSuggestions(new String[] {"Suggestion 1", "Suggestion 2", "Suggestion 3"})
      .add(
          new LinkOutSuggestion()
              .setDestinationName("Suggestion Link")
              .setUrl("https://assistant.google.com/"))
      .add("Which type of response would you like to see next?");
  return responseBuilder.build();
}

Node.js

if (!conv.screen) {
  conv.ask('Chips can be demonstrated on screen devices.');
  conv.ask('Which response would you like to see next?');
  return;
}

conv.ask('These are suggestion chips.');
conv.ask(new Suggestions('Suggestion 1'));
conv.ask(new Suggestions(['Suggestion 2', 'Suggestion 3']));
conv.ask(new LinkOutSuggestion({
  name: 'Suggestion Link',
  url: 'https://assistant.google.com/',
}));
conv.ask('Which type of response would you like to see next?');

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
  return responseBuilder
      .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
      .add("Which response would you like to see next?")
      .build();
}

responseBuilder
    .add("These are suggestion chips.")
    .addSuggestions(new String[] {"Suggestion 1", "Suggestion 2", "Suggestion 3"})
    .add(
        new LinkOutSuggestion()
            .setDestinationName("Suggestion Link")
            .setUrl("https://assistant.google.com/"))
    .add("Which type of response would you like to see next?");
return responseBuilder.build();

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "These are suggestion chips."
            }
          },
          {
            "simpleResponse": {
              "textToSpeech": "Which type of response would you like to see next?"
            }
          }
        ],
        "suggestions": [
          {
            "title": "Suggestion 1"
          },
          {
            "title": "Suggestion 2"
          },
          {
            "title": "Suggestion 3"
          }
        ],
        "linkOutSuggestion": {
          "destinationName": "Suggestion Link",
          "url": "https://assistant.google.com/"
        }
      }
    }
  }
}

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "These are suggestion chips."
              }
            },
            {
              "simpleResponse": {
                "textToSpeech": "Which type of response would you like to see next?"
              }
            }
          ],
          "suggestions": [
            {
              "title": "Suggestion 1"
            },
            {
              "title": "Suggestion 2"
            },
            {
              "title": "Suggestion 3"
            }
          ],
          "linkOutSuggestion": {
            "destinationName": "Suggestion Link",
            "url": "https://assistant.google.com/"
          }
        }
      }
    }
  ]
}

Réponses multimédias

Figure 6. Exemple de réponse multimédia (smartphone)

Les réponses multimédias permettent à vos actions de lire du contenu audio dont la durée de lecture dépasse la limite de 240 secondes du langage SSML. Le composant principal d'une réponse multimédia est la fiche de suivi unique. La fiche permet à l'utilisateur d'effectuer les opérations suivantes:

  • Lire à nouveau les 10 dernières secondes.
  • Avancer de 30 secondes.
  • Affichez la durée totale du contenu multimédia.
  • Un indicateur de progression de la lecture audio
  • Affichez le temps de lecture écoulé.

Les réponses multimédias sont compatibles avec les commandes audio suivantes pour les interactions vocales:

  • "Hey Google, lance la lecture."
  • "Hey Google, pause."
  • "Hey Google, arrête."
  • "Ok Google, recommence."

Les utilisateurs peuvent également contrôler le volume en disant, par exemple, "Hey Google, monte le volume" ou "Hey Google, règle le volume à 50 %". Les intents de votre action sont prioritaires s'ils traitent des expressions d'entraînement similaires. Laissez l'Assistant gérer ces requêtes, sauf si une action spécifique est justifiée par votre action.

Propriétés

Les réponses multimédias présentent les exigences suivantes et des propriétés facultatives que vous pouvez configurer:

  • Compatible avec les surfaces dotées de la fonctionnalité actions.capability.MEDIA_RESPONSE_AUDIO.
  • Le fichier audio pour la lecture doit être dans un fichier .mp3 correctement formaté. Le streaming en direct n'est pas disponible.
  • Le fichier multimédia pour la lecture doit être spécifié sous la forme d'une URL HTTPS.
  • Image (facultatif)
    • Vous pouvez éventuellement inclure une icône ou une image.
    • Icône
      • Votre icône apparaît sous forme de miniature sans bordure à droite de la fiche du lecteur multimédia.
      • La taille doit être de 36 x 36 dp. Les images de plus grande taille sont redimensionnées.
    • Image
      • La hauteur du conteneur de l'image sera de 192 dp.
      • Votre image s'affiche en haut de la fiche du lecteur multimédia et s'étend sur toute la largeur de la fiche. La plupart des images apparaissent avec des barres en haut ou sur les côtés.
      • Les GIF animés sont autorisés.
    • Vous devez spécifier la source de l'image en tant qu'URL.
    • Veuillez saisir un texte alternatif dans toutes les images.

Comportement sur les surfaces

Les réponses multimédias sont compatibles avec les téléphones Android et Google Home. Le comportement des réponses multimédias dépend de la surface sur laquelle les utilisateurs interagissent avec vos actions.

Sur les téléphones Android, les utilisateurs peuvent voir les réponses multimédias lorsque l'une des conditions suivantes est remplie:

  • L'Assistant Google est au premier plan, et l'écran du téléphone est allumé.
  • L'utilisateur quitte l'Assistant Google pendant la lecture et y retourne dans les 10 minutes suivant la fin de la lecture. Lors du retour à l'Assistant Google, l'utilisateur voit la fiche multimédia et les chips de suggestion.
  • L'Assistant permet aux utilisateurs de contrôler le volume de l'appareil dans votre action de conversation en disant, par exemple, "Augmente le volume" ou "Règle le volume sur 50 %". Si certains de vos intents gèrent des expressions d'entraînement similaires, ils sont prioritaires. Nous vous recommandons de laisser l'Assistant gérer ces requêtes, sauf si votre action est associée à un motif spécifique.

Les commandes multimédias sont disponibles lorsque le téléphone est verrouillé. Sur Android, les commandes s'affichent également dans la zone de notification.

Figure 7. Exemple de réponse multimédia (écran connecté)

Exemple de code

L'exemple de code suivant montre comment mettre à jour vos réponses enrichies pour inclure des médias.

Node.js

app.intent('Media Response', (conv) => {
  if (!conv.surface.capabilities
    .has('actions.capability.MEDIA_RESPONSE_AUDIO')) {
      conv.ask('Sorry, this device does not support audio playback.');
      conv.ask('Which response would you like to see next?');
      return;
  }

  conv.ask('This is a media response example.');
  conv.ask(new MediaObject({
    name: 'Jazz in Paris',
    url: 'https://storage.googleapis.com/automotive-media/Jazz_In_Paris.mp3',
    description: 'A funky Jazz tune',
    icon: new Image({
      url: 'https://storage.googleapis.com/automotive-media/album_art.jpg',
      alt: 'Album cover of an ocean view',
    }),
  }));
  conv.ask(new Suggestions(['Basic Card', 'List',
    'Carousel', 'Browsing Carousel']));
});

Java

@ForIntent("Media Response")
public ActionResponse mediaResponse(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (!request.hasCapability(Capability.MEDIA_RESPONSE_AUDIO.getValue())) {
    return responseBuilder
        .add("Sorry, this device does not support audio playback.")
        .add("Which response would you like to see next?")
        .build();
  }

  responseBuilder
      .add("This is a media response example.")
      .add(
          new MediaResponse()
              .setMediaObjects(
                  new ArrayList<MediaObject>(
                      Arrays.asList(
                          new MediaObject()
                              .setName("Jazz in Paris")
                              .setDescription("A funky Jazz tune")
                              .setContentUrl(
                                  "https://storage.googleapis.com/automotive-media/Jazz_In_Paris.mp3")
                              .setIcon(
                                  new Image()
                                      .setUrl(
                                          "https://storage.googleapis.com/automotive-media/album_art.jpg")
                                      .setAccessibilityText("Album cover of an ocean view")))))
              .setMediaType("AUDIO"))
      .addSuggestions(new String[] {"Basic Card", "List", "Carousel", "Browsing Carousel"});
  return responseBuilder.build();
}

Node.js

if (!conv.surface.capabilities
  .has('actions.capability.MEDIA_RESPONSE_AUDIO')) {
    conv.ask('Sorry, this device does not support audio playback.');
    conv.ask('Which response would you like to see next?');
    return;
}

conv.ask('This is a media response example.');
conv.ask(new MediaObject({
  name: 'Jazz in Paris',
  url: 'https://storage.googleapis.com/automotive-media/Jazz_In_Paris.mp3',
  description: 'A funky Jazz tune',
  icon: new Image({
    url: 'https://storage.googleapis.com/automotive-media/album_art.jpg',
    alt: 'Album cover of an ocean view',
  }),
}));
conv.ask(new Suggestions(['Basic Card', 'List',
  'Carousel', 'Browsing Carousel']));

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
if (!request.hasCapability(Capability.MEDIA_RESPONSE_AUDIO.getValue())) {
  return responseBuilder
      .add("Sorry, this device does not support audio playback.")
      .add("Which response would you like to see next?")
      .build();
}

responseBuilder
    .add("This is a media response example.")
    .add(
        new MediaResponse()
            .setMediaObjects(
                new ArrayList<MediaObject>(
                    Arrays.asList(
                        new MediaObject()
                            .setName("Jazz in Paris")
                            .setDescription("A funky Jazz tune")
                            .setContentUrl(
                                "https://storage.googleapis.com/automotive-media/Jazz_In_Paris.mp3")
                            .setIcon(
                                new Image()
                                    .setUrl(
                                        "https://storage.googleapis.com/automotive-media/album_art.jpg")
                                    .setAccessibilityText("Album cover of an ocean view")))))
            .setMediaType("AUDIO"))
    .addSuggestions(new String[] {"Basic Card", "List", "Carousel", "Browsing Carousel"});
return responseBuilder.build();

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "This is a media response example."
            }
          },
          {
            "mediaResponse": {
              "mediaType": "AUDIO",
              "mediaObjects": [
                {
                  "contentUrl": "https://storage.googleapis.com/automotive-media/Jazz_In_Paris.mp3",
                  "description": "A funky Jazz tune",
                  "icon": {
                    "url": "https://storage.googleapis.com/automotive-media/album_art.jpg",
                    "accessibilityText": "Album cover of an ocean view"
                  },
                  "name": "Jazz in Paris"
                }
              ]
            }
          }
        ],
        "suggestions": [
          {
            "title": "Basic Card"
          },
          {
            "title": "List"
          },
          {
            "title": "Carousel"
          },
          {
            "title": "Browsing Carousel"
          }
        ]
      }
    }
  }
}

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "This is a media response example."
              }
            },
            {
              "mediaResponse": {
                "mediaType": "AUDIO",
                "mediaObjects": [
                  {
                    "contentUrl": "https://storage.googleapis.com/automotive-media/Jazz_In_Paris.mp3",
                    "description": "A funky Jazz tune",
                    "icon": {
                      "url": "https://storage.googleapis.com/automotive-media/album_art.jpg",
                      "accessibilityText": "Album cover of an ocean view"
                    },
                    "name": "Jazz in Paris"
                  }
                ]
              }
            }
          ],
          "suggestions": [
            {
              "title": "Basic Card"
            },
            {
              "title": "List"
            },
            {
              "title": "Carousel"
            },
            {
              "title": "Browsing Carousel"
            }
          ]
        }
      }
    }
  ]
}

Conseils

Votre réponse doit inclure une mediaResponse avec un mediaType de AUDIO et un mediaObject dans le tableau d'éléments de la réponse enrichie. Une réponse multimédia n'accepte qu'un seul objet multimédia. Un objet multimédia doit inclure l'URL de contenu du fichier audio. Un objet multimédia peut éventuellement inclure un nom, un sous-texte (description), ainsi qu'une icône ou une URL d'image.

Sur les téléphones et les appareils Google Home, lorsque la lecture audio de votre action est terminée, l'Assistant Google vérifie si la réponse multimédia correspond à une FinalResponse. Dans le cas contraire, il envoie un rappel à votre traitement, ce qui vous permet de répondre à l'utilisateur.

Votre action doit inclure des chips de suggestion si la réponse n'est pas un FinalResponse.

Gérer le rappel à la fin de la lecture

Votre action doit gérer l'intent actions.intent.MEDIA_STATUS pour inviter l'utilisateur à effectuer un suivi (par exemple, pour lire un autre titre). Votre action reçoit ce rappel une fois la lecture du contenu multimédia terminée. Dans le rappel, l'argument MEDIA_STATUS contient des informations d'état sur le média actuel. La valeur de l'état sera FINISHED ou STATUS_UNSPECIFIED.

Utiliser Dialogflow

Si vous souhaitez utiliser un embranchement conversationnel dans Dialogflow, vous devez configurer un contexte d'entrée de actions_capability_media_response_audio dans l'intent pour vous assurer qu'il ne se déclenche que sur les surfaces compatibles avec une réponse multimédia.

Créer votre traitement

L'extrait de code ci-dessous montre comment écrire le code de traitement pour votre action. Si vous utilisez Dialogflow, remplacez actions.intent.MEDIA_STATUS par le nom d'action spécifié dans l'intent qui reçoit l'événement actions_intent_MEDIA_STATUS (par exemple, "media.status.update).

Node.js

app.intent('Media Status', (conv) => {
  const mediaStatus = conv.arguments.get('MEDIA_STATUS');
  let response = 'Unknown media status received.';
  if (mediaStatus && mediaStatus.status === 'FINISHED') {
    response = 'Hope you enjoyed the tune!';
  }
  conv.ask(response);
  conv.ask('Which response would you like to see next?');
});

Java

@ForIntent("Media Status")
public ActionResponse mediaStatus(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String mediaStatus = request.getMediaStatus();
  String response = "Unknown media status received.";
  if (mediaStatus != null && mediaStatus.equals("FINISHED")) {
    response = "Hope you enjoyed the tune!";
  }
  responseBuilder.add(response);
  responseBuilder.add("Which response would you like to see next?");
  return responseBuilder.build();
}

Node.js

app.intent('actions.intent.MEDIA_STATUS', (conv) => {
  const mediaStatus = conv.arguments.get('MEDIA_STATUS');
  let response = 'Unknown media status received.';
  if (mediaStatus && mediaStatus.status === 'FINISHED') {
    response = 'Hope you enjoyed the tune!';
  }
  conv.ask(response);
  conv.ask('Which response would you like to see next?');
});

Java

@ForIntent("actions.intent.MEDIA_STATUS")
public ActionResponse mediaStatus(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String mediaStatus = request.getMediaStatus();
  String response = "Unknown media status received.";
  if (mediaStatus != null && mediaStatus.equals("FINISHED")) {
    response = "Hope you enjoyed the tune!";
  }
  responseBuilder.add(response);
  responseBuilder.add("Which response would you like to see next?");
  return responseBuilder.build();
}

JSON

Notez que le fichier JSON ci-dessous décrit une requête de webhook.

{
  "responseId": "151b68df-98de-41fb-94b5-caeace90a7e9-21947381",
  "queryResult": {
    "queryText": "actions_intent_MEDIA_STATUS",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "Webhook failed for intent: Media Status",
    "fulfillmentMessages": [
      {
        "text": {
          "text": [
            "Webhook failed for intent: Media Status"
          ]
        }
      }
    ],
    "outputContexts": [
      {
        "name": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA/contexts/actions_capability_media_response_audio"
      },
      {
        "name": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA/contexts/actions_capability_account_linking"
      },
      {
        "name": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA/contexts/actions_capability_web_browser"
      },
      {
        "name": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA/contexts/actions_capability_screen_output"
      },
      {
        "name": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA/contexts/actions_capability_audio_output"
      },
      {
        "name": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA/contexts/google_assistant_input_type_voice"
      },
      {
        "name": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA/contexts/actions_intent_media_status",
        "parameters": {
          "MEDIA_STATUS": {
            "@type": "type.googleapis.com/google.actions.v2.MediaStatus",
            "status": "FINISHED"
          }
        }
      }
    ],
    "intent": {
      "name": "projects/df-responses-kohler/agent/intents/068b27d3-c148-4044-bfab-dfa37eebd90d",
      "displayName": "Media Status"
    },
    "intentDetectionConfidence": 1,
    "languageCode": "en"
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "user": {
        "locale": "en-US",
        "lastSeen": "2019-08-04T23:57:15Z",
        "userVerificationStatus": "VERIFIED"
      },
      "conversation": {
        "conversationId": "ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA",
        "type": "ACTIVE",
        "conversationToken": "[]"
      },
      "inputs": [
        {
          "intent": "actions.intent.MEDIA_STATUS",
          "rawInputs": [
            {
              "inputType": "VOICE"
            }
          ],
          "arguments": [
            {
              "name": "MEDIA_STATUS",
              "extension": {
                "@type": "type.googleapis.com/google.actions.v2.MediaStatus",
                "status": "FINISHED"
              }
            }
          ]
        }
      ],
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.ACCOUNT_LINKING"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          },
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          }
        ]
      },
      "isInSandbox": true,
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.WEB_BROWSER"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            }
          ]
        }
      ],
      "requestType": "SIMULATOR"
    }
  },
  "session": "projects/df-responses-kohler/agent/sessions/ABwppHHsebncupHK11oKhsCTgyH96GRNYH-xpeeMTqb-cvOxbd67QenbRlZM4bGAIB8_KXdTfI7-7lYVKN1ovAhCaA"
}

JSON

Notez que le fichier JSON ci-dessous décrit une requête de webhook.

{
  "user": {
    "locale": "en-US",
    "lastSeen": "2019-08-06T07:38:40Z",
    "userVerificationStatus": "VERIFIED"
  },
  "conversation": {
    "conversationId": "ABwppHGcqunXh1M6IE0lu2sVqXdpJfdpC5FWMkMSXQskK1nzb4IkSUSRqQzoEr0Ly0z_G3mwyZlk5rFtd1w",
    "type": "NEW"
  },
  "inputs": [
    {
      "intent": "actions.intent.MEDIA_STATUS",
      "rawInputs": [
        {
          "inputType": "VOICE"
        }
      ],
      "arguments": [
        {
          "name": "MEDIA_STATUS",
          "extension": {
            "@type": "type.googleapis.com/google.actions.v2.MediaStatus",
            "status": "FINISHED"
          }
        }
      ]
    }
  ],
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.WEB_BROWSER"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      },
      {
        "name": "actions.capability.ACCOUNT_LINKING"
      }
    ]
  },
  "isInSandbox": true,
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.WEB_BROWSER"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        }
      ]
    }
  ],
  "requestType": "SIMULATOR"
}

Fiches

Les fiches vous permettent d'afficher des données tabulaires dans votre réponse (par exemple, les résultats sportifs, les résultats des élections et les vols). Vous pouvez définir les colonnes et les lignes (jusqu'à trois) que l'Assistant doit afficher dans votre fiche. Vous pouvez également définir des colonnes et des lignes supplémentaires, ainsi que leur priorité.

Les tables sont différentes des listes verticales, car elles affichent des données statiques et ne peuvent pas interagir, comme les éléments de liste.

Figure 8. Exemple de fiche de tableau (écran connecté)

Propriétés

Les fiches des tables présentent les exigences et les propriétés facultatives que vous pouvez configurer:

  • Compatible avec les surfaces disposant de la capacité actions.capability.SCREEN_OUTPUT.

La section suivante récapitule comment personnaliser les éléments d'une fiche.

Nom Est facultatif Est personnalisable Notes de personnalisation
title Oui Oui Titre global du tableau. Ce champ doit être défini si le sous-titre est défini. Vous pouvez personnaliser la famille et la couleur des polices.
subtitle Oui Non Sous-titre du tableau.
image Oui Oui Image associée au tableau.
Row Non Oui

Données de ligne du tableau. consiste en un tableau d'objets Cell et une propriété divider_after qui indique la présence d'un séparateur après la ligne.

L'affichage des trois premières lignes est garanti, mais il est possible que d'autres n'apparaissent pas sur certaines surfaces.

Veuillez utiliser le simulateur pour afficher les lignes à afficher pour une surface donnée. Sur les surfaces compatibles avec la fonctionnalité WEB_BROWSER, vous pouvez rediriger l'utilisateur vers une page Web contenant plus de données. L'association au Web n'est actuellement pas disponible pour les écrans connectés.

ColumnProperties Oui Oui En-tête et alignement d'une colonne. Comprend une propriété header (représentant le texte d'en-tête d'une colonne) et une propriété horizontal_alignment (de type HorizontalAlignment).
Cell Non Oui Décrit une cellule d'une ligne. Chaque cellule contient une chaîne représentant une valeur textuelle. Vous pouvez personnaliser le texte de la cellule.
Button Oui Oui Objet bouton qui apparaît généralement au bas d'une fiche. Une fiche de table ne peut comporter qu'un seul bouton. Vous pouvez personnaliser la couleur du bouton.
HorizontalAlignment Oui Oui Alignement horizontal du contenu dans la cellule. Les valeurs peuvent être LEADING, CENTER ou TRAILING. Si ce paramètre n'est pas spécifié, le contenu est aligné sur le bord gauche de la cellule.

Exemple de code

Les extraits de code suivants montrent comment intégrer une fiche de table simple:

Node.js

app.intent('Simple Table Card', (conv) => {
  if (!conv.screen) {
    conv.ask('Sorry, try this on a screen device or select the ' +
      'phone surface in the simulator.');
    conv.ask('Which response would you like to see next?');
    return;
  }

  conv.ask('This is a simple table example.');
  conv.ask(new Table({
    dividers: true,
    columns: ['header 1', 'header 2', 'header 3'],
    rows: [
      ['row 1 item 1', 'row 1 item 2', 'row 1 item 3'],
      ['row 2 item 1', 'row 2 item 2', 'row 2 item 3'],
    ],
  }));
  conv.ask('Which response would you like to see next?');
});

Java

@ForIntent("Simple Table Card")
public ActionResponse simpleTable(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
    return responseBuilder
        .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
        .add("Which response would you like to see next?")
        .build();
  }

  responseBuilder
      .add("This is a simple table example.")
      .add(
          new TableCard()
              .setColumnProperties(
                  Arrays.asList(
                      new TableCardColumnProperties().setHeader("header 1"),
                      new TableCardColumnProperties().setHeader("header 2"),
                      new TableCardColumnProperties().setHeader("header 3")))
              .setRows(
                  Arrays.asList(
                      new TableCardRow()
                          .setCells(
                              Arrays.asList(
                                  new TableCardCell().setText("row 1 item 1"),
                                  new TableCardCell().setText("row 1 item 2"),
                                  new TableCardCell().setText("row 1 item 3"))),
                      new TableCardRow()
                          .setCells(
                              Arrays.asList(
                                  new TableCardCell().setText("row 2 item 1"),
                                  new TableCardCell().setText("row 2 item 2"),
                                  new TableCardCell().setText("row 2 item 3"))))));
  return responseBuilder.build();
}

Node.js

if (!conv.screen) {
  conv.ask('Sorry, try this on a screen device or select the ' +
    'phone surface in the simulator.');
  conv.ask('Which response would you like to see next?');
  return;
}

conv.ask('This is a simple table example.');
conv.ask(new Table({
  dividers: true,
  columns: ['header 1', 'header 2', 'header 3'],
  rows: [
    ['row 1 item 1', 'row 1 item 2', 'row 1 item 3'],
    ['row 2 item 1', 'row 2 item 2', 'row 2 item 3'],
  ],
}));
conv.ask('Which response would you like to see next?');

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
  return responseBuilder
      .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
      .add("Which response would you like to see next?")
      .build();
}

responseBuilder
    .add("This is a simple table example.")
    .add(
        new TableCard()
            .setColumnProperties(
                Arrays.asList(
                    new TableCardColumnProperties().setHeader("header 1"),
                    new TableCardColumnProperties().setHeader("header 2"),
                    new TableCardColumnProperties().setHeader("header 3")))
            .setRows(
                Arrays.asList(
                    new TableCardRow()
                        .setCells(
                            Arrays.asList(
                                new TableCardCell().setText("row 1 item 1"),
                                new TableCardCell().setText("row 1 item 2"),
                                new TableCardCell().setText("row 1 item 3"))),
                    new TableCardRow()
                        .setCells(
                            Arrays.asList(
                                new TableCardCell().setText("row 2 item 1"),
                                new TableCardCell().setText("row 2 item 2"),
                                new TableCardCell().setText("row 2 item 3"))))));
return responseBuilder.build();

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "This is a simple table example."
            }
          },
          {
            "tableCard": {
              "rows": [
                {
                  "cells": [
                    {
                      "text": "row 1 item 1"
                    },
                    {
                      "text": "row 1 item 2"
                    },
                    {
                      "text": "row 1 item 3"
                    }
                  ],
                  "dividerAfter": true
                },
                {
                  "cells": [
                    {
                      "text": "row 2 item 1"
                    },
                    {
                      "text": "row 2 item 2"
                    },
                    {
                      "text": "row 2 item 3"
                    }
                  ],
                  "dividerAfter": true
                }
              ],
              "columnProperties": [
                {
                  "header": "header 1"
                },
                {
                  "header": "header 2"
                },
                {
                  "header": "header 3"
                }
              ]
            }
          },
          {
            "simpleResponse": {
              "textToSpeech": "Which response would you like to see next?"
            }
          }
        ]
      }
    }
  }
}

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "This is a simple table example."
              }
            },
            {
              "tableCard": {
                "columnProperties": [
                  {
                    "header": "header 1"
                  },
                  {
                    "header": "header 2"
                  },
                  {
                    "header": "header 3"
                  }
                ],
                "rows": [
                  {
                    "cells": [
                      {
                        "text": "row 1 item 1"
                      },
                      {
                        "text": "row 1 item 2"
                      },
                      {
                        "text": "row 1 item 3"
                      }
                    ],
                    "dividerAfter": true
                  },
                  {
                    "cells": [
                      {
                        "text": "row 2 item 1"
                      },
                      {
                        "text": "row 2 item 2"
                      },
                      {
                        "text": "row 2 item 3"
                      }
                    ],
                    "dividerAfter": true
                  }
                ]
              }
            },
            {
              "simpleResponse": {
                "textToSpeech": "Which response would you like to see next?"
              }
            }
          ]
        }
      },
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ]
    }
  ]
}

Les extraits de code suivants montrent comment implémenter une fiche de tableau complexe:

Node.js

app.intent('Advanced Table Card', (conv) => {
  if (!conv.screen) {
    conv.ask('Sorry, try this on a screen device or select the ' +
      'phone surface in the simulator.');
    conv.ask('Which response would you like to see next?');
    return;
  }

  conv.ask('This is a table with all the possible fields.');
  conv.ask(new Table({
    title: 'Table Title',
    subtitle: 'Table Subtitle',
    image: new Image({
      url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
      alt: 'Alt Text',
    }),
    columns: [
      {
        header: 'header 1',
        align: 'CENTER',
      },
      {
        header: 'header 2',
        align: 'LEADING',
      },
      {
        header: 'header 3',
        align: 'TRAILING',
      },
    ],
    rows: [
      {
        cells: ['row 1 item 1', 'row 1 item 2', 'row 1 item 3'],
        dividerAfter: false,
      },
      {
        cells: ['row 2 item 1', 'row 2 item 2', 'row 2 item 3'],
        dividerAfter: true,
      },
      {
        cells: ['row 3 item 1', 'row 3 item 2', 'row 3 item 3'],
      },
    ],
    buttons: new Button({
      title: 'Button Text',
      url: 'https://assistant.google.com',
    }),
  }));
  conv.ask('Which response would you like to see next?');
});

Java

@ForIntent("Advanced Table Card")
public ActionResponse advancedTable(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
    return responseBuilder
        .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
        .add("Which response would you like to see next?")
        .build();
  }

  responseBuilder
      .add("This is a table with all the possible fields.")
      .add(
          new TableCard()
              .setTitle("Table Title")
              .setSubtitle("Table Subtitle")
              .setImage(
                  new Image()
                      .setUrl(
                          "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                      .setAccessibilityText("Alt text"))
              .setButtons(
                  Arrays.asList(
                      new Button()
                          .setTitle("Button Text")
                          .setOpenUrlAction(
                              new OpenUrlAction().setUrl("https://assistant.google.com"))))
              .setColumnProperties(
                  Arrays.asList(
                      new TableCardColumnProperties()
                          .setHeader("header 1")
                          .setHorizontalAlignment("CENTER"),
                      new TableCardColumnProperties()
                          .setHeader("header 2")
                          .setHorizontalAlignment("LEADING"),
                      new TableCardColumnProperties()
                          .setHeader("header 3")
                          .setHorizontalAlignment("TRAILING")))
              .setRows(
                  Arrays.asList(
                      new TableCardRow()
                          .setCells(
                              Arrays.asList(
                                  new TableCardCell().setText("row 1 item 1"),
                                  new TableCardCell().setText("row 1 item 2"),
                                  new TableCardCell().setText("row 1 item 3")))
                          .setDividerAfter(false),
                      new TableCardRow()
                          .setCells(
                              Arrays.asList(
                                  new TableCardCell().setText("row 2 item 1"),
                                  new TableCardCell().setText("row 2 item 2"),
                                  new TableCardCell().setText("row 2 item 3")))
                          .setDividerAfter(true),
                      new TableCardRow()
                          .setCells(
                              Arrays.asList(
                                  new TableCardCell().setText("row 2 item 1"),
                                  new TableCardCell().setText("row 2 item 2"),
                                  new TableCardCell().setText("row 2 item 3"))))));
  return responseBuilder.build();
}

Node.js

if (!conv.screen) {
  conv.ask('Sorry, try this on a screen device or select the ' +
    'phone surface in the simulator.');
  conv.ask('Which response would you like to see next?');
  return;
}

conv.ask('This is a table with all the possible fields.');
conv.ask(new Table({
  title: 'Table Title',
  subtitle: 'Table Subtitle',
  image: new Image({
    url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
    alt: 'Alt Text',
  }),
  columns: [
    {
      header: 'header 1',
      align: 'CENTER',
    },
    {
      header: 'header 2',
      align: 'LEADING',
    },
    {
      header: 'header 3',
      align: 'TRAILING',
    },
  ],
  rows: [
    {
      cells: ['row 1 item 1', 'row 1 item 2', 'row 1 item 3'],
      dividerAfter: false,
    },
    {
      cells: ['row 2 item 1', 'row 2 item 2', 'row 2 item 3'],
      dividerAfter: true,
    },
    {
      cells: ['row 3 item 1', 'row 3 item 2', 'row 3 item 3'],
    },
  ],
  buttons: new Button({
    title: 'Button Text',
    url: 'https://assistant.google.com',
  }),
}));
conv.ask('Which response would you like to see next?');

Java

ResponseBuilder responseBuilder = getResponseBuilder(request);
if (!request.hasCapability(Capability.SCREEN_OUTPUT.getValue())) {
  return responseBuilder
      .add("Sorry, try ths on a screen device or select the phone surface in the simulator.")
      .add("Which response would you like to see next?")
      .build();
}

responseBuilder
    .add("This is a table with all the possible fields.")
    .add(
        new TableCard()
            .setTitle("Table Title")
            .setSubtitle("Table Subtitle")
            .setImage(
                new Image()
                    .setUrl(
                        "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png")
                    .setAccessibilityText("Alt text"))
            .setButtons(
                Arrays.asList(
                    new Button()
                        .setTitle("Button Text")
                        .setOpenUrlAction(
                            new OpenUrlAction().setUrl("https://assistant.google.com"))))
            .setColumnProperties(
                Arrays.asList(
                    new TableCardColumnProperties()
                        .setHeader("header 1")
                        .setHorizontalAlignment("CENTER"),
                    new TableCardColumnProperties()
                        .setHeader("header 2")
                        .setHorizontalAlignment("LEADING"),
                    new TableCardColumnProperties()
                        .setHeader("header 3")
                        .setHorizontalAlignment("TRAILING")))
            .setRows(
                Arrays.asList(
                    new TableCardRow()
                        .setCells(
                            Arrays.asList(
                                new TableCardCell().setText("row 1 item 1"),
                                new TableCardCell().setText("row 1 item 2"),
                                new TableCardCell().setText("row 1 item 3")))
                        .setDividerAfter(false),
                    new TableCardRow()
                        .setCells(
                            Arrays.asList(
                                new TableCardCell().setText("row 2 item 1"),
                                new TableCardCell().setText("row 2 item 2"),
                                new TableCardCell().setText("row 2 item 3")))
                        .setDividerAfter(true),
                    new TableCardRow()
                        .setCells(
                            Arrays.asList(
                                new TableCardCell().setText("row 2 item 1"),
                                new TableCardCell().setText("row 2 item 2"),
                                new TableCardCell().setText("row 2 item 3"))))));
return responseBuilder.build();

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "This is a table with all the possible fields."
            }
          },
          {
            "tableCard": {
              "title": "Table Title",
              "subtitle": "Table Subtitle",
              "image": {
                "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
                "accessibilityText": "Alt Text"
              },
              "rows": [
                {
                  "cells": [
                    {
                      "text": "row 1 item 1"
                    },
                    {
                      "text": "row 1 item 2"
                    },
                    {
                      "text": "row 1 item 3"
                    }
                  ],
                  "dividerAfter": false
                },
                {
                  "cells": [
                    {
                      "text": "row 2 item 1"
                    },
                    {
                      "text": "row 2 item 2"
                    },
                    {
                      "text": "row 2 item 3"
                    }
                  ],
                  "dividerAfter": true
                },
                {
                  "cells": [
                    {
                      "text": "row 3 item 1"
                    },
                    {
                      "text": "row 3 item 2"
                    },
                    {
                      "text": "row 3 item 3"
                    }
                  ]
                }
              ],
              "columnProperties": [
                {
                  "header": "header 1",
                  "horizontalAlignment": "CENTER"
                },
                {
                  "header": "header 2",
                  "horizontalAlignment": "LEADING"
                },
                {
                  "header": "header 3",
                  "horizontalAlignment": "TRAILING"
                }
              ],
              "buttons": [
                {
                  "title": "Button Text",
                  "openUrlAction": {
                    "url": "https://assistant.google.com"
                  }
                }
              ]
            }
          },
          {
            "simpleResponse": {
              "textToSpeech": "Which response would you like to see next?"
            }
          }
        ]
      }
    }
  }
}

JSON

Notez que le fichier JSON ci-dessous décrit une réponse webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "This is a table with all the possible fields."
              }
            },
            {
              "tableCard": {
                "title": "Table Title",
                "subtitle": "Table Subtitle",
                "image": {
                  "url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
                  "accessibilityText": "Alt Text"
                },
                "rows": [
                  {
                    "cells": [
                      {
                        "text": "row 1 item 1"
                      },
                      {
                        "text": "row 1 item 2"
                      },
                      {
                        "text": "row 1 item 3"
                      }
                    ],
                    "dividerAfter": false
                  },
                  {
                    "cells": [
                      {
                        "text": "row 2 item 1"
                      },
                      {
                        "text": "row 2 item 2"
                      },
                      {
                        "text": "row 2 item 3"
                      }
                    ],
                    "dividerAfter": true
                  },
                  {
                    "cells": [
                      {
                        "text": "row 3 item 1"
                      },
                      {
                        "text": "row 3 item 2"
                      },
                      {
                        "text": "row 3 item 3"
                      }
                    ]
                  }
                ],
                "columnProperties": [
                  {
                    "header": "header 1",
                    "horizontalAlignment": "CENTER"
                  },
                  {
                    "header": "header 2",
                    "horizontalAlignment": "LEADING"
                  },
                  {
                    "header": "header 3",
                    "horizontalAlignment": "TRAILING"
                  }
                ],
                "buttons": [
                  {
                    "title": "Button Text",
                    "openUrlAction": {
                      "url": "https://assistant.google.com"
                    }
                  }
                ]
              }
            },
            {
              "simpleResponse": {
                "textToSpeech": "Which response would you like to see next?"
              }
            }
          ]
        }
      }
    }
  ]
}

Personnaliser vos réponses

Vous pouvez modifier l'apparence de vos réponses enrichies en créant un thème personnalisé. Si vous définissez un thème pour votre projet Actions, les réponses enrichies apportées aux actions de votre projet sont stylisées en fonction de votre thème. Ce branding personnalisé peut être utile pour définir un aspect unique de la conversation lorsque les utilisateurs appellent vos actions sur une surface avec un écran.

Pour définir un thème de réponse personnalisé:

  1. Dans la console Actions, accédez à Développer > Personnalisation du thème.
  2. Définissez tout ou partie des options suivantes :
    • Couleur de l'arrière-plan à utiliser comme arrière-plan de vos fiches. En général, vous devez utiliser une couleur claire pour l'arrière-plan afin que le contenu de la carte soit facile à lire.
    • La couleur principale est la couleur principale des textes d'en-tête et éléments de l'interface utilisateur de vos fiches. En règle générale, une couleur principale plus foncée doit être contrastée avec l'arrière-plan.
    • La famille de polices décrit le type de police utilisé pour les titres et d'autres éléments de texte bien visibles.
    • Le style de l'angle de l'image peut modifier l'apparence des angles de vos fiches.
    • L'image de fond utilise une image personnalisée à la place de la couleur d'arrière-plan. Vous devez fournir deux images différentes pour les appareils de surface en mode paysage ou portrait, respectivement. Notez que si vous utilisez une image de fond, la couleur principale est le blanc.
  3. Cliquez sur Enregistrer.
Figure 9. Personnaliser le thème dans la console Actions