การรวม Agent Dialogflow เข้ากับแอปพลิเคชัน React

เผยแพร่แล้ว: 2022-03-10
สรุปอย่างรวดเร็ว ↬ เมื่อพูดถึงการสร้างตัวช่วยสนทนาเชิงสนทนาที่สามารถใช้ได้ในระดับขนาดเล็กหรือระดับองค์กร Dialogflow น่าจะเป็นหนึ่งในตัวเลือกแรกๆ ที่จะแสดงในรายการค้นหาของคุณ — และทำไมถึงไม่เป็นเช่นนั้น มีคุณลักษณะหลายอย่าง เช่น ความสามารถในการประมวลผลการป้อนข้อมูลเสียงและข้อความ ให้การตอบสนองแบบไดนามิกโดยใช้เว็บฮุคที่กำหนดเอง เชื่อมต่อกับอุปกรณ์ที่เปิดใช้งาน Google หลายล้านเครื่องโดยใช้ผู้ช่วยของ Google และอื่นๆ อีกมากมาย แต่นอกเหนือจากคอนโซลที่มีให้สำหรับการออกแบบและจัดการตัวแทน เราจะสร้างผู้ช่วยแชทที่สามารถใช้ภายในเว็บแอปพลิเคชันที่เราสร้างขึ้นด้วยได้อย่างไร

Dialogflow เป็นแพลตฟอร์มที่ทำให้กระบวนการสร้างและออกแบบผู้ช่วยแชทในการสนทนาในการประมวลผลภาษาธรรมชาติ ซึ่งสามารถประมวลผลเสียงหรือข้อความเมื่อใช้งานจากคอนโซล Dialogflow หรือจากเว็บแอปพลิเคชันที่ผสานรวม

แม้ว่าจะมีการอธิบายสั้นๆ เกี่ยวกับ Dialogflow Agent ในบทความนี้ แต่คาดว่าคุณมีความเข้าใจเกี่ยวกับ Node.js และ Dialogflow หากคุณกำลังเรียนรู้เกี่ยวกับ Dialogflow เป็นครั้งแรก บทความนี้จะให้คำอธิบายที่ชัดเจนว่า Dialogflow คืออะไรและแนวคิดของ Dialogflow คืออะไร

บทความนี้เป็นแนวทางเกี่ยวกับวิธีการสร้างเอเจนต์ Dialogflow พร้อมการสนับสนุนด้วยเสียงและการแชทที่สามารถรวมเข้ากับเว็บแอปพลิเคชันด้วยความช่วยเหลือของแอปพลิเคชันแบ็คเอนด์ Express.js เป็นลิงค์ระหว่างเว็บแอปพลิเคชัน React.js และตัวแทน บน Dialogflow เอง ในตอนท้ายของบทความ คุณควรสามารถเชื่อมต่อ Agent Dialogflow ของคุณเองกับเว็บแอปพลิเคชันที่คุณต้องการได้

เพื่อให้คู่มือนี้ง่ายต่อการติดตาม คุณสามารถข้ามไปยังส่วนใดของบทช่วยสอนที่คุณสนใจมากที่สุด หรือติดตามตามลำดับต่อไปนี้ตามที่ปรากฏ:

  • การตั้งค่าตัวแทน Dialogflow
  • การผสานรวมเอเจนต์ Dialogflow
  • การตั้งค่าแอปพลิเคชัน Node Express
    • การตรวจสอบสิทธิ์ด้วย Dialogflow
    • บัญชีบริการคืออะไร?
    • การจัดการอินพุตเสียง
  • การรวมเข้ากับเว็บแอปพลิเคชัน
    • การสร้างส่วนต่อประสานการแชท
    • การบันทึกการป้อนข้อมูลด้วยเสียงของผู้ใช้
  • บทสรุป
  • อ้างอิง

1. การตั้งค่าตัวแทน Dialogflow

ตามที่อธิบายในบทความนี้ ผู้ช่วยแชทบน Dialogflow เรียกว่า Agent และประกอบด้วยองค์ประกอบที่เล็กกว่า เช่น ความตั้งใจ การเติมเต็ม ฐานความรู้ และอื่นๆ อีกมากมาย Dialogflow มีคอนโซลสำหรับผู้ใช้เพื่อสร้าง ฝึกอบรม และออกแบบขั้นตอนการสนทนาของตัวแทน ในกรณีการใช้งานของเรา เราจะกู้คืนตัวแทนที่ถูกส่งออกไปยังโฟลเดอร์ ZIP หลังจากผ่านการฝึกอบรม โดยใช้คุณสมบัติตัวแทนส่งออกและนำเข้า

ก่อนที่เราจะดำเนินการนำเข้า เราจำเป็นต้องสร้างตัวแทนใหม่ที่จะรวมกับตัวแทนที่จะถูกกู้คืน ในการสร้าง Agent ใหม่จากคอนโซล จำเป็นต้องมีชื่อเฉพาะและยังมีโปรเจ็กต์บน Google Cloud ที่จะลิงก์กับ Agent ด้วย หากไม่มีโปรเจ็กต์บน Google Cloud ที่จะลิงก์ด้วย คุณสามารถสร้างโปรเจ็กต์ใหม่ได้ที่นี่

ก่อนหน้านี้มีการสร้างและฝึกอบรมตัวแทนเพื่อแนะนำผลิตภัณฑ์ไวน์ให้กับผู้ใช้ตามงบประมาณของพวกเขา เอเจนต์นี้ถูกส่งออกไปยัง ZIP แล้ว คุณสามารถดาวน์โหลดโฟลเดอร์ได้ที่นี่และกู้คืนไปยังตัวแทนที่สร้างขึ้นใหม่ของเราจากแท็บส่งออกและนำเข้าที่พบในหน้าการตั้งค่าตัวแทน

การกู้คืนตัวแทนที่ส่งออกก่อนหน้านี้จากโฟลเดอร์ ZIP
การกู้คืนตัวแทนที่ส่งออกก่อนหน้านี้จากโฟลเดอร์ ZIP (ตัวอย่างขนาดใหญ่)

ตัวแทนนำเข้าได้รับการฝึกอบรมมาก่อนหน้านี้เพื่อแนะนำผลิตภัณฑ์ไวน์ให้กับผู้ใช้ตามงบประมาณของผู้ใช้ในการซื้อไวน์หนึ่งขวด

เมื่อผ่านตัวแทนที่นำเข้า เราจะเห็นว่ามีการสร้าง Intent สามรายการจากหน้า Intents เป้าหมายหนึ่งเป็นเจตนาทางเลือก ใช้เมื่อตัวแทนไม่รู้จักอินพุตจากผู้ใช้ อีกประการหนึ่งคือความตั้งใจในการต้อนรับที่ใช้เมื่อเริ่มการสนทนากับตัวแทน และใช้เจตนาสุดท้ายเพื่อแนะนำไวน์ให้กับผู้ใช้โดยพิจารณาจาก พารามิเตอร์จำนวนภายในประโยค สิ่งที่เรากังวลคือความตั้งใจ get-wine-recommendation

ความตั้งใจนี้มีบริบทอินพุตเดียวของ wine-recommendation มาจากความตั้งใจในการต้อนรับเริ่มต้นเพื่อเชื่อมโยงการสนทนากับเจตนานี้

“บริบทคือระบบภายในตัวแทนที่ใช้ในการควบคุมการไหลของการสนทนาจากเจตนาหนึ่งไปยังอีกเจตนาหนึ่ง”

ด้านล่างบริบทคือ วลีการฝึกอบรม ซึ่งเป็นประโยคที่ใช้ในการฝึกอบรมตัวแทนเกี่ยวกับประเภทของข้อความที่คาดหวังจากผู้ใช้ ตัวแทนสามารถจดจำประโยคของผู้ใช้และเจตนาได้ผ่านวลีการฝึกอบรมที่หลากหลายภายในเจตนา

วลีการฝึกอบรมภายในตัวแทนของเรา get-wine-recommendation (ดังที่แสดงด้านล่าง) ระบุตัวเลือกไวน์และหมวดหมู่ราคา:

รายการวลีการฝึกอบรมที่มีเจตนารับไวน์แนะนำ
หน้าเจตจำนงรับไวน์แสดงวลีการฝึกอบรมที่มีอยู่ (ตัวอย่างขนาดใหญ่)

เมื่อมองจากภาพด้านบน เราจะเห็นวลีฝึกหัดที่แสดงอยู่ในรายการ และตัวเลขสกุลเงินจะถูกเน้นด้วยสีเหลืองสำหรับแต่ละรายการ การเน้นสีนี้เรียกว่าคำอธิบายประกอบใน Dialogflow และจะทำโดยอัตโนมัติเพื่อแยกประเภทข้อมูลที่รู้จักซึ่งเรียกว่าเอนทิตีจากประโยคของผู้ใช้

หลังจากจับคู่ความตั้งใจนี้ในการสนทนากับตัวแทนแล้ว คำขอ HTTP จะถูกส่งไปยังบริการภายนอกเพื่อรับไวน์ที่แนะนำตามราคาที่แยกเป็นพารามิเตอร์จากประโยคของผู้ใช้ ผ่านการใช้เว็บฮุคที่เปิดใช้งานซึ่งพบภายใน ส่วนการปฏิบัติตามข้อกำหนดที่ด้านล่างของหน้าความตั้งใจนี้

เราสามารถทดสอบเอเจนต์โดยใช้โปรแกรมจำลอง Dialogflow ซึ่งอยู่ในส่วนด้านขวาของคอนโซล Dialogflow ในการทดสอบ เราเริ่มการสนทนาด้วยข้อความ “ สวัสดี ” และตามด้วยปริมาณไวน์ที่ต้องการ เว็บฮุคจะถูกเรียกทันที และตัวแทนจะแสดงคำตอบที่สมบูรณ์ซึ่งคล้ายกับที่แสดงด้านล่าง

การทดสอบ webhook ตัวแทนที่นำเข้า
ทดสอบเว็บฮุคการเติมเต็มของตัวแทนที่นำเข้าโดยใช้โปรแกรมจำลองตัวแทนในคอนโซล (ตัวอย่างขนาดใหญ่)

จากภาพด้านบน เราจะเห็น URL ของเว็บฮุคที่สร้างขึ้นโดยใช้ Ngrok และการตอบสนองของตัวแทนทางด้านขวามือซึ่งแสดงไวน์ภายในช่วงราคา 20 ดอลลาร์ที่ผู้ใช้พิมพ์

ณ จุดนี้ Dialogflow Agent ได้รับการตั้งค่าอย่างสมบูรณ์ ตอนนี้ เราสามารถเริ่มต้นด้วยการรวมระบบตัวแทนนี้เข้ากับเว็บแอปพลิเคชัน เพื่อให้ผู้ใช้รายอื่นสามารถเข้าถึงและโต้ตอบกับตัวแทนโดยไม่ต้องเข้าถึงคอนโซล Dialogflow ของเรา

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

การผสานรวมเอเจนต์ Dialogflow

แม้ว่าจะมีวิธีการอื่นในการเชื่อมต่อกับ Dialogflow Agent เช่น การส่งคำขอ HTTP ไปยังตำแหน่งข้อมูล REST วิธีที่แนะนำในการเชื่อมต่อกับ Dialogflow คือการใช้ไลบรารีไคลเอ็นต์อย่างเป็นทางการที่มีอยู่ในภาษาการเขียนโปรแกรมหลายภาษา สำหรับ JavaScript แพ็คเกจ @google-cloud/dialogflow นั้นพร้อมสำหรับการติดตั้งจาก NPM

ภายในแพ็คเกจ @google-cloud/dialogflow ใช้ gRPC สำหรับการเชื่อมต่อเครือข่าย และทำให้แพ็คเกจไม่ได้รับการสนับสนุนภายในสภาพแวดล้อมของเบราว์เซอร์ ยกเว้นเมื่อแพตช์โดยใช้ webpack วิธีที่แนะนำในการใช้แพ็คเกจนี้มาจากสภาพแวดล้อมของโหนด เราสามารถทำได้โดยการตั้งค่าแอปพลิเคชันแบ็คเอนด์ Express.js เพื่อใช้แพ็คเกจนี้ จากนั้นให้บริการข้อมูลไปยังเว็บแอปพลิเคชันผ่านจุดปลาย API และนี่คือสิ่งที่เราจะทำต่อไป

การตั้งค่าแอปพลิเคชัน Node Express

ในการตั้งค่าแอปพลิเคชันด่วน เราสร้างไดเร็กทอรีโปรเจ็กต์ใหม่ จากนั้นจึงดึงการพึ่งพาที่จำเป็นโดยใช้ yarn จากเทอร์มินัลบรรทัดคำสั่งที่เปิดอยู่

 # create a new directory and ( && ) move into directory mkdir dialogflow-server && cd dialogflow-server # create a new Node project yarn init -y # Install needed packages yarn add express cors dotenv uuid

เมื่อติดตั้งการพึ่งพาที่จำเป็นแล้ว เราสามารถดำเนินการตั้งค่าเซิร์ฟเวอร์ Express.js แบบลีนที่จัดการการเชื่อมต่อบนพอร์ตที่ระบุโดยเปิดใช้งานการสนับสนุน CORS สำหรับเว็บแอป

 // index.js const express = require("express") const dotenv = require("dotenv") const cors = require("cors") dotenv.config(); const app = express(); const PORT = process.env.PORT || 5000; app.use(cors()); app.listen(PORT, () => console.log(` server running on port ${PORT}`));

เมื่อดำเนินการแล้ว โค้ดในข้อมูลโค้ดด้านบนจะเริ่มต้นเซิร์ฟเวอร์ HTTP ที่รับฟังการเชื่อมต่อบน PORT Express.js ที่ระบุ นอกจากนี้ยังมีการแชร์ทรัพยากรข้ามต้นทาง (CORS) ที่เปิดใช้งานในคำขอทั้งหมดโดยใช้แพ็คเกจ cors เป็นมิดเดิลแวร์ Express สำหรับตอนนี้ เซิร์ฟเวอร์นี้ฟังเฉพาะการเชื่อมต่อ ไม่สามารถตอบสนองต่อคำขอได้ เนื่องจากไม่มีเส้นทางที่สร้างไว้ ดังนั้นให้สร้างสิ่งนี้

ตอนนี้เราจำเป็นต้องเพิ่มเส้นทางใหม่สองเส้นทาง: เส้นทางแรกสำหรับการส่งข้อมูลข้อความในขณะที่อีกเส้นทางหนึ่งสำหรับการป้อนข้อมูลด้วยเสียงที่บันทึกไว้ พวกเขาทั้งสองจะยอมรับคำขอ POST และส่งข้อมูลที่มีอยู่ในเนื้อหาคำขอไปยังตัวแทน Dialogflow ในภายหลัง

 const express = require("express") const app = express() app.post("/text-input", (req, res) => { res.status(200).send({ data : "TEXT ENDPOINT CONNECTION SUCCESSFUL" }) }); app.post("/voice-input", (req, res) => { res.status(200).send({ data : "VOICE ENDPOINT CONNECTION SUCCESSFUL" }) }); module.exports = app

ด้านบน เราได้สร้างอินสแตนซ์เราเตอร์แยกต่างหากสำหรับเส้นทาง POST ที่สร้างขึ้นทั้งสองเส้นทาง ซึ่งสำหรับตอนนี้ ตอบกลับด้วยรหัสสถานะ 200 และการตอบสนองจำลองแบบฮาร์ดโค้ดเท่านั้น เมื่อเราตรวจสอบสิทธิ์กับ Dialogflow เสร็จแล้ว เราสามารถกลับมาใช้การเชื่อมต่อจริงกับ Dialogflow ภายในปลายทางเหล่านี้ได้

สำหรับขั้นตอนสุดท้ายในการตั้งค่าแอปพลิเคชันแบ็กเอนด์ เราเมาต์อินสแตนซ์เราเตอร์ที่สร้างไว้ก่อนหน้านี้ซึ่งสร้างในแอปพลิเคชัน Express โดยใช้ app.use และพาธฐานสำหรับเส้นทาง

 // agentRoutes.js const express = require("express") const dotenv = require("dotenv") const cors = require("cors") const Routes = require("./routes") dotenv.config(); const app = express(); const PORT = process.env.PORT || 5000; app.use(cors()); app.use("/api/agent", Routes); app.listen(PORT, () => console.log(` server running on port ${PORT}`));

ด้านบน เราได้เพิ่มเส้นทางพื้นฐานไปยังทั้งสองเส้นทาง เราสามารถทดสอบเส้นทางใดทางหนึ่งผ่านคำขอ POST โดยใช้ cURL จากบรรทัดคำสั่ง เช่นเดียวกับที่ทำด้านล่างด้วยเนื้อหาคำขอที่ว่างเปล่า

 curl -X https://localhost:5000/api/agent/text-response

หลังจากเสร็จสิ้นการร้องขอข้างต้น เราสามารถคาดหวังว่าจะเห็นการตอบสนองที่มีข้อมูลอ็อบเจ็กต์ถูกพิมพ์ไปยังคอนโซล

ตอนนี้เราเหลือการเชื่อมต่อกับ Dialogflow ซึ่งรวมถึงการจัดการการรับรองความถูกต้อง การส่ง และรับข้อมูลจาก Agent บน Dialogflow โดยใช้แพ็คเกจ @google-cloud/dialogflow

การตรวจสอบสิทธิ์ด้วย Dialogflow

Agent Dialogflow ทั้งหมดที่สร้างขึ้นจะลิงก์กับโปรเจ็กต์บน Google Cloud ในการเชื่อมต่อภายนอกกับ Agent Dialogflow เราตรวจสอบสิทธิ์กับโครงการบน Google Cloud และใช้ Dialogflow เป็นหนึ่งในทรัพยากรของโครงการ จากหกวิธีที่ใช้ได้ในการเชื่อมต่อกับโปรเจ็กต์บน google-cloud การใช้ตัวเลือกบัญชีบริการเป็นวิธีที่สะดวกที่สุดเมื่อเชื่อมต่อกับบริการเฉพาะบน google cloud ผ่านไลบรารีของไคลเอ็นต์

หมายเหตุ : สำหรับแอปพลิเคชันที่พร้อมสำหรับการผลิต ขอแนะนำให้ใช้คีย์ API ที่มีอายุสั้นแทนคีย์บัญชีบริการ เพื่อลดความเสี่ยงที่คีย์บัญชีบริการจะตกไปอยู่ในมือของผู้ไม่ประสงค์ดี

บัญชีบริการคืออะไร?

บัญชีบริการเป็นบัญชีประเภทพิเศษบน Google Cloud ซึ่งสร้างขึ้นเพื่อการโต้ตอบที่ไม่ใช่ของมนุษย์ ส่วนใหญ่ผ่าน API ภายนอก ในแอปพลิเคชันของเรา บัญชีบริการจะเข้าถึงได้ผ่านคีย์ที่สร้างขึ้นโดยไลบรารีไคลเอนต์ Dialogflow เพื่อตรวจสอบสิทธิ์กับ Google Cloud

เอกสารเกี่ยวกับการสร้างและจัดการบัญชีบริการบนระบบคลาวด์เป็นแนวทางที่ดีเยี่ยมในการสร้างบัญชีบริการ เมื่อสร้างบัญชีบริการ ควรกำหนดบทบาทผู้ดูแลระบบ Dialogflow API ให้กับบัญชีบริการที่สร้างขึ้นดังที่แสดงในขั้นตอนสุดท้าย บทบาทนี้ให้การควบคุมดูแลบัญชีบริการผ่านตัวแทน Dialogflow ที่เชื่อมโยง

ในการใช้บัญชีบริการ เราต้องสร้างรหัสบัญชีบริการ ขั้นตอนต่อไปนี้จะสรุปวิธีการสร้างในรูปแบบ JSON:

  1. คลิกที่บัญชีบริการที่สร้างขึ้นใหม่เพื่อไปยังหน้าบัญชีบริการ
  2. เลื่อนไปที่ส่วนคีย์และคลิกดรอปดาวน์ Add Key และคลิกที่ตัวเลือก Create new key ซึ่งจะเปิด modal
  3. เลือกรูปแบบไฟล์ JSON และคลิกปุ่มสร้างที่ด้านล่างขวาของโมดอล

หมายเหตุ: ขอแนะนำให้เก็บคีย์บัญชีบริการไว้เป็นส่วนตัวและไม่ผูกกับ ระบบควบคุมเวอร์ชัน ใดๆ เนื่องจากมีข้อมูลที่ละเอียดอ่อนสูงเกี่ยวกับโครงการบน Google Cloud ซึ่งสามารถทำได้โดยการเพิ่มไฟล์ลงในไฟล์ .gitignore

ด้วยการสร้างบัญชีบริการและรหัสบัญชีบริการที่มีอยู่ในไดเรกทอรีของโครงการ เราสามารถใช้ไลบรารีไคลเอนต์ Dialogflow เพื่อส่งและรับข้อมูลจากตัวแทน Dialogflow

 // agentRoute.js require("dotenv").config(); const express = require("express") const Dialogflow = require("@google-cloud/dialogflow") const { v4 as uuid } = require("uuid") const Path = require("path") const app = express(); app.post("/text-input", async (req, res) => { const { message } = req.body; // Create a new session const sessionClient = new Dialogflow.SessionsClient({ keyFilename: Path.join(__dirname, "./key.json"), }); const sessionPath = sessionClient.projectAgentSessionPath( process.env.PROJECT_ID, uuid() ); // The dialogflow request object const request = { session: sessionPath, queryInput: { text: { // The query to send to the dialogflow agent text: message, }, }, }; // Sends data from the agent as a response try { const responses = await sessionClient.detectIntent(request); res.status(200).send({ data: responses }); } catch (e) { console.log(e); res.status(422).send({ e }); } }); module.exports = app;

เส้นทางทั้งหมดข้างต้นส่งข้อมูลไปยังตัวแทน Dialogflow และได้รับการตอบสนองตามขั้นตอนต่อไปนี้

  • อันดับแรก
    จะตรวจสอบสิทธิ์กับ Google Cloud จากนั้นจึงสร้างเซสชันด้วย Dialogflow โดยใช้รหัสโครงการของโครงการระบบคลาวด์ของ Google ที่เชื่อมโยงกับตัวแทน Dialogflow และ ID สุ่มเพื่อระบุเซสชันที่สร้างขึ้น ในแอปพลิเคชันของเรา เรากำลังสร้างตัวระบุ UUID ในแต่ละเซสชันที่สร้างโดยใช้แพ็คเกจ JavaScript UUID สิ่งนี้มีประโยชน์มากเมื่อทำการบันทึกหรือติดตามการสนทนาทั้งหมดที่จัดการโดยตัวแทน Dialogflow
  • ที่สอง
    เราสร้างคำขอข้อมูลวัตถุตามรูปแบบที่ระบุในเอกสาร Dialogflow ออบเจ็กต์คำขอนี้มีเซสชันที่สร้างขึ้นและข้อมูลข้อความที่ได้รับจากเนื้อหาคำขอซึ่งจะถูกส่งต่อไปยังตัวแทน Dialogflow
  • ที่สาม
    เมื่อใช้เมธอด detectIntent จากเซสชัน Dialogflow เราส่งอ็อบเจ็กต์คำขอแบบอะซิงโครนัสและรอการตอบกลับของ Agent โดยใช้ไวยากรณ์ ES6 async / await ในบล็อก try-catch หากวิธี detectIntent ส่งคืนข้อยกเว้น เราสามารถตรวจจับข้อผิดพลาดและส่งคืนได้ กว่าจะทำให้แอปพลิเคชั่นหยุดทำงานทั้งหมด ตัวอย่างของวัตถุตอบกลับที่ส่งคืนจากตัวแทนมีอยู่ในเอกสารประกอบของ Dialogflow และสามารถตรวจสอบได้เพื่อทราบวิธีการดึงข้อมูลออกจากวัตถุ

เราสามารถใช้ประโยชน์จากบุรุษไปรษณีย์เพื่อทดสอบการเชื่อมต่อ Dialogflow ที่นำไปใช้ด้านบนในเส้นทาง dialogflow-response ของไดอะล็อกโฟลว์ บุรุษไปรษณีย์เป็นแพลตฟอร์มการทำงานร่วมกันสำหรับการพัฒนา API พร้อมคุณสมบัติในการทดสอบ API ที่สร้างขึ้นในขั้นตอนการพัฒนาหรือการผลิต

หมายเหตุ: หากยังไม่ได้ติดตั้ง แอปพลิเคชัน Postman บนเดสก์ท็อปก็ไม่จำเป็นในการทดสอบ API ตั้งแต่เดือนกันยายน 2020 เว็บไคลเอ็นต์ของบุรุษไปรษณีย์จะย้ายไปอยู่ในสถานะพร้อมใช้งานทั่วไป (GA) และใช้งานได้โดยตรงจากเบราว์เซอร์

เมื่อใช้ Postman Web Client เราสามารถสร้างพื้นที่ทำงานใหม่หรือใช้ที่มีอยู่เพื่อสร้างคำขอ POST ไปยังตำแหน่งข้อมูล API ของเราที่ https://localhost:5000/api/agent/text-input และเพิ่มข้อมูลด้วยคีย์ของ message และค่าของ “ สวัสดี ” ลงในพารามิเตอร์การสืบค้น

เมื่อคลิกปุ่ม ส่ง ระบบจะส่งคำขอ POST ไปยังเซิร์ฟเวอร์ Express ที่ทำงานอยู่ โดยมีการตอบกลับคล้ายกับที่แสดงในภาพด้านล่าง:

ทดสอบจุดปลาย API สำหรับการป้อนข้อความโดยใช้บุรุษไปรษณีย์
ทดสอบจุดปลาย API สำหรับการป้อนข้อความโดยใช้บุรุษไปรษณีย์ (ตัวอย่างขนาดใหญ่)

ภายในภาพด้านบน เราสามารถเห็นข้อมูลการตอบสนองที่ปรับแต่งแล้วจาก Dialogflow Agent ผ่านเซิร์ฟเวอร์ Express ข้อมูลที่ส่งคืนถูกจัดรูปแบบตามข้อกำหนดการตอบสนองตัวอย่างที่ให้ไว้ในเอกสารประกอบ Dialogflow Webhook

การจัดการอินพุตเสียง

ตามค่าเริ่มต้น เอเจนต์ Dialogflow ทั้งหมดจะเปิดใช้งานเพื่อประมวลผลทั้งข้อมูลข้อความและเสียง และยังส่งคืนการตอบกลับในรูปแบบข้อความหรือเสียง อย่างไรก็ตาม การทำงานกับข้อมูลอินพุตหรือเอาต์พุตเสียงอาจซับซ้อนกว่าข้อมูลข้อความเล็กน้อย

ในการจัดการและประมวลผลอินพุตเสียง เราจะเริ่มใช้งานปลายทาง /voice-input ที่เราได้สร้างไว้ก่อนหน้านี้เพื่อรับไฟล์เสียงและส่งไปยัง Dialogflow เพื่อแลกกับการตอบกลับจากตัวแทน:

 // agentRoutes.js import { pipeline, Transform } from "stream"; import busboy from "connect-busboy"; import util from "promisfy" import Dialogflow from "@google-cloud/dialogflow" const app = express(); app.use( busboy({ immediate: true, }) ); app.post("/voice-input", (req, res) => { const sessionClient = new Dialogflow.SessionsClient({ keyFilename: Path.join(__dirname, "./recommender-key.json"), }); const sessionPath = sessionClient.projectAgentSessionPath( process.env.PROJECT_ID, uuid() ); // transform into a promise const pump = util.promisify(pipeline); const audioRequest = { session: sessionPath, queryInput: { audioConfig: { audioEncoding: "AUDIO_ENCODING_OGG_OPUS", sampleRateHertz: "16000", languageCode: "en-US", }, singleUtterance: true, }, }; const streamData = null; const detectStream = sessionClient .streamingDetectIntent() .on("error", (error) => console.log(error)) .on("data", (data) => { streamData = data.queryResult }) .on("end", (data) => { res.status(200).send({ data : streamData.fulfillmentText }} }) detectStream.write(audioRequest); try { req.busboy.on("file", (_, file, filename) => { pump( file, new Transform({ objectMode: true, transform: (obj, _, next) => { next(null, { inputAudio: obj }); }, }), detectStream ); }); } catch (e) { console.log(`error : ${e}`); } });

ในภาพรวมระดับสูง เส้นทาง /voice-input ด้านบนได้รับการป้อนข้อมูลด้วยเสียงของผู้ใช้เป็นไฟล์ที่มีข้อความที่กำลังพูดกับผู้ช่วยแชท และส่งไปยังตัวแทน Dialogflow เพื่อให้เข้าใจกระบวนการนี้ดีขึ้น เราสามารถแบ่งออกเป็นขั้นตอนย่อยๆ ต่อไปนี้:

  • อันดับแรก เราเพิ่มและใช้ connect-busboy เป็นมิดเดิลแวร์ Express เพื่อแยกวิเคราะห์ข้อมูลแบบฟอร์มที่ส่งในคำขอจากเว็บแอปพลิเคชัน หลังจากนั้น เราตรวจสอบสิทธิ์กับ Dialogflow โดยใช้รหัสบริการ และสร้างเซสชัน เช่นเดียวกับที่เราทำในเส้นทางก่อนหน้านี้
    จากนั้นใช้วิธี promisify จากโมดูล Node.js util ในตัว เราได้รับและบันทึกสัญญาที่เทียบเท่ากับวิธีไปป์ไลน์สตรีมเพื่อใช้ในภายหลังเพื่อไพพ์สตรีมหลายรายการและดำเนินการล้างข้อมูลหลังจากสตรีมเสร็จสิ้น
  • ต่อไป เราสร้างวัตถุคำขอที่มีเซสชันการตรวจสอบสิทธิ์ Dialogflow และการกำหนดค่าสำหรับไฟล์เสียงที่กำลังจะถูกส่งไปยัง Dialogflow อ็อบเจ็กต์คอนฟิกูเรชันเสียงที่ซ้อนกันช่วยให้เอเจนต์ Dialogflow ทำการแปลงคำพูดเป็นข้อความบนไฟล์เสียงที่ส่ง
  • ต่อไป โดยใช้เซสชันที่สร้างขึ้นและวัตถุคำขอ เราจะตรวจจับความตั้งใจของผู้ใช้จากไฟล์เสียงโดยใช้วิธี detectStreamingIntent ซึ่งจะเปิดสตรีมข้อมูลใหม่จากเอเจนต์ Dialogflow ไปยังแอปพลิเคชันแบ็กเอนด์ ข้อมูลจะส่งกลับเป็นบิตเล็ก ๆ ผ่านสตรีมนี้และใช้ข้อมูล " เหตุการณ์ " จากสตรีมที่อ่านได้ที่เราจัดเก็บข้อมูลในตัวแปร streamData เพื่อใช้ในภายหลัง หลังจากที่สตรีมปิด เหตุการณ์ " end " จะเริ่มทำงาน และเราจะส่งการตอบกลับจากเอเจนต์ Dialogflow ที่จัดเก็บไว้ในตัวแปร streamData ไปยังเว็บแอปพลิเคชัน
  • สุดท้ายนี้ การใช้เหตุการณ์การสตรีมไฟล์จาก connect-busboy เราได้รับสตรีมของไฟล์เสียงที่ส่งในเนื้อหาคำขอ และเราส่งต่อไปยังสัญญาที่เทียบเท่ากับไปป์ไลน์ที่เราสร้างขึ้นก่อนหน้านี้ ฟังก์ชันนี้คือไพพ์สตรีมไฟล์เสียงที่มาจากคำขอไปยังสตรีม detectStreamingIntent เราไพพ์สตรีมไฟล์เสียงไปยังสตรีมที่เปิดโดยวิธี

เพื่อทดสอบและยืนยันว่าขั้นตอนข้างต้นทำงานตามที่กำหนดไว้ เราสามารถขอทดสอบที่มีไฟล์เสียงในเนื้อหาของคำขอไปยังปลายทาง /voice-input โดยใช้บุรุษไปรษณีย์

ทดสอบปลายทาง API อินพุตเสียงโดยใช้บุรุษไปรษณีย์
ทดสอบจุดปลาย API อินพุตเสียงโดยใช้บุรุษไปรษณีย์พร้อมไฟล์เสียงที่บันทึกไว้ (ตัวอย่างขนาดใหญ่)

ผลลัพธ์ของบุรุษไปรษณีย์ด้านบนแสดงการตอบสนองที่ได้รับหลังจากส่งคำขอ POST โดยมีข้อมูลรูปแบบข้อความเสียงที่บันทึกไว้ว่า " สวัสดี ” รวมอยู่ในเนื้อหาของคำขอ

ณ จุดนี้ เรามีแอปพลิเคชัน Express.js ที่ใช้งานได้ซึ่งส่งและรับข้อมูลจาก Dialogflow ซึ่งทั้งสองส่วนของบทความนี้เสร็จสิ้นแล้ว ตอนนี้เหลือเพียงการรวม Agent นี้เข้ากับเว็บแอปพลิเคชันโดยใช้ API ที่สร้างขึ้นที่นี่จากแอปพลิเคชัน Reactjs

การรวมเข้ากับเว็บแอปพลิเคชัน

เพื่อใช้ REST API ที่สร้างขึ้นของเรา เราจะขยายแอปพลิเคชัน React.js ที่มีอยู่ซึ่งมีโฮมเพจที่แสดงรายการไวน์ที่ดึงมาจาก API และการสนับสนุนสำหรับนักตกแต่งโดยใช้ปลั๊กอินตัวตกแต่งข้อเสนอของ Babel เราจะปรับโครงสร้างใหม่เล็กน้อยโดยแนะนำ Mobx สำหรับการจัดการสถานะและคุณสมบัติใหม่ในการแนะนำไวน์จากองค์ประกอบการแชทโดยใช้ปลายทาง REST API ที่เพิ่มจากแอปพลิเคชัน Express.js

ในการเริ่มต้น เราเริ่มจัดการสถานะของแอปพลิเคชันโดยใช้ MobX ขณะที่เราสร้างร้านค้า Mobx ด้วยค่าที่สังเกตได้สองสามค่าและบางวิธีเป็นการกระทำ

 // store.js import Axios from "axios"; import { action, observable, makeObservable, configure } from "mobx"; const ENDPOINT = process.env.REACT_APP_DATA_API_URL; class ApplicationStore { constructor() { makeObservable(this); } @observable isChatWindowOpen = false; @observable isLoadingChatMessages = false; @observable agentMessages = []; @action setChatWindow = (state) => { this.isChatWindowOpen = state; }; @action handleConversation = (message) => { this.isLoadingChatMessages = true; this.agentMessages.push({ userMessage: message }); Axios.post(`${ENDPOINT}/dialogflow-response`, { message: message || "Hi", }) .then((res) => { this.agentMessages.push(res.data.data[0].queryResult); this.isLoadingChatMessages = false; }) .catch((e) => { this.isLoadingChatMessages = false; console.log(e); }); }; } export const store = new ApplicationStore();

ด้านบน เราได้สร้างร้านค้าสำหรับคุณลักษณะองค์ประกอบการแชทภายในแอปพลิเคชันโดยมีค่าต่อไปนี้:

  • isChatWindowOpen
    ค่าที่เก็บไว้ที่นี่จะควบคุมการมองเห็นองค์ประกอบการแชทที่แสดงข้อความของ Dialogflow
  • isLoadingChatMessages
    ใช้เพื่อแสดงตัวบ่งชี้การโหลดเมื่อมีการร้องขอให้ดึงการตอบสนองจากตัวแทน Dialogflow
  • agentMessages
    อาร์เรย์นี้เก็บการตอบสนองทั้งหมดที่มาจากคำขอที่ได้รับการตอบสนองจากตัวแทน Dialogflow ข้อมูลในอาร์เรย์จะแสดงในส่วนประกอบในภายหลัง
  • handleConversation
    เมธอดนี้ตกแต่งเป็นการดำเนินการเพิ่มข้อมูลลงในอาร์เรย์ agentMessages ขั้นแรก จะเพิ่มข้อความของผู้ใช้ที่ส่งผ่านเป็นอาร์กิวเมนต์ จากนั้นจึงส่งคำขอโดยใช้ Axios ไปยังแอปพลิเคชันแบ็กเอนด์เพื่อรับการตอบกลับจาก Dialogflow หลังจากแก้ไขคำขอแล้ว จะเพิ่มการตอบสนองจากคำขอลงในอาร์เรย์ agentMessages

หมายเหตุ: ในกรณีที่ไม่มี มัณฑนากร รองรับในแอปพลิเคชัน MobX จัดเตรียม makeObservable ซึ่งสามารถใช้ในคอนสตรัคเตอร์ของคลาสร้านค้าเป้าหมาย ดูตัวอย่าง ที่นี่

ด้วยการตั้งค่าร้านค้า เราจำเป็นต้องรวมโครงสร้างแอปพลิเคชันทั้งหมดด้วยส่วนประกอบที่มีลำดับสูงกว่าของผู้ให้บริการ MobX โดยเริ่มจากองค์ประกอบรูทในไฟล์ index.js

 import React from "react"; import { Provider } from "mobx-react"; import { store } from "./state/"; import Home from "./pages/home"; function App() { return ( <Provider ApplicationStore={store}> <div className="App"> <Home /> </div> </Provider> ); } export default App;

ด้านบนเราห่อส่วนประกอบรูทแอพด้วย MobX Provider และเราส่งผ่านในร้านค้าที่สร้างขึ้นก่อนหน้านี้เป็นหนึ่งในค่าของผู้ให้บริการ ตอนนี้ เราสามารถดำเนินการอ่านจากร้านค้าภายในส่วนประกอบที่เชื่อมต่อกับร้านค้าได้

การสร้างส่วนต่อประสานการแชท

ในการแสดงข้อความที่ส่งหรือได้รับจากคำขอ API เราจำเป็นต้องมีองค์ประกอบใหม่ที่มีอินเทอร์เฟซการแชทที่แสดงข้อความในรายการ ในการดำเนินการนี้ เราสร้างองค์ประกอบใหม่เพื่อแสดงข้อความที่ฮาร์ดโค้ดก่อน จากนั้นจึงแสดงข้อความในรายการที่สั่งซื้อในภายหลัง

 // ./chatComponent.js import React, { useState } from "react"; import { FiSend, FiX } from "react-icons/fi"; import "../styles/chat-window.css"; const center = { display: "flex", jusitfyContent: "center", alignItems: "center", }; const ChatComponent = (props) => { const { closeChatwindow, isOpen } = props; const [Message, setMessage] = useState(""); return ( <div className="chat-container"> <div className="chat-head"> <div style={{ ...center }}> <h5> Zara </h5> </div> <div style={{ ...center }} className="hover"> <FiX onClick={() => closeChatwindow()} /> </div> </div> <div className="chat-body"> <ul className="chat-window"> <li> <div className="chat-card"> <p>Hi there, welcome to our Agent</p> </div> </li> </ul> <hr style={{ background: "#fff" }} /> <form onSubmit={(e) => {}} className="input-container"> <input className="input" type="text" onChange={(e) => setMessage(e.target.value)} value={Message} placeholder="Begin a conversation with our agent" /> <div className="send-btn-ctn"> <div className="hover" onClick={() => {}}> <FiSend style={{ transform: "rotate(50deg)" }} /> </div> </div> </form> </div> </div> ); }; export default ChatComponent

องค์ประกอบด้านบนมีมาร์กอัป HTML พื้นฐานที่จำเป็นสำหรับแอปพลิเคชันแชท มีส่วนหัวที่แสดงชื่อตัวแทนและไอคอนสำหรับปิดหน้าต่างแชท ฟองข้อความที่มีข้อความแบบฮาร์ดโค้ดในแท็กรายการ และสุดท้ายคือช่องป้อนข้อมูลที่มีตัวจัดการเหตุการณ์ onChange แนบกับช่องป้อนข้อมูลเพื่อจัดเก็บข้อความที่พิมพ์ลงไป สถานะท้องถิ่นของส่วนประกอบโดยใช้ useState ของ React

การแสดงตัวอย่างส่วนประกอบการแชทด้วยข้อความที่ฮาร์ดโค้ดจากตัวแทนการแชท
การแสดงตัวอย่างส่วนประกอบการแชทพร้อมข้อความแบบฮาร์ดโค้ดจากตัวแทนการแชท (ตัวอย่างขนาดใหญ่)

จากภาพด้านบน องค์ประกอบการแชทจะทำงานตามที่ควรจะเป็น โดยแสดงหน้าต่างแชทที่มีสไตล์ซึ่งมีข้อความแชทเดียวและช่องป้อนข้อมูลที่ด้านล่าง อย่างไรก็ตาม เราต้องการให้ข้อความที่แสดงเป็นการตอบสนองจริงที่ได้รับจากคำขอ API ไม่ใช่ข้อความที่ฮาร์ดโค้ด

เราเดินหน้าปรับโครงสร้างองค์ประกอบ Chat ใหม่ โดยคราวนี้จะเชื่อมต่อและใช้ประโยชน์จากค่าในร้านค้า MobX ภายในส่วนประกอบ

 // ./components/chatComponent.js import React, { useState, useEffect } from "react"; import { FiSend, FiX } from "react-icons/fi"; import { observer, inject } from "mobx-react"; import { toJS } from "mobx"; import "../styles/chat-window.css"; const center = { display: "flex", jusitfyContent: "center", alignItems: "center", }; const ChatComponent = (props) => { const { closeChatwindow, isOpen } = props; const [Message, setMessage] = useState(""); const { handleConversation, agentMessages, isLoadingChatMessages, } = props.ApplicationStore; useEffect(() => { handleConversation(); return () => handleConversation() }, []); const data = toJS(agentMessages); return ( <div className="chat-container"> <div className="chat-head"> <div style={{ ...center }}> <h5> Zara {isLoadingChatMessages && "is typing ..."} </h5> </div> <div style={{ ...center }} className="hover"> <FiX onClick={(_) => closeChatwindow()} /> </div> </div> <div className="chat-body"> <ul className="chat-window"> {data.map(({ fulfillmentText, userMessage }) => ( <li> {userMessage && ( <div style={{ display: "flex", justifyContent: "space-between", }} > <p style={{ opacity: 0 }}> . </p> <div key={userMessage} style={{ background: "red", color: "white", }} className="chat-card" > <p>{userMessage}</p> </div> </div> )} {fulfillmentText && ( <div style={{ display: "flex", justifyContent: "space-between", }} > <div key={fulfillmentText} className="chat-card"> <p>{fulfillmentText}</p> </div> <p style={{ opacity: 0 }}> . </p> </div> )} </li> ))} </ul> <hr style={{ background: "#fff" }} /> <form onSubmit={(e) => { e.preventDefault(); handleConversation(Message); }} className="input-container" > <input className="input" type="text" onChange={(e) => setMessage(e.target.value)} value={Message} placeholder="Begin a conversation with our agent" /> <div className="send-btn-ctn"> <div className="hover" onClick={() => handleConversation(Message)} > <FiSend style={{ transform: "rotate(50deg)" }} /> </div> </div> </form> </div> </div> ); }; export default inject("ApplicationStore")(observer(ChatComponent));

จากส่วนที่ไฮไลต์ของโค้ดด้านบน เราจะเห็นได้ว่าตอนนี้คอมโพเนนต์แชททั้งหมดได้รับการแก้ไขเพื่อดำเนินการใหม่ดังต่อไปนี้

  • มีการเข้าถึงค่าที่เก็บ MobX หลังจากฉีดค่า ApplicationStore องค์ประกอบนี้ยังได้รับการกำหนดให้เป็นผู้สังเกตการณ์ค่าที่จัดเก็บเหล่านี้ ดังนั้นจึงแสดงผลใหม่เมื่อค่าใดค่าหนึ่งเปลี่ยนแปลง
  • เราเริ่มการสนทนากับตัวแทนทันทีหลังจากเปิดองค์ประกอบการแชทโดยเรียกใช้วิธี handleConversation ภายในเบ็ด useEffect เพื่อส่งคำขอทันทีที่องค์ประกอบนั้นแสดงผล
  • ขณะนี้เรากำลังใช้ค่า isLoadingMessages ภายในส่วนหัวของคอมโพเนนต์ Chat เมื่อคำขอรับการตอบกลับจากตัวแทนอยู่ในระหว่างเดินทาง เราตั้งค่า isLoadingMessages true และอัปเดตส่วนหัวเป็น Zara กำลังพิมพ์...
  • อาร์เรย์ agentMessages ภายในร้านได้รับการอัปเดตเป็นพร็อกซีโดย MobX หลังจากตั้งค่าแล้ว จากองค์ประกอบนี้ เราแปลงพร็อกซีนั้นกลับไปเป็นอาร์เรย์โดยใช้ยูทิลิตี้ toJS จาก MobX และเก็บค่าไว้ในตัวแปรภายในส่วนประกอบ อาร์เรย์นั้นได้รับการทำซ้ำเพิ่มเติมเพื่อเติมค่าในกรอบการแชทด้วยค่าภายในอาร์เรย์โดยใช้ฟังก์ชันแผนที่

ขณะนี้ใช้องค์ประกอบการแชท เราสามารถพิมพ์ประโยคและรอการตอบกลับเพื่อแสดงในตัวแทน

องค์ประกอบการแชทแสดงรายการข้อมูลส่งคืนจากคำขอ HTTP ไปยังแอปพลิเคชันด่วน
องค์ประกอบการแชทแสดงรายการข้อมูลส่งคืนจากคำขอ HTTP ไปยังแอปพลิเคชันด่วน (ตัวอย่างขนาดใหญ่)

การบันทึกการป้อนข้อมูลด้วยเสียงของผู้ใช้

โดยค่าเริ่มต้น เอเจนต์ Dialogflow ทั้งหมดสามารถยอมรับการป้อนข้อมูลด้วยเสียงหรือข้อความในภาษาที่ระบุจากผู้ใช้ อย่างไรก็ตาม จำเป็นต้องมีการปรับเปลี่ยนเล็กน้อยจากเว็บแอปพลิเคชันเพื่อเข้าถึงไมโครโฟนของผู้ใช้และบันทึกเสียงพูด

เพื่อให้บรรลุสิ่งนี้ เราแก้ไขร้าน MobX เพื่อใช้ HTML MediaStream Recording API เพื่อบันทึกเสียงของผู้ใช้ภายในสองวิธีใหม่ในร้านค้า MobX

 // store.js import Axios from "axios"; import { action, observable, makeObservable } from "mobx"; class ApplicationStore { constructor() { makeObservable(this); } @observable isRecording = false; recorder = null; recordedBits = []; @action startAudioConversation = () => { navigator.mediaDevices .getUserMedia({ audio: true, }) .then((stream) => { this.isRecording = true; this.recorder = new MediaRecorder(stream); this.recorder.start(50); this.recorder.ondataavailable = (e) => { this.recordedBits.push(e.data); }; }) .catch((e) => console.log(`error recording : ${e}`)); }; };

เมื่อคลิกที่ไอคอนบันทึกจากองค์ประกอบการแชท เมธอด startAudioConversation ในร้านค้า MobX ด้านบน จะถูกเรียกใช้เพื่อตั้งค่าวิธีการที่คุณสมบัติ isRecording ที่สังเกตได้เป็นจริง สำหรับองค์ประกอบการแชทเพื่อให้การตอบกลับด้วยภาพเพื่อแสดงการบันทึกกำลังดำเนินการอยู่

โดยใช้อินเทอร์เฟซตัวนำทางของเบราว์เซอร์ วัตถุ Media Device จะเข้าถึงเพื่อขอไมโครโฟนของอุปกรณ์ของผู้ใช้ หลังจากที่ได้รับอนุญาตสำหรับคำขอ getUserMedia แล้ว จะแก้ไขคำสัญญาด้วยข้อมูล MediaStream ซึ่งเราจะส่งต่อไปยังตัวสร้าง MediaRecorder เพื่อสร้างเครื่องบันทึกโดยใช้แทร็กสื่อในสตรีมที่ส่งคืนจากไมโครโฟนของอุปกรณ์ของผู้ใช้ จากนั้นเราจะจัดเก็บอินสแตนซ์ตัวบันทึกสื่อในคุณสมบัติ recorder ของร้านค้า เนื่องจากเราจะเข้าถึงได้จากวิธีอื่นในภายหลัง

ต่อไป เราเรียกวิธีการเริ่มต้นบนอินสแตนซ์ตัวบันทึก และหลังจากเซสชันการบันทึกสิ้นสุดลง ฟังก์ชัน ondataavailable จะถูกเรียกใช้ด้วยอาร์กิวเมนต์เหตุการณ์ที่มีสตรีมที่บันทึกไว้ใน Blob ซึ่งเราเก็บไว้ในคุณสมบัติอาร์เรย์ที่ recordedBits

การออกจากระบบข้อมูลในอาร์กิวเมนต์เหตุการณ์ที่ส่งผ่านไปยังเหตุการณ์ ondataavailable ที่เริ่มทำงาน เราจะเห็น Blob และคุณสมบัติของ Blob ในคอนโซลของเบราว์เซอร์

คอนโซล Devtools ของเบราว์เซอร์แสดง Blob ที่ออกจากระบบซึ่งสร้างโดย Media Recorder หลังจากสิ้นสุดการบันทึก ดึงคำคม
คอนโซล Devtools ของเบราว์เซอร์แสดง Blob ที่ออกจากระบบซึ่งสร้างโดย Media Recorder หลังจากสิ้นสุดการบันทึก (ตัวอย่างขนาดใหญ่)

ตอนนี้เราสามารถเริ่มสตรีม MediaRecorder ได้แล้ว เราต้องสามารถหยุดสตรีม MediaRecorder ได้เมื่อผู้ใช้บันทึกอินพุตเสียงของตนเสร็จแล้ว และส่งไฟล์เสียงที่สร้างขึ้นไปยังแอปพลิเคชัน Express.js

วิธีการใหม่ที่เพิ่มไปยังร้านค้าด้านล่างจะหยุดการสตรีมและทำการร้องขอ POST ที่มีการป้อนข้อมูลด้วยเสียงที่บันทึกไว้

 //store.js import Axios from "axios"; import { action, observable, makeObservable, configure } from "mobx"; const ENDPOINT = process.env.REACT_APP_DATA_API_URL; class ApplicationStore { constructor() { makeObservable(this); } @observable isRecording = false; recorder = null; recordedBits = []; @action closeStream = () => { this.isRecording = false; this.recorder.stop(); this.recorder.onstop = () => { if (this.recorder.state === "inactive") { const recordBlob = new Blob(this.recordedBits, { type: "audio/mp3", }); const inputFile = new File([recordBlob], "input.mp3", { type: "audio/mp3", }); const formData = new FormData(); formData.append("voiceInput", inputFile); Axios.post(`${ENDPOINT}/api/agent/voice-input`, formData, { headers: { "Content-Type": "multipart/formdata", }, }) .then((data) => {}) .catch((e) => console.log(`error uploading audio file : ${e}`)); } }; }; } export const store = new ApplicationStore();

The method above executes the MediaRecorder's stop method to stop an active stream. Within the onstop event fired after the MediaRecorder is stopped, we create a new Blob with a music type and append it into a created FormData.

As the last step., we make POST request with the created Blob added to the request body and a Content-Type: multipart/formdata added to the request's headers so the file can be parsed by the connect-busboy middleware from the backend-service application.

With the recording being performed from the MobX store, all we need to add to the chat-component is a button to execute the MobX actions to start and stop the recording of the user's voice and also a text to show when a recording session is active.

 import React from 'react' const ChatComponent = ({ ApplicationStore }) => { const { startAudiConversation, isRecording, handleConversation, endAudioConversation, isLoadingChatMessages } = ApplicationStore const [ Message, setMessage ] = useState("") return ( <div> <div className="chat-head"> <div style={{ ...center }}> <h5> Zara {} {isRecording && "is listening ..."} </h5> </div> <div style={{ ...center }} className="hover"> <FiX onClick={(_) => closeChatwindow()} /> </div> </div> <form onSubmit={(e) => { e.preventDefault(); handleConversation(Message); }} className="input-container" > <input className="input" type="text" onChange={(e) => setMessage(e.target.value)} value={Message} placeholder="Begin a conversation with our agent" /> <div className="send-btn-ctn"> {Message.length > 0 ? ( <div className="hover" onClick={() => handleConversation(Message)} > <FiSend style={{ transform: "rotate(50deg)" }} /> </div> ) : ( <div className="hover" onClick={() => handleAudioInput()} > <FiMic /> </div> )} </div> </form> </div> ) } export default ChatComponent

From the highlighted part in the chat component header above, we use the ES6 ternary operators to switch the text to “ Zara is listening …. ” whenever a voice input is being recorded and sent to the backend application. This gives the user feedback on what is being done.

Also, besides the text input, we added a microphone icon to inform the user of the text and voice input options available when using the chat assistant. If a user decides to use the text input, we switch the microphone button to a Send button by counting the length of the text stored and using a ternary operator to make the switch.

We can test the newly connected chat assistant a couple of times by using both voice and text inputs and watch it respond exactly like it would when using the Dialogflow console!

บทสรุป

In the coming years, the use of language processing chat assistants in public services will have become mainstream. This article has provided a basic guide on how one of these chat assistants built with Dialogflow can be integrated into your own web application through the use of a backend application.

The built application has been deployed using Netlify and can be found here. Feel free to explore the Github repository of the backend express application here and the React.js web application here. They both contain a detailed README to guide you on the files within the two projects.

อ้างอิง

  • Dialogflow Documentation
  • Building A Conversational NLP Enabled Chatbot Using Google's Dialogflow by Nwani Victory
  • MobX
  • https://web.postman.com
  • Dialogflow API: Node.js Client
  • Using the MediaStream Recording API