iT邦幫忙

第 11 屆 iThome 鐵人賽

4
Software Development

Line Bot 心得分享 LineMessagingApi + LUIS + BotFramework系列 第 5

[Day05] 在 LINE Bot 加入 LUIS 語意分析服務

今天要介紹的是微軟的語意分析服務 LUIS,製作聊天機器人最困難的部分是如何分析自然語言,也就是如何理解使用者想表達的意圖,而 LUIS 正是為了解決這個問題而誕生,微軟幫我們完成了語意分析底層的 AI,我們只需要根據需求建立範例語句,接著訓練、發布模型,就能透過呼叫服務的方式,得到 AI 分析的結果。

LUIS 的功能很多,建立訓練資料也需要一些經驗,我自己也沒有完全摸透,所以這篇只能帶大家簡單認識,更多的部分就給大家自行挖掘了。

LUIS 功能介紹

LUIS 功能主要分為 意圖實體,意圖用於判斷使用者想做什麼事,例如: 我想買1杯珍珠奶茶、我想買1杯綠茶,這兩句話都是想買飲料,只是買的東西不同,買飲料這件事就是一個意圖,建立意圖後就可以將這部分的邏輯做統一的處理。

而實體有點像變數,珍珠奶茶和綠茶可以看做飲料實體,可以建立一個飲料實體並將所有的品項加入,程式內就可以像取變數一樣將飲料名取出,1杯、2杯也可以看做實體,數值類型的實體,實體的種類很多這裡就不一一介紹,後面會挑幾個比較常用的舉例說明。

建立 LUIS 應用

接下來會以訂飲料為例子建立一個範例給大家看,首先進入 LUIS 管理頁面。

網址: https://www.luis.ai

建立新應用程式,選擇要 LUIS 分析的語系。

https://ithelp.ithome.com.tw/upload/images/20191206/20106865RG4zuPrHKK.jpg

建立意圖

點選生成選單,建立訂飲料意圖。

https://ithelp.ithome.com.tw/upload/images/20191206/20106865aVCksS9UKN.jpg

接著建立一些例句,例句越多預測的越準,這裡先將訂飲料簡單分為五個部分 品項甜度冰塊大小幾杯,前三項為必填,後兩項為非必填。

例句:

  • 1杯冰淇淋紅茶少糖微冰L
  • 可可芭蕾無糖去冰*20
  • 檸檬多多正常甜正常冰中杯
  • 檸檬梅汁少冰無糖M 5杯
  • 波霸紅茶拿鐵微糖正常冰*1
  • 珍珠奶茶微糖去冰
  • 紅茶 半糖 去冰
  • 綠茶半糖少冰
  • 梅子綠去冰無糖506杯
  • 珍奶正常糖正常冰L *58

https://ithelp.ithome.com.tw/upload/images/20191206/20106865vQFm7FwuFP.jpg

實體簡介

實體可以分為下列幾大類:

  • Simple(簡單實體): 用於捕獲基本的文字類型。

  • Composite(複合實體): 用於組合多個實體,例如訂飲料需要品項實體、甜度實體、冰塊實體,可以將三者組合為一個飲料複合實體描述飲料所包含的項目。

  • List(列表實體): 用於值是固定列表或值具有同義詞的場景,例如可以將所有的飲料品項列為清單實體,而同義詞則像珍奶為珍珠奶茶的同義詞、美金和美元也是。

  • Regex(正規式): 用於正規式。

還有一類特別的實體 預生成實體,這類實體為系統內建,用於基本類型的資料,例如: 數字、日期、貨幣、網址、Email、等等...。

建立實體

根據上面的介紹訂飲料範例需要建立下列實體:

  • 品項: 飲料的品項有簡稱,例如珍奶和珍珠奶茶,建立為清單實體

  • 甜度: 甜度為固定選項,建立為清單實體

  • 冰塊: 冰塊為固定選項,建立為清單實體

  • 大小: 大小為固定選項,建立為清單實體

  • 幾杯: 幾杯為數字+,可以建立一個預生成實體 number,加上一個列表實體單位,單位可以是*,再用複合使體將兩者結合表示幾杯

幾杯(複合實體) = 數字(number) + 單位(清單實體)

建立好的實體如下。

https://ithelp.ithome.com.tw/upload/images/20191206/20106865nUdx2BZhyf.jpg

  • 品項

https://ithelp.ithome.com.tw/upload/images/20191206/20106865BUPLArIcRE.jpg

  • 甜度
    https://ithelp.ithome.com.tw/upload/images/20191206/20106865GIuYnKkhYl.jpg

  • 冰塊

https://ithelp.ithome.com.tw/upload/images/20191206/20106865DtXvo1RzE3.jpg

  • 大小

https://ithelp.ithome.com.tw/upload/images/20191206/20106865rxTrgXZcHc.jpg

  • 單位

https://ithelp.ithome.com.tw/upload/images/20191206/20106865z8LlUDBXSh.jpg

  • 幾杯

https://ithelp.ithome.com.tw/upload/images/20191206/201068653qz9iWgxwO.jpg

接著回到意圖例句的畫面,設定例句中實體的部分。

https://ithelp.ithome.com.tw/upload/images/20191206/20106865mY4Jren8DD.jpg

設定完後點選上方的訓練,LUIS 就會開始訓練 AI 模型。

https://ithelp.ithome.com.tw/upload/images/20191206/201068659Kd3rNsZYV.jpg

接著可以開啟測試功能,看看目前例句捕獲到的實體是否精準。

https://ithelp.ithome.com.tw/upload/images/20191206/20106865yVOFdNNYQS.jpg

匯入功能

LUIS 有匯入功能,這裡提供我的設定檔,方便讀者快速建立模型。

https://ithelp.ithome.com.tw/upload/images/20191206/201068651IBtlyDj2w.jpg

檔案需儲存為 .json 副檔名。

{
  "luis_schema_version": "4.0.0",
  "versionId": "0.1",
  "name": "iBot",
  "desc": "",
  "culture": "zh-cn",
  "tokenizerVersion": "1.0.0",
  "intents": [
    {
      "name": "None"
    },
    {
      "name": "訂飲料"
    }
  ],
  "entities": [],
  "composites": [
    {
      "name": "幾杯",
      "children": [
        "number",
        "單位"
      ],
      "roles": []
    }
  ],
  "closedLists": [
    {
      "name": "冰塊",
      "subLists": [
        {
          "canonicalForm": "正常冰",
          "list": []
        },
        {
          "canonicalForm": "少冰",
          "list": []
        },
        {
          "canonicalForm": "微冰",
          "list": []
        },
        {
          "canonicalForm": "去冰",
          "list": []
        }
      ],
      "roles": []
    },
    {
      "name": "品項",
      "subLists": [
        {
          "canonicalForm": "茉莉綠茶",
          "list": [
            "綠茶"
          ]
        },
        {
          "canonicalForm": "阿薩姆紅茶",
          "list": [
            "紅茶"
          ]
        },
        {
          "canonicalForm": "四季春春青茶",
          "list": [
            "四季春"
          ]
        },
        {
          "canonicalForm": "黃金烏龍",
          "list": [
            "烏龍"
          ]
        },
        {
          "canonicalForm": "波霸紅茶",
          "list": [
            "波霸紅"
          ]
        },
        {
          "canonicalForm": "梅子綠",
          "list": []
        },
        {
          "canonicalForm": "冰淇淋紅茶",
          "list": []
        },
        {
          "canonicalForm": "珍珠奶茶",
          "list": [
            "珍奶"
          ]
        },
        {
          "canonicalForm": "波霸奶茶",
          "list": []
        },
        {
          "canonicalForm": "檸檬梅汁",
          "list": []
        },
        {
          "canonicalForm": "檸檬多多",
          "list": []
        },
        {
          "canonicalForm": "波霸紅茶拿鐵",
          "list": []
        },
        {
          "canonicalForm": "珍珠紅茶拿鐵",
          "list": []
        },
        {
          "canonicalForm": "可可芭蕾",
          "list": []
        }
      ],
      "roles": []
    },
    {
      "name": "單位",
      "subLists": [
        {
          "canonicalForm": "杯",
          "list": []
        },
        {
          "canonicalForm": "*",
          "list": []
        }
      ],
      "roles": []
    },
    {
      "name": "大小",
      "subLists": [
        {
          "canonicalForm": "大杯",
          "list": [
            "L",
            "l",
            "大"
          ]
        },
        {
          "canonicalForm": "中杯",
          "list": [
            "M",
            "m",
            "中"
          ]
        }
      ],
      "roles": []
    },
    {
      "name": "甜度",
      "subLists": [
        {
          "canonicalForm": "少糖",
          "list": []
        },
        {
          "canonicalForm": "半糖",
          "list": []
        },
        {
          "canonicalForm": "微糖",
          "list": []
        },
        {
          "canonicalForm": "無糖",
          "list": []
        },
        {
          "canonicalForm": "正常甜",
          "list": [
            "正常糖"
          ]
        }
      ],
      "roles": []
    }
  ],
  "patternAnyEntities": [],
  "regex_entities": [],
  "prebuiltEntities": [
    {
      "name": "number",
      "roles": []
    }
  ],
  "model_features": [],
  "regex_features": [],
  "patterns": [
    {
      "pattern": "{品項} {甜度} {冰塊} {單位}{number}",
      "intent": "訂飲料"
    }
  ],
  "utterances": [
    {
      "text": "1杯冰淇淋紅茶少糖微冰L",
      "intent": "訂飲料",
      "entities": [
        {
          "entity": "幾杯",
          "startPos": 0,
          "endPos": 1
        }
      ]
    },
    {
      "text": "可可芭蕾無糖去冰*20",
      "intent": "訂飲料",
      "entities": [
        {
          "entity": "幾杯",
          "startPos": 8,
          "endPos": 10
        }
      ]
    },
    {
      "text": "梅子綠去冰無糖506杯",
      "intent": "訂飲料",
      "entities": [
        {
          "entity": "幾杯",
          "startPos": 7,
          "endPos": 10
        }
      ]
    },
    {
      "text": "檸檬多多正常甜正常冰中杯",
      "intent": "訂飲料",
      "entities": []
    },
    {
      "text": "檸檬梅汁少冰無糖M 5杯",
      "intent": "訂飲料",
      "entities": [
        {
          "entity": "幾杯",
          "startPos": 10,
          "endPos": 11
        }
      ]
    },
    {
      "text": "波霸紅茶拿鐵微糖正常冰*1",
      "intent": "訂飲料",
      "entities": [
        {
          "entity": "幾杯",
          "startPos": 11,
          "endPos": 12
        }
      ]
    },
    {
      "text": "珍奶正常糖正常冰L *58",
      "intent": "訂飲料",
      "entities": [
        {
          "entity": "幾杯",
          "startPos": 10,
          "endPos": 12
        }
      ]
    },
    {
      "text": "珍珠奶茶微糖去冰",
      "intent": "訂飲料",
      "entities": []
    },
    {
      "text": "紅茶 半糖 去冰",
      "intent": "訂飲料",
      "entities": []
    },
    {
      "text": "綠茶半糖少冰",
      "intent": "訂飲料",
      "entities": []
    }
  ],
  "settings": []
}

發行 LUIS

測試完後點選上方選單的發布,接著選擇生產後按發布。

https://ithelp.ithome.com.tw/upload/images/20191206/20106865K5oANopvbF.jpg

接下來幾個資訊必須記住程式中會用到 應用程式ID密鑰地區

https://ithelp.ithome.com.tw/upload/images/20191206/20106865v8qExx2ePo.jpg

https://ithelp.ithome.com.tw/upload/images/20191206/20106865VdTQDniR8q.jpg


程式部分

需安裝的套件(Nuget):

  • Microsoft.Bot.Builder.AI.Luis

appsettings.json 新增下列三個參數分別對應:

  • luisAppId: 應用程式ID
  • luisAppKey: 密鑰
  • luisDomain: 地區

完整的 appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "LineBot": {
    "channelSecret": "...",
    "accessToken": "...",
    "luisAppId": "...",
    "luisAppKey": "...",
    "luisDomain": "westus"
  }
}

調整 LineBotConfig.cs

public class LineBotConfig
{
    public string channelSecret { get; set; }
    public string accessToken { get; set; }
    public string luisAppId { get; set; }
    public string luisAppKey { get; set; }
    public string luisDomain { get; set; }
}

調整 Startup.cs

services.AddSingleton<LineBotConfig, LineBotConfig>((s) => new LineBotConfig
{
    channelSecret = Configuration["LineBot:channelSecret"],
    accessToken = Configuration["LineBot:accessToken"],
    luisAppId = Configuration["LineBot:luisAppId"],
    luisAppKey = Configuration["LineBot:luisAppKey"],
    luisDomain = Configuration["LineBot:luisDomain"]
});

建立 LUISRuntimeClientEx.cs 並在內部新增 GetPrediction 方法方便程式呼叫。

public class LUISRuntimeClientEx: LUISRuntimeClient
{
    private readonly string _appId;
    public LUISRuntimeClientEx(ServiceClientCredentials credentials, string appId)
        : base(credentials)
    {
        _appId = appId;
    }

    public async Task<LuisResult> GetPrediction(string query)
    {
        var prediction = new Prediction(this);
        return await prediction.ResolveAsync(
            appId: _appId,
            query: query,
            timezoneOffset: null,
            verbose: true,
            staging: false,
            spellCheck: false,
            bingSpellCheckSubscriptionKey: null,
            log: false,
            cancellationToken: CancellationToken.None);
    }
}

LineBotController.cs 新增 LUISRuntimeClientEx

//luisClient
var luisClientEx = new LUISRuntimeClientEx(
    new ApiKeyServiceClientCredentials(_lineBotConfig.luisAppKey), _lineBotConfig.luisAppId);
luisClientEx.Endpoint = $"https:/{_lineBotConfig.luisDomain.api.cognitive.microsoft.com";

完整的 LineBotController.cs

[Route("api/linebot")]
public class LineBotController : Controller
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly HttpContext _httpContext;
    private readonly LineBotConfig _lineBotConfig;
    private readonly ILogger _logger;

    public LineBotController(IServiceProvider serviceProvider,
        LineBotConfig lineBotConfig,
        ILogger<LineBotController> logger)
    {
        _httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
        _httpContext = _httpContextAccessor.HttpContext;
        _lineBotConfig = lineBotConfig;
        _logger = logger;
    }

    [HttpPost("run")]
    public async Task<IActionResult> Post()
    {
        try
        {
            //lineMessagingClient
            var events = await _httpContext.Request.GetWebhookEventsAsync(_lineBotConfig.channelSecret);
            var lineMessagingClient = new LineMessagingClient(_lineBotConfig.accessToken);

            //luisClient
            var luisClientEx = new LUISRuntimeClientEx(
                new ApiKeyServiceClientCredentials(_lineBotConfig.luisAppKey), _lineBotConfig.luisAppId);
            luisClientEx.Endpoint = $"https://{_lineBotConfig.luisDomain}.api.cognitive.microsoft.com";
            
            var lineBotApp = new LineBotApp(lineMessagingClient, luisClientEx);
            await lineBotApp.RunAsync(events);
        }
        catch (Exception ex)
        {
            _logger.LogError(JsonConvert.SerializeObject(ex));
        }
        return Ok();
    }
}

主要的程式邏輯會寫在 LineBotApp.cs,首先調整建構式如下

...
private readonly LUISRuntimeClientEx _LUISRuntimeClientEx;
public LineBotApp(LineMessagingClient lineMessagingClient,LUISRuntimeClientEx LUISRuntimeClientEx)
{
    ...
    _LUISRuntimeClientEx = LUISRuntimeClientEx;
}

主要程式邏輯:

  • 將使用者訊息丟給 LUIS 取得分析結果

  • 判斷意圖是否為訂飲料

  • 如果是就將實體取出分析

  • 如實體有缺少則返回提示訊息

  • 如實體資料正確,則整理並計算金額後回傳

1. 將使用者訊息丟給 LUIS 取得分析結果

呼叫剛剛新增的 GetPrediction 方法,可以取得 LUIS 分析的結果。

var luisResult = await _LUISRuntimeClientEx.GetPrediction(text);

回傳格式如下:

以 "2杯綠茶半糖去冰m" 為例

{
  "query": "2杯綠茶半糖去冰m",
  "alteredQuery": null,
  "topScoringIntent": {
    "intent": "訂飲料",
    "score": 0.989621758
  },
  "intents": [
    {
      "intent": "訂飲料",
      "score": 0.989621758
    },
    {
      "intent": "None",
      "score": 0.0114329439
    }
  ],
  "entities": [
    {
      "entity": "2 杯",
      "type": "幾杯",
      "startIndex": 0,
      "endIndex": 1,
      "score": 0.51323694
    },
    {
      "entity": "2",
      "type": "builtin.number",
      "startIndex": 0,
      "endIndex": 0,
      "resolution": {
        "value": "2"
      }
    },
    {
      "entity": "半",
      "type": "builtin.number",
      "startIndex": 4,
      "endIndex": 4,
      "resolution": {
        "value": "0.5"
      }
    },
    {
      "entity": "去冰",
      "type": "冰塊",
      "startIndex": 6,
      "endIndex": 7,
      "resolution": {
        "values": [
          "去冰"
        ]
      }
    },
    {
      "entity": "綠茶",
      "type": "品項",
      "startIndex": 2,
      "endIndex": 3,
      "resolution": {
        "values": [
          "茉莉綠茶"
        ]
      }
    },
    {
      "entity": "杯",
      "type": "單位",
      "startIndex": 1,
      "endIndex": 1,
      "resolution": {
        "values": [
          "杯"
        ]
      }
    },
    {
      "entity": "m",
      "type": "大小",
      "startIndex": 8,
      "endIndex": 8,
      "resolution": {
        "values": [
          "中杯"
        ]
      }
    },
    {
      "entity": "半糖",
      "type": "甜度",
      "startIndex": 4,
      "endIndex": 5,
      "resolution": {
        "values": [
          "半糖"
        ]
      }
    }
  ],
  "compositeEntities": [
    {
      "parentType": "幾杯",
      "value": "2 杯",
      "children": [
        {
          "type": "builtin.number",
          "value": "2"
        },
        {
          "type": "單位",
          "value": "杯"
        }
      ]
    }
  ],
  "sentimentAnalysis": null,
  "connectedServiceResult": null
}

2. 判斷意圖是否為訂飲料

LUIS 有一個預設的意圖 None,當分析的結果都不符合我們建立的意圖時,會歸類到 None,另外 LUIS 還會給結果打 分數,這邊建議將分數設個限制,否則會常常誤判,例如我輸入 "太好笑了" 也被歸類到訂飲料上 ~"~

if (luisResult.TopScoringIntent.Intent == "None" ||
    luisResult.TopScoringIntent.Score < 0.8)
{
    result.Add(new TextMessage("無法理解"));
    return result;
}

3. 如果是就將實體取出分析

這邊介紹幾種取得實體的方式

  • 取得一般實體
//取得甜度
var sugar = luisResult.Entities.FirstOrDefault(it => it.Type == "甜度");
  • 取得複合實體
//取得幾杯複合實體
var countCompositeEntity = luisResult.CompositeEntities.FirstOrDefault(it => it.ParentType == "幾杯");
//取得幾杯
var count = countCompositeEntity?.Children.FirstOrDefaul(it => it.Type == "builtin.number");
  • 取得列表實體的規範化文字

列表實體如果有設定別名,則會在 JSON 內附帶規範化文字,可以看到上方的綠茶實體,resolution 屬性內的 茉莉綠茶 就是規範化文字,這個設計可以方便程式將相同的東西做統一的處理,寫法蠻特別的,如下:

//取得甜度規範化文字
var sugar = luisResult.Entities.FirstOrDefault(it => it.Type == "甜度");
sugar.AdditionalProperties.TryGetValue("resolution", out dynamic sugarResolution);
var sugarValue = sugarResolution.values[0];

4. 如實體有缺少則返回提示訊息

完整部分可參考下方完整程式。

...
if (drink == null)
{
    result.Add(new TextMessage($"缺少或查無飲料品項"));
    validate = false;
}
...

5. 如實體資料正確,則整理並計算金額後回傳

完整部分可參考下方完整程式。

if (validate)
{
    ...
    //計算金額
    var price = _drinkPrice[$"{drinkValue}{sizeValue}"] * int.Parse(countValue);
    //回傳結果
    result.Add(new TextMessage($"{drinkValue} {sugarValue} {iceValue} {sizeValue} {countValue}杯 金額 {price}元"));
}

如何將 luisResult 轉成 JSON

題外話,紀錄一下如何將 luisResult 轉成 JSON。

//原始JSON資料
result.Add(new TextMessage($"{JsonConvert.SerializeObjec(luisResult)}"));

完整的 LineBotApp.cs

public class LineBotApp : WebhookApplication
{
    private readonly LineMessagingClient _messagingClient;
    private readonly LUISRuntimeClientEx _LUISRuntimeClientEx;
    public LineBotApp(LineMessagingClient lineMessagingClient, LUISRuntimeClientEx LUISRuntimeClientEx)
    {
        _messagingClient = lineMessagingClient;
        _LUISRuntimeClientEx = LUISRuntimeClientEx;
    }

    protected override async Task OnMessageAsync(MessageEvent ev)
    {
        var result = null as List<ISendMessage>;
        switch (ev.Message)
        {
            //文字訊息
            case TextEventMessage textMessage:
                {
                    //頻道Id
                    var channelId = ev.Source.Id;
                    //使用者Id
                    var userId = ev.Source.UserId;
                    //LUIS
                    result = await LUIS(channelId, textMessage.Text);
                    if (result != null)
                        break;
                }
                break;
        }
        if (result != null)
            await _messagingClient.ReplyMessageAsync(ev.ReplyToken, result);
    }

    //定義飲料對應的價格
    private static readonly Dictionary<string, int> _drinkPrice = new Dictionary<string, int>
    {
        ["茉莉綠茶中杯"] = 25,
        ["茉莉綠茶大杯"] = 30,
        ["阿薩姆紅茶中杯"] = 25,
        ["阿薩姆紅茶大杯"] = 30,
        ["四季春春青茶中杯"] = 25,
        ["四季春春青茶大杯"] = 30,
        ["黃金烏龍中杯"] = 25,
        ["黃金烏龍大杯"] = 30,
        ["波霸紅茶中杯"] = 30,
        ["波霸紅茶大杯"] = 40,
        ["梅子綠中杯"] = 35,
        ["梅子綠大杯"] = 50,
        ["冰淇淋紅茶中杯"] = 35,
        ["冰淇淋紅茶大杯"] = 50,
        ["珍珠奶茶中杯"] = 35,
        ["珍珠奶茶大杯"] = 50,
        ["波霸奶茶中杯"] = 35,
        ["波霸奶茶大杯"] = 50,
        ["檸檬梅汁中杯"] = 45,
        ["檸檬梅汁大杯"] = 60,
        ["檸檬多多中杯"] = 50,
        ["檸檬多多大杯"] = 70,
        ["波霸紅茶拿鐵中杯"] = 45,
        ["波霸紅茶拿鐵大杯"] = 60,
        ["珍珠紅茶拿鐵中杯"] = 45,
        ["珍珠紅茶拿鐵大杯"] = 60,
        ["可可芭蕾中杯"] = 45,
        ["可可芭蕾大杯"] = 60,
    };

    protected async Task<List<ISendMessage>> LUIS(string channelId, string text)
    {
        var luisResult = await _LUISRuntimeClientEx.GetPrediction(text);

        var result = new List<ISendMessage>();

        if (luisResult.TopScoringIntent.Intent == "None" ||
            luisResult.TopScoringIntent.Score < 0.8)
        {
            result.Add(new TextMessage("無法理解"));
            return result;
        }

        //取得品項
        var drink = luisResult.Entities.FirstOrDefault(it => it.Type == "品項");
        //取得甜度
        var sugar = luisResult.Entities.FirstOrDefault(it => it.Type == "甜度");
        //取得冰塊
        var ice = luisResult.Entities.FirstOrDefault(it => it.Type == "冰塊");
        //取得大小
        var size = luisResult.Entities.FirstOrDefault(it => it.Type == "大小");
        //取得幾杯複合實體
        var countCompositeEntity = luisResult.CompositeEntities?.FirstOrDefault(it => it.ParentType == "幾杯");
        //取得幾杯
        var count = countCompositeEntity?.Children.FirstOrDefault(it => it.Type == "builtin.number");

        //顯示意圖和分數
        result.Add(new TextMessage($"意圖: {luisResult.TopScoringIntent.Intent}({luisResult.TopScoringIntent.Score?.ToString("0.####") ?? "0"})"));
        
        //檢查必填欄位是否都有值
        var validate = true;

        if (drink == null)
        {
            result.Add(new TextMessage($"缺少或查無飲料品項"));
            validate = false;
        }
        else if (sugar == null)
        {
            result.Add(new TextMessage($"缺少甜度,範例: 正常甜、少糖、半糖、微糖、無糖"));
            validate = false;
        }
        else if (ice == null)
        {
            result.Add(new TextMessage($"缺少冰塊,範例: 正常冰、少冰、微冰、去冰"));
            validate = false;
        }

        //資料正確
        if (validate)
        {
            //取得品項規範化文字
            drink.AdditionalProperties.TryGetValue("resolution", out dynamic drinkResolution);
            var drinkValue = drinkResolution.values[0];
            //取得甜度規範化文字
            sugar.AdditionalProperties.TryGetValue("resolution", out dynamic sugarResolution);
            var sugarValue = sugarResolution.values[0];
            //取得冰塊文字
            var iceValue = ice.Entity;
            //取得大小規範化文字,預設為大杯
            dynamic sizeResolution = null;
            size?.AdditionalProperties.TryGetValue("resolution", out sizeResolution);
            var sizeValue = size == null ? "大杯" : sizeResolution.values[0];
            //取得幾杯
            var countValue = count == null ? "1" : count.Value;
            //計算金額
            var price = _drinkPrice[$"{drinkValue}{sizeValue}"] * int.Parse(countValue);
            //回傳結果
            result.Add(new TextMessage($"{drinkValue} {sugarValue} {iceValue} {sizeValue} {countValue}杯 金額 {price}元"));
        }

        //原始JSON資料
        //result.Add(new TextMessage($"{JsonConvert.SerializeObject(luisResult)}"));
        return result;
    }
}

測試結果

https://ithelp.ithome.com.tw/upload/images/20191206/20106865SXekuBSJFj.jpg

結語

這篇簡單的介紹了 LUIS 最核心的兩個功能 意圖實體,用訂飲料當作範例,舉了幾個實體應用的場景,希望讓大家更了解不同實體的使用時機,當然我的用法可能也不完全正確,小弟也是第一次實作,如有錯誤再麻煩各位大大指正。

LUIS 改版還蠻快的,自己研究的時候還在 V2,現在已經更新到 V3 了,也還有很多功能是我沒深入研究的,可以玩的地方還很多,留給有興趣的大大自行挖掘,下一篇會介紹如何建立 Line Bot 的圖文選單,今天就到這裡,感謝大家觀看。

持續補坑中,鐵人賽發文功能不要太快關閉啊啊啊~~~ /images/emoticon/emoticon13.gif


上一篇
[Day04] 如何在 Azure App Service 上設定 NLog 存放路徑
下一篇
[Day06] 如何建立 LINE Bot 的圖文選單 - Rich Menu
系列文
Line Bot 心得分享 LineMessagingApi + LUIS + BotFramework27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
1
deh
iT邦研究生 1 級 ‧ 2019-12-06 17:11:59

喔喔!這好像挺有趣的,看看哪裡可以用來玩玩

Line Bot + LUIS 蠻適合結合爬蟲做些自用的小程式。/images/emoticon/emoticon12.gif

0
Homura
iT邦高手 1 級 ‧ 2020-01-13 17:48:26

原來還有這種東西啊!
改天玩玩

玩起來 ╰( ̄▽ ̄)╭

0
ccpp0815
iT邦新手 5 級 ‧ 2020-09-27 23:37:27

大大想請問一下有沒有較新版的?

0
arguskao
iT邦新手 4 級 ‧ 2022-06-09 20:45:20

這個跟Dialogflow好像,下次能寫一篇這個的介紹嗎?

我好久沒有寫文章啦 /images/emoticon/emoticon16.gif

arguskao iT邦新手 4 級 ‧ 2022-06-11 08:30:22 檢舉

工作太忙齁

我要留言

立即登入留言