אנחנו המפתחים בקיאים בפיתוח יישומים: ניידים, ממוחשבים או יישומי אינטרנט. יישומים אלה בדרך כלל מריצים
את אותו התהליך: המשתמש ממלא טופס (או לוחץ על כפתור), היישום מחשב נתונים חדשים כדי להציג, ומסך מעודכן מוצג. עם השנים, ארכיטקטורות ודפוסי עיצוב התפתחו כדי לתמוך בסוג כזה של פיתוח יישומים. למשל אפשר להזכיר הזרקת תלות (Dependency Injection), מיקרו שירותיםAspect-based programming,
MVC: Model View Controller, ואחרים.
עם זאת, כל זה לא עובד עבור צ 'אטבוטים או עוזרות קוליות כמו אלקסה, אשר אין טופס אחד כדי למלא או לחצנים ספציפיים ללחוץ. המשתמש יכול להגיד משהו, לפעמים לא קשור לשאלה הנוכחית. למשל, בוט מכירות עשוי לשאול את המשתמש לגבי גודל הנעליים שהיא חיפשה, והמשתמש יגיב על ידי כך שישאל את הבוט אם יש לו נעליים במקום. עוזרת קולית במכונית יכולה להיות בעיצומו של שיחה על מציאת מסעדה בקרבת מקום, בעוד המשתמש מבקש להתקשר לחבר שלו שגר באזור. החבר אינו עונה, והבוט צפוי לחזור לנקודה שבה הוא נשאר.
כמובן, אפשר להגביל את השיחה, אבל זה היה ללא ספק לפגוע בחוויית המשתמש. לחלופין, אפשר לנסות לספק פתרונות תכנותיים אד-הוק – אבל זה עלול לגרום לבסיס קוד מורכב, גדול וקשה לשמירה, ולכן קשה לשמור עליו.
אחת ה- frameworks המעטים שיכולים לפתור זאת נמצא בServo. המערכת עושה זאת על ידי "עצי התנהגות",
שהינם פרדיגמה תכנותית שפותחה בתעשייה שבה יש נסיון בפיתוח הרובוטים של שנים כבר – תעשיית המשחקים.
במדריך זה נסביר כיצד להתחיל בוט פשוט עם Servo. אניח כאן שיש לקורא נסיון ב- Github, NodeJS, וחשוב מכך, איך להשתמש ב- NLU ו NLP מנועי (עם INTENT, ישויות וכו '). אם לא, יש משאבים מצוינים בכל רחבי האינטרנט – יש פשוט לחפש "tutorial wit" או "tutorial LUIS".
מתחילים
סרוו היא מסגרת קוד פתוח ובתור שכזה, אפשר לבצע FORK ב- Github ולפעול לפי readme . משם יש להריץ npm start להתחיל. זה צריך להפעיל את עורך עץ ההתנהגות, ואת שרת Servo במחשב המקומי שלך, כל אחד על התהליך שלו. לאחר מכן, פתח localhost: 8000 בדפדפן , יתקבל מסך כניסה. נעבור את ה- Login, נבחר Projects, ונפתח פרוייקט חדש משלנו:
פרוייקט חדש תבנית ראשונית
עץ זה מייצג בוט שעוסק בקבוצה יפה של נושאים וניתן להשתמש בו להדרכה עבור ה-framework. נלחץ על Debugger ולאחר מכן על לחצן ההרצה ▶ ️, נתבקש לענות עם גילנו. בואו ננסה מספר מספרים ונראה מה קורה:
● אם מכניסים גיל מבוגר (למשל, 55), הבוט יגיב על ידי ציטוט הגיל שלך, וישלחך להצביע
● אם נכניס מספר קטן מ -18, התגובה תהיה שאנו צעירים מדי מכדי להצביע
● בגיל 32, הבוט ייתן לך הערה חנונית על הגיל שלך
אבל: מה אם נענה עם משהו אחר לגמרי?
זה מעניין: אם נלך עם משהו כמו "מי אתה?", נקבל תשובה. לאחר מכן, הבוט יקח אותנו בחזרה לשאלת הגיל עבור תגובות שאינן מובנות, הבוט ייתן לך הודעת עזרה לפני שישאל את השאלה שוב.
איך זה עובד?
בואו נתחיל להסתכל על הצומת המרכזי "AGE?". נבחר אותו ונלחץ על הטאב Properties מימין.
נראה כי סוג ה-NODE הוא AskAndMap, יש GUID ייחודי וכותרת. נלחץ על Properties
ובתוך ה-JSON שמוצג נבחין בכמה פריטים מעניינים:
ראשית, ה-prompt:
"prompt": [ "What's your age?", "How old are you?"]
|
עוסק בשאלות שהבוט שואל את המשתמש. אם cyclePrompts הוא true, הוא יחזור דרכם, אחרת הוא יגיע לסוף
ויישאר עם האחרון. שנית, מערך ה-contexts משמש לבחירת הילד הנכון. לאחר שמשתמש מגיב לשאלה, התגובה נשלחת למנוע NLUשמריץ את מערכות ה-AI שמוציאות מתוך הטקסט את הINTENT – הכוונה. מגיע Servo עם מודל ברירת מחדל, מוגדר במאפייני הroot). מנוע NLU שואב intents ו- entities מן המשפט. לאחר מכן
מתבצעת התאמה מול ה-contexts, והבוט ממשיך אל הילד הקשור להתאמה הטובה ביותר שנבחרה .
לכן, אם המשתמש הגיב עם מספר (למשל 22, חמש עשרה וכו '), ההקשר הראשון יהיה נבחר, כי זה ישות מספר אשר צפוי כאן:
{
"contexts": [
{
"entities": [
{
"contextFieldName": "age", "entityName": "number",
"entityIndex": 0
}
]
}
]
}
|
לאחר מכן, ה-flow ממשיכה כלפי מטה עד סוף השיחה. נתעמק בזה מייד. אם המשתמש הגיב עם משהו
שה- NLU לא הבין, הcontext השלישי נבחר:
"helper": true,
"default": true
|
יש כמה הבדלים בין default לבין helper, אבל על זה, בזמן אחר. אם, מצד שני, המשתמש אומר משהו שה- NLU מזהה, אבל הוא שונה מבחינה הקשרית (למשל "מי אתה?"), הבוט בוחר בהקשר להמשיך בו, על פי הכוונה. כפי שניתן לראות, חלק מההקשרים נבחרים אם בתשובה היתה אחת מתוך מספר כוונות אפשריות.
עצי התנהגות
Servoלא המציאה מתודולוגיות תכנות חדשות, אלא בחרה להסתמך על תקני התעשייה ככל האפשר. אחת
הפרדיגמות המוצלחות ביותר, במיוחד במשחקים AI, הייתה עצי התנהגות (Behavior Trees). רוב מנועי המשחקים, כגון Unityאו Unreal,מגיעים עם עורך BT, והם שימושיים מאוד לבנות התנהגויות המבוססות על כללים. Servo בנויה על גבי עורך Behavior3 בעל מבנה מעולה, שנכתב ב- Javascript על ידי Renato de Pontes פריירה.
מילה על AI: בעוד ש- deep learning – למידה עמוקה – מקבלת הרבה hype (ובצדק) נראה כי עבור יישומים רבים בעולם האמיתי נדרש עדיין תכנות מבוסס כללים. AI עושה פלאים על סיווג זרמי נתונים גדולים, אבל צריך לעשות משהו עם הסיווג הזה – וכאן יש צורך בתכנות רגיל. במובן זה, סרוו משלבת את התכונות הטובות של שתי הפרדיגמות הללו. אפשר לקרוא עוד על עצי התנהגות, אבל כאן אני הולך ללמד במהירות את הדברים החשובים. בואו נסתכל על הצד
השמאלי של העץ, אשר מגיע דרך הילד השמאלי עם הכנסת גיל:
לעצי התנהגות שש לולאה ראשית, אשר אחראית על ביצוע ה- node(צומת בעץ) הנוכחי כמה פעמים בשנייה. ביצוע הצומת יכול להחזיר אחת משלוש תוצאות:
הצלחה, כישלון או "עדיין רץ" – Success, Failure, Running. אם לצומת יש ילדים, הוא מריץ את ילדיו ולאחר מכן, בהתבסס על תוצאת הההרצה, הצומת מחזיר את תוצאת הביצוע שלו. צומת שיש לו ילדים נקרא
הצומת Composite, ולאלה יש רק שני סוגים עיקריים. בואו נזרום עם ה-flow ונבין אותם. הצומת המסומן ב-? נקרא צומת עדיפות,Priority ופועל כמו בורר OR. הוא מנסה לבצע את ילדיו משמאל לימין. אם אחד הילדים מחזיר הצלחה, הצומת מפסיק לנסות ומחזיר הצלחה גם הוא. לכן זה נקרא "עדיפות", כי זה נותן עדיפות לשמאלי ביותר בקרב הילדים. אם הילד לא הצליח, אז ה-Priority לא מצליח, ומחזיר Failure.
כאן, הריצה ממשיכה כלפי מטה אל הצומת →, שנקרא רצף – Sequence. רצף הוא כמו AND: הוא מבצע את ילדיו משמאל לימין, מצפה מכולם להצליח. אם אחד הילדים נכשל, כל הרצף נכשל, ומחזיר כישלון – Failure
אז, הריצה ממשיכה לרדת מטה בעץ, עד הצומת age < 18. זהו תנאי Condition, שמשווה את הגיל ל -18 (נדבר בעוד רגע על ההשוואה הזו). אם התנאי יצליח, הביצוע ימשיך לצומת “you can vote”.זוהי פעולה – Action, וכפי שהשם מרמז, כאן כל האקשן מתרחשת. בחרו את העלה הזה, ותראו שזו פעולת GeneralMessage, ששולחת משפט למשתמש (“you can vote”). לאחר מכן אנו ממשיכים למשושה הירוק "good-bye". זהו תת-עץ! נלחץ עליו בדבל-קליק ונוכל להיכנס אליו.
מה אם הגיל הוא פחות מ18? כדאי להעיף מבט לפני שנמשיך, לקרוא ולנסות לענות על כך לבד. אם הגיל קטן מ-18, התנאי נכשל, מה שגורם לSequence להיכשל, ואז ה-Priorityעובר לילד הבא: too young.
די קל, לא?
זיכרון היררכי
עכשיו בואו נסתכל לתוך הפרטים של Conditions וActions, ולדבר על אזורי זיכרון. נבחר את התנאי age>=18 ונפתח את המאפיינים שלו:
"left": "context.age",
"operator": ">",
"right": "18"
|
מה קורה פה? למעשה, ניתן לקרוא קטע עזרה קצר בשדה הDescriptionשל הצומת: ”Compare fields across global,context, volatile and message memories. left and right operands should have a dot notation with the object name. Eg: message.chat_message, context.amount etc. Operator could be any logical operator like ===, <,< ==, !==, ==> etc. “
ואכן, זהו אופרטור לוגי פשוט, המחזיר אמת או שקר עבור הצלחה או כישלון. בינתיים הכל טוב. אבל מאיפה הגיע
הcontext?
ובכן, זוכר את הcontexts במערך הצומת "age?"? זה המקום שממנו בא הקונטקסט. מתברר שכאשר נבחר context, כל הentitites והIntents "ממופים" לשדות שהוגדרו בcontext היה לנו, בהקשר זה:
"contextFieldName": "age",
"entityName": "number",
"entityIndex": 0
|
ואכן, זה מגדיר את מה שקרה: ה- NLU זיהה ישות number. לאחר מכן, המערכת יצרה שדה age ב בcontext. ברגע שיש, השדה הזה זמין לכל צאצאי ההקשר. זה באמת חשוב, לא בפני עצמו, אלא בגלל שאלה שהוא מביא: מה אם יש עוד הקשר. במילים אחרות, מה אם עוד שאלה היא לעקוב אחר השאלה גיל?
למרבה המזל, הדרך שבה היא פועלת ידועה לכל מי שיודע משהו על תכנות מונחה עצמים, ובמיוחד על ירושת
JavaScript. אם שאלה נוספת עוקבת אחר שאלת אב, אזי ייווצר הקשר ילד חדש. אם צמתים מכן מתייחסים context.field מסוימים הוא חיפש כלפי מעלה, עד שהוא מוצא שדה התואם את השם או עד שהוא מגיע לשורש של העץ.
זה די חזק, כי ברגע הבוט הבין את הגיל של המשתמש שלך, וגם אם זה המשיך לדבר על נושאים חדשים, אתה עדיין יכול
להתייחס context.age ואת המסגרת תביא את האחרון דיבר על גיל. אגב, למה את השם המפואר "זיכרון היררכי"? ובכן, זה כנראה איך המוח שלנו מזהה ישויות.
סוגים אחרים של זיכרון כוללים:
● global: זיכרון גלובלי שלם
● message: ההודעה האחרונה הגיעה מהמשתמש
● volatile: זיכרון שאינו מסודר לעולם למסד הנתונים. זה טוב עבור אובייקטים מורכבים בזיכרון.
● local: לכל צומת זיכרון
● כמו כן, זיכרון FSM לא מתועד, שבו ניתן לגשת למאפיינים של תהליך השיחה, כמוגדר בשורש של עץ ההתנהגות הראשי. עם אלה מגיע גם סוג Action חשוב, שנקרא SetFieldAction. אם צריך להגדיר שדה באחד מאותם אזורי זיכרון, זה המקום.
התשובה
היצירה האחרונה שעדיין חסרה בתכנית היא התשובה למשתמש. איך אפשר לבנות אותה? בשביל זה אפשר להעיף מבט על במאפיינים של GeneralMessageשנקרא " time to vote!":
"prompt": [
"Congrats! at <%=context.age%> you can vote",
" At <%=context.age%> you are old and wise, you can vote!"
],
|
אני בטוח שראיתם את הסימן <% =%>. זוהי טכניקה ידועה לפיתוח אינטרנט בשם "templating". <% = אומר למסגרת להעריך את הביטוי כנגד אובייקט המכיל את כל אזורי הזיכרון, כך שנוכל גם להשתמש ב- <% = global.fieldName%> וכו'. אגב, אנחנו לא מוגבלים רק לביטויים, אלא יכולים להשתמש גם בקוד. לדוגמה, כתיבת: <% if (context.age> = 100) {%> you are a very senior voter
<%{%>
או
<%' if ( context.age> 100) print 'you are a very senior voter' %>
ידפיסו את המשפט עבור אלו עם גיל גדול מ- 100.
Templating זמין עבור Actions רבים. יש לבדוק את ה-description כדי לראות אם הצומת זקוק ל-dot notation או memory field. אם לא, אז אפשר להשתמש ב templating.
ניקוי באגים כפי כנראה כבר ראית , servo מגיע מאובזר עם דבאגר מובנה. אפשר להגדיר נקודות עצירה – breakpoints (לעלים בלבד כרגע) , run, step, ולהציג את אזורי הזיכרון השונים שנדונו לעיל.
Debugger מפסיק בנקודת עצירה
שתי הערות חשובות:
● את breakpoints מגיעים "פוסט טיק", כלומר, לאחר ביצוע הצומת
● לאחר שינוי בעץ, יש לזכור לבצע Publish!למרות ש- Servo לא ישכח להזכיר לך, זה יכול להיות מבלבל
ממעמקים
סרוו מציג framework עשיר עם תכונות נוספות רבות אשר עוזרות להאיץ פיתוח בוטים, אוטומציה ומערכות מוכוונותי זרימה. בין שאר התכונות:
● Alexa, Facebook, ועוד clients
● ממשקים לשיהיה בכיףמסדי נתונים פופולריים
● Context switch – כלומר, קפיצה בין תתי שיחות
● חזרה לאחור ותיקונים במהלך שיחה
● בדיקות אוטומטיות
● תת-תהליכים ועצי משנה
רב הדברים מפורטים בתיעוד שמגיע עם המערכת. חלק אחר מחכה לחוקר האמיץ. שיהיה בכיף!