На этой неделе я много думал об OpenWhisk и последовательностях, а точнее о том, как безсерверные технологии в целом могут помочь в разработке, позволяя объединять различные действия для формирования новых. Подобно тому, как Lego можно разобрать на части и собрать в новые конфигурации, меня очень интересуют возможности бессерверных действий, когда они связаны вместе.
Чтобы было ясно, я знаю, что в основном говорю здесь о повторном использовании кода, и в этом абсолютно нет ничего нового. Одна из причин, по которой я так люблю Node, — это простота использования npm и возможность использовать различные разрозненные пакеты для включения в свое приложение. Но с бессерверным интерфейсом все немного иначе, он мощнее и проще.
Во-первых — я могу комбинировать действия с совершенно разных платформ. Одна последовательность может состоять из кода Python, Swift и Node, и мне будет все равно.
Во-вторых, мне кажется, что бессерверный код еще больше приспособлен к такому подходу типа «мэшап». Вынуждая вас писать код, который только принимает ввод и возвращает вывод, он кажется еще более подходящим для использования в сочетании друг с другом.
Я, наверное, слишком усердствую в этом, но, как я уже сказал, я в восторге от этого! В этом ключе я создал две довольно крутые демонстрации этого в действии, которыми я хотел поделиться.
Чуть больше двух недель назад я написал пакет OpenWhisk для работы с сервисом Tone Analyzer IBM Watson. С тех пор я также работал над другими различными простыми действиями, включая одно для работы с Twitter и другое для работы с RSS-каналами. Вот первое демо, которое я создал.
Для моей первой демонстрации я создал последовательность, которая сочетает в себе два действия:
- RSS to Entries — это действие просто берет URL-адрес RSS и возвращает массив записей.
- Tone Analyzer — принимает строку и возвращает для нее анализ тона.
Чтобы собрать их вместе, я создал третье действие, которое назвал flattenRSSEntries. Все, что делало это действие, — это массировало данные одного действия, чтобы сделать их подходящими для другого действия. (У меня есть мысли по этому поводу в конце статьи.) Вот это действие.
function main(args) {
let string = args.entries.reduce( (cur, val) => {
if(val.description) cur+=val.description;
return cur;
}, '\n').trim();
return {
text:string,
sentences:false,
isHTML:true
}
}
exports.main = main;
По сути — взять массив — свести его к описанию из RSS-записи, а потом выплюнуть вывод для тонального анализатора.
И да — это так. Мне буквально пришлось написать около 10 строк кода для клея. Затем я сделал последовательность из трех действий:
wsk action update rssToTone --sequence utils/rssentries,flattenRSSEntries, mywatson/tone
И тогда я могу запустить его в командной строке следующим образом:
wsk action invoke rssToTone --param rssurl https://www.raymondcamden.com/index.xml -b -r
Вот результат:
{
"document_tone": {
"tone_categories": [
{
"category_id": "emotion_tone",
"category_name": "Emotion Tone",
"tones": [
{
"score": 0.147034,
"tone_id": "anger",
"tone_name": "Anger"
},
{
"score": 0.093125,
"tone_id": "disgust",
"tone_name": "Disgust"
},
{
"score": 0.130901,
"tone_id": "fear",
"tone_name": "Fear"
},
{
"score": 0.575317,
"tone_id": "joy",
"tone_name": "Joy"
},
{
"score": 0.575186,
"tone_id": "sadness",
"tone_name": "Sadness"
}
]
},
{
"category_id": "language_tone",
"category_name": "Language Tone",
"tones": [
{
"score": 0.815472,
"tone_id": "analytical",
"tone_name": "Analytical"
},
{
"score": 0,
"tone_id": "confident",
"tone_name": "Confident"
},
{
"score": 0.543053,
"tone_id": "tentative",
"tone_name": "Tentative"
}
]
},
{
"category_id": "social_tone",
"category_name": "Social Tone",
"tones": [
{
"score": 0.645549,
"tone_id": "openness_big5",
"tone_name": "Openness"
},
{
"score": 0.228331,
"tone_id": "conscientiousness_big5",
"tone_name": "Conscientiousness"
},
{
"score": 0.228709,
"tone_id": "extraversion_big5",
"tone_name": "Extraversion"
},
{
"score": 0.174568,
"tone_id": "agreeableness_big5",
"tone_name": "Agreeableness"
},
{
"score": 0.611825,
"tone_id": "emotional_range_big5",
"tone_name": "Emotional Range"
}
]
}
]
}
}
По-видимому, мои самые сильные эмоции — это смесь грусти и радости. Хорошо, я могу жить с этим. ;)
Твиттер в тон
Для моей второй демонстрации я создал последовательность, которая объединяет два действия:
- Получить твиты — это действие просто позволяет вам искать учетную запись Twitter или ключевое слово. Я использовал его для получения твитов для аккаунта.
- Tone Analyzer — принимает строку и возвращает для нее анализ тона.
Чтобы объединить эти две вещи, я снова сделал новое действие под названием flattenTweets. Это было немного сложнее. Я решил удалить ретвиты из примера. Я также думал об удалении ответов, но боялся, что у меня не будет достаточно данных. В нынешнем виде все еще кажется, что входные данные могут быть недостаточно глубокими для хорошего анализа, но я полагаю, что могу побеспокоиться об этом позже. Вот это действие:
function main(args) {
/*
only add if no retweeted status
*/
let string = args.tweets.reduce( (cur, val) => {
if(val.text && !val.retweeted_status) cur+=' '+val.text;
return cur;
}, '').trim();
return {
text:string,
sentences:false,
isHTML:true
}
}
exports.main = main;
Как и раньше, я сделал свою последовательность, а затем я мог вызвать ее из CLI следующим образом:
wsk action invoke twitterToTone --param account raymondcamden -b -r
Если вам любопытно, это результат в моей ленте Twitter.
{
"document_tone": {
"tone_categories": [
{
"category_id": "emotion_tone",
"category_name": "Emotion Tone",
"tones": [
{
"score": 0.061874,
"tone_id": "anger",
"tone_name": "Anger"
},
{
"score": 0.532221,
"tone_id": "disgust",
"tone_name": "Disgust"
},
{
"score": 0.092915,
"tone_id": "fear",
"tone_name": "Fear"
},
{
"score": 0.586786,
"tone_id": "joy",
"tone_name": "Joy"
},
{
"score": 0.539941,
"tone_id": "sadness",
"tone_name": "Sadness"
}
]
},
{
"category_id": "language_tone",
"category_name": "Language Tone",
"tones": [
{
"score": 0,
"tone_id": "analytical",
"tone_name": "Analytical"
},
{
"score": 0,
"tone_id": "confident",
"tone_name": "Confident"
},
{
"score": 0.63355,
"tone_id": "tentative",
"tone_name": "Tentative"
}
]
},
{
"category_id": "social_tone",
"category_name": "Social Tone",
"tones": [
{
"score": 0.040865,
"tone_id": "openness_big5",
"tone_name": "Openness"
},
{
"score": 0,
"tone_id": "conscientiousness_big5",
"tone_name": "Conscientiousness"
},
{
"score": 0.00002,
"tone_id": "extraversion_big5",
"tone_name": "Extraversion"
},
{
"score": 0.011157,
"tone_id": "agreeableness_big5",
"tone_name": "Agreeableness"
},
{
"score": 0.007357,
"tone_id": "emotional_range_big5",
"tone_name": "Emotional Range"
}
]
}
]
}
}
disgust
был немного странным, но я как бы спорил с кем-то (хороший спор, я бы сказал!), так что, возможно, это произошло из-за этого.
Вывод
Во-первых, позвольте мне поделиться репозиторием, где вы можете найти этот код: https://github.com/cfjedimaster/Serverless-Examples. Дайте мне знать в комментариях ниже, если у вас есть какие-либо вопросы по этому поводу.
Я просто не могу свыкнуться с тем, как… приглашать сюда. Одна из вещей, которую я начал осознавать, став старше как разработчик, это то, что с некоторыми платформами, естественно, весело играть. Некоторые, естественно, приглашают к экспериментам и просто пробуют что-то новое. В то время как большинство платформ допускают одни и те же вещи, я просто гораздо больше хочу использовать что-то, что поощряет мою способность создавать глупые демонстрации (обычно с участием кошек, но сегодня я потерпел неудачу, я полагаю). Вот почему я люблю безсерверные технологии и OpenWhisk — мне кажется, что я могу все.
Еще кое-что
Ладно, серьезно, можете перестать читать, и я не обижусь. Итак, один из аспектов, который отчасти — я не скажу, беспокоил меня — но немного застрял у меня в голове — заключался в том, что мне нужно было написать свой собственный «объединяющий» код, чтобы мои последовательности работали. Для Twitter это имело смысл, так как я не просто сводил массив в строку, но также применял некоторую условную логику в качестве фильтра. Хотя для RSS я буквально просто брал один ключ из массива объектов.
Для этого было бы круто, если бы я мог использовать что-то вроде JSONPath для манипулирования результатами. В таких случаях я хотел бы иметь возможность вообще пропустить действие объединитель и, возможно, просто применить его как метаданные.
Теперь — вы можете сказать — как насчет создания действия JSONPath? Хорошо, но вот с этим проблема. Прямо сейчас в OpenWhisk вы не можете указать параметр по умолчанию для действия в последовательности за пределами первого действия. Итак, предполагая, что у меня есть действие JSONPath, когда я создавал свою последовательность, я не мог указать параметр по умолчанию для пути и применить его ко всей последовательности. Оно будет передано первому действию, но не передано второму, если только я не изменю свое первое действие, которое вы в любом случае не хотите делать. Ваши действия должны быть атомарными, автономными и т. д., и не иметь знаний о таких внешних вещах.
Чтобы было ясно, оба «столяра» заняли у меня около 5 минут работы. Я просто не могу отделаться от мысли, что может быть есть способ лучше и круче? Потом еще раз — может быть, нет. Я как бы сказал, что эта часть записи в блоге была немного не по теме остальных, но я также хотел бы получить любые отзывы по этой проблемной области.
Первоначально опубликовано на www.raymondcamden.com 7 апреля 2017 г.