في يونيو ، نظمت Yandex هاكاثونًا عبر الإنترنت بين مطوري المهارات الصوتية. كنا في Just AI نقوم للتو بتحديث إطار عملنا مفتوح المصدر في Kotlin لدعم ميزات Alice الجديدة الرائعة. وكان من الضروري الخروج بنوع من الأمثلة البسيطة لـ README ...
حول كيفية تحول بضع مئات من الأسطر من التعليمات البرمجية على Kotlin إلى Yandex.
أليس + كوتلن = JAICF
تمتلك Just AI إطار عمل مفتوح المصدر ومجاني تمامًا لتطوير التطبيقات الصوتية وروبوتات الدردشة النصية - JAICF . إنه مكتوب بلغة Kotlin ، وهي لغة برمجة من JetBrains ، وهي معروفة جيدًا لجميع أجهزة androids والخوادم الذين يكتبون مشروعًا داميًا (حسنًا ، أو يعيدون كتابته من Java). يهدف الإطار إلى تسهيل إنشاء تطبيقات محادثة بدقة لمختلف مساعدي الصوت والنص وحتى الهاتف.
لدى Yandex أليس ، مساعد صوتي بصوت لطيف وواجهة برمجة تطبيقات مفتوحة لمطوري الطرف الثالث. أي أنه يمكن لأي مطور توسيع وظائف Alice لملايين المستخدمين وحتى الحصول على أموال من Yandex مقابل ذلك .
نحن بالطبعتم تكوين صداقات رسمية لـ JAICF مع أليس ، لذا يمكنك الآن كتابة المهارات في Kotlin. وهذا ما يبدو عليه.
البرنامج النصي -> الخطاف التلقائي -> الحوار
أي مهارة من مهارات Alicia عبارة عن حوار صوتي بين المستخدم والمساعد الرقمي. تم وصف مربع الحوار في JAICF في شكل نصوص ، والتي يتم تشغيلها بعد ذلك على خادم webhook ، المسجل في Yandex.Dialogues.
سيناريو
لنأخذ مهارة توصلنا إليها من أجل الهاكاثون. يساعد على توفير المال عند التسوق في المتاجر. أولا ، انظر كيف يعمل.
هنا يمكنك أن ترى كيف يسأل المستخدم أليس - "أخبرني ما هو الأكثر ربحًا - الكثير من الروبلات مقابل هذا المبلغ أو ذاك؟"
تطلق أليس على الفور مهارتنا (لأنها تسمى "ما هو أكثر ربحية") وتنقل إليها جميع المعلومات الضرورية - نية المستخدم والبيانات من طلبه .
المهارة ، بدورها ، تتفاعل مع النية ، وتعالج البيانات ، وترجع استجابة مفيدة. تقول أليس الإجابة وتوقف ، لأن المهارة تنهي الجلسة (يسمونها "مهارة التمرير الواحد").
إليك مثل هذا السيناريو البسيط ، والذي يسمح لك ، مع ذلك ، بحساب مقدار ربحية منتج واحد أكثر من منتج آخر. وفي نفس الوقت اربح عمودًا يتحدث من Yandex.
كيف تبدو في 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.")
}
}
}
النص الكامل متاح على جيثب .
كما ترى ، هذا كائن عادي يمتد فئة السيناريو من مكتبة JAICF. في الأساس ، يعد النص عبارة عن آلة حالة ، حيث تكون كل عقدة حالة محتملة للمحادثة. هذه هي الطريقة التي ننفذ بها العمل مع السياق ، لأن سياق الحوار هو مكون مهم جدًا في أي تطبيق صوتي.
لنفترض أن نفس العبارة يمكن تفسيرها بشكل مختلف اعتمادًا على سياق الحوار. بالمناسبة ، هذا هو أحد الأسباب التي دفعتنا إلى اختيار Kotlin لإطار عملنا - فهو يسمح لك بإنشاء DSL مقتضب ، حيث يكون مناسبًا لإدارة مثل هذه السياقات المتداخلة والانتقالات بينها.
يتم تنشيط الدولة معالمنشط (على سبيل المثال ، النية ) وينفذ كتلة التعليمات البرمجية المتداخلة - الإجراء . وداخل الحدث ، يمكنك فعل ما تريد ، ولكن الشيء الرئيسي هو إعادة بعض الإجابات المفيدة للمستخدم أو استجواب شيء ما. يتم ذلك من خلال ردود الفعل . اتبع الروابط للحصول على وصف مفصل لكل من هذه الكيانات.
النوايا والفتحات
النية هي تمثيل مستقل عن اللغة لطلب المستخدم. في الواقع ، إنه معرف لما يريد المستخدم الحصول عليه من تطبيق المحادثة الخاص بك.
تعلمت أليس مؤخرًا كيفية تحديد نوايا مهارتك تلقائيًا إذا وصفت أولاً قواعد نحوية خاصة. علاوة على ذلك ، فهي تعرف كيفية استخراج البيانات اللازمة من العبارة في شكل فتحات - على سبيل المثال ، سعر وحجم البضائع ، كما في مثالنا.
لتجعل من كل عمل، تحتاج لوصف مثل هذه القواعد و فتحات . هذه هي القواعد النحوية في مهارتنا ، وهذه هي الخاناتنستخدمه فيه. يتيح ذلك لمهاراتنا أن تتلقى عند المدخل ليس فقط سطرًا من طلب المستخدم باللغة الروسية ، ولكن أيضًا معرف مستقل عن اللغة وفتحات محولة بالإضافة إلى (سعر كل منتج وحجمه).
JAICF ، بالطبع ، يدعم أي محرك NLU آخر (على سبيل المثال ، Caila أو Dialogflow ) ، ولكن في مثالنا أردنا استخدام ميزة Alice المحددة هذه لإظهار كيفية عملها.
الويب هوك
حسنًا ، لدينا النص. كيف نتحقق من أنها تعمل؟
بالطبع ، سيقدر أتباع نهج التطوير القائم على الاختبار وجود آلية اختبار آلية مدمجة للنصوص التفاعلية في JAICF ، والتي نستخدمها شخصيًا باستمرار ، نظرًا لأننا نقوم بمشاريع كبيرة ، ومن الصعب التحقق من جميع التغييرات يدويًا. لكن مثالنا صغير جدًا ، لذا من الأفضل أن نبدأ الخادم على الفور ونحاول التحدث إلى أليس.
لتشغيل البرنامج النصي ، تحتاج إلى webhook - خادم يقبل الطلبات الواردة من Yandex عندما يبدأ المستخدم في التحدث بمهاراتك . ليس من الصعب على الإطلاق بدء تشغيل الخادم - تحتاج فقط إلى تكوين الروبوت الخاص بك وتعليق بعض نقاط النهاية عليه.
val skill = BotEngine(
model = MainScenario.model,
activators = arrayOf(
AliceIntentActivator,
BaseEventActivator,
CatchAllActivator
)
)
هذه هي الطريقة التي يتم بها تكوين الروبوت - هنا نصف البرامج النصية المستخدمة فيه ، ومكان تخزين بيانات المستخدم والمنشطات التي نحتاجها لكي يعمل البرنامج النصي (قد يكون هناك العديد منها).
fun main() {
embeddedServer(Netty, System.getenv("PORT")?.toInt() ?: 8080) {
routing {
httpBotRouting("/" to AliceChannel(skill, useDataStorage = true))
}
}.start(wait = true)
}
وهذه هي الطريقة التي يبدأ بها خادم يحتوي على خطاف ويب على هذا النحو تمامًا - ما عليك سوى تحديد القناة التي يجب أن تعمل نقطة النهاية عندها . قمنا بتشغيل خادم JetBrains Ktor هنا ، ولكن يمكنك استخدام أي خادم آخر في JAICF .
استخدمنا هنا ميزة أخرى لـ Alice - تخزين بيانات المستخدم في قاعدة بياناتها الداخلية (خيار useDataStorage ). سيقوم JAICF تلقائيًا بحفظ واستعادة السياق من هناك وكل ما يكتبه نصنا هناك. التسلسل شفاف.
حوار
يمكننا أخيرًا اختبار كل شيء! يعمل الخادم محليًا ، لذلك نحتاج إلى عنوان URL عام مؤقت حتى تتمكن الطلبات من Alice من الوصول إلى webhook الخاص بنا من الإنترنت. للقيام بذلك ، من الملائم استخدام أداة ngrok المجانية ، ببساطة عن طريق تشغيل أمر في المحطة مثل
ngrok http 8080
وصول جميع الطلبات في الوقت الفعلي على جهاز الكمبيوتر الخاص بك - حتى تتمكن من تصحيح الأخطاء وتحرير الكود.
يمكنك الآن أخذ عنوان URL https المستلم وتحديده عند إنشاء مربع حوار Aliego جديد على Yandex. الحوارات . هناك يمكنك أيضًا اختبار مربع الحوار بالنص. ولكن إذا كنت تريد التحدث إلى مهارة بصوت ما ، فيمكن لأليس الآن نشر المهارات الخاصة بسرعة، والتي كانت متاحة لك فقط وقت التطوير. لذلك ، دون الخضوع لاعتدال طويل من Yandex ، يمكنك بالفعل بدء التحدث بمهاراتك مباشرةً من تطبيق Alice أو من مكبر صوت ذكي.
النشر
لقد اختبرنا كل شيء ونحن جاهزون لنشر المهارة لجميع مستخدمي أليس! للقيام بذلك ، يجب استضافة خطاف الويب الخاص بنا في مكان ما على خادم عام بعنوان URL ثابت. من حيث المبدأ ، يمكن تشغيل التطبيقات على JAICF في أي مكان حيث يتم دعم Java (حتى على هاتف Android الذكي).
قمنا بتشغيل مثالنا على Heroku . لقد أنشأنا للتو تطبيقًا جديدًا وسجلنا عنوان مستودع Github الخاص بنا حيث يتم تخزين رمز المهارة. يبني Heroku ويدير كل شيء من المصدر نفسه. علينا فقط تسجيل عنوان URL العام الناتج في Yandex. الحوارات وإرسالها كلها للاعتدال .
مجموع
يتبع هذا البرنامج التعليمي الصغير خطى Yandex hackathon ، حيث فاز السيناريو أعلاه " أيهما أكثر ربحية " بواحدة من ثلاث محطات Yandex.Store! هنا ، بالمناسبة ، يمكنك أن ترى كيف كانت .
ساعدني إطار عمل JAICF على Kotlin في تنفيذ نص الحوار وتصحيحه بسرعة ، دون عناء العمل مع Alice's API والسياقات وقواعد البيانات ، مع عدم تقييد الإمكانات (كما هو الحال غالبًا مع المكتبات المماثلة).
روابط مفيدة
مستند JAICF الكامل موجود هنا .
توجد هنا تعليمات حول كيفية تكوين المهارات لأليس .
يمكن العثور على مصدر المهارة نفسها هناك .
وإذا أحببت
لا تتردد في المساهمة في JAICF ، كما يفعل الزملاء من Yandex بالفعل ، أو اترك علامة النجمة على Github .
وإذا كانت لديك أي أسئلة ، فإننا نجيب عليها على الفور في Slack المريح .