對話動作功能將於 2023 年 6 月 13 日淘汰。詳情請參閱「對話動作已淘汰」。

使用用戶端或伺服器端執行要求繼續建構

您現在已經掌握基本知識,可以使用 Canvas 專屬方法提升及自訂動作。建立動作專案時,您可以選擇使用用戶端執行要求模型或伺服器端執行要求模型開發動作。如要進一步瞭解這些選項,請參閱啟用互動式畫布

如果選取用戶端執行要求模型選項,可以在動作中使用以下內容:

  • 「使用用戶端執行要求建構」下方的所有 API
  • 「使用伺服器端執行要求建構」下方的所有 API

  • onTtsMark() 回呼

如果選取伺服器執行要求選項,可以在動作中使用以下項目:

  • 「使用伺服器端執行要求建構」下方的所有 API
  • 回呼

有些互動式 Canvas API 不建議搭配特定執行要求模型使用。下表列出您在選取用戶端執行要求選項時啟用的 API,以及我們推薦或建議每個模型使用的 API:

API 名稱 是否支援伺服器執行要求模型? 支援用戶端執行要求模型?
sendTextQuery() 支援 (但不建議提供,詳情請參閱 sendtextQuery())
outputTts()
triggerScene()
createIntentHandler(), expect(), clearExpectations(), prompt()
createNumberSlot(),createTextSlot, createConfirmationSlot, createOptionsSlot()
setHomeParam(), getHomeParam(), setUserParam(), getUserParam()

以下各節說明如何在互動式 Canvas 動作中實作用戶端和伺服器端執行要求模型的 API。

透過用戶端執行要求進行建構

您可以在網頁應用程式邏輯中實作下列互動式 Canvas API:

outputTts()

這個 API 可讓您從裝置輸出文字轉語音 (TTS),而不必傳送 Actions Builder 的靜態提示或叫用 Webhook。如果不需要與 TTS 相關聯的伺服器端邏輯,您可以使用用戶端中的 outputTts() 來略過送往伺服器的行程,並加快回應使用者的速度。

用戶端outputTts()可能會中斷或取消伺服器端 TTS。如要避免服務中斷,請採取下列防護措施:

  • 避免在工作階段開始時呼叫 outputTts(),而應在動作的第一個對話回頭使用伺服器端 TTS。
  • 避免在沒有使用者操作期間連續呼叫 outputTts()

下列程式碼片段說明如何使用 outputTts() 從用戶端輸出 TTS:

interactiveCanvas.outputTts(
      '<speak>This is an example response.</speak>', true);

您也可以使用 outputTts() 搭配 onTtsMark(),將 SSML 標記放入文字序列中。使用 onTtsMark() 可將網頁動畫或遊戲狀態在 SSML TTS 字串的特定時間點同步處理,如以下程式碼片段所示:

interactiveCanvas.outputTts(
      '<speak>Speak as <mark name="number" /> number <break time="700ms"/>' +
      '<say-as interpret-as="cardinal">12345</say-as> <break time="300ms"/> ' +
      'Speak as <mark name="digits" /> digits <break time="700ms"/>' +
      '<say-as interpret-as="characters">12345</say-as></speak>', true);

在上述範例中,自訂回應的兩個標記會傳送至採用 TTS 的網頁應用程式。

在用戶端上處理意圖執行要求

在互動式畫布的伺服器執行要求模型中,所有意圖都必須由 Webhook 處理,這樣會增加動作的延遲時間。您可以處理網頁應用程式中的意圖,而不必呼叫 Webhook。

如要處理用戶端意圖,您可以使用下列 API:

  • createIntentHandler():此方法可讓您在網頁應用程式程式碼中,定義 Actions Builder 中定義的自訂意圖。
  • expect():啟用/註冊意圖處理常式的方法,方便使用者比對意圖。
  • clearExpectations():這個方法會清除目前所有已啟用意圖的預期結果,如此一來,即使使用者說出符合意圖的語音,系統也不會比對意圖。
  • deleteHandler():停用個別意圖處理常式的方法,無法比對這些意圖處理常式。

透過這些 API,您可以選擇為互動式 Canvas 操作的不同狀態,啟用或停用意圖。您必須在意圖處理常式中使用 expect() 來啟用這些意圖。

啟用意圖處理常式

啟用意圖處理常式的程序分為兩個步驟。首先,您必須在 Actions Builder 中定義意圖。接下來,如要比對意圖,您必須在意圖處理常式上呼叫 expect()

如要在用戶端上設定及啟用意圖處理常式,請按照下列步驟操作:

  1. 在 Actions 主控台開啟專案,然後新增「自訂意圖」
  2. 在「這是一個全域意圖嗎?」選取「是」

  3. 設定意圖,然後按一下「Save」(儲存)

  4. 在網頁應用程式邏輯中定義意圖的處理常式,如以下程式碼片段所示:

    /**
    * Define handler for intent.
    */
    const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
      matchedIntent => {
        console.log("Intent match handler to reserve a table was triggered!");
      });
    
    /**
    * Define handler for intent with an argument.
    */
    const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
      matchedIntent => {
        const numberOfPeople = matchedIntent.getIntentArg('numberOfPeople');
        console.log(`Intent match handler to reserve a table for ${number of people} was triggered!`);
      });
    
  5. 呼叫 expect() 方法以註冊意圖處理常式,如以下程式碼片段所示:

    /**
    * Define handler for intent and expect() it.
    */
    const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
      matchedIntent => {
        console.log("Intent match handler to reserve a table was triggered!");
      });
    var handler = interactiveCanvas.expect(bookTableIntent);
    

停用意圖處理常式

定義意圖處理常式後,您可以視需要為動作啟用或停用意圖。當您呼叫 expect() 來啟用意圖時,系統會傳回含有 deleteHandler() 方法的物件,您可以使用該方法停用新建立的處理常式。即使意圖目前並未啟用,意圖處理常式定義仍會保持不變,因此您可以在需要時重新啟用意圖。

若要停用意圖處理常式,請在意圖處理常式上呼叫 deleteHandler(),如以下程式碼片段所示:

    /**
    * Define handler for intent and expect() it.
    */
    const bookTableIntent = interactiveCanvas.createIntentHandler('reserveTable',
      matchedIntent => {
        console.log("Intent match handler to reserve a table was triggered!");
      });
    var handler = interactiveCanvas.expect(bookTableIntent);
    
    // Delete the handler for `bookTableIntent`.
    handler.deleteHandler();
    

您可以呼叫 expect() 來重新新增已停用的意圖處理常式,如以下程式碼片段所示:

    // Re-add the `bookTableIntent` handler.
    handler = interactiveCanvas.expect(bookTableIntent);

如要大量停用意圖,您可以使用 clearExpectations() 方法,停用所有目前啟用的意圖。下列程式碼片段說明如何清除所有意圖處理常式的期望:

interactiveCanvas.clearExpectations();

在用戶端處理運算單元填充

您不必在 Actions Builder 中為場景新增版位填充,而是可以直接在網頁應用程式中處理運算單元填充。

如要處理用戶端上的運算單元填充,您必須先使用下列其中一個 API 建立運算單元:

  • createNumberSlot(callback, hints):一種可讓您在網頁應用程式程式碼中定義數字版位的方法。用於提示使用者輸入號碼。
  • createTextSlot(callback, hints):一種可在網頁應用程式程式碼中定義文字版位的方法。用於提示使用者輸入字詞。
  • createConfirmationSlot(callback, hints):一種可讓您在網頁應用程式程式碼中定義確認版位的方法。用於提示使用者確認 (是/否)。
  • createOptionsSlot(options, callback, hints):一種可讓您在網頁應用程式程式碼中定義選項版位的方法。用於提示使用者從預先定義的選項清單中選取。

建立運算單元時,您可以選擇定義 triggerHints,這類關鍵字可改善動作的自然語言理解 (NLU) 系統。這些關鍵字應為使用者在填入版位時可能會說的簡短字詞。舉例來說,一個數值版位的 triggerHints 關鍵字可以是 years。當使用者在對話中針對年齡回答問題,「並有回應時」(例如 30 年) 時,您的動作就更可能辨識出使用者已有正確填充版位。

建立運算單元後,您可以使用 prompt API 提示使用者取得運算單元:

  • prompt(tts, slot):一種輸出文字,會輸出給使用者,提示使用者填入預期的版位。

呼叫 prompt() 會傳回承諾,以及已供應運算單元的狀態和值。

建立號碼版位

數字版位可讓您在對話期間提示使用者輸入數字。如要進一步瞭解運算單元填充,請參閱動作建構工具說明文件的運算單元填充一節。

如要提示使用者在用戶端填入數字版位,請按照下列步驟操作:

  1. 呼叫 createNumberSlot() 方法,以在網頁應用程式邏輯中建立數字運算單元:

    /**
     * Create number slot.
     */
    const triggerHints = { associatedWords: ['guess number', 'number'] };
    const slot = interactiveCanvas.createNumberSlot(
      number => {
        console.log(`Number guessed: ${number}.`);
      }, triggerHints);
    
    
  2. 呼叫 prompt() 方法可提示使用者提供運算單元,並處理傳回承諾中的運算單元值,如以下程式碼片段所示:

    const promptPromise = interactiveCanvas.prompt(
      { text: 'What number am I thinking of between 1 and 10?' }, slot);
    
    promptPromise.then(
      answer => {
        if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
          // answer === {value: 5, status: ANSWERED}
          // Do something with answer.value
        } else {
          console.error('Promise returned unsuccessful status ' + answer.status);
        }
      });
    

建立文字版位

文字版位可讓您在對話期間提示使用者輸入字詞。如要進一步瞭解運算單元填充,請參閱動作建構工具說明文件的運算單元填充一節。

如要提示使用者在用戶端填滿文字版位,請按照下列步驟操作:

  1. 呼叫 createTextSlot() 方法,以在網頁應用程式邏輯中建立文字版位:

    /**
     * Create text slot.
     */
    const triggerHints = { associatedWords: ['favorite color', 'color'] };
    const slot = interactiveCanvas.createTextSlot(
      text => {
        console.log(`Favorite color: ${text}.`);
      }, triggerHints);
    
    
  2. 呼叫 prompt() 方法可提示使用者提供運算單元,並處理傳回承諾中的運算單元值,如以下程式碼片段所示:

    const promptPromise = interactiveCanvas.prompt(
      { text: 'What is your favorite color?' }, slot);
    
    promptPromise.then(
      answer => {
        if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
          // answer === {value: "red", status: ANSWERED}
          // Do something with answer.value
        } else {
          console.error('Promise returned unsuccessful status ' + answer.status);
        }
      });
    

建立確認位置

確認位置可讓您提示使用者確認 (使用者可以回答「是」或「否」來填入版位)。如要進一步瞭解運算單元填充,請參閱動作建構工具說明文件的運算單元填充一節。

如要提示使用者在用戶端中填入確認版位,請按照下列步驟操作:

  1. 呼叫 createConfirmationSlot() 方法,以在網頁應用程式邏輯中建立確認運算單元:

    /**
     * Create confirmation slot (boolean).
     */
    const triggerHints = { associatedWords: ['user confirmation', 'confirmation'] };
    const slot = interactiveCanvas.createConfirmationSlot(
      yesOrNo => {
        console.log(`Confirmation: ${yesOrNo}`);
      }, triggerHints);
    
    
  2. 呼叫 prompt() 方法可提示使用者提供運算單元,並處理傳回承諾中的運算單元值,如以下程式碼片段所示:

    const promptPromise = interactiveCanvas.prompt(
      { text: 'Do you agree to the Terms of Service?' }, slot);
    
    promptPromise.then(
      answer => {
        if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
          // answer === {value: true, status: ANSWERED}
          // Do something with answer.value
        } else {
          console.error('Promise returned unsuccessful status ' + answer.status);
        }
      });
    

建立選項版位

選項運算單元可讓您提示使用者從預先定義的選項清單中選取。如要進一步瞭解運算單元填充,請參閱動作建構工具說明文件的運算單元填充一節。

如要提示使用者在用戶端填入選項版位,請按照下列步驟操作:

  1. 呼叫 createOptionsSlot() 方法,在網頁應用程式邏輯中建立選項版位:

    /**
     * Create options slot (list selection).
     */
    const triggerHints = { associatedWords: ['select fruit', 'choose fruit'] };
    // Define selectable options
    const options = [{
      key: 'apple',
      synonyms: ['apple', 'large apple', 'gala apple'],
    }, {
      key: 'banana',
      synonyms: ['banana', 'green banana', 'plantain'],
    }];
    const slot = interactiveCanvas.createOptionsSlot(
      options,
      selectedOption => {
        console.log(`You have selected ${selectedOption} as your fruit.`);
      }, triggerHints);
    
    
  2. 呼叫 prompt() 方法可提示使用者提供運算單元,並處理傳回承諾中的運算單元值,如以下程式碼片段所示:

    const promptPromise = interactiveCanvas.prompt(
      { text: 'Would you like a banana or an apple?' }, slot);
    
    promptPromise.then(
      answer => {
        if (answer.status == interactiveCanvas.AnswerStatus.ANSWERED) {
          // answer === {value: 'apple', status: ANSWERED}
          // Do something with answer.value
        } else {
          console.error('Promise returned unsuccessful status ' + answer.status);
        }
      });
    

triggerScene()

您可以利用 triggerScene() API,從用戶端執行要求轉換為互動性畫布操作中的其他場景。使用 triggerScene() 時,如果使用者需要存取 Actions 需要存取 Webhook 的系統情境,您也可以從用戶端執行要求切換為伺服器端執行要求。舉例來說,當使用者需要連結帳戶或接收通知時,您可以呼叫 triggerScene();然後,您可以從該場景傳回 Canvas 提示,從該場景返回用戶端執行要求。

下列程式碼片段說明如何在動作中實作 triggerScene()

interactiveCanvas.triggerScene('SceneName').then((status) => {
  console.log("sent the request to trigger scene.");
}).catch(e => {
  console.log("Failed to trigger a scene.");
})

用戶端的住家和使用者儲存空間

您可以使用 Webhook 呼叫用戶端 API 處理網頁應用程式和使用者儲存空間,藉此使用 Webhook 來取得住家和使用者儲存空間值。您的網頁應用程式之後可在多個工作階段中 (例如提示與條件) 使用這些儲存的值,並視需要存取特定家庭或使用者的值。使用這些 API 可以減少互動式 Canvas Action 的延遲時間,因為您不再需要呼叫 Webhook 取得並設定儲存空間值。

網頁應用程式和首頁儲存空間的通用原則與 Webhook 相同。如要進一步瞭解住家和使用者儲存空間,請參閱「住家儲存空間」和「使用者儲存空間」的說明文件。

用戶端家用儲存空間

家庭儲存空間可讓您根據首頁圖表儲存住家使用者的值,而且在家中所有工作階段之間都能共用。舉例來說,如果使用者在家中玩互動式 Canvas 遊戲,遊戲得分就能儲存在家庭儲存空間中,其他家庭成員也能繼續使用遊戲中的得分。

如要讓動作支援住家儲存空間,請按照下列步驟操作:

  1. 在 Actions 主控台,前往「Deploy」(部署) >「Directory information」(目錄資訊) >「其他資訊」
  2. 在「你的動作是否使用住家儲存空間嗎?」下方勾選「是」方塊。

如要在網頁應用程式中將資料寫入家用儲存空間,請呼叫 setHomeParam() 方法,如以下程式碼片段所示:

interactiveCanvas.setHomeParam('familySize',  10).then(
      result => {
        console.log('Set home param success');
      },
      fail => {
        console.error(err);
      });

如要從網頁應用程式中讀取家庭儲存空間的值,請呼叫 getHomeParam() 方法,如以下程式碼片段所示:

interactiveCanvas.getHomeParam('familySize').then(
      value => {
        console.log(JSON.stringify(result));
      },
      err => {
        console.error(err);
      }
  );

如要清除所有現有的居家儲存空間,請呼叫 resetHomeParam() 方法,如以下程式碼片段所示:

interactiveCanvas.resetHomeParam();

用戶端使用者儲存空間

使用者儲存空間可讓您在多個工作階段中,為特定經過驗證的使用者儲存參數值。舉例來說,如果使用者玩遊戲,系統會儲存該使用者的遊戲分數。在後續的遊戲工作階段中,使用者可以繼續以相同的分數繼續玩遊戲。

如要將值寫入網頁應用程式中的使用者儲存空間,請呼叫 setUserParam() 方法,如以下程式碼片段所示:

interactiveCanvas.setUserParam('color',  'blue').then(
      result => {
        console.log('Set user param success');
      },
      err => {
        console.error(err);
      });

如要讀取網頁應用程式中使用者儲存空間的值,請呼叫 getUserParam() 方法,如以下程式碼片段所示:

interactiveCanvas.getUserParam('color').then(
      value => {
        console.log(JSON.stringify(result));
      },
      err => {
        console.error(err);
      }
  );

如要清除所有現有的使用者儲存空間,請呼叫 resetUserParam() 方法,如以下程式碼片段所示:

interactiveCanvas.resetUserParam();

setCanvasState()

setCanvasState() 方法可讓您從互動式 Canvas 網頁應用程式傳送執行要求到執行要求,並通知 Google 助理網頁應用程式已更新其狀態。網頁應用程式會以 JSON 物件的形式傳送更新後的狀態。

呼叫 setCanvasState() 不會叫用意圖。叫用 setCanvasState() 後,如果叫用 sendTextQuery(),或使用者查詢與對話中的意圖相符,則在後續對話中,使用先前對話內容中設定的 setCanvasState() 就可以在後續的對話中使用。

下列程式碼片段使用 setCanvasState() 設定 Canvas 狀態資料:

JavaScript

this.action.canvas.setCanvasState({ score: 150 })
    

透過 Webhook 參照 Canvas 狀態

您可以在執行要求程式碼中參照已儲存的 Canvas 狀態值。如要參照值,請使用 conv.context.canvas.state.KEY 語法,其中 KEY 是設定 Canvas 狀態值時提供的鍵。

舉例來說,如果您曾經將遊戲的高分數值儲存為 Canvas 狀態的 score 參數,請使用 conv.context.canvas.state.score 參照該值,以便在執行要求中存取該值:

Node.js

app.handle('webhook-name', conv => {
    console.log(conv.context.canvas.state.score);
})
    

在提示訊息中參照畫布狀態

可以在提示中參照已儲存的 Canvas 狀態值。如要參照值,請使用 $canvas.state.KEY 語法,其中 KEY 是設定 Canvas 狀態值時提供的鍵。

舉例來說,如果先前在 Canvas 狀態中為遊戲儲存了較高的分數值做為 score 參數,則請使用 $canvas.state.score 參照該值,以便在提示中存取該值:

JSON

{
  "candidates": [{
    "first_simple": {
      "variants": [{
        "speech": "Your high score is $canvas.state.score."
      }]
    }
  }]
}
    

條件中的畫布畫布狀態

你也可以在條件中參照已儲存的 Canvas 狀態值。如要參照值,請使用 canvas.state.KEY 語法,其中 KEY 是設定 Canvas 狀態值時提供的鍵。

舉例來說,假設您先前在 Canvas 狀態中儲存了遊戲的分數值做為 score 這個條件,而且想要將條件與條件 999 的值進行比較,則可以使用 canvas.state.score 在條件中參照已儲存的值。您的條件運算式如下所示:

條件語法

canvas.state.score >= 999
    

sendTextQuery()

sendTextQuery() 方法會將文字查詢傳送至 Conversational Action,以程式輔助的方式比對意圖。這個範例使用 sendTextQuery(),在使用者點選按鈕時重新啟動三角形轉動遊戲。使用者按一下「Restart 遊戲」按鈕時,sendTextQuery() 會傳送與 Restart game 意圖相符的文字查詢,並傳回承諾內容。如果意圖已觸發,這個承諾會啟動 SUCCESS,如未觸發,則顯示 BLOCKED。下列程式碼片段會比對意圖,並處理承諾的成功和失敗案例:

JavaScript

…
/**
* Handle game restarts
*/
async handleRestartGame() {
    console.log(`Request in flight`);
    this.button.texture = this.button.textureButtonDisabled;
    this.sprite.spin = false;
    const res = await this.action.canvas.sendTextQuery('Restart game');
    if (res.toUpperCase() !== 'SUCCESS') {
        console.log(`Request in flight: ${res}`);
        return;
    }
    console.log(`Request in flight: ${res}`);
    this.button.texture = this.button.textureButtonDisabled;
    this.sprite.spin = false;
}
…
    

如果承諾使用 SUCCESS 的結果顯示,Restart game Webhook 處理常式會傳送 Canvas 回應給網頁應用程式:

JavaScript

…
app.handle('restart', conv => {
  conv.add(new Canvas({
    data: {
      command: 'RESTART_GAME'
    }
  }));
});
…
    

這個 Canvas 回應會觸發 onUpdate() 回呼,以執行下方 RESTART_GAME 程式碼片段中的程式碼:

JavaScript

…
RESTART_GAME: (data) => {
    this.scene.button.texture = this.scene.button.textureButton;
    this.scene.sprite.spin = true;
    this.scene.sprite.tint = 0x00FF00; // green
    this.scene.sprite.rotation = 0;
},
…
    

使用伺服器端執行要求進行建構

您可以在 Webhook 中導入下列互動式 Canvas API:

啟用全螢幕模式

根據預設,互動式 Canvas 網頁應用程式會在螢幕頂端加上「動作」這個標題及動作的名稱。您可以使用 enableFullScreen 移除標頭,並在載入畫面替換成暫時的浮動式訊息,讓使用者在與動作互動時仍能維持全螢幕模式。浮動式訊息顯示動作的顯示名稱、開發人員名稱,以及退出動作的操作說明,浮動式訊息顏色會隨使用者在裝置上選取的主題而改變。

圖 1. 載入畫面中的浮動式訊息訊息。

如果使用者經常與動作互動,載入畫面就會暫時停止顯示浮動式訊息。如果使用者有一段時間未與動作互動,啟動動作時便會再次顯示浮動式訊息。

您可以在 Webhook 或動作建構工具的靜態提示中啟用全螢幕模式。

如要在 Webhook 中啟用全螢幕模式,請按照下列步驟操作:

  1. 在工作階段中,Webhook 傳回的第一個 canvas 回應,將 enableFullScreen 欄位設為 true。以下程式碼片段是使用 Node.js 用戶端程式庫的實作範例:

     const { conversation, Canvas } = require('@assistant/conversation');
     const functions = require('firebase-functions');
    
     const app = conversation();
    
     app.handle('invocation_fullscreen', conv => {
       conv.add(new Canvas(
         {
           url: 'https://example-url.com',
           enableFullScreen: true
         }));
     });
    
     exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
    

如要在動作建構工具的靜態提示中啟用全螢幕模式,請按照下列步驟操作:

  1. Actions 主控台中開啟專案。
  2. 按一下導覽列中的「開發」,然後開啟內含第一個 canvas 回應的提示。
  3. enable_full_screen 設為 true,如以下程式碼片段所示:

     {
      "candidates": [
        {
          "canvas": {
            "url": "https://example-url.com",
            "enable_full_screen": true
          }
        }
      ]
    }
    

continueTtsDuringTouch

根據預設,如果使用者在使用互動式畫布動作時輕觸螢幕,TT 就會停止播放。您可以啟用 TTS,以便在使用者透過 continueTtsDuringTouch 輕觸螢幕時繼續播放。不能在同一個工作階段中開啟或關閉這個行為。

您可以在 Webhook 或 Actions Builder 的靜態提示中實作這個行為。

如要在使用者輕觸 Webhook 中的螢幕後繼續執行文字轉語音,請按照下列步驟操作:

  • 在工作階段中,Webhook 傳回的第一個 canvas 回應,將 continueTtsDuringTouch 欄位設為 true。以下程式碼片段是使用 Node.js 用戶端程式庫的實作範例:

    const { conversation, Canvas } = require('@assisant/conversation');
    const functions = require('firebase-functions');
    
    const app = conversation();
    
    app.handle('intent-name', conv => {
      conv.add(new Canvas(
        {
          url: 'https://example-url.com',
          continueTtsDuringTouch: true
        }));
    });
    
    exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
    

如要讓使用者在動作建構工具的靜態提示中輕觸螢幕以繼續執行文字轉語音,請按照下列步驟操作:

  1. Actions 主控台中開啟專案。
  2. 按一下導覽列中的「開發」,然後開啟內含第一個 canvas 回應的提示。
  3. continue_tts_during_touch 設為 true,如以下程式碼片段所示:

      {
       "candidates": [
         {
           "canvas": {
             "url": "https://example-url.com",
             "continue_tts_during_touch": true
           }
         }
       ]
     }