Dialogflow에서 살펴보기
계속을 클릭하여 Dialogflow에서 응답 샘플을 가져옵니다. 그런 다음 아래 단계에 따라 샘플을 배포하고 테스트합니다.
- 에이전트 이름을 입력하고 샘플의 새 Dialogflow 에이전트를 만듭니다.
- 에이전트 가져오기를 완료한 후 에이전트로 이동을 클릭합니다.
- 기본 탐색 메뉴에서 Fulfillment로 이동합니다.
- 인라인 편집기를 사용 설정한 후 배포를 클릭합니다. 편집기에는 샘플 코드가 포함됩니다.
- 기본 탐색 메뉴에서 통합으로 이동한 다음 Google 어시스턴트를 클릭합니다.
- 표시되는 모달 창에서 자동 변경 변경사항을 사용 설정하고 테스트를 클릭하여 작업 시뮬레이터를 엽니다.
- 시뮬레이터에서
Talk to my test app
를 입력하여 샘플을 테스트합니다.
사용자와의 상호작용을 개선하기 위해 시각적 요소를 표시하려면 리치 응답을 사용하세요. 이러한 시각적 요소는 대화를 계속하는 방법에 관한 힌트를 제공할 수 있습니다.
리치 응답은 화면 전용 또는 오디오 및 화면 환경에 표시될 수 있습니다. 다음 구성요소를 포함할 수 있습니다.
대화 디자인 가이드라인을 검토하여 이러한 시각적 요소를 작업에 통합하는 방법을 알아볼 수도 있습니다.
속성
리치 응답에는 다음과 같은 요구사항과 선택적 속성을 구성할 수 있습니다.
actions.capability.SCREEN_OUTPUT
기능이 있는 표시 경로에서 지원됩니다.- 리치 응답의 첫 번째 항목은 단순 응답이어야 합니다.
- 간단한 응답 최대 2개.
- 최대 1개의 기본 카드 또는
StructuredResponse
을 사용할 수 있습니다. - 추천 검색어 칩 최대 8개
- 추천 칩은
FinalResponse
에서 허용되지 않습니다. - 스마트 디스플레이에서 웹에 연결하는 기능은 현재 지원되지 않습니다.
다음 섹션에서는 다양한 유형의 리치 응답을 빌드하는 방법을 보여줍니다.
기본 카드

기본 카드에는 다음을 포함할 수 있는 정보가 표시됩니다.
- 이미지
- 직함
- 부제목
- 텍스트 본문
- 연결 버튼
- 테두리
기본 카드는 주로 표시 용도로 사용합니다. 간결하고 사용자에게 핵심(또는 요약) 정보를 표시하고, 원하는 경우 사용자가 웹링크를 사용하여 자세히 알아볼 수 있도록 설계되었습니다.
대부분의 경우 카드 아래에 추천 검색어 칩을 추가하여 대화를 계속하거나 피벗해야 합니다.
채팅 풍선에 있는 카드에 표시된 정보를 절대 반복하지 마세요.
속성
기본 카드 응답 유형에는 다음 요구사항과 구성할 수 있는 선택적 속성이 있습니다.
actions.capability.SCREEN_OUTPUT
기능이 있는 표시 경로에서 지원됩니다.- 서식이 지정된 텍스트(이미지가 없는 경우 필수)
- 기본적으로 일반 텍스트
- 링크를 포함해서는 안 됩니다.
- 이미지가 있는 경우 10행 제한, 이미지가 없는 경우 15행 제한 이는 약 500자 (이미지 포함) 또는 750자 (이미지 없음)입니다. 화면 크기가 작은 휴대전화는 큰 화면 전화보다 먼저 텍스트를 자릅니다. 텍스트가 너무 많은 줄을 포함하면 줄임표가 있는 마지막 단어 나누기에서 잘립니다.
- 마크다운의 제한된 하위 집합이 지원됩니다.
- 뒤에 공백이 두 개 있는 \n 새 줄
**bold**
*italics*
- 이미지(서식 있는 텍스트가 없는 경우 필수)
- 모든 이미지의 높이는 192dp가 되었습니다.
- 이미지의 가로세로 비율이 화면과 다르면 세로 또는 가로 가장자리에 회색 막대가 가운데에 배치됩니다.
- 이미지 소스는 URL입니다.
- 모션 GIF는 허용됩니다.
선택사항
- 제목
- 일반 텍스트
- 글꼴 및 크기가 수정되었습니다.
- 최대 1줄이며 추가 문자는 잘립니다.
- 제목을 지정하지 않으면 카드 높이가 접힙니다.
- 부제
- 일반 텍스트
- 글꼴 및 글꼴 크기가 고정되었습니다.
- 최대 1줄이며 추가 문자는 잘립니다.
- 부제목이 지정되지 않으면 카드 높이가 축소됩니다.
- 링크 버튼
- 링크 제목은 필수 항목입니다.
- 링크 최대 1개
- 개발자 도메인 외부의 사이트로 연결되는 링크는 허용됩니다.
- 링크 텍스트는 오해의 소지가 없어야 합니다. 이는 승인 과정에서 확인됩니다.
- 기본 카드에는 링크 없이는 상호작용 기능이 제공되지 않습니다. 링크를 탭하면 사용자를 링크로 이동하지만 카드의 기본 본문은 비활성 상태로 유지됩니다.
- 테두리
- 카드와 이미지 컨테이너 사이의 테두리를 조정하여 기본 카드의 표현을 맞춤설정할 수 있습니다.
- JSON 문자열 속성
imageDisplayOptions
을 설정하여 구성됨

샘플 코드
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?'); });
자바
@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?');
자바
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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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?" } } ] } } } ] }
탐색 캐러셀

탐색 캐러셀은 사용자가 세로로 스크롤하여 컬렉션의 타일을 선택할 수 있는 리치 응답입니다. 탐색 캐러셀은 선택한 타일을 웹브라우저 (또는 모든 타일이 AMP를 지원하는 경우 AMP 브라우저)에서 열어 웹 콘텐츠를 위해 특별히 설계되었습니다. 탐색 캐러셀은 나중에 탐색할 수 있도록 사용자의 어시스턴트 표면에도 유지됩니다.
속성
탐색 캐러셀 응답 유형에는 다음 요구사항과 구성할 수 있는 선택적 속성이 있습니다.
actions.capability.SCREEN_OUTPUT
및actions.capability.WEB_BROWSER
기능이 둘 다 있는 표시 경로에서 지원됩니다. 이 응답 유형은 현재 스마트 디스플레이에서 사용할 수 없습니다.- 탐색 캐러셀
- 타일은 최대 10개입니다.
- 타일은 2개 이상이어야 합니다.
- 캐러셀의 타일은 모두 웹 콘텐츠에 연결되어야 합니다 (AMP 콘텐츠
권장).
- 사용자를 AMP 뷰어로 이동하려면 AMP 콘텐츠 타일의
urlHintType
를 'AMP_CONTENT'로 설정해야 합니다.
- 사용자를 AMP 뷰어로 이동하려면 AMP 콘텐츠 타일의
- 캐러셀 타일 탐색
- 카드 일관성 (필수):
- 탐색 캐러셀의 모든 타일에는 동일한 구성요소가 있어야 합니다. 예를 들어 한 타일에 이미지 필드가 있다면 캐러셀에 있는 나머지 타일에도 이미지 필드가 있어야 합니다.
- 탐색 캐러셀의 모든 타일이 AMP 지원 콘텐츠에 연결되면 사용자는 추가 기능이 있는 AMP 브라우저로 이동합니다. AMP가 아닌 콘텐츠에 연결된 타일이 있으면 모든 타일에서 사용자를 웹브라우저로 안내합니다.
- 이미지(선택사항)
- 이미지의 높이는 128dp x 232dp여야 합니다.
- 이미지의 가로세로 비율이 이미지 경계 상자와 일치하지 않으면 이미지의 중심에 양쪽 막대가 표시됩니다. 스마트폰에서는 이미지가 모서리가 둥근 정사각형 중앙에 배치됩니다.
- 이미지 링크가 깨진 경우 대신 자리표시자 이미지가 사용됩니다.
- 이미지에 대체 텍스트가 필요합니다.
- 제목 (필수)
- 기본 텍스트 카드와 동일한 서식 지정 옵션
- 제목은 고유해야 합니다 (음성 선택 지원).
- 최대 2줄의 텍스트를 입력할 수 있습니다.
- 글꼴 크기 16 sp.
- 설명(선택사항)
- 기본 텍스트 카드와 동일한 서식 지정 옵션
- 텍스트 최대 4줄
- 생략 부호가 사용된 경우 (...)
- 글꼴 크기: 14sp, 회색.
- 바닥글(선택사항)
- 글꼴 및 글꼴 크기가 고정되었습니다.
- 텍스트 한 줄로 제한됩니다.
- 생략 부호가 사용된 경우 (...)
- 하단에 고정되므로 본문 텍스트가 줄인 타일은 하위 텍스트 위에 공백이 있을 수 있습니다.
- 글꼴 크기: 14sp, 회색.
- 카드 일관성 (필수):
- 상호작용
- 사용자는 세로로 스크롤하여 항목을 볼 수 있습니다.
- 카드 탭: 항목을 탭하면 브라우저로 연결되고 연결된 페이지가 표시됩니다.
- 음성 입력
- 마이크 동작
- 사용자에게 탐색 캐러셀이 전송되더라도 마이크가 다시 열리지 않습니다.
- 사용자는 여전히 마이크를 탭하거나 어시스턴트 ('OK Google')를 호출하여 마이크를 다시 열 수 있습니다.
- 마이크 동작
안내
기본적으로 마이크는 탐색 캐러셀이 전송된 후 종료된 상태로 유지됩니다. 나중에 대화를 계속하려면 캐러셀 아래에 추천 칩을 추가하는 것이 좋습니다.
목록에 표시된 옵션을 추천 검색어 칩으로 반복해서 사용하지 마세요. 이 컨텍스트의 칩은 대화를 피벗하는 데 사용됩니다 (선택 항목은 아님).
목록과 마찬가지로 캐러셀 카드에 수반되는 채팅 풍선은 오디오의 일부 (TTS/SSML)입니다. 여기서 오디오 (TTS/SSML)는 캐러셀의 첫 번째 타일을 통합하며 캐러셀의 모든 요소를 읽지 않는 것이 좋습니다. 첫 번째 항목과 그 이유를 언급하는 것이 가장 좋습니다 (예: 가장 인기 있는 항목, 가장 최근에 구매한 항목, 가장 많이 언급된 항목).
샘플 코드
Node.js
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(); }
Node.js
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();
JSON
아래 JSON은 웹훅 응답을 설명합니다.
{ "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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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" } ] } ] }
선택한 항목 처리
캐러셀 탐색에 브라우저 핸드오프가 처리되므로 탐색 상호작용 항목과의 사용자 상호작용에는 후속 처리가 필요하지 않습니다. 사용자가 탐색 캐러셀 항목과 상호작용한 후에는 마이크가 다시 열리지 않으므로 대화를 종료하거나 위 안내에 따라 응답에 추천 칩을 포함해야 합니다.
추천 검색어 칩

계속 추천하거나 대화를 피봇팅하기 위해 답장에 힌트를 넣으세요. 대화 중에 기본 클릭 유도 문구가 있다면 이를 첫 번째 추천 칩으로 등록하는 것이 좋습니다.
가능하면 항상 도움말 풍선에 하나의 주요 제안을 포함해야 하지만, 응답이나 채팅 대화가 자연스럽게 느껴지는 경우에만 추천해야 합니다.
속성
추천 칩에는 다음 요구사항과 선택적 속성을 구성할 수 있습니다.
actions.capability.SCREEN_OUTPUT
기능이 있는 표시 경로에서 지원됩니다.- 추천 검색어 칩을 웹에 연결하려면 노출 영역에도
actions.capability.WEB_BROWSER
기능이 있어야 합니다. 이 기능은 현재 스마트 디스플레이에서 사용할 수 없습니다. - 최대 8개 칩
- 최대 텍스트 길이는 25자(영문 기준)입니다.
일반 텍스트만 지원됩니다.

샘플 코드
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?'); ; });
자바
@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?');
자바
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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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/" } } } } ] }
미디어 응답

미디어 응답을 사용하면 작업의 재생 시간이 SSML 240초보다 긴 오디오 콘텐츠를 재생할 수 있습니다. 미디어 응답의 기본 구성요소는 단일 트랙 카드입니다. 사용자는 카드를 통해 다음 작업을 할 수 있습니다.
- 마지막 10초 다시보기
- 30초 앞으로 건너뜁니다.
- 미디어 콘텐츠의 총 길이를 확인합니다.
- 오디오 재생 진행률 표시기를 확인합니다.
- 경과된 재생 시간을 확인합니다.
미디어 응답은 음성 상호작용을 위해 다음과 같은 오디오 컨트롤을 지원합니다.
- “Hey Google, 재생해 줘.”
- “Hey Google, 일시중지해 줘.”
- “Hey Google, 중지해 줘.”
- “Hey Google, 다시 시작해 줘.”
사용자는 "Hey Google, 볼륨 높여 줘" 또는 "Hey Google, 볼륨을 50%로 설정해 줘"와 같이 말하여 볼륨을 제어할 수도 있습니다. 작업의 인텐트가 비슷한 학습 문구를 처리하는 경우 우선 적용됩니다. 작업에 특별한 이유가 없다면 어시스턴트에서 이러한 사용자 요청을 처리하도록 허용합니다.
속성
미디어 응답에는 다음 요구사항과 개발자가 구성할 수 있는 선택적 속성이 있습니다.
actions.capability.MEDIA_RESPONSE_AUDIO
기능이 있는 표시 경로에서 지원됩니다.- 재생할 오디오는 올바른 형식의
.mp3
파일이어야 합니다. 실시간 스트리밍은 지원되지 않습니다. - 재생할 미디어 파일을 HTTPS URL로 지정해야 합니다.
- 이미지(선택사항)
- 원하는 경우 아이콘 또는 이미지를 포함할 수 있습니다.
- 아이콘
- 아이콘은 미디어 플레이어 카드 오른쪽에 테두리 없는 썸네일로 표시됩니다.
- 크기는 36 x 36dp여야 합니다. 더 큰 이미지는 이에 맞게 크기가 조정됩니다.
- 이미지
- 이미지 컨테이너의 높이는 192dp입니다.
- 이미지는 미디어 플레이어 카드 상단에 표시되며 카드의 전체 너비에 걸쳐 있습니다. 대부분의 이미지는 상단 또는 측면에 막대가 표시됩니다.
- 모션 GIF는 허용됩니다.
- 이미지 소스를 URL로 지정해야 합니다.
- 모든 이미지에는 대체 텍스트가 필요합니다.
표시 경로 동작
미디어 응답은 Android 휴대전화 및 Google Home에서 지원됩니다. 미디어 응답의 동작은 사용자가 작업과 상호작용하는 노출 영역에 따라 다릅니다.
Android 휴대전화에서는 다음 조건 중 하나가 충족되면 미디어 응답을 볼 수 있습니다.
- Google 어시스턴트가 포그라운드에 있고 휴대전화 화면이 켜져 있습니다.
- 오디오가 재생되는 동안 사용자가 Google 어시스턴트를 종료하고 재생이 완료된 지 10분 이내에 Google 어시스턴트로 돌아갑니다. Google 어시스턴트로 돌아오면 사용자에게 미디어 카드와 추천 칩이 표시됩니다.
- 어시스턴트는 사용자가 "볼륨 높여 줘" 또는 "볼륨 50%로 설정해 줘"라고 말하여 대화 작업 내에서 기기 볼륨을 제어할 수 있습니다. 유사한 학습 문구를 처리하는 인텐트가 있는 경우 인텐트가 우선 적용됩니다. 작업에 특별한 이유가 없다면 어시스턴트에서 이러한 사용자 요청을 처리하도록 하는 것이 좋습니다.
휴대전화가 잠겨 있을 때도 미디어 컨트롤을 사용할 수 있습니다. Android에서는 알림 영역에도 컨트롤이 표시됩니다.

샘플 코드
다음 코드 샘플은 미디어를 포함하도록 리치 응답을 업데이트하는 방법을 보여줍니다.
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'])); });
자바
@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']));
자바
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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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" } ] } } } ] }
안내
응답은 리치 응답의 항목 배열 내에 mediaType
AUDIO
을 포함하고 mediaObject
을 포함하는 mediaResponse
을 포함해야 합니다. 미디어 응답은 단일 미디어 객체를 지원합니다. 미디어 객체에는 오디오 파일의 콘텐츠 URL이 포함되어야 합니다. 미디어 객체는 선택적으로 이름, 하위 텍스트(설명), 아이콘 또는 이미지 URL을 포함할 수 있습니다.
휴대전화 및 Google Home에서 작업이 오디오 재생을 완료하면 Google 어시스턴트는 미디어 응답이 FinalResponse
인지 확인합니다.
그러지 않으면 처리에 콜백을 전송하여 사용자에게 응답할 수 있습니다.
응답이 FinalResponse
가 아닌 경우 작업에 추천 칩이 포함되어야 합니다.
재생 완료 후 콜백 처리
작업은 사용자에게 추가 작업 (예: 다른 노래 재생)을 요청하는 actions.intent.MEDIA_STATUS
인텐트를 처리해야 합니다. 미디어 재생이 완료되면 작업이 이 콜백을 수신합니다. 콜백의 MEDIA_STATUS
인수에는 현재 미디어에 관한 상태 정보가 포함됩니다. 상태 값은 FINISHED
또는 STATUS_UNSPECIFIED
입니다.
Dialogflow 사용
Dialogflow에서 대화형 브랜치를 실행하려면 인텐트에 actions_capability_media_response_audio
의 입력 컨텍스트를 설정하여 미디어 응답을 지원하는 노출 영역에서만 트리거되도록 해야 합니다.
처리 빌드
아래의 코드 스니펫은 작업의 fulfillment 코드를 작성하는 방법을 보여줍니다. Dialogflow를 사용하는 경우 actions.intent.MEDIA_STATUS
를 actions_intent_MEDIA_STATUS
이벤트를 수신하는 인텐트에 지정된 작업 이름(예: '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?'); });
자바
@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?'); });
자바
@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
아래 JSON은 웹훅 요청을 설명합니다.
{ "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
아래 JSON은 웹훅 요청을 설명합니다.
{ "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" }
표 카드
테이블 카드를 사용하면 응답에 표 형식 데이터 (예: 스포츠 순위, 선거 결과, 항공편)를 표시할 수 있습니다. 테이블 카드에 어시스턴트가 표시해야 하는 열과 행 (각각 최대 3개)을 정의할 수 있습니다. 추가 열과 행을 우선순위와 함께 정의할 수도 있습니다.
테이블은 정적 데이터를 표시하고 목록 요소와 같이 상호작용할 수 없기 때문에 세로 목록과 다릅니다.

속성
테이블 카드에는 다음과 같은 요구사항과 선택적 속성을 구성할 수 있습니다.
actions.capability.SCREEN_OUTPUT
기능이 있는 표시 경로에서 지원됩니다.
다음 섹션에서는 표 카드의 요소를 맞춤설정하는 방법을 요약합니다.
이름 | 선택사항 | 맞춤설정 가능 | 맞춤설정 참고사항 |
---|---|---|---|
title |
예 | 예 | 테이블의 전체 제목입니다. 부제목이 설정된 경우 설정해야 합니다. 글꼴 모음과 색상을 맞춤설정할 수 있습니다. |
subtitle |
예 | 아니요 | 표의 부제목입니다. |
image |
예 | 예 | 테이블과 연결된 이미지입니다. |
Row |
아니요 | 예 |
테이블의 행 데이터 처음 3개 행은 표시되지만 일부 행이 특정 표시 경로에 나타나지 않을 수 있습니다. 시뮬레이터로 테스트하여 특정 표시 경로에 어떤 행이 표시되는지 확인하세요. |
ColumnProperties |
예 | 예 | 열의 헤더 및 정렬입니다. header 속성 (열의 헤더 텍스트를 나타냄)과 horizontal_alignment 속성 (HorizontalAlignment 유형)으로 구성됩니다. |
Cell |
아니요 | 예 | 행에 있는 셀을 설명합니다. 각 셀에는 텍스트 값을 나타내는 문자열이 포함됩니다. 셀의 텍스트를 맞춤설정할 수 있습니다. |
Button |
예 | 예 | 일반적으로 카드 하단에 표시되는 버튼 객체입니다. 테이블 카드에는 버튼을 1개만 포함할 수 있습니다. 버튼 색상을 맞춤설정할 수 있습니다. |
HorizontalAlignment |
예 | 예 | 셀 내 콘텐츠의 가로 정렬 값은 LEADING , CENTER , TRAILING 일 수 있습니다. 지정하지 않으면 셀의 앞쪽 가장자리에 콘텐츠가 정렬됩니다. |
샘플 코드
다음 스니펫은 간단한 테이블 카드를 구현하는 방법을 보여줍니다.
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?'); });
자바
@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?');
자바
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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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" } ] } ] }
다음 스니펫은 복잡한 테이블 카드를 구현하는 방법을 보여줍니다.
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?'); });
자바
@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?');
자바
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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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
아래 JSON은 웹훅 응답을 설명합니다.
{ "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?" } } ] } } } ] }
응답 맞춤설정
맞춤 테마를 만들어 리치 응답의 모양을 변경할 수 있습니다. 작업 프로젝트의 테마를 정의하면 프로젝트의 작업 전체에 걸쳐 다양한 응답이 테마에 따라 스타일이 지정됩니다. 이 맞춤 브랜딩은 사용자가 화면이 있는 표면에서 작업을 호출할 때 대화의 고유한 디자인과 분위기를 정의하는 데 유용합니다.
커스텀 응답 테마를 설정하려면 다음 단계를 따르세요.
- Actions 콘솔에서 개발 > 테마 맞춤설정으로 이동합니다.
- 다음 중 일부 또는 전부를 설정합니다.
- 배경 색상: 카드의 배경으로 사용됩니다. 일반적으로 카드의 콘텐츠를 쉽게 읽을 수 있도록 배경에 밝은 색상을 사용해야 합니다.
- 기본 색상은 카드의 헤더 텍스트 및 UI 요소의 기본 색상입니다. 일반적으로 배경과 대비되도록 더 어두운 기본 색상을 사용해야 합니다.
- 글꼴 모음은 제목 및 기타 눈에 띄는 텍스트 요소에 사용되는 글꼴 유형을 설명합니다.
- 이미지 모서리 스타일은 카드 모서리의 모양을 변경할 수 있습니다.
- 배경 이미지는 배경 색상 대신 맞춤 이미지를 사용합니다. 노출 영역 기기가 가로 모드 또는 세로 모드일 때 사용할 두 이미지를 각각 제공해야 합니다. 배경 이미지를 사용하는 경우 기본 색상이 흰색으로 설정됩니다.
- 저장을 클릭합니다.
