2016/12/26

我認為一個良好的 chat bot 架構設計

名詞介紹:
intent:意圖,bot 能做的事情,可以想像成一個 intent 就是一個 function。
keyword:從一個句子當中擷取出的關鍵字,可以想像成是 function 的參數,舉例來說可能是日期、時間或地點、名詞等。

我們可以把 user 輸入的每一個句子看做是高維度空間上的一個點,然後我們對這個空間做分群,假設我們的 bot 有 3 個 intent,那麼整個空間就會被分為 4 群,多 1 個群是因為不做回應。

而句子座標落在哪一群,就代表著 bot 要使用哪一個 intent 來做回應,也就是說,我們可以把機器人回應變成一個對句子做分群的問題,然後套用分群演算法。透過標示每一個句子應該落在哪一個 intent 裡,我們可以強化接下來 bot 的精確度。

怎麼把一個句子變成高維度空間上的一個點,會是一個門檻。

如果要做到 bot 可以根據前後文來做回答,那麼 keyword 的記憶會變成是一個非常重要的關鍵,比方說:

user : 「你知道日本嗎?」
bot : 「知道阿」

user : 「好玩嗎?」
bot : 「蛤你說什麼東西好玩?」

像這樣的 bot 沒有記憶能力,他只能回應上一句話,他應該要把所有提到過的 keyword 都先記住(使用類似 session 的方式去記)。當 user 觸發一個需要參數的 intent 時,應該優先考慮前面提過的 keyword,當 bot 覺得前面提到過的 keyword 都不適合做為此次呼叫的 intent 時,再跟 user 要求一個新的 keyword。

另一方面,Function Currying 是比較容易的。
https://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96

user : 「我下個禮拜一想請假」
bot : 「想要請什麼假?」

user : 「想請病假,因為我下個禮拜會肚子痛」
bot : 「可以~很可以」

user 呼叫請假(日期, 假別),可以透過 Function Currying 拆解成 請假(日期)(假別)

也就是說,前面所說的分群,還需要考慮 intent 如果是有參數的,那麼 bot 現在是要延續前一個 intent 的 Function Currying 的呼叫,還是 bot 會認為 user 想要呼叫另一個新的 intent,這是分群演算法也要能做到的。

user 先叫 intent 後傳參數,看起來容易做。但是先傳參數,再叫 intent 就比較困難。判斷到底該使用哪一個 keyword 作為參數會是一個難題。

目前為止沒有看過任何一隻 bot 能做到這點,做得到就神惹。

在 intent 數量很大而難以設計出良好 UX 的情境下,那才會是 chat bot 的擅場。所以我認為一個 bot 在 intent 數量很少的情況下,應該做成 App 或 Web,而不是 bot。也就是說,做一個 bot framework 必須考量如何讓 bot 擁有者能夠輕易地新增一個 intent 。一個太少 intent 的 bot 行為看起來跟智障沒兩樣。正因為卡米狗能夠讓 user 很輕易地新增一個 intent ,所以卡米狗能夠看起來很聰明。卡米狗的下一步,是讓每一個 intent 能夠自動對應到相似的句子,而不是完全match,這樣就能看起來更聰明些。

現階段這種看起來像智障的 bot ,有可能會被全部都串到個人助理類型的 bot 作為 proxy,比方說 siri 會根據使用者的句子,決定現在該把訊息傳給哪一個 bot 來做回應,然後這個個人助理就可以看起來很聰明。

比方說我是 microsoft ,我開發了一個 microsoft bot framework, 我知道所有 bot 的 intent ,所以我能夠知道 user 輸入的句子應該用哪一個 bot 來做 proxy,所以我能開發一個個人助理,他可能有上百萬個 intent ,而這些 intent 是開發者輸入的.... 科科。

結論:想開發聰明的 chat bot ,就要先開發 bot framework。

LINE Taiwan TechPulse大會心得

https://linecorp.com/zh-hant/pr/news/zh-hant/2016/1604

為了展現 Line bot 的魅力,Line 決定採用 Line bot 的介面去做活動報名以及議程的顯示,中場遊戲進行等等,但UX還有待加強,比方說chat bot不適合用文字來顯示議程,可以考慮用 ImageMap 來顯示。

雖然說這是開發者大會,但是會場對於開發者來說非常不友善,因為會場沒有插座,也沒有一個能連的wifi,所以身為開發者的我感到呼吸困難。

不知道是不是有經費上的問題,中午沒有附便當,但是卻附了 XBOX、PS4 、茶點和很有誠意的伴手禮。

整個活動對於時間上的掌控是有問題的,計畫趕不上變化,說好的中午 12:00 到 13:30 吃飯時間,變成了12:40~13:40。在附近沒有餐廳的情況下,約300人同時離開會場去覓食,13:40 會場人數回來不到一半。

看起來 Line 應該是第一次辦活動,還有很大的進步空間。


之後的 Line 群組會有管理者權限功能,以後不用怕翻群了。

之後的 Line 群組可以有很多聊天室。

之後的 Line 群組可以開發 App 稱為 Group App。

Group App 跟 Line Messaging API 是兩回事。


根據統計,Mobile App 的成長量小於 Mobile Web,所以開發網站還是比較重要der~

2016/11/23

chatbot meetup #1 筆記

chatbot meetup #1
http://chatbot.kktix.cc/events/meetup-01


Microsoft Bot Framework x Cognitive Service 快速打造智能聊天機器人
Eric:
平台的演進:PC=》Web=》Smartphone=》Messaging
Messaging 是最新一代的平台
Bot 是一種在 Messaging 平台上開發的 App

人類花大量時間在聊天平台上聊天
Apple google amazon 他們開發的是 interface

Microsoft Bot Framework 提供SDK C# nodeJs
Bot Builder 做好bot
Bot Connector 發佈在聊天平台上
BotDirectory 曝光bot、bot列表

Microsoft Cognitive Services 提供電腦視覺 聽覺 語意分析等等的服務
(web api 形式)
Vision
image => 裡面有什麼、有人嗎、幾歲?男的還是女的、ocr
Speech
Speech <=> text
Language
LUIS.ai
Intent (function)
Entity (param)
輸入句子
輸出 Intent 的機率和標出 Entity
Function Currying

Knowledge
Search


Introduction to NLP for Chatbots
李昀樵 - 台大數位語音實驗室:
compose.ai
做了三個bot demo 平台能力
全台實價登錄
追劇吧
ubike小幫手

不需要寫code就可以做bot的平台
用UI拉決策樹

BOT 開發的重點
要記住 CONTEXT
各種參數的輸入是可以解讀的,沒有輸入就給預設值、或者去問USER

Part-of-speech(POS):
把一句話的每個字的詞性抓出來

Infomation Extration(IE)
取出一個句子裡的參數

Dialog
輸出對話回應

中文斷詞
Jieba 結巴分詞(python)
OOV(系統沒看過的詞)
中研院斷詞系統 http://ckipsvr.iis.sinica.edu.tw/

Named entity recognition(NER)
辨識專有名詞
wikipedia 是最大名詞集合
Google NLP also return wikipedia_url

Stanford NLP

Intent Parsing
Adapt https://github.com/MycroftAi/adapt)
Stanford NLP

API
api.ai
wit.ai
Microsoft LUIS

Q&A 系統
對輸入句子做分群 然後回答對應的輸出
word embedding features
Facebook facetext


Messenger Platform's New Opportunities and Insights
Po Cheng Chu 蝴蝶 Botimize:
Customer Service
Intention Extraction
luis.ai
api.ai
wit.ai
Human-Bot Hybrid
80% by bot
20% by human

Bots
Travelflan
Taobao

E-Commerce
Payment
Stripe
Paypal
Token

Account Linking

Checkout and pay in webview

Bots
Domino's Pizza

Subscription
Stadard Messaging
24小時打一次
Subscription Messaging
不能發廣告訊息

Bots
CNN
TechCrunch

Avatar
Personality

NPC-like

Bots
Hearthstone
D. Trump Bot

Match-Making
Platform
匿名聊天
民調

Human Computation
工人智慧

Bots
Her/Him
Sensay
Swelly

Game
Webview

Interaction Focused

Bots
Space Adventure
Murder Mystery Game

Utility / Micro-app
Mention
@bot 去叫 bot 做事

Account linking

Bots
Polly.ai (做民調)
Gif

Facebook Messenger SDK 1.3
Link with Parameter
在 webview 上的操作做到一半
跳到 chatbot 上繼續做

List Template
Checkbox Plugin


Zeroth.AI

怎麼做AI?
Human Computation(工人智慧)
工人智慧造人工智慧

Travelflan
旅遊顧問

Designjar
收集資料

http://www.botimize.io/
bot界的GA



如何透過 Golang 與 Heroku 來一件部署 臉書機器人與 Line
Evan Lin:
GO 超棒
ruby 很快樂 但超慢
C 很快

python: 戰tab/space
GO 存檔就自動排版
GO 寫 test 很方便

PetNeedMe
CodeTengu


Facebook 不想讓你知道的事
Howard Chang
Her/Him & Her/Her 作者

Messenger Platform 上可以用的 PS_ID
FB login 登入的ID是 FB_ID

1. 透過 profile_pic 的檔名,兩邊的API的頭貼其實是一樣的路徑
2. 在 FB login 時加入 query string psid=#{psid}
3. 用 message_id 直接作messenger外掛可以拿到 message_id
可以傳貼圖 也可以傳任何訊息



Aloha.AI
customer service
QA bot
預測接下來user想問的三個問題


卡米狗 line bot
 郭佳甯
  卡米狗超棒der 還沒加入卡米教的趕快加入

2016/10/17

chrome extension - long finger 瀏覽器擴充功能開發紀錄

注意:本文中有許多連結必須手動複製貼上到網址列上才能正確開啟(chrome 開頭的那些網址)

long finger 簡介

long finger 安裝網址 : https://chrome.google.com/webstore/detail/long-finger/aammiagfeadjchafpeeiacggolmfnmmo

點子以及圖片資源是由 Leggy 提供,感謝 Leggy。

這是一個會把滑鼠游標從食指改成中指的擴充功能。他沒什麼用,唯一的作用應該是舒壓吧。實作起來相當容易,程式碼也足夠短,很適合作為一個練手的作品。


擴充功能簡介


chrome 擴充功能入口:chrome://extensions/

在這個頁面可以看見目前安裝的所有擴充功能。如果只是單純嘗試開發,還沒打算上架的話,在這裡有一個「開發人員模式」的按鈕,先把他打勾。


這樣就可以安裝還沒發佈到 google 商店的擴充功能。一個擴充功能的核心檔案是manifest.json,我們可以透過特定網址去找到任一擴充功能的 manifest.json,網址格式為:

chrome-extension://擴充功能ID/manifest.json

舉例來說,在安裝好 long finger 之後開啟網址:
chrome-extension://aammiagfeadjchafpeeiacggolmfnmmo/manifest.json
會看到:
{
   "browser_action": {
      "default_icon": "longFinger.png"
   },
   "content_scripts": [ {
      "css": [ "longFinger.css" ],
      "js": [ "longFinger.js" ],
      "matches": [ "http://*/*", "https://*/*" ]
   } ],
   "description": "This extension change cursor index finger to long finger.",
   "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlWMwgzcpj1/kt1KbNVUbvQRth2wvV2WX9RKRxJ1aWAsovQjEWKytM2pkbZ2xbm/V9tSNODZW438PfS46IV12iE3aTIc0mbHDML3ScJZeBfKs8dFIt0nxWERpIVctS2xQIUPS4mfKsWdk6LckPuHQmFyz13hR13hbUoqz7DkCqWv3Sz5sbDZbqKBg9GZliWUjeafzOTjkT3rO5khVqmT8NcfJ0TYAe1sJ4u2PpHByC8+dT0iQsE6xEz2/gG66HwnpU4HibcyAujfAy4fcgOmWfL8kcm4HcvFqJ+C8s3jtlUxm8o6rzrNtw+w30r5NLLdC+GcSjw3Z9zeF4U+3swa7mQIDAQAB",
   "manifest_version": 2,
   "name": "long finger",
   "update_url": "https://clients2.google.com/service/update2/crx",
   "version": "3.0"
} 
manifest.json 是整個擴充功能的起始點,透過 manifest.json 的設定,你可以觸發其他的程式檔。我們可以看見這裡有幾個重要的檔案連結:

chrome-extension://aammiagfeadjchafpeeiacggolmfnmmo/longFinger.css
chrome-extension://aammiagfeadjchafpeeiacggolmfnmmo/longFinger.js

這兩個檔案落在 content scripts 中。content_scripts 有三個參數,分別是 matchs、css、js。當使用者開啟網頁時,若網址符合 matches 的規則,就會將 css 跟 js 附加在該網頁上。


long finger 實作


讓我們把注意力放在 longFinger.css 和 longFinger.js 上。

先來看看 longFinger.css:
a{
 cursor: url("http://etrex.tw/a/flippers3.gif"),auto !important;
}
button{
 cursor: url("http://etrex.tw/a/flippers3.gif"),auto !important;
}
.longFinger{
 cursor: url("http://etrex.tw/a/flippers3.gif"),auto !important; 
}
longFinger.css 上寫的是只要是 a 或 button 或是任何帶有 longFinger class 的 html tag 就會把游標改為 http://etrex.tw/a/flippers3.gif

接下來看看 longFinger.js:
document.querySelector('body').addEventListener("mousemove", function(e){
 var target = e.target;
 var cursor = window.getComputedStyle(target,null).getPropertyValue("cursor");
 if(cursor == "pointer"){
  target.classList.add("longFinger");
 }
});
longFinger.js 寫著,當滑鼠在整個網頁上移動時,檢查目前游標指著的元素(element)是否造成游標為食指形狀,如果是的話,就幫這個元素加上 longFinger class。

這樣就大功告成了!


擴充功能測試


我已將 long finger 放在 github 上:https://github.com/etrex/long-finger。你可以在 github 上面看到資料夾結構是由一個資料夾 long finger 下帶著四個檔案所組成:manifest.json、longFinger.js、longFinger.css、longFinger.png。

只要下載long finger 資料夾,並且在 chrome://extensions/ 選擇載入未封裝的擴充功能,然後選擇 long finger 資料夾,就可以完成擴充功能的安裝。

2016/10/7

用 C# 做一個 file upload proxy server

實現功能

讓 js 用 ajax 的方式傳檔案到跨網域的 server 上,而不會遇到跨網域問題。

核心程式(C#)


using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;

namespace Tools
{
    public class HttpCaller
    {
        public static string MultipartForm(string url, HttpRequestBase request)
        {
            var ignoreHeader = new string[] { "Content-Length", "Content-Type", "Host", "Referer" };
            string result = string.Empty;
            var mediaType = "";
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Clear();
                    foreach (var key in request.Headers.AllKeys)
                    {
                        if (key.Equals("Content-Type"))
                        {
                            mediaType = request.Headers[key];
                        }
                        if (ignoreHeader.Contains(key))
                        {
                            continue;
                        }
                        client.DefaultRequestHeaders.Add(key, request.Headers[key]);
                    }
                    HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, url);
                    message.Content = new ByteArrayContent(StreamToBytes(request.InputStream));
                    message.Content.Headers.Remove("Content-Type");
                    message.Content.Headers.Add("Content-Type", mediaType);
                    result = client.SendAsync(message).Result.Content.ReadAsStringAsync().Result;
                }
            }
            catch (Exception ex)
            {
                result = ex.Message;
            }
            return result;
        }

        private static byte[] StreamToBytes(Stream stream)
        {
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }
    }
}

使用方法(C# ASP.NET MVC 專案)


public ActionResult upload()
{
   var targetUrl = Request.Form["targetUrl"];
   var json = HttpCaller.MultipartForm(targetUrl, Request);
   return Content(json);
}

使用方法(JS)


function uploadRequest(targetUrl, requestObject, files, callback) {
    var formData = new FormData();
    for (var key in requestObject) {
        formData.append(key, requestObject[key]);
    }
    for (var i = 0 ; i < files.length ; i++){
        formData.append("file" + (i+1), files[i]);
    }
    formData.append("targetUrl", targetUrl);

    var request = new XMLHttpRequest();
    request.open("POST", "upload");
    request.onload = function (e) {
        var response = e.target.response;
        callback(response);
    };
    request.send(formData);
}