Thử làm chatbot

 Mình có thời hạn là đến trưa mai, thực ra mình có thể kết thúc nó từ giờ đến đêm mai, nhưng do mấy ngày trước mới thức liên tục 48h, mấy ngày sau đó lại thức 24h trước khi thuyết trình, nên mình hơi ngán.

Mình đặt mục tiêu là tìm hiểu khá sâu về chatbot nhưng là ứng dụng, còn nghiên cứu về chatbot thì để đấy đã, tại mình mới đang tập trung nghiên cứu về mảng computer vision. Đây chỉ là một challenge nhỏ.

23:00: Sau 2 tiếng tìm hiểu thì mình thấy mọi chuyện khá đơn giản khi áp dụng mô hình NLP, mình chọn engine là DialogFlow còn môi trường thử nghiệm thực tế là facebook, ở phía backend mình sẽ viết bằng nodejs cho dễ deploy. 

Ban đầu mình định làm bot hỏi giá vàng, và nhiều thứ nữa, nhưng thấy nó khá đơn giản và không thể hiện được sức mạnh của mô hình này, nên mình quyết định sẽ làm 1 thứ phức tạp hơn, mình sẽ làm chatbot tư vấn cho một trang bán sản phẩm. Hoặc khi bạn đọc bài viết này bạn có thể bình luận một ứng dụng của chatbot, thực ra mình cũng có khá nhiều ý tưởng điên rồ trong đầu như tư vấn về một lĩnh vực nào đó, điều đó không thể thực hiện trong một sớm một chiều nên mình sẽ quyết định chọn cái ở trên. 

Bắt đầu

Có 3 thực thể ở đây là DialogFlow, app của bạn, Facebook App. Nếu như ở trên, dialogflow kết nối trực tiếp với facebook app, ta sẽ không thể xử lý linh họat nhiều trường hợp, ví dụ cần lưu cơ sở dữ liệu, cần tính toán một cái gì đó phức tạp. 

Trong thư mục app của bạn, phải cài dialogflow bằng npm nhé. 

Để kết nối 3 thứ đó lại, ta cần biết những tham số sau:


3 tham số đầu có thể lấy dễ dàng trong facebook app, SERVER_URL chính là địa chỉ server mà ta deploy, để kết nối với dialogflow, ta cần tạo 1 google service, chọn dialogflow api client, tạo key, tải key về máy nó sẽ có mọi cái mình cần, để bảo mật, nên lưu các giá trị trên ở trên server.

Trong facebook app, cấu hình ta cần thêm 1 page vào để trò chuyện, và cũng kết nối tham số như trên, với token ở đây là ta tự sinh (tương tự như mật khẩu của bạn nhưng nên dài và khó đoán):



Sau khi kết nối, ta cần đọc document chatbot của dialogflow, nói chung google hỗ trợ rất nhiều khoản này, ta chỉ cần gọi đến các hàm đó, theo đúng ý mình sẽ can thiệp được vào nội dung đoạn chat, ở đây, mình dùng nodejs.

Ví dụ, ta cần xây dựng 1 chatbot tra cứu thời tiết, điều đầu tiên là cần dạy model thông qua dialogflow, vì nó là giao diện cho người dùng cuối, nên rất dễ dùng, bạn không cần phải viết 1 dòng code nào cả, dưới đâu là minh họa:

Đầu tiên, tạo 1 thực tể (entities) là tên các thành phố, thực ra cũng không cần thiết lắm, sau đó tạo 1 intents ví dụ như ở đây là 1 intents khi người dùng nhắn hỏi thời tiết: 


 Cứ viết vào đó vài mẫu phổ thông câu hỏi có thể xảy ra của người dùng, mạng NLP sẽ học trên những tin nhắn mẫu đó, ta tạo thực thể entities để giúp nó hiểu cái nào là thành phố, cái nào là tên riêng.


Nếu phần trainning là nội dung mà người hỏi thì responses lại là phần mà chatbot trả lời. Sau đó ta lưu lại, ở web app của ta, ta cần handle tức là là truy cập vào nội dung lúc cái intents này chạy, vì vậy, ta đặt tên hành động action của nó là get-current-weather, ta sẽ kế thừa 1 hàm tên là handleDialogFlowAction, với case là get-current-weather, như vậy ta có thể truy cập vào toàn bộ nội dung cuộc trò chuyện của action trên, lấy ra được cả các tham số nếu có, việc bây giờ chỉ là gọi api lấy thông tin thời tiết thành phố, rồi lại trả về.


Web app đứng giữa dialogflow và facebook app, webapp sẽ gửi json đến facebook app.

Như thế là hoàn thành 1 chatbot, để làm nhiều thứ hay ho hơn, ta cần hiểu thêm về dialogflow, cũng như các loại json mà facebook nhận.

Đầu tiên là context, cái này rất là hay ho: ví dụ, khi bạn nhắn mua hàng, máy tính sẽ phải nhắn lại bạn muốn mua gì, sau đó khi bạn nói bạn mua gì, máy tính sẽ nói các đặc điểm của sản phẩm đó, sau đó nó hỏi bạn có đồng ý mua không, bạn đồng ý, rồi nó hỏi bạn thông tin để lên đơn hàng, cả quá trình đó được gọi là 1 context, tức 1 đoạn hội thoại dài, chứ không chỉ gồm 1 intents, hỏi thế nào thì chỉ trả lời thế đấy.

Để làm được điều này cũng vô cùng đơn giản trong DialogFlow, ta cùng xem ví dụ dưới đây:

Intents 1: Người nhận nói muốn mua hàng

Context: in: rỗng, out: buy

Training phraes: tôi muốn mua hàng, v.v.v

Response: Bạn muốn mua gì ?

Intents 2: Người nhận nói mua gì

Context: in: buy (ở intent1), out: buy-product

Training phares: tên sản phẩm, v.v.v

Intents 3: Người nhận điền thông tin

Context: in: buy-product, out: rỗng

Training phares: OK

Ở intents 3, là đoạn hội thoại về thông tin người nhận, ta cần họ xác nhận 1 số thông tin, vì vậy, cần tạo các tham số như tên người nhận, địa chỉ, số điện thoại người nhận hàng, với mỗi tham số đó, ta cần hỏi, có thể sử dụng prompts của dialogFlow như sau:


Nói chung context bao gồm 1 chuỗi intents, đầu ra của intents này lại là đầu vào của intents kia, nối tiếp, nối tiếp.

Một thứ hay ho nữa là folow-up intents, ví dụ, khi bạn hỏi bạn có muốn mua hàng không ? Không phải lúc nào người ta cũng đồng ý như kịch bản sẵn có, folow-up intents cho phép viết các kịch bản khác nhau cho một intents:


Với những thứ trên, chúng ta đã có thể nắm được cơ bản về dialogflow, chỉ cần vận dụng linh hoạt, chatbot sẽ đạt đến trình độ của con người.

Đến đây, bạn có thể thấy trí tuệ nhân tạo nó cũng không có gì xa vời, hoặc nếu bạn là người ham tìm hiểu, bạn sẽ thấy mọi thứ.... quá dễ dàng ? Bạn muốn xây dựng 1 mạng NLP của riêng mình, hãy bình luận ở dưới, mình sẽ viết 1 bài như thế, nếu bạn không bình luận, mình cũng sẽ viết thôi, vì mình cũng muốn, thực ra, xây dựng 1 cái của riêng mình không phải giống như là một việc làm vô ích là thiết kế lại cái bánh xe, ở đây, ta cần biết thiết kế mạng, khi hiểu rõ nó, ta có thể vận dụng nó, cải thiện nó ở một cách hoàn toàn khác, nâng cao hơn.

Tiếp theo, để làm phức tạp hóa chatbot, đến đây, mọi thứ về chatbot hầu như đã làm được, bot có thể trò chuyện, bot có thể lấy api để biết giá vàng, thông tin thời tiết từ bên ngoài, hoặc bạn viết 1 thuật toán nào đó như tính toán trung bình rồi lấy input của người nhắn cho vào, rồi lại tính ngược lại,..v.v Tuy vậy, bot vẫn còn có 1 điểm yếu lớn, nó chưa có khả năng ghi nhớ.

Vì vậy, ta cần database, với nó, ta có thể làm những chatbot hay ho hơn như ghi chú, đăng ký theo dõi chatbot, v..v. Vì đang dùng server heroku, nên mình sẽ dùng postgrest làm database, setup quan trọng nhất những cậu lệnh terminal sau:


Câu lệnh đầu tiên để kiểm tra xem có postgres chưa, câu lệnh thứ 2 tạo postgres, vào data.heroku.com xem cái database vừa tạo. Cài npm install --save --save-exact pg để code. Cài đặt https://www.pgadmin.org/download/pgadmin-4-apt/ để tạo database.

Mở pgadmin vừa cài, kết nối database vừa tạo ở trên theo thông tin trong data.heroku.com


Tạo table, với id tăng, vào cột user set:


Chú ý khi kết nối database với nodejs thì tắt ssl, lỗi này làm mình mất 1 tiếng mới tìm ra:


Sử dụng truy vấn cũng tương tự như các cơ sở dữ liệu khác, ví dụ dưới đây lấy thông tin người dùng lưu vào bảng user:


Như thế ta đã có cơ sở dữ liệu, sẽ có nhiều trò hay làm được với nó, tuy khá là cầu kì và lâu, nhưng tiếp xúc với một cơ sở dữ liệu mới cũng tốt mà, bạn có thể dùng bất kỳ cơ sở dữ liệu nào.

Để đỡ căng thẳng cho bài viết thì đây là phần nhẹ nhàng, dưới đây là cách gọi API đến facebook app để tạo tin nhắn lúc người dùng lần đầu mở cuộc trò chuyện, tạo menu nhanh (persitent menu), tạo lời chào ban đầu làm cho chatbot page xịn xò hơn:




Được rồi, mình định hướng dẫn tiếp phần lưu địa chỉ khách hàng vào database nhưng có vẻ nó quá đơn giản nên thôi, cuối bài viết sẽ có toàn bộ code cho mọi người.

Đây là phần khá hay ho nè, đôi lúc trong ứng dụng của bạn, trong 1 thời điểm nào đó, bạn muốn nó trỏ đến một cái intents nào đó mà không cần người dùng gõ ngữ cảnh đúng như vậy: ví dụ, cái menu persitent bạn vừa tạo có cái nút là 'Theo dõi đơn hàng', và bạn muốn khi người dùng nhấn vào nút này, nó sẽ nhảy đến context theo dõi đơn hàng, hay khi người dùng lần đầu mua hàng, bạn tìm kiếm trong database không thấy địa chỉ, đáng lẽ ra kịch bản lúc đó là nó sẽ in ra địa chỉ, hỏi người dùng có đồng ý không, nhưng không, nó không in ra địa chỉ, vì đây là lần đầu ? Bạn phải làm thế nào, đơn giản, lúc đó, trong code lập trình của bạn, bạn sẽ nhảy đến cái intents hỏi địa chỉ, thấy nó thần kỳ không, event trong dialogflow cho phép ta thế. Như thế, trong 1 intent, có các công cụ đến giờ ta đã được học như: trainning pharse, context, event, action/parameter, response. 

Ở trong code, bạn chỉ cần gọi đến hàm sendEventToDialogFlow đã viết trước theo hướng dẫn google:


Mình đang tìm hiểu cái phần gửi tự động 1 loạt tin nhắn cho các người đăng ký trên facebook, như vậy là phải víêt cả 1 web để soạn nội dung gửi, đơn giản hơn thì có thể chỉ cần get API để gửi nhưng mình nghĩ viết app nó sẽ tổng quát hơn, nhưng giờ khá muộn, mình mới xong phần dăng ký người dùng, rất đơn giản, chỉ cần thêm một row trong cơ sở dữ liệu, 0 là không đăng ký, các giá trị khác là đăng ký theo loại nào đấy.


Chúc ngủ ngon. Cài thêm plugins: npm install passport -save, npm install passport-facebook -save, npm install express-session, npm install ejs -save nhé.

Plugin passportjs dùng để lấy quyền truy cập của ứng dụng facebook của mình, express-session giúp ta không cần đăng nhập nhiều lần mỗi lần refresh, ejs là một template giao diện giúp chia ra cấu trúc file thành các module, linh hoạt, hỗ trợ ajax.


Trên đây là code theo document của các plugin, những phần trọng tâm, còn phần template chia view, routes, thì khá dơn giản, cuối bài sẽ có code. 

Để bảo mật, tức là không phải ai vào đăng nhập thì cũng cho vào hệ thống, phải thêm một điều kiện lúc chuyển trang, là = id của admin.

Phần code gửi cho toàn bộ này ít lỗi nên mình không đề cập nhiều, thực ra mình chưa có user nào không active sau 24h nên sau đó mình sẽ quay lại đây dể debug outside 24h sau.

Đăng nhập vào web là có thể gửi tin nhắn cho tất cả người dùng đã đăng ký:


Xin lỗi vì giao diện hơi đơn giản, mình hứa từ sau mình sẽ làm nó trông đẹp hơn sau khi mình bớt lười.

Tiếp theo sẽ tìm hiểu về webview trong messenger, hãy hiểu là nhúng website mình vào messenger, đầu tiên, hãy thêm website cần nhúng vào whitelist của fanpage setting (message advance), ở persitent setting menu thêm button website cần nhúng vào. 

{

          "title":"Cài đặt cá nhân",

          "type":"web_url",

          "url":"https://chat-bot-khuvuontuoitho.herokuapp.com/webviews/webview",

          "webview_height_ratio":"tall",

          "messenger_extensions":true

 },

Trên đây là đoạn json nhúng website vào.


Hãy đọc bộ sdk js web của facebook để có thể nhúng được webview như trên tại đây (Messenger extentions sdk).

Bên dưới là ví dụ về một webview submit form cá nhân hóa cho từng người dùng:


Thực tế là, đôi lúc chatbot sẽ không hiểu người dùng nhắn gì, sự hạn chế của công nghệ và thời gian bạn bỏ vào sẽ là chất lượng của chatbot, vì vậy, để tránh người dùng cảm thấy khó chịu và sẽ không bao giờ quay lại do đang nói chuyện với 1 thực thể không thú vị chút nào, chúng ta có một kĩ thụât ở đây gọi là handover protocol (giao thức chuyển giao):

Dưới đây là một số cài đặt để làm được điều đó, đầu tiên bạn phải cài đặt 2 thực thể nhận tin nhắn, ưu tiên đầu tiên là chatbot, thứ 2 có thể là admin fanpage hoặc 1 app khác.

Tiếp theo bạn phải viết một API gửi tới facebook để xác nhận handover protocol.

Tạo 1 intent đơn giản để thử mô phỏng quá trình, dưới đây là một số hình minh họa chi tiết quá trình làm:



Cần thềm 2 quyền là standby và handovers.


Hàm để gửi API tới facebook.


Handle cái hàm kia vào webhook.


Tạo intent ngữ cảnh


Quay trở lại, như đã nói từ hôm qua, để kiểm tra chế độ gửi tin cho bot sau 24h, thì mình phải thêm 2 thuộc tính vào phần json gửi đi cho facebook, nó được gọi là messenger tags, lưu ý, không nên spam tính năng này nếu không bạn sẽ bị khóa gửi tin nhắn.


Trên đây là đoạn json gửi đi, hoặc bạn có thể dùng curl để gửi trực tiếp từ terminal. 

Mình có quá nhiều tham vọng với chatbot này, như một trợ lý cá nhân, một con bot trader, chắc chắn mình sẽ làm, nhưng trong khuôn khổ bài viết này, là không thể, mình vẫn đang tìm hiểu tiếp, càng ngày mình càng thấy chatbot hay ho, mình tin, sau bài viết này, bạn sẽ hiểu tất cả công nghệ trong chatbot mà chắc chắn còn lâu mới bị lạc hậu. 

Để bớt căng thẳng thì mình đã viết một api get giá vàng vui vui cho chatbot, tham khảo api dưới đây:


Để có thể chơi tất tay với dialogflow, google cung cấp tính năng fulfillment, với nó, bạn có thể can thiệp xâu hơn vào tin nhắn gửi đến, để tùy biến tin nhắn gửi đi, như kiểm tra xem có đúng người dùng nhắn số điện thoại không, nhảy đến một intent khác.

Mình mất rất nhiều thời gian để tạo webhook tới dialogflow trên server cũ nhưng bị facebook từ chối (mình cũng không hiểu, bao giờ hiểu sẽ giải thích lại), nên mình tạo một server mới kết nối tới dialogflow, và, ping, nó đã chạy:



Trong phần cuối bài viết này, mình sẽ nói về một số tính năng nâng cao, đầu tiên là tùy chọn entities của dialogflow, định nghĩa từ đồng nghĩa với thực thể thì dễ hiểu rồi (defint synonyms), regexp entity cho phép định nghĩa một thực thể theo biểu thức chính quy, allow automated expansion tự động thêm một thực thể mà mô hình NLP cho là cùng nhãn vào, Fuzzy matching trộn các giá trị trong thực tể đó, ví dụ thực thể develop bạn có web, fontend, backend, thì fuzzy matching bạn có thể bắt web-fontend cũng ở trong thực thể đó.


Hiện tại, chưa có gì nâng cao muốn nói nữa, mình sẽ kết thúc tìm hiểu chatbot ở đây, hẹn gặp lại các bạn trong các dự án thực tế về chatbot.

Cảm ơn.


Nhận xét

Bài đăng phổ biến từ blog này

Hiểu về Norm Regularization

Những thuật toán nền tảng trong lĩnh vực Trí tuệ nhân tạo (Artificial Intelligence I)