繼續透過用戶端或伺服器端執行要求進行建構

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

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

  • 「Build with Client-side fulfillment」下方的所有 API 清單
  • 「Build with Server-side fulfillment」下方列出的所有 API

  • onTtsMark() 回呼

如果選取伺服器執行要求模型選項,您可以在動作中使用下列內容:

  • 「Build with Server-side fulfillment」下方列出的所有 API
  • 回呼

某些互動式 Canvas API 不建議與特定執行要求模型搭配使用。下表列出了當您選取用戶端執行要求選項時已啟用的 API,以及每個模型是否建議使用或不建議這些 API:

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

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

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

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

outputTts()

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

用戶端 outputTts() 可能會中斷或取消伺服器端 TTS。您可以採取下列預防措施,避免中斷伺服器端 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():這個方法可讓您在網頁應用程式程式碼中,針對動作建構工具中定義的自訂意圖定義意圖處理常式。
  • expect():啟動/註冊意圖處理常式的方法,以便使用者能夠與意圖進行比對。
  • clearExpectations():這個方法可清除目前所有已啟用意圖的期望,即使使用者說出符合意圖的語音內容,也無法比對意圖。
  • deleteHandler():停用個別意圖處理常式的方法,因此無法比對這些意圖。

透過這些 API,您可選擇性地啟用或停用互動式畫布動作的不同意圖。您必須在意圖處理常式上使用 expect() 來啟用這些意圖。

啟用意圖處理常式

啟用意圖處理常式的過程有兩個步驟。首先,您必須在動作建構工具中定義意圖。接下來,如要讓意圖相符,您必須在意圖處理常式上呼叫 expect()

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

  1. 在 Actions 主控台開啟專案,然後新增「Custom intent」(自訂意圖)
  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();

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

您可以在動作建構工具中直接處理運算單元,而不必在動作建構工具中新增運算單元填補。

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

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

建立運算單元時,您可以選擇定義 triggerHints,這種關鍵字可改善動作的自然語言理解 (NLU) 系統。這些關鍵字應該是使用者填入版位時可能會說的簡短字詞。舉例來說,數字版位的 triggerHints 關鍵字可能是 years。當使用者在對話中回覆關於年齡的問題時,的回應中會顯示「我三十歲」回應時,您的動作就更有可能識別使用者填入了合適的運算單元。

建立運算單元後,您可以使用 prompt API 提示使用者設定版位:

  • prompt(tts, slot):這個方法會將 TTS 輸出給使用者,提示使用者預期要填入的版位。

呼叫 prompt() 會傳回承諾,以及已填入版位的狀態和值。

建立號碼運算單元

數字位置可讓您在對話期間提示使用者輸入電話號碼。如要進一步瞭解運算單元填充,請參閱 Actions Builder 說明文件的「運算單元填充」一節。

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

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

    /**
     * Create number slot.
     */
    const triggerHints = { associatedWords: ['guess number', 'number'] };
    const slot = interactiveCanvas.createNumberSlot(
      number => {
        console.log(`Number guessed: ${number}.`);
      }, triggerHints);
    
    
  2. 呼叫 prompt() 方法以提示使用者請求時段,並從傳回的 promise 中處理版位值,如以下程式碼片段所示:

    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);
        }
      });
    

建立文字版位

文字位置可讓您在對話期間提示使用者輸入字詞。如要進一步瞭解運算單元填充,請參閱 Actions Builder 說明文件的「運算單元填充」一節。

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

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

    /**
     * Create text slot.
     */
    const triggerHints = { associatedWords: ['favorite color', 'color'] };
    const slot = interactiveCanvas.createTextSlot(
      text => {
        console.log(`Favorite color: ${text}.`);
      }, triggerHints);
    
    
  2. 呼叫 prompt() 方法以提示使用者請求時段,並從傳回的 promise 中處理版位值,如以下程式碼片段所示:

    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);
        }
      });
    

建立確認時段

確認位置可讓您提示使用者確認 (使用者可以回應「是」或「否」來填滿版位)。如要進一步瞭解運算單元填充,請參閱 Actions Builder 說明文件的「運算單元填充」一節。

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

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

    /**
     * Create confirmation slot (boolean).
     */
    const triggerHints = { associatedWords: ['user confirmation', 'confirmation'] };
    const slot = interactiveCanvas.createConfirmationSlot(
      yesOrNo => {
        console.log(`Confirmation: ${yesOrNo}`);
      }, triggerHints);
    
    
  2. 呼叫 prompt() 方法以提示使用者請求時段,並從傳回的 promise 中處理版位值,如以下程式碼片段所示:

    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);
        }
      });
    

建立選項版位

選項版位可讓您提示使用者從預先定義的選項清單中選取選項。如要進一步瞭解運算單元填充,請參閱 Actions Builder 說明文件的「運算單元填充」一節。

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

  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() 方法以提示使用者請求時段,並從傳回的 promise 中處理版位值,如以下程式碼片段所示:

    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(),在使用者需要存取需要 Webhook 的 Actions Builder 中的系統情境時,從用戶端執行要求切換為伺服器端執行要求。舉例來說,您可以在使用者需要連結帳戶或接收通知時呼叫 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 來處理網頁應用程式中的住家和使用者儲存空間。接著,您的網頁應用程式可以在多個工作階段 (例如提示和條件) 中使用這些儲存的值,並可視需要存取特定家庭或使用者的值。使用這些 API 可縮短互動式畫布動作中的延遲時間,因為不再需要呼叫 Webhook 來取得及設定儲存空間值。

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

用戶端首頁儲存空間

「首頁儲存空間」可讓您根據首頁圖表儲存家庭使用者的值,並可在家庭中的所有工作階段之間共用。舉例來說,如果使用者在家庭中玩互動式畫布遊戲,遊戲分數可以儲存在家用儲存空間中,而其他家庭成員可以透過儲存的分數繼續玩遊戲。

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

  1. 在 Actions 控制台中,依序前往「Deploy」(部署) >「Directory information」(目錄資訊) >「Additional Information」(其他資訊)
  2. 針對「您的動作會使用住家儲存空間嗎?」,勾選「是」方塊。

如要在網頁應用程式中將值寫入 Home Storage,請呼叫 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 狀態值。如要參照這個值,請使用 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 狀態值。如要參照這個值,請使用 $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 狀態值。如要參照該值,請使用 canvas.state.KEY 語法,其中 KEY 是設定 Canvas 狀態值時指定的鍵。

舉例來說,如果您先前以 Canvas 狀態儲存遊戲的最高分值做為參數 score,而您想將該遊戲與條件中的 999 值進行比較,可以使用 canvas.state.score 參照條件中的儲存值。條件運算式如下所示:

條件語法

canvas.state.score >= 999
    

sendTextQuery()

sendTextQuery() 方法會將文字查詢傳送至對話動作,以程式輔助方式比對意圖。這個範例使用 sendTextQuery(),在使用者點選按鈕時重新啟動旋轉三角形的遊戲。當使用者按一下「Restart Game」按鈕時,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;
}
…
    

如果 Webhook 會在 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);
    

如要在 Actions Builder 的靜態提示中啟用全螢幕模式,請按照下列步驟操作:

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

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

continueTtsDuringTouch

根據預設,當使用者使用互動式畫布動作時輕觸螢幕,文字轉語音功能就會停止播放。您可以讓文字轉語音功能在使用者輕觸螢幕 continueTtsDuringTouch 時繼續播放。無法在同一個工作階段中開啟或關閉這個行為。

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

如要讓 TTS 在使用者輕觸 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. 按一下導覽列中的「Develop」,開啟含有第一個 canvas 回應的提示。
  3. continue_tts_during_touch 設為 true,如以下程式碼片段所示:

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