Bir Dialogflow Aracısını Bir React Uygulamasına Entegre Etme

Yayınlanan: 2022-03-10
Kısa özet ↬ Küçük veya kurumsal düzeyde kullanılabilecek bir sohbet sohbet yardımcısı oluşturmaya gelince, Dialogflow büyük olasılıkla arama listenizde görünecek ilk seçeneklerden biri olacaktır - ve neden olmasın? Ses ve metin girişlerini işleme yeteneği, özel web kancaları kullanarak dinamik yanıtlar sağlama, Google asistanını kullanarak milyonlarca Google özellikli cihaza bağlanma ve çok daha fazlası gibi çeşitli özellikler sunar. Ancak bir Aracıyı tasarlamak ve yönetmek için sağlanan konsolunun yanı sıra, yerleşik web uygulamalarımızda da kullanılabilecek bir sohbet asistanını nasıl oluşturabiliriz?

Dialogflow, Dialogflow konsolundan veya entegre bir web uygulamasından kullanıldığında ses veya metin girişini işleyebilen doğal dil işleme konuşma sohbet yardımcısı oluşturma ve tasarlama sürecini basitleştiren bir platformdur.

Entegre Dialogflow Agent bu makalede kısaca anlatılsa da Node.js ve Dialogflow hakkında bilgi sahibi olmanız beklenmektedir. Dialogflow'u ilk kez öğreniyorsanız, bu makale Dialogflow'un ne olduğu ve kavramları hakkında net bir açıklama sunar.

Bu makale, bir React.js Web uygulaması ile Agent arasında bir bağlantı olarak bir Express.js arka uç uygulaması yardımıyla bir web uygulamasına entegre edilebilen ses ve sohbet desteğine sahip bir Dialogflow aracısının nasıl oluşturulduğuna dair bir kılavuzdur. Dialogflow'un kendisinde. Makalenin sonunda, kendi Dialogflow aracınızı tercih ettiğiniz web uygulamasına bağlayabilmelisiniz.

Bu kılavuzun kolayca takip edilmesini sağlamak için, öğreticinin en çok ilginizi çeken bölümüne atlayabilir veya bu bölümleri göründükleri sırayla takip edebilirsiniz:

  • Dialogflow Aracısı Ayarlama
  • Bir Dialogflow Aracısını Entegre Etme
  • Node Express Uygulaması Kurma
    • Dialogflow ile Kimlik Doğrulama
    • Hizmet Hesapları Nelerdir?
    • Ses Girişlerini Kullanma
  • Bir Web Uygulamasına Entegrasyon
    • Sohbet Arayüzü Oluşturma
    • Kullanıcı Sesi Girişini Kaydetme
  • Çözüm
  • Referanslar

1. Bir Dialogflow Aracısı Ayarlama

Bu makalede açıklandığı gibi, Dialogflow'taki bir sohbet asistanına Aracı adı verilir ve amaçlar, yerine getirme, bilgi tabanı ve çok daha fazlası gibi daha küçük bileşenlerden oluşur. Dialogflow, kullanıcıların bir Aracının konuşma akışını oluşturması, eğitmesi ve tasarlaması için bir konsol sağlar. Kullanım durumumuzda, aracı Dışa Aktarma ve İçe Aktarma özelliğini kullanarak eğitildikten sonra bir ZIP klasörüne dışa aktarılan bir aracıyı geri yükleyeceğiz.

İçe aktarma işlemini gerçekleştirmeden önce, restore edilmek üzere olan ajan ile birleştirilecek yeni bir ajan oluşturmamız gerekiyor. Konsoldan yeni bir Aracı oluşturmak için benzersiz bir ad ve ayrıca aracıyı bağlamak için Google Cloud'da bir proje gerekir. Google Cloud'da bağlantı kurulacak mevcut bir proje yoksa, burada yeni bir proje oluşturulabilir.

Daha önce bir aracı, kullanıcıya bütçelerine göre şarap ürünleri önermek için oluşturulmuş ve eğitilmiştir. Bu aracı bir ZIP'ye aktarıldı; Klasörü buradan indirebilir ve aracı Ayarları sayfasında bulunan Dışa Aktar ve İçe Aktar sekmesinden yeni oluşturulan aracımıza geri yükleyebilirsiniz.

Daha önce dışa aktarılan bir aracıyı bir ZIP klasöründen geri yükleme
Daha önce dışa aktarılan bir aracıyı bir ZIP klasöründen geri yükleme. (Büyük önizleme)

İthal edilen temsilci, daha önce, kullanıcının bir şişe şarap satın alma bütçesine dayalı olarak kullanıcıya bir şarap ürünü önermek üzere eğitilmiştir.

İçe aktarılan aracıyı incelerken, niyetler sayfasından oluşturulan üç amacı olduğunu göreceğiz. Biri, Aracı bir kullanıcıdan gelen girdiyi tanımadığında kullanılan bir geri dönüş amacıdır, diğeri Aracı ile bir konuşma başlatıldığında kullanılan bir Hoş Geldiniz amacıdır ve son niyet, kullanıcıya şu sonuca göre bir şarap önermek için kullanılır. cümle içindeki miktar parametresi. Bizim için endişe verici olan, get-wine-recommendation niyetidir

Bu amacın, konuşmayı bu amaca bağlamak için Varsayılan Karşılama amacından gelen wine-recommendation tek bir girdi bağlamı vardır.

"Bir Bağlam, bir amaçtan diğerine bir konuşmanın akışını kontrol etmek için kullanılan bir Aracı içindeki bir sistemdir."

Bağlamların altında, bir aracıyı bir kullanıcıdan ne tür ifadeler bekleyeceği konusunda eğitmek için kullanılan cümleler olan Eğitim ifadeleri bulunur. Bir amaç içindeki çok çeşitli eğitim ifadeleri aracılığıyla, bir aracı bir kullanıcının cümlesini ve içine düştüğü amacı tanıyabilir.

Temsilcilerimizin get-wine-recommendation alma amacındaki eğitim ifadeleri (aşağıda gösterildiği gibi) şarap seçimini ve fiyat kategorisini belirtir:

Get-wine-tavsiye amaçlı mevcut eğitim ifadelerinin listesi.
Mevcut eğitim ifadelerini gösteren şarap önerisi alma amacı sayfası. (Büyük önizleme)

Yukarıdaki resme baktığımızda, mevcut eğitim ifadelerinin listelendiğini görebiliriz ve para birimi rakamı her biri için sarı renkle vurgulanmıştır. Bu vurgulama, Dialogflow'ta bir açıklama olarak bilinir ve bir kullanıcının cümlesinden varlık olarak bilinen tanınan veri türlerini çıkarmak için otomatik olarak yapılır.

Bu amaç, aracıyla yapılan bir konuşmada eşleştirildikten sonra, içinde bulunan etkinleştirilmiş web kancası kullanılarak, bir kullanıcının cümlesinden bir parametre olarak çıkarılan fiyata dayalı olarak önerilen şarabı almak için harici bir hizmete bir HTTP isteği yapılır. Bu amaç sayfasının altındaki Yerine Getirme bölümü.

Agent'ı Dialogflow konsolunun sağ bölümünde bulunan Dialogflow öykünücüsünü kullanarak test edebiliriz. Test etmek için sohbete “ Merhaba ” mesajı ile başlıyoruz ve istenilen miktarda şarap ile devam ediyoruz. Web kancası hemen çağrılacak ve aracı tarafından aşağıdakine benzer zengin bir yanıt gösterilecektir.

İçe aktarılan aracı aracısı web kancası test ediliyor.
Konsoldaki Aracı öykünücüsünü kullanarak içe aktarılan aracının yerine getirme web kancasını test etme. (Büyük önizleme)

Yukarıdaki resimden, Ngrok kullanılarak oluşturulan web kancası URL'sini ve sağ taraftaki temsilcinin, kullanıcı tarafından yazılan 20 $ fiyat aralığında bir şarap gösteren yanıtını görebiliriz.

Bu noktada, Dialogflow aracısı tamamen kurulmuştur. Artık diğer kullanıcıların Dialogflow konsolumuza erişmeden aracıya erişmelerini ve aracıyla etkileşim kurmalarını sağlamak için bu aracıyı bir web uygulamasına entegre etmeye başlayabiliriz.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Bir Dialogflow Aracısını Entegre Etme

Bir Dialogflow Aracısına bağlanmanın REST uç noktalarına HTTP istekleri yapmak gibi başka yolları olsa da, Dialogflow'a bağlanmanın önerilen yolu, çeşitli programlama dillerinde bulunan resmi istemci kitaplığının kullanılmasıdır. JavaScript için, @google-cloud/dialogflow paketi NPM'den yüklenebilir.

@google-cloud/dialogflow paketi dahili olarak ağ bağlantıları için gRPC'yi kullanır ve bu, paketin web paketi kullanılarak yamalandığı durumlar dışında tarayıcı ortamında desteklenmemesine neden olur, bu paketi kullanmanın önerilen yolu bir Düğüm ortamındandır. Bunu, bu paketi kullanmak için bir Express.js arka uç uygulaması kurarak ve ardından API uç noktaları aracılığıyla web uygulamasına veri sunarak yapabiliriz ve bundan sonra yapacağımız şey budur.

Node Express Uygulaması Kurma

Ekspres bir uygulama kurmak için yeni bir proje dizini oluşturuyoruz, ardından açık bir komut satırı terminalinden yarn kullanarak gerekli bağımlılıkları alıyoruz.

 # 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

Gerekli bağımlılıklar yüklendikten sonra, web uygulaması için CORS desteği etkinleştirilmiş olarak belirli bir bağlantı noktasındaki bağlantıları yöneten çok yalın bir Express.js sunucusu kurmaya devam edebiliriz.

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

Yürütüldüğünde, yukarıdaki snippet'teki kod, belirtilen bir PORT Express.js üzerindeki bağlantıları dinleyen bir HTTP sunucusunu başlatır. Ayrıca, bir Express ara yazılımı olarak cors paketini kullanan tüm isteklerde Çapraz Kaynaklı kaynak paylaşımına (CORS) sahiptir. Şimdilik bu sunucu sadece bağlantıları dinliyor, oluşturulan bir rota olmadığı için bir isteğe cevap veremiyor, o yüzden bunu oluşturalım.

Şimdi iki yeni rota eklememiz gerekiyor: biri metin verisi göndermek için, diğeri ise kayıtlı bir ses girişi göndermek için. Her ikisi de bir POST isteğini kabul edecek ve daha sonra istek gövdesinde bulunan verileri Dialogflow aracısına gönderecektir.

 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

Yukarıda, oluşturulan iki POST yolu için, şimdilik yalnızca 200 durum kodu ve sabit kodlanmış bir sahte yanıtla yanıt veren ayrı bir yönlendirici örneği oluşturduk. Dialogflow ile kimlik doğrulamayı bitirdiğimizde, bu uç noktalarda Dialogflow ile gerçek bir bağlantı kurmak için geri gelebiliriz.

Arka uç uygulama kurulumumuzun son adımı için, önceden oluşturulmuş yönlendirici örneğini app.use ve rota için bir temel yol kullanarak Express uygulamasına monte ediyoruz.

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

Yukarıda, iki yola bir temel yol ekledik, bunlardan herhangi birini bir komut satırından cURL kullanarak bir POST isteği aracılığıyla test edebiliriz, aşağıda boş bir istek gövdesiyle yapıldığı gibi;

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

Yukarıdaki isteğin başarıyla tamamlanmasından sonra, konsola yazdırılan nesne verilerini içeren bir yanıt görmeyi bekleyebiliriz.

Şimdi, @google-cloud/dialogflow paketini kullanarak Dialogflow'ta Temsilciden kimlik doğrulama, gönderme ve veri almayı içeren gerçek bir Dialogflow bağlantısı kurmamız kaldı.

Dialogflow ile Kimlik Doğrulama

Oluşturulan her Dialogflow aracısı, Google Cloud'daki bir projeye bağlanır. Dialogflow aracısına harici olarak bağlanmak için Google bulutunda projeyle kimlik doğrulaması yapıyoruz ve projenin kaynaklarından biri olarak Dialogflow'u kullanıyoruz. Google bulutta bir projeye bağlanmak için mevcut altı yoldan Hizmet hesapları seçeneğini kullanmak, google buluttaki belirli bir hizmete istemci kitaplığı aracılığıyla bağlanırken en uygunudur.

Not : Üretime hazır uygulamalar için, bir hizmet hesabı anahtarının yanlış ellere geçmesi riskini azaltmak için Hizmet Hesabı anahtarları yerine kısa ömürlü API anahtarlarının kullanılması önerilir.

Hizmet Hesapları Nedir?

Hizmet hesapları, Google Cloud'da, çoğunlukla harici API'ler aracılığıyla insan dışı etkileşim için oluşturulan özel bir hesap türüdür. Uygulamamızda, Google Cloud ile kimlik doğrulaması yapmak için Dialogflow istemci kitaplığı tarafından oluşturulan bir anahtar aracılığıyla hizmet hesabına erişilecektir.

Hizmet hesapları oluşturmaya ve yönetmeye ilişkin bulut belgeleri, bir hizmet hesabı oluşturmak için mükemmel bir kılavuz sağlar. Hizmet hesabı oluşturulurken, son adımda gösterildiği gibi oluşturulan hizmet hesabına Dialogflow API Yönetici rolü atanmalıdır. Bu rol, hizmet hesabına bağlı Dialogflow aracısı üzerinde yönetim denetimi sağlar.

Hizmet hesabını kullanmak için bir Hizmet Hesabı Anahtarı oluşturmamız gerekiyor. Aşağıdaki adımlar, JSON biçiminde nasıl oluşturulacağını özetlemektedir:

  1. Hizmet hesabı sayfasına gitmek için yeni oluşturulan Hizmet Hesabına tıklayın.
  2. Anahtarlar bölümüne gidin ve Anahtar Ekle açılır menüsünü tıklayın ve bir kip açan Yeni anahtar oluştur seçeneğini tıklayın.
  3. Bir JSON dosya formatı seçin ve kipin sağ alt kısmındaki Oluştur düğmesine tıklayın.

Not: Bir hizmet hesabı anahtarının gizli tutulması ve Google Cloud'daki bir proje hakkında oldukça hassas veriler içerdiğinden herhangi bir sürüm kontrol sistemine bağlanmaması önerilir. Bu, dosyayı .gitignore dosyasına ekleyerek yapılabilir.

Oluşturulan bir hizmet hesabı ve projemizin dizininde bulunan bir hizmet hesabı anahtarı ile, Dialogflow aracısından veri göndermek ve almak için Dialogflow istemci kitaplığını kullanabiliriz.

 // 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;

Yukarıdaki yolun tamamı, Dialogflow aracısına veri gönderir ve aşağıdaki adımlarla bir yanıt alır.

  • Öncelikle
    Google bulutuyla kimlik doğrulaması yapar, ardından Dialogflow aracısına bağlı Google bulut projesinin proje kimliğini ve ayrıca oluşturulan oturumu tanımlamak için rastgele bir kimliği kullanarak Dialogflow ile bir oturum oluşturur. Uygulamamızda JavaScript UUID paketi kullanılarak oluşturulan her oturumda bir UUID tanımlayıcı oluşturuyoruz. Bu, bir Dialogflow aracısı tarafından işlenen tüm konuşmaları günlüğe kaydederken veya takip ederken çok kullanışlıdır.
  • ikinci
    Dialogflow belgelerinde belirtilen biçimi izleyerek bir istek nesnesi verisi oluştururuz. Bu istek nesnesi, oluşturulan oturumu ve Dialogflow aracısına iletilecek olan istek gövdesinden alınan mesaj verilerini içerir.
  • Üçüncü
    Dialogflow oturumundan detectIntent yöntemini kullanarak request nesnesini asenkron olarak göndeririz ve bir try-catch bloğunda detectIntent async/await sözdizimini kullanarak Agent'ın yanıtını beklerizDetectIntent yöntemi bir istisna döndürürse, hatayı yakalayıp geri döndürebiliriz. tüm uygulamayı çökertmekten daha fazla. Aracıdan döndürülen yanıt nesnesinin bir örneği Dialogflow belgelerinde sağlanır ve nesneden verilerin nasıl çıkarılacağını bilmek için incelenebilir.

Yukarıda dialogflow-response yolunda uygulanan Dialogflow bağlantısını test etmek için Postman'ı kullanabiliriz. Postman, geliştirme veya üretim aşamalarında oluşturulmuş API'leri test etme özelliklerine sahip, API geliştirme için bir işbirliği platformudur.

Not: Henüz yüklenmemişse, bir API'yi test etmek için Postman masaüstü uygulamasına gerek yoktur. Eylül 2020'den itibaren, Postman'ın web istemcisi Genel Olarak Kullanılabilir (GA) durumuna geçti ve doğrudan bir tarayıcıdan kullanılabilir.

Postacı Web İstemcisini kullanarak, https://localhost:5000/api/agent/text-input adresindeki API uç noktamıza bir POST isteği oluşturmak için yeni bir çalışma alanı oluşturabilir veya mevcut olanı kullanabilir ve bir anahtarla veri ekleyebiliriz. “ Merhabamessage ve değerini sorgu parametrelerine girin.

Gönder düğmesine tıklandığında, çalışan Express sunucusuna aşağıdaki resimde gösterilene benzer bir yanıtla bir POST isteği yapılır:

Postman kullanarak metin girişi API uç noktasını test etme.
Postman kullanarak metin girişi API uç noktasını test etme. (Büyük önizleme)

Yukarıdaki resimde, Express sunucusu aracılığıyla Dialogflow aracısından gelen güzelleştirilmiş yanıt verilerini görebiliriz. Döndürülen veriler, Dialogflow Webhook belgelerinde verilen örnek yanıt tanımına göre biçimlendirilir.

Ses Girişlerini Kullanma

Varsayılan olarak, tüm Dialogflow aracılarının hem metin hem de ses verilerini işlemesi ve ayrıca metin veya ses biçiminde bir yanıt döndürmesi sağlanır. Ancak, ses giriş veya çıkış verileriyle çalışmak, metin verilerinden biraz daha karmaşık olabilir.

Ses girişlerini işlemek ve işlemek için, ses dosyalarını almak ve bunları Aracıdan yanıt karşılığında Dialogflow'a göndermek için daha önce oluşturduğumuz /voice-input uç noktası için uygulamaya başlayacağız:

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

Yüksek bir genel bakışta, yukarıdaki /voice-input yolu, bir kullanıcının sesli girişini sohbet yardımcısına söylenen mesajı içeren bir dosya olarak alır ve bunu Dialogflow aracısına gönderir. Bu süreci daha iyi anlamak için aşağıdaki küçük adımlara ayırabiliriz:

  • İlk olarak, web uygulamasından istekte gönderilen form verilerini ayrıştırmak için bir Express ara yazılımı olarak connect-busboy'u ekler ve kullanırız. Bundan sonra Service Key kullanarak Dialogflow ile kimlik doğrulaması yapıyoruz ve bir önceki rotada yaptığımız gibi bir oturum oluşturuyoruz.
    Ardından, yerleşik Node.js util modülündeki promisify yöntemini kullanarak, daha sonra birden fazla akışı yönlendirmek ve akışlar tamamlandıktan sonra bir temizleme gerçekleştirmek için kullanılacak Akış boru hattı yönteminin söz eşdeğerini alır ve kaydederiz.
  • Ardından, Dialogflow kimlik doğrulama oturumunu içeren bir istek nesnesi ve Dialogflow'a gönderilmek üzere olan ses dosyası için bir yapılandırma oluşturuyoruz. Yuvalanmış ses yapılandırma nesnesi, Dialogflow aracısının gönderilen ses dosyasında Konuşmadan Metne dönüştürme gerçekleştirmesini sağlar.
  • Ardından, oluşturulan oturumu ve istek nesnesini kullanarak, Dialogflow aracısından arka uç uygulamasına yeni bir veri akışı detectStreamingIntent yöntemini kullanarak ses dosyasından bir kullanıcının niyetini tespit ederiz. Veriler bu akış aracılığıyla küçük bitler halinde geri gönderilecek ve okunabilir akıştaki veri “ olay ”ını kullanarak verileri daha sonra kullanmak üzere streamData değişkeninde saklıyoruz. Akış kapatıldıktan sonra “ end ” olayı tetiklenir ve streamData değişkeninde depolanan Dialogflow aracısından yanıtı Web Uygulamasına göndeririz.
  • Son olarak connect-busboy'dan dosya akışı olayını kullanarak, istek gövdesine gönderilen ses dosyasının akışını alıyoruz ve daha önce oluşturduğumuz Pipeline'ın söz eşdeğerine iletiyoruz. Bunun işlevi, istekten gelen ses dosyası akışını Dialogflow akışına aktarmaktır, ses dosyası akışını yukarıdaki detectStreamingIntent yöntemi tarafından açılan akışa yönlendiririz.

Yukarıdaki adımların düzenlendiği gibi çalıştığını test etmek ve onaylamak için, Postman kullanarak /voice-input uç noktasına istek gövdesinde bir ses dosyası içeren bir test talebi yapabiliriz.

Postman kullanarak ses girişi API uç noktasını test etme.
Kayıtlı bir ses dosyasıyla postacı kullanarak ses girişi API uç noktasını test etme. (Büyük önizleme)

Yukarıdaki Postacı sonucu, talebin gövdesinde yer alan “ Merhaba ” diyen kayıtlı bir sesli not mesajının form verileriyle bir POST talebi yaptıktan sonra alınan yanıtı gösterir.

Bu noktada artık Dialogflow'dan veri gönderen ve alan işlevsel bir Express.js uygulamamız var, bu makalenin iki bölümü de tamamlandı. Şimdi burada bir Reactjs uygulamasından oluşturulan API'leri tüketerek bu Aracıyı bir web uygulamasına entegre etmekle nerede kaldı.

Bir Web Uygulamasına Entegrasyon

Yerleşik REST API'mizi kullanmak için, zaten bir API'den alınan şarapların listesini gösteren bir ana sayfasına ve babel teklif dekoratörleri eklentisini kullanan dekoratörler için desteğe sahip olan bu mevcut React.js uygulamasını genişleteceğiz. Durum yönetimi için Mobx'i ve ayrıca Express.js uygulamasından eklenen REST API uç noktalarını kullanarak bir sohbet bileşeninden bir şarap önermek için yeni bir özelliği tanıtarak biraz yeniden düzenleyeceğiz.

Başlamak için, birkaç gözlemlenebilir değer ve eylem olarak bazı yöntemlerle bir Mobx mağazası oluştururken MobX kullanarak uygulamanın durumunu yönetmeye başlarız.

 // 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();

Yukarıda, uygulama içindeki sohbet bileşeni özelliği için aşağıdaki değerlere sahip bir mağaza oluşturduk:

  • isChatWindowOpen
    Burada saklanan değer, Dialogflow mesajlarının gösterildiği sohbet bileşeninin görünürlüğünü kontrol eder.
  • isLoadingChatMessages
    Bu, Dialogflow aracısından yanıt alma isteği yapıldığında yükleme göstergesini göstermek için kullanılır.
  • agentMessages
    Bu dizi, Dialogflow aracısından yanıt almak için yapılan isteklerden gelen tüm yanıtları depolar. Dizideki veriler daha sonra bileşende görüntülenir.
  • handleConversation
    Bir eylem olarak dekore edilmiş bu yöntem, agentMessages dizisine veri ekler. İlk olarak, bir argüman olarak iletilen kullanıcının mesajını ekler, ardından Dialogflow'tan bir yanıt almak için arka uç uygulamasına Axios kullanarak bir istekte bulunur. İstek çözümlendikten sonra, istekten gelen yanıtı agentMessages dizisine ekler.

Not: Bir Uygulamada dekoratör desteğinin olmaması durumunda , MobX , hedef mağaza sınıfının yapıcısında kullanılabilecek makeObservable sağlar. Burada bir örneğe bakın .

Mağaza kurulumu ile, index.js dosyasındaki kök bileşenden başlayarak tüm uygulama ağacını MobX Sağlayıcı üst düzey bileşenle sarmamız gerekiyor.

 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;

Yukarıda root App bileşenini MobX Provider ile sarıyoruz ve Provider'ın değerlerinden biri olarak önceden oluşturulmuş mağazaya geçiyoruz. Artık mağazaya bağlı bileşenler içinde mağazadan okumaya geçebiliriz.

Sohbet Arayüzü Oluşturma

API isteklerinden gönderilen veya alınan mesajları görüntülemek için, mesajların listelendiği bazı sohbet arayüzüne sahip yeni bir bileşene ihtiyacımız var. Bunu yapmak için, önce bazı sabit kodlanmış mesajları görüntülemek için yeni bir bileşen oluşturuyoruz, ardından mesajları sıralı bir listede görüntülüyoruz.

 // ./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

Yukarıdaki bileşen, bir sohbet uygulaması için gereken temel HTML işaretlemesine sahiptir. Aracının adını ve sohbet penceresini kapatmak için bir simgeyi gösteren bir başlığı, bir liste etiketinde sabit kodlanmış bir metin içeren bir mesaj balonu ve son olarak, girilen metni depolamak için giriş alanına eklenmiş bir onChange olay işleyicisine sahip bir giriş alanı vardır. React'in useState'ini kullanarak bileşenin yerel durumu.

Sohbet Aracısından sabit kodlanmış bir mesajla Sohbet Bileşeninin bir önizlemesi
Sohbet Aracısından sabit kodlanmış bir mesajla Sohbet Bileşeninin bir önizlemesi. (Büyük önizleme)

Yukarıdaki resimden, sohbet bileşeni olması gerektiği gibi çalışır ve tek bir sohbet mesajı ve altta giriş alanı olan bir tarz sohbet penceresi gösterir. Ancak gösterilen mesajın, sabit kodlanmış metin değil, API isteğinden alınan gerçek yanıtlar olmasını istiyoruz.

Chat bileşenini yeniden düzenlemek için ilerliyoruz, bu sefer bileşen içindeki MobX mağazasındaki değerleri bağlayıp kullanıyoruz.

 // ./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));

Yukarıdaki kodun vurgulanan bölümlerinden, tüm sohbet bileşeninin aşağıdaki yeni işlemleri gerçekleştirmek için değiştirildiğini görebiliriz;

  • ApplicationStore değerini enjekte ettikten sonra MobX depolama değerlerine erişimi vardır. Bileşen ayrıca bu depolama değerlerinin bir gözlemcisi haline getirildi, böylece değerlerden biri değiştiğinde yeniden işleniyor.
  • Bileşen oluşturulduktan hemen sonra bir istek yapmak için bir useEffect kancası içindeki handleConversation yöntemini çağırarak, sohbet bileşeni açıldıktan hemen sonra Agent ile konuşmayı başlatırız.
  • Artık Chat bileşeni başlığındaki isLoadingMessages değerini kullanıyoruz. Temsilciden yanıt alma isteği yayındayken, isLoadingMessages değerini true olarak ayarlar ve üstbilgiyi Zara yazıyor olarak güncelleriz…
  • Mağaza içindeki agentMessages dizisi, değerleri ayarlandıktan sonra MobX tarafından bir proxy'ye güncellenir. Bu bileşenden, toJS yardımcı programını kullanarak bu proxy'yi tekrar bir diziye dönüştürüyoruz ve değerleri bileşen içindeki bir değişkende saklıyoruz. Bu dizi, bir harita işlevi kullanarak sohbet balonlarını dizi içindeki değerlerle doldurmak için daha fazla yinelenir.

Şimdi sohbet bileşenini kullanarak bir cümle yazabilir ve aracıda bir yanıtın görüntülenmesini bekleyebiliriz.

Hızlı uygulamaya HTTP isteğinden döndürülen bir liste verilerini gösteren sohbet bileşeni.
Hızlı uygulamaya HTTP isteğinden döndürülen bir liste verilerini gösteren sohbet bileşeni. (Büyük önizleme)

Kullanıcı Sesi Girişini Kaydetme

Varsayılan olarak, tüm Dialogflow aracıları, bir kullanıcıdan belirtilen herhangi bir dilde ses veya metin tabanlı girişi kabul edebilir. Ancak, bir kullanıcının mikrofonuna erişmek ve bir ses girişini kaydetmek için bir web uygulamasından birkaç ayar yapılması gerekir.

Bunu başarmak için MobX mağazasını, bir kullanıcının sesini MobX mağazasında iki yeni yöntemle kaydetmek için HTML MediaStream Kayıt API'sini kullanacak şekilde değiştirdik.

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

Sohbet bileşeninden kayıt simgesine tıklandığında, yukarıdaki MobX mağazasındaki startAudioConversation yöntemi, gözlemlenebilir isRecording özelliğinin true olarak ayarlanması için çağrılır, sohbet bileşeninin bir kaydın devam ettiğini göstermek için görsel geri bildirim sağlaması için.

Tarayıcının gezgin arayüzünü kullanarak, kullanıcının cihaz mikrofonunu istemek için Medya Cihazı nesnesine erişilir. getUserMedia isteğine izin verildikten sonra, kullanıcının cihaz mikrofonundan döndürülen akıştaki medya parçalarını kullanarak bir kayıt cihazı oluşturmak için MediaRecorder oluşturucusuna ileteceğimiz bir MediaStream verileriyle sözünü çözer. Daha sonra Medya kaydedici örneğini, daha sonra başka bir yöntemden erişeceğimiz için mağazanın recorder özelliğinde saklarız.

Daha sonra, kayıt cihazı örneğinde başlatma yöntemini çağırırız ve kayıt oturumu sona erdikten sonra, ondataavailable işlevi, recordBits dizi özelliğinde depoladığımız bir Blob'da recordedBits akışı içeren bir olay argümanıyla tetiklenir.

ondataavailable olayına iletilen olay bağımsız değişkenindeki verilerin oturumunu kapatarak, Blob'u ve özelliklerini tarayıcı konsolunda görebiliriz.

Bir kayıt sona erdikten sonra Medya Kaydedici tarafından oluşturulan oturumu kapatılmış Blob'u gösteren Tarayıcı Devtools konsolu.
Bir kayıt sona erdikten sonra Medya Kaydedici tarafından oluşturulan oturumu kapatılmış Blob'u gösteren Tarayıcı Devtools konsolu. (Büyük önizleme)

Artık bir MediaRecorder akışını başlatabileceğimize göre, bir kullanıcının ses girişini kaydetmesi bittiğinde MediaRecorder akışını durdurabilmemiz ve oluşturulan ses dosyasını Express.js uygulamasına gönderebilmemiz gerekiyor.

Aşağıdaki mağazaya eklenen yeni yöntem akışı durdurur ve kaydedilen ses girişini içeren bir POST isteği yapar.

 //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!

Çözüm

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.

Referanslar

  • 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