[AWS Serverless] - Sử dụng Step Function để xây dựng API xác định cảm xúc của khách hàng trong feedback
Serverless là gì
Hãy xem lại bài viết này của mình để nắm căn bản phần Serverless này.
Mô hình hệ thống chúng ta sẽ xây dựng hôm nay
Giới thiệu AWS Step function - Hỗ trợ Lowcode và Workflow trên AWS
AWS Step function hỗ trợ chúng ta xây dựng các workflow kết nối nhiều service của AWS lại trên một giao diện trực quan và dễ hiểu. Dễ dàng trình bày và trực quan khi trao đổi và thể thiện trên tài liệu. Như hình bên dưới:
Theo mình thấy điểm mạnh của dịch vụ này so với các dịch vụ tương đương trên thị trường và nó cho phép tích hợp nhanh với các dịch vụ khác trên AWS nhanh, thậm chí không cần đến ngôn ngữ lập trình. Trong bài viết này mình cũng chỉ sử dụng Serverless Framework để provision các resource của AWS chứ cũng không dùng ngôn ngữ lập trình khi xử lý business.
Các bạn có thể vào link Workshop này để xem thêm các showcase và tutorial nhé. https://catalog.workshops.aws/stepfunctions/en-US
Chuẩn bị môi trường
- Cài đặt Nodejs(Vì mục 2 cài bằng NPM), chi tiết ở link này
- Cài đặt Serverless Framework, chi tiết ở link này
- Cài đặt AWS CLI, chi tiết ở link này
- Do ví dụ sử dụng lệnh make nên mình sử dụng Ubuntu/MacOS.
Cài đặt AWS CLI để kết nối tài khoản AWS của bạn
aws configure
Các bạn cài đặt Client ID và Client Secret như bên dưới, 2 thông số còn lại optional bạn không cài cũng được.
AWS Access Key ID [****]:
AWS Secret Access Key [****]:
Default region name [us-west-1]:
Default output format [None]:
Chúng ta hãy xem trước Workflow mà chúng ta sẽ xây dựng hôm nay
Trong mô hình trên chúng ta sẽ đi qua các bước:
- Chúng ta sẽ dịch từ tiếng việt qua tiếng anh thông qua Amazon Translate, một AI dịch thuật của AWS. (bởi vì AI detect cảm xúc chỉ hiểu tiếng anh nên mình tạo ra work-around chỗ này)
- Chúng ta sẽ dùng Amazon Comprehend để đọc đoạn message (feedback của khách hàng) để xác định cảm xúc của khách hàng là tiêu cực hay tích cực, hoặc bình thường.
- Nếu chúng ta phát hiện cảm xúc tiêu cực trong message (NEGATIVE) thì chúng ta sẽ insert vào DynamoDB (Giả định sẽ có một bộ phận chăm sóc khách hàng sẽ vào kiểm tra các feedback đó)
- Nếu cảm xúc là tích cực (POSITIVE) hoặc bình thường (NEUTRAL) thì chúng ta kết thúc flow, không xử lý gì thêm.
Hãy xem cấu trúc chính của sourcecode nào !
Các bạn clone sourcecode mình viết về máy bằng lệnh bên dưới (hãy cho mình một star để ủng hộ mình ra thêm nhiều bài viết và source mẫu nhé):
git clone https://github.com/tanthanhkid/serverless-aws-golang-boilerplate-stepfunction.git
Đầu tiên chúng ta xem file serverless.yml để xem các thành phần mà source Serverless này sẽ tạo trên account AWS của chúng ta.
serverless.yml
Đoạn này nói cho chúng ta biết sourcecode này sẽ deploy lên tài khoản AWS với runtime của Golang và version 1.x ở Region ap-southeast-1 (Singapore). Ngoài ra, bên dưới còn cho chúng ta điền thêm một biến môi trường của Lambda, nơi chúng ta sẽ để connection string và để khi code chạy sẽ lấy connection string từ đây ra để kết nối vào DB Instance.
provider:
name: aws
runtime: go1.x
region: ap-southeast-1
Đoạn code bên dưới mình có bổ sung thêm phần bảo mật, Bởi vì API của chúng ta public internet, nên việc vô tình lộ URL và Request có thể dẫn tới bị tấn công từ các bên có chủ đích phá hoại. Hậu quả có thể xảy ra là dịch vụ của chúng ta bị dừng hoạt động hoặc quá tải, bill AWS tăng đột biến.
Cách xử lý là chúng ta phải có một x-api-key(được in ra trong console sau khi deploy serverless hoặc các bạn vào AWS Console để lấy) gắn vào header như sau:
plugins:
- serverless-add-api-key # plugin này cho phép chúng ta tạo thêm apikey và usageplan để bảo vệ API khi public
- serverless-step-functions # plugin này hỗ trợ chúng ta tạo step function
custom:
apiKeys:
dev:
- name: stepfunction-viblo
usagePlan:
name: "stepfunction-viblo"
description: "stepfunction-viblo usage plan demo"
quota:
limit: 10000
period: MONTH
throttle:
burstLimit: 100
rateLimit: 20
Chúng ta sẽ cần DynamoDB để lưu trữ các feedback tiêu cực từ khách hàng, sau khi Amazon Comprehend chạy xong.
# từ dòng 87
NEGATIVEFEEDBACK:
Type: AWS::DynamoDB::Table
Properties:
TableName: NEGATIVE_FEEDBACK # Tên table của chúng ta là NEGATIVE_FEEDBACK
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: ID # Đây chúng ta sẽ lưu UUID của các lần chạy Step Function
AttributeType: S
KeySchema:
- AttributeName: ID
KeyType: HASH
Tiếp theo, chúng ta sẽ tạo IAM Role và đính kèm các Policy cần thiết để Step Function của chúng ta có đủ các quyền để sử dụng các dịch vụ có liên quan trong Workflow.
# từ dòng 98
StateMachineRole:
Type: AWS::IAM::Role
Properties:
RoleName: CustomerFeedbackSentimentRole
Path: /path_of_state_machine_roles/
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- states.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: statePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: # ở môi trường prod đừng cho quyền fullaccess như bên dưới, chỉ áp dụng ở môi trường lab
- 's3:*'
- 'dynamodb:*'
- 'translate:*'
- 'comprehend:*'
Resource: "*"
Tới nhân vật chính của chúng ta là Step Function.
# từ dòng 124
stepFunctions:
stateMachines:
myStateMachine:
name: CustomerFeedbackSentiment # Đây là tên mà mình chọn
role:
Fn::GetAtt: ["StateMachineRole", "Arn"] # Ở đây chỉ định là Step Function này sẽ có Role mới tạo phía trên
events:
- http:
path: action/detectsentiment # Phần này giúp tạo ra một API Public ra ngoài để chúng ta test bằng Postman cho tiện, cũng dùng để cho các hệ thống khác gọi được
method: POST
private: true # Bật cờ này lên thì API này sẽ yêu cầu API Key để xác thực trước khi cho phép xử lý tiếp (an toàn tí)
definition: # Phần này là phần chính của Workflow
StartAt: TranslateText
States:
TranslateText: # Ở đây chúng ta sẽ gọi Amazon Translate để dịch từ VI -> EN
Type: Task
Resource:
arn:aws:states:::aws-sdk:translate:translateText
Parameters:
SourceLanguageCode: vi
TargetLanguageCode: en
Text.$: $.Comment # Input của chúng ta sẽ truyền vào đây, lưu ý các biến nhớ có "$." phía trước
ResultPath: $.translatedOutput # Output của bước này sẽ tên là translatedOutput
Next: DetectSentiment
DetectSentiment: # Ở đây mình gọi tiếp tới Amazon Comprehend để lấy kết quả dịch ra tiếng anh phía trên để cho AI đoán xem cảm xúc trong câu nói này là gì
Type: Task
Resource:
arn:aws:states:::aws-sdk:comprehend:detectSentiment
Parameters:
LanguageCode: en
Text.$: $.translatedOutput # truyền đoạn tiếng anh vừa dịch phía trên ra vào đây
ResultPath: $.translatedOutput
Next: Is this a Negative feedback ?
Is this a Negative feedback ?: # Phần này mình sẽ kiểm tra kết quả, bạn hiểu nó như IF/ELSE trong lập trình bình thường cũng được
Type: Choice
Choices:
- Variable: "$.translatedOutput.Sentiment"
StringMatches: 'NEGATIVE' # Mình kiểm tra nếu kết quả khớp chuỗi "NEGATIVE" hay không
Next: DynamoDB PutItem # Nếu khớp mình sẽ insert vào DB
Default: Success # Không khớp mình kết thúc flow
Success:
Type: Succeed
DynamoDB PutItem: # Ở đây mô tả Task insert dữ liệu mới vào DB
Type: Task
Resource:
arn:aws:states:::dynamodb:putItem
Parameters:
TableName: NEGATIVE_FEEDBACK # Tên table mình chỉ định ở đây
Item: # các cột và dữ liệu mình mô tả ở trường Item này
ID:
S.$: $$.Execution.Name # mình dùng UUID của step function cho mỗi lần chạy làm ID của DB luôn
COMMENT:
S.$: $.Comment # mình đưa lại input ban đầu vào đây để bộ phận chăm sóc khách hàng đọc lại
End: true
Chạy thôi nào!
Nếu các bạn sử dụng Ubuntu và MacOS thì chỉ cần chạy lệnh bên dưới ở ngoài root:
make deploy
Các bạn truy cập vào tài khoản AWS của mình, sau đó vào Cloudformation và chọn stack có tên bắt đầu bằng "goservice-stepfunction-viblo".
Vào tab Output để lấy API Endpoint.
Hãy thử nào dùng AI của chúng ta nào !
Chúng ta lên xem Step Function của chúng ta xử lý như thế nào bên dưới nhé.
Đi đúng luồng mong muốn rồi. Chúng ta kiểm tra DynamoDB xem được insert chưa:
Bây giờ cùng thử một câu feedback vui vẻ.
Vào Step function kiểm tra thì kết quả như mong đợi, POSITIVE sẽ không insert vào DB.
Chúc các bạn thành công !
All rights reserved