Explorar no Dialogflow
Clique em Continuar para importar nossa amostra de respostas para o Dialogflow. Em seguida, siga as etapas abaixo para implantar e testar a amostra:
- Digite um nome de agente e crie um novo agente do Dialogflow para a amostra.
- Depois que a importação do agente for concluída, clique em Go to agent.
- No menu de navegação principal, acesse Fulfillment.
- Ative o Editor in-line e clique em Implantar. O editor contém o código de amostra.
- No menu de navegação principal, acesse Integrações e clique em Google Assistente.
- Na janela modal exibida, ative as Alterações de visualização automática e clique em Testar para abrir o simulador do Actions.
- No simulador, digite
Talk to my test app
para testar a amostra.
Use uma resposta avançada se quiser exibir elementos visuais para melhorar as interações do usuário com a ação. Esses elementos visuais podem dar dicas sobre como continuar uma conversa.
As respostas avançadas podem aparecer somente na tela ou em experiências de áudio e tela. Eles podem conter os seguintes componentes:
- Uma ou duas respostas simples (balões de chat).
- Um cartão básico opcional.
- Ícones de sugestão opcionais.
- Um ícone de link opcional.
Você também pode consultar nossas diretrizes de design de conversa para aprender a incorporar esses elementos visuais à ação.
Propriedades
As respostas avançadas têm os seguintes requisitos e propriedades opcionais que podem ser configuradas:
- Compatível com plataformas com o recurso
actions.capability.SCREEN_OUTPUT
. - O primeiro item de uma resposta avançada precisa ser uma resposta simples.
- No máximo duas respostas simples.
- No máximo, um cartão básico ou
StructuredResponse
. - No máximo, oito ícones de sugestão.
- Os ícones de sugestão não são permitidos em uma
FinalResponse
- No momento, não é possível vincular à Web usando smart displays.
As seções a seguir mostram como criar vários tipos de respostas avançadas.
Cartão básico

Um card básico exibe informações que podem incluir o seguinte:
- Imagem
- Título
- Subtítulo
- Corpo do texto
- Botão "Vincular"
- Borda
Use cartões básicos principalmente para fins de exibição. Eles foram concisos para apresentar informações importantes (ou resumidas) aos usuários e permitir que eles aprendam mais se você escolher (usando um link da Web).
Na maioria das situações, é necessário adicionar ícones de sugestão abaixo dos cartões para continuar ou dinamizar a conversa.
Evite repetir as informações apresentadas no card no balão de chat a qualquer custo.
Propriedades
O tipo básico de resposta do cartão tem os seguintes requisitos e propriedades opcionais que podem ser configurados:
- Compatível com plataformas com o recurso
actions.capability.SCREEN_OUTPUT
. - Texto formatado (obrigatório se não houver imagem)
- Texto simples por padrão.
- Não pode conter um link.
- Limite de 10 linhas com imagem, limite de 15 linhas sem imagem. Isso equivale a cerca de 500 (com imagem) ou 750 caracteres (sem imagem). Os smartphones com tela menor também truncam o texto antes dos smartphones com tela maior. Se o texto contiver muitas linhas, ele será truncado na última quebra de palavra com reticências.
- Um subconjunto limitado de marcações é permitido:
- Nova linha seguida por um espaço duplo por \n
**bold**
*italics*
- Imagem (obrigatória se não houver texto formatado)
- Todas as imagens tiveram 192 dp de altura.
- Se a proporção da imagem for diferente da tela, ela será centralizada com barras cinza nas bordas vertical ou horizontal.
- A origem da imagem é um URL.
- GIFs são permitidos.
Opcional
- Título
- Texto simples.
- Fonte e tamanho fixos.
- No máximo, uma linha; caracteres extras são truncados.
- A altura do card será recolhida se nenhum título for especificado.
- Subtítulo
- Texto simples.
- Fonte e tamanho da fonte fixos.
- No máximo, uma linha; caracteres extras são truncados.
- A altura do card será recolhida se nenhuma legenda for especificada.
- Botão "Vincular"
- O título do link é obrigatório
- No máximo, um link
- Links para sites fora do domínio do desenvolvedor são permitidos.
- O texto do link não pode ser enganoso. Isso é verificado no processo de aprovação.
- Um cartão básico não tem recursos de interação sem um link. O toque no link leva o usuário ao link, enquanto o corpo principal do cartão permanece inativo.
- Borda
- A borda entre o cartão e o contêiner de imagem pode ser ajustada para personalizar a apresentação do cartão básico.
- Configurado pela propriedade da string JSON
imageDisplayOptions

Exemplo de código
Node.js (link em inglês)
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 (link em inglês)
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
Observe que o JSON abaixo descreve uma resposta do 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
Observe que o JSON abaixo descreve uma resposta do 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?" } } ] } } } ] }
Navegação no carrossel

Um carrossel de navegação é uma resposta avançada que permite que os usuários rolem verticalmente e selecionem um bloco em uma coleção. Os carrosséis de navegação são projetados especificamente para conteúdo da Web abrindo o bloco selecionado em um navegador da Web (ou um navegador AMP, se todos os blocos estiverem ativados para AMP). O carrossel de navegação também persiste na superfície do Assistente do usuário para navegação posterior.
Propriedades
O tipo de resposta do carrossel de navegação tem os seguintes requisitos e propriedades opcionais que você pode configurar:
- Compatível com plataformas que têm os recursos
actions.capability.SCREEN_OUTPUT
eactions.capability.WEB_BROWSER
. No momento, esse tipo de resposta não está disponível em smart displays. - Navegar no carrossel
- Máximo de dez blocos.
- Mínimo de dois blocos.
- Todos os blocos no carrossel precisam estar vinculados ao conteúdo da Web (conteúdo AMP
recomendado).
- Para que o usuário seja levado a um visualizador de AMP, o
urlHintType
nos blocos de conteúdo AMP precisa ser definido como "AMP_CONTENT".
- Para que o usuário seja levado a um visualizador de AMP, o
- Como procurar blocos do carrossel
- Consistência de blocos (obrigatório):
- Todos os blocos em um carrossel de navegação precisam ter os mesmos componentes. Por exemplo, se um bloco tiver um campo de imagem, o restante dos blocos no carrossel também precisará ter campos de imagem.
- Se todos os blocos no carrossel de navegação vincularem ao conteúdo ativado para AMP, o usuário será levado a um navegador AMP com funcionalidade adicional. Se algum bloco vincular a um conteúdo não AMP, todos os blocos direcionarão os usuários para um navegador da Web.
- Imagem (opcional)
- A imagem é forçada a ter 128 dp de altura x 232 dp de largura.
- Se a proporção não corresponder à caixa delimitadora de imagens, a imagem será centralizada com barras em ambos os lados. Em smartphones, a imagem é centralizada em um quadrado com cantos arredondados.
- Se um link de imagem estiver corrompido, uma imagem de marcador será usada.
- O texto alternativo é obrigatório em uma imagem.
- Título (obrigatório)
- As mesmas opções de formatação do card de texto básico.
- Os títulos devem ser únicos (para suportar a seleção de voz).
- Máximo de duas linhas de texto.
- Tamanho da fonte: 16 sp.
- Descrição (opcional)
- As mesmas opções de formatação do card de texto básico.
- Máximo de quatro linhas de texto.
- Truncada com reticências (...)
- Tamanho da fonte: 14 sp, cinza.
- Rodapé (opcional)
- Fonte e tamanho da fonte fixos.
- Máximo de uma linha de texto.
- Truncada com reticências (...)
- Ancorados na parte inferior, então blocos com menos linhas de corpo de texto podem ter um espaço em branco acima do subtexto.
- Tamanho da fonte: 14 sp, cinza.
- Consistência de blocos (obrigatório):
- Interação
- O usuário pode rolar verticalmente para visualizar os itens.
- Cartão de toque: tocar em um item leva o usuário a um navegador, exibindo a página vinculada.
- Entrada de texto por voz
- Comportamento do microfone
- O microfone não é reaberto quando um carrossel de navegação é enviado ao usuário.
- O usuário ainda pode tocar no microfone ou invocar o Assistente ("OK Google") para reabri-lo.
- Comportamento do microfone
Orientação
Por padrão, o microfone permanece fechado depois que um carrossel de navegação é enviado. Se você quiser continuar a conversa depois, recomendamos adicionar ícones de sugestão abaixo do carrossel.
Nunca repita as opções apresentadas na lista como ícones de sugestão. Os ícones nesse contexto são usados para dinamizar a conversa (não para a seleção de opções).
Assim como nas listas, o balão de bate-papo que acompanha o cartão de carrossel é um subconjunto do áudio (TTS/SSML). O áudio (TTS/SSML) aqui integra o primeiro bloco no carrossel, e não recomendamos a leitura de todos os elementos do carrossel. É melhor mencionar o primeiro item e o motivo de ele estar lá. Por exemplo, o mais popular, o mais comprado, o mais falado.
Exemplo de código
Node.js (link em inglês)
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', }), ], })); });
Java
@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(); }
Node.js (link em inglês)
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', }), ], }));
Java
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();
JSON
Observe que o JSON abaixo descreve uma resposta do 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" } } ] } } ] } } } }
JSON
Observe que o JSON abaixo descreve uma resposta do 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" } ] } ] }
Como processar o item selecionado
O fulfillment de acompanhamento não é necessário para interações do usuário com itens do carrossel do navegador, já que o carrossel processa a transferência do navegador. Lembre-se de que o microfone não será aberto novamente depois que o usuário interagir com um item do carrossel de navegação, então interrompa a conversa ou inclua ícones de sugestão na sua resposta, de acordo com a orientação acima.
Ícones de sugestão

Use ícones de sugestão para dar respostas e continuar ou dinamizar a conversa. Se, durante a conversa, houver uma call-to-action principal, liste-a como o primeiro ícone de sugestão.
Sempre que possível, incorpore uma sugestão importante como parte do balão do bate-papo, mas faça isso apenas se a resposta ou a conversa do bate-papo parecer natural.
Propriedades
Os ícones de sugestão têm os seguintes requisitos e propriedades opcionais que podem ser configurados:
- Compatível com plataformas com o recurso
actions.capability.SCREEN_OUTPUT
. - Para vincular ícones de sugestão à Web, as plataformas também precisam ter o
recurso
actions.capability.WEB_BROWSER
. No momento, esse recurso não está disponível em smart displays. - Máximo de oito ícones.
- O tamanho máximo do texto é de 25 caracteres.
Compatível apenas com texto simples.

Exemplo de código
Node.js (link em inglês)
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 (link em inglês)
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
Observe que o JSON abaixo descreve uma resposta do 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
Observe que o JSON abaixo descreve uma resposta do 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/" } } } } ] }
Respostas de mídia

As respostas de mídia permitem que suas ações reproduzam conteúdo de áudio com uma duração de reprodução maior que o limite de 240 segundos de SSML. O principal componente de uma resposta de mídia é o cartão de faixa única. O cartão permite que o usuário execute estas operações:
- Reproduzir novamente os últimos 10 segundos.
- Avance por 30 segundos.
- Veja a duração total do conteúdo de mídia.
- Veja um indicador de progresso para a reprodução de áudio.
- Veja o tempo decorrido de reprodução.
As respostas de mídia são compatíveis com os seguintes controles de áudio para interação por voz:
- "Ok Google, tocar."
- "Ok Google, pausar."
- "Ok Google, parar."
- "Ok Google, começar de novo."
Os usuários também podem controlar o volume dizendo frases como"Ok Google, aumente o volume" ou "Ok Google, ajuste o volume para 50%". As intents na sua ação terão precedência se processarem frases de treinamento semelhantes. Deixe o Google Assistente processar essas solicitações de usuários, a menos que sua ação tenha um motivo específico para isso.
Propriedades
As respostas de mídia têm os seguintes requisitos e propriedades opcionais que você pode configurar:
- Compatível com plataformas com o recurso
actions.capability.MEDIA_RESPONSE_AUDIO
. - O áudio para reprodução precisa estar em um arquivo
.mp3
formatado corretamente. A transmissão ao vivo não é compatível. - O arquivo de mídia para reprodução precisa ser especificado como um URL HTTPS.
- Imagem (opcional)
- Você tem a opção de incluir um ícone ou uma imagem.
- Ícone
- O ícone aparece como uma miniatura sem borda à direita do card do player de mídia.
- O tamanho deve ser 36 x 36 dp. Imagens maiores são redimensionadas para se ajustarem.
- Imagem
- O contêiner da imagem terá 192 dp de altura.
- A imagem é exibida na parte superior do card do player de mídia e ocupa toda a largura dele. A maioria das imagens será exibida com barras na parte superior ou laterais.
- GIFs são permitidos.
- Você deve especificar a origem da imagem como um URL.
- O texto alternativo é obrigatório em todas as imagens.
Comportamento nas plataformas
As respostas de mídia são compatíveis com smartphones Android e no Google Home. O comportamento de respostas de mídia depende da superfície em que os usuários interagem com suas ações.
Em smartphones Android, os usuários podem ver respostas de mídia quando qualquer uma destas condições for atendida:
- O Google Assistente está em primeiro plano, e a tela do smartphone está ativada.
- O usuário sai do Google Assistente durante a reprodução de áudio e retorna ao Google Assistente até 10 minutos após a conclusão da reprodução. Ao retornar ao Google Assistente, o usuário vê o card de mídia e os ícones de sugestão.
- O Assistente permite que os usuários controlem o volume do dispositivo na ação de conversa, dizendo "aumentar o volume" ou "definir o volume para 50%". Se você tiver intents que processam frases de treinamento semelhantes, suas intents terão precedência. Recomendamos permitir que o Assistente gerencie essas solicitações de usuários, a menos que sua ação tenha um motivo específico para isso.
Os controles de mídia ficam disponíveis enquanto o smartphone está bloqueado. No Android, os controles também aparecem na área de notificação.

Exemplo de código
O exemplo de código a seguir mostra como atualizar suas respostas avançadas para incluir mídia.
Node.js (link em inglês)
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 (link em inglês)
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
Observe que o JSON abaixo descreve uma resposta do 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
Observe que o JSON abaixo descreve uma resposta do 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" } ] } } } ] }
Orientação
Sua resposta precisa incluir um mediaResponse
com um mediaType
de AUDIO
e
contendo um mediaObject
na matriz de itens da resposta avançada. Uma resposta de mídia é compatível com um único objeto de mídia. Um objeto de mídia precisa incluir o URL de conteúdo do arquivo de áudio. Um objeto de mídia pode incluir um nome, subtexto
(descrição) e um ícone ou URL de imagem.
Em smartphones e no Google Home, quando a ação conclui a reprodução de áudio,
o Google Assistente verifica se a resposta é FinalResponse
.
Caso contrário, ele envia um callback para o fulfillment, permitindo que você responda ao
usuário.
Sua ação precisa incluir ícones de sugestão se a
resposta não for um FinalResponse
.
Como gerenciar o callback após a conclusão da reprodução
Sua ação precisa processar a intent actions.intent.MEDIA_STATUS
para solicitar
que o usuário faça o acompanhamento (por exemplo, para tocar outra música). Seu Action recebe
esse callback quando a reprodução de mídia é concluída. No callback, o argumento MEDIA_STATUS
contém informações de status sobre a mídia atual. O valor de status será FINISHED
ou STATUS_UNSPECIFIED
.
Como usar o Dialogflow
Se você quiser executar a ramificação de conversa no Dialogflow, precisará configurar um contexto de entrada de actions_capability_media_response_audio
na intent para garantir que ela seja acionada apenas em superfícies compatíveis com uma resposta de mídia.
Como criar um fulfillment
O snippet de código abaixo mostra como você pode escrever o código de fulfillment da sua ação. Se você estiver usando o Dialogflow, substitua actions.intent.MEDIA_STATUS
pelo nome da ação especificada na intent que recebe o evento actions_intent_MEDIA_STATUS
(por exemplo, "media.status.update").
Node.js (link em inglês)
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 (link em inglês)
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
Observe que o JSON abaixo descreve uma solicitação 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
Observe que o JSON abaixo descreve uma solicitação 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" }
Cards de tabela
Com eles, é possível exibir dados tabulares na resposta, como classificações esportivas, resultados eleitorais e voos. É possível definir colunas e linhas (até três cada) que o Assistente precisa mostrar no card da tabela. Também é possível definir colunas e linhas adicionais, além da priorização.
As tabelas são diferentes das listas verticais porque não exibem dados estáticos, como elementos de listas.

Propriedades
Os cartões de tabela têm os seguintes requisitos e propriedades opcionais que podem ser configurados:
- Compatível com plataformas com o recurso
actions.capability.SCREEN_OUTPUT
.
A seção a seguir resume como personalizar os elementos em um cartão de tabela.
Nome | É opcional | É personalizável | Notas de personalização |
---|---|---|---|
title |
Sim | Sim | Título geral da tabela. Precisa ser definido se a legenda for definida. Você pode personalizar a família e a cor da fonte. |
subtitle |
Sim | Não | Subtítulo da tabela. |
image |
Sim | Sim | Imagem associada à tabela. |
Row |
Não | Sim |
Dados de linha da tabela. Consiste em uma matriz de objetos As três primeiras linhas são garantidas, mas outras podem não aparecer em determinadas superfícies. Teste com o simulador para ver quais linhas são mostradas para uma determinada superfície. Em plataformas compatíveis com o recurso |
ColumnProperties |
Sim | Sim | Cabeçalho e alinhamento de uma coluna. Consiste em uma propriedade header (que representa o texto do cabeçalho de uma coluna) e uma propriedade horizontal_alignment (do tipo HorizontalAlignment ). |
Cell |
Não | Sim | Descreve uma célula em uma linha. Cada célula contém uma string que representa um valor de texto. Você pode personalizar o texto da célula. |
Button |
Sim | Sim | Um objeto de botão que geralmente aparece na parte inferior de um card. Um cartão de tabela pode ter apenas um botão. Você pode personalizar a cor do botão. |
HorizontalAlignment |
Sim | Sim | Alinhamento horizontal do conteúdo na célula. Os valores podem ser LEADING , CENTER ou TRAILING . Se não for especificado, o conteúdo será alinhado à borda inicial da célula. |
Exemplo de código
Os snippets a seguir mostram como implementar um cartão de tabela simples:
Node.js (link em inglês)
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 (link em inglês)
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
Observe que o JSON abaixo descreve uma resposta do 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
Observe que o JSON abaixo descreve uma resposta do 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" } ] } ] }
Os snippets a seguir mostram como implementar um cartão de tabela complexo:
Node.js (link em inglês)
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 (link em inglês)
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
Observe que o JSON abaixo descreve uma resposta do 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
Observe que o JSON abaixo descreve uma resposta do 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?" } } ] } } } ] }
Como personalizar suas respostas
Você pode alterar a aparência das suas respostas avançadas criando um tema personalizado. Se você definir um tema para o projeto do Actions, as respostas avançadas em todas as ações dele serão estilizadas de acordo com o tema. Esse branding personalizado pode ser útil para definir uma aparência exclusiva para a conversa quando os usuários invocam suas ações em uma superfície com uma tela.
Para definir um tema de resposta personalizado, faça o seguinte:
- No Console do Actions, acesse Desenvolver > Personalização do tema.
- Defina uma ou todas as opções a seguir:
- Cor de fundo a ser usada como o plano de fundo de seus cartões. Em geral, use uma cor clara para o plano de fundo para que o conteúdo do cartão seja fácil de ler.
- A cor principal é a cor principal dos textos do cabeçalho e dos elementos da IU dos cartões. Em geral, use uma cor primária mais escura para contrastar com o plano de fundo.
- Família de fontes descreve o tipo de fonte usado para títulos e outros elementos de texto em destaque.
- O estilo de canto da imagem pode alterar a aparência dos cantos dos seus cards.
- A imagem de plano de fundo usa uma imagem personalizada no lugar da cor de fundo. Você precisará fornecer duas imagens diferentes para quando o dispositivo de superfície estiver no modo paisagem ou retrato, respectivamente. Se você usar uma imagem de plano de fundo, a cor principal será definida como branco.
- Clique em Salvar.
