Alice di Kotlin: mengubah kode menjadi Yandex.Station



Pada bulan Juni, Yandex menyelenggarakan hackathon online di antara para pengembang keterampilan suara. Kami di Just AI baru saja memperbarui kerangka kerja sumber terbuka kami di Kotlin untuk mendukung fitur-fitur baru Alice yang keren. Dan perlu untuk memberikan semacam contoh sederhana untuk README ...



Tentang bagaimana beberapa ratus baris kode di Kotlin berubah menjadi Yandex.Station



Alice + Kotlin = JAICF



Just AI memiliki open source dan framework gratis untuk mengembangkan aplikasi suara dan chatbots teks - JAICF . Ini ditulis di Kotlin , bahasa pemrograman dari JetBrains, yang dikenal baik oleh semua android dan server yang menulis perusahaan berdarah (baik, atau menulis ulang dari Java). Kerangka kerja ini bertujuan untuk memfasilitasi pembuatan aplikasi percakapan yang tepat untuk berbagai asisten suara, teks, dan bahkan telepon.



Yandex memiliki Alice, asisten suara dengan suara yang menyenangkan dan API terbuka untuk pengembang pihak ketiga. Artinya, setiap pengembang dapat memperluas fungsionalitas Alice untuk jutaan pengguna dan bahkan mendapatkan uang dari Yandex untuk ini .



Kami tentu sajaresmi membuat JAICF berteman dengan Alice , jadi sekarang Anda dapat menulis keterampilan di Kotlin. Dan seperti inilah kelihatannya.



Script -> Webhook -> Dialog





Keterampilan Alicia apa pun adalah dialog suara antara pengguna dan asisten digital. Dialog tersebut dijelaskan di JAICF dalam bentuk skrip, yang kemudian dijalankan di server webhook, yang terdaftar di Yandex.Dialogues.



Skenario



Mari kita ambil keterampilan yang kita buat untuk hackathon. Ini membantu menghemat uang saat berbelanja di toko. Pertama, lihat cara kerjanya.





Di sini Anda dapat melihat bagaimana pengguna bertanya pada Alice - "Katakan padaku apa yang lebih menguntungkan - begitu banyak rubel untuk jumlah ini dan itu atau begitu banyak untuk itu?"



Alice segera meluncurkan keterampilan kita (karena disebut "Yang lebih menguntungkan") dan mentransfer semua informasi yang diperlukan ke sana - maksud pengguna dan data dari permintaannya .



Keterampilan, pada gilirannya, bereaksi terhadap maksud, memproses data, dan mengembalikan respons yang berguna. Alice mengatakan jawabannya dan mematikan, karena keterampilan mengakhiri sesi (mereka menyebutnya "keterampilan satu lulus").



Berikut adalah skenario sederhana, yang, bagaimanapun, memungkinkan Anda untuk dengan cepat menghitung seberapa besar satu produk lebih menguntungkan daripada yang lain. Dan pada saat yang sama memenangkan kolom pidato dari Yandex.




Seperti apa tampilannya di Kotlin?
object MainScenario: Scenario() {
    init {
        state("profit") {
            activators {
                intent("CALCULATE.PROFIT")
            }

            action {
                activator.alice?.run {
                    val a1 = slots["first_amount"]
                    val a2 = slots["second_amount"]
                    val p1 = slots["first_price"]
                    val p2 = slots["second_price"]
                    val u1 = slots["first_unit"]
                    val u2 = slots["second_unit"] ?: firstUnit

                    context.session["first"] = Product(a1?.value?.double ?: 1.0, p1!!.value.int, u1!!.value.content)
                    context.session["second"] = p2?.let {
                        Product(a2?.value?.double ?: 1.0, p2.value.int, u2!!.value.content)
                    }

                    reactions.go("calculate")
                }
            }

            state("calculate") {
                action {
                    val first = context.session["first"] as? Product
                    val second = context.session["second"] as? Product

                    if (second == null) {
                        reactions.say("   ?")
                    } else {
                        val profit = try {
                            ProfitCalculator.calculateProfit(first!!, second)
                        } catch (e: Exception) {
                            reactions.say("   , .   .")
                            return@action
                        }

                        if (profit == null || profit.percent == 0) {
                            reactions.say("     .")
                        } else {
                            val variant = when {
                                profit.product === first -> ""
                                else -> ""
                            }

                            var reply = "$variant   "

                            reply += when {
                                profit.percent < 10 -> "   ${profit.percent}%."
                                profit.percent < 100 -> " ${profit.percent}%."
                                else -> "  ${profit.percent}%."
                            }

                            context.client["last_reply"] = reply
                            reactions.say(reply)
                            reactions.alice?.endSession()
                        }
                    }
                }
            }

            state("second") {
                activators {
                    intent("SECOND.PRODUCT")
                }

                action {
                    activator.alice?.run {
                        val a2 = slots["second_amount"]
                        val p2 = slots["second_price"]
                        val u2 = slots["second_unit"]

                        val first = context.session["first"] as Product
                        context.session["second"] = Product(
                            a2?.value?.double ?: 1.0,
                            p2!!.value.int,
                            u2?.value?.content ?: first.unit
                        )

                        reactions.go("../calculate")
                    }
                }
            }
        }

        fallback {
            reactions.say(",   . " +
                    "  :  , 2   230   3   400.")
        }
    }
}




Skrip lengkap tersedia di Github .



Seperti yang Anda lihat, ini adalah objek biasa yang memperluas kelas Skenario dari pustaka JAICF. Pada dasarnya, skrip adalah mesin negara, di mana setiap node adalah kemungkinan keadaan percakapan. Beginilah cara kami mengimplementasikan pekerjaan dengan konteks, karena konteks dialog merupakan komponen yang sangat penting dari aplikasi suara apa pun.



Katakanlah frasa yang sama dapat diartikan berbeda tergantung pada konteks dialognya. Ngomong-ngomong, ini adalah salah satu alasan mengapa kami memilih Kotlin untuk framework kami - ini memungkinkan Anda membuat DSL singkat , yang akan memudahkan untuk mengelola konteks dan transisi bersarang di antara keduanya.



Status diaktifkan denganaktivator (misalnya, maksud ) dan menjalankan blok kode bersarang - aksi . Dan di dalam aksinya, Anda dapat melakukan apa pun yang Anda inginkan, tetapi yang utama adalah mengembalikan beberapa jawaban yang berguna kepada pengguna atau menginterogasi sesuatu. Ini dilakukan melalui reaksi . Ikuti tautan untuk penjelasan mendetail dari masing-masing entitas ini.



Maksud dan slot







Intent adalah representasi bahasa-independen dari permintaan pengguna. Sebenarnya, ini adalah pengenal dari apa yang pengguna ingin dapatkan dari aplikasi percakapan Anda.



Alice baru-baru ini mempelajari cara menentukan maksud untuk keahlian Anda secara otomatis jika Anda menjelaskan tata bahasa khusus terlebih dahulu. Selain itu, dia tahu cara mengekstrak data yang diperlukan dari frasa dalam bentuk slot - misalnya, harga dan volume barang, seperti dalam contoh kami.



Agar semuanya berfungsi, Anda perlu menjelaskan tata bahasa dan slot ini . Ini adalah tata bahasa dalam keahlian kami, dan ini adalah slotnyakami menggunakannya di dalamnya. Hal ini memungkinkan keahlian kami untuk menerima di pintu masuk tidak hanya sebaris permintaan pengguna dalam bahasa Rusia, tetapi juga pengenal bahasa-independen dan slot yang dikonversi sebagai tambahan (harga setiap produk dan volumenya).



JAICF, tentu saja, mendukung mesin NLU lainnya (misalnya, Caila atau Dialogflow ), tetapi dalam contoh kami, kami ingin menggunakan fitur Alice khusus ini untuk menunjukkan cara kerjanya.



Webhook



Oke, kami memiliki skripnya. Bagaimana kami memeriksa apakah itu berhasil?



Tentu saja, penganut pendekatan pengembangan yang digerakkan oleh pengujian akan menghargai keberadaan mekanisme pengujian otomatis bawaan untuk skrip interaktif di JAICF , yang secara pribadi kami gunakan terus-menerus, karena kami melakukan proyek besar, dan sulit untuk memeriksa semua perubahan secara manual. Tetapi contoh kita cukup kecil, jadi sebaiknya kita segera memulai server dan mencoba berbicara dengan Alice.



Untuk menjalankan skrip, Anda memerlukan webhook - server yang menerima permintaan masuk dari Yandex saat pengguna mulai berbicara dengan keahlian Anda. Sama sekali tidak sulit untuk memulai server - Anda hanya perlu mengkonfigurasi bot Anda dan meletakkan beberapa titik akhir di atasnya.



val skill = BotEngine(
    model = MainScenario.model,
    activators = arrayOf(
        AliceIntentActivator,
        BaseEventActivator,
        CatchAllActivator
    )
)


Beginilah cara bot dikonfigurasi - di sini kami menjelaskan skrip apa yang digunakan di dalamnya, di mana menyimpan data pengguna dan aktivator apa yang kami butuhkan agar skrip berfungsi (mungkin ada beberapa di antaranya).



fun main() {
    embeddedServer(Netty, System.getenv("PORT")?.toInt() ?: 8080) {
        routing {
            httpBotRouting("/" to AliceChannel(skill, useDataStorage = true))
        }
    }.start(wait = true)
}


Dan beginilah cara server dengan webhook memulai begitu saja - Anda hanya perlu menentukan saluran mana di mana titik akhir harus berfungsi. Kami menjalankan server JetBrains Ktor di sini, tetapi Anda dapat menggunakan yang lain di JAICF .



Di sini kita telah menggunakan satu lagi fitur Alice - menyimpan data pengguna di database internalnya (opsi useDataStorage ). JAICF akan secara otomatis menyimpan dan memulihkan konteks dari sana dan semua yang ditulis skrip kami di sana. Serialisasi transparan.



Dialog



Akhirnya kita bisa menguji semuanya! Server berjalan secara lokal, jadi kami memerlukan URL publik sementara agar permintaan dari Alice dapat mencapai webhook kami dari Internet. Untuk melakukan ini, akan lebih mudah menggunakan alat ngrok gratis , cukup dengan menjalankan perintah di terminal seperti ngrok http 8080







Semua permintaan akan tiba secara real time di PC Anda - sehingga Anda dapat men-debug dan mengedit kode.



Sekarang Anda dapat mengambil URL https yang diterima dan menentukannya saat membuat dialog Aliego baru di Yandex. Dialog . Di sana Anda juga dapat menguji dialog dengan teks. Tetapi jika Anda ingin berbicara dengan suatu keterampilan dengan suara, sekarang Alice dapat dengan cepat menerbitkan keterampilan pribadi, yang pada saat pengembangan hanya tersedia untuk Anda. Jadi, tanpa melalui moderasi lama dari Yandex, Anda sudah bisa mulai berbicara dengan keahlian Anda langsung dari aplikasi Alice atau dari speaker pintar.







Publikasi



Kami telah menguji semuanya dan siap untuk mempublikasikan keahlian tersebut untuk semua pengguna Alice! Untuk melakukan ini, webhook kita harus dihosting di suatu tempat di server publik dengan URL konstan. Pada prinsipnya, aplikasi di JAICF dapat dijalankan di mana saja di mana Java didukung (bahkan di smartphone Android).



Kami menjalankan contoh kami di Heroku . Kami baru saja membuat aplikasi baru dan mendaftarkan alamat repositori Github kami tempat kode keahlian disimpan. Heroku membangun dan menjalankan semuanya dari sumber itu sendiri. Kami hanya perlu mendaftarkan URL publik yang dihasilkan di Yandex. Dialog dan kirimkan semuanya untuk moderasi .



Total



Tutorial kecil ini mengikuti jejak hackathon Yandex , di mana skenario " Mana yang lebih menguntungkan " di atas memenangkan salah satu dari tiga Yandex.Stations! Di sini, omong-omong, Anda bisa melihat bagaimana keadaannya .



Kerangka kerja JAICF di Kotlin membantu saya mengimplementasikan dan men-debug skrip dialog dengan cepat, tanpa repot bekerja dengan API, konteks, dan basis data Alice, sekaligus tidak membatasi kemungkinan (seperti yang sering terjadi pada pustaka serupa).



tautan berguna



Dokumen lengkap JAICF ada di sini .

Petunjuk untuk membuat keterampilan untuk Alice ada di sini .

Sumber dari skill itu sendiri dapat ditemukan disana .



Dan jika Anda suka



Jangan ragu untuk berkontribusi di JAICF , seperti yang sudah dilakukan kolega dari Yandex , atau tinggalkan tanda bintang di Github .



Dan jika Anda memiliki pertanyaan, kami segera menjawabnya di Slack kami yang nyaman .



All Articles