1dayでやりきる！スタートアップ向けの少人数CSのコールセンター作り(Amazon Connect + Lambda + Slack)

# TL;DR

社員２０−３０名の小さいスタートアップでコールセンター設備が必要になったので、それの準備やら用意したもののお話をします。

# 背景

Moff AdventCalender第２号です。Moffで初の取りくみで社内Advent Calenderを実施
しようということで、扱っている人も少ないであろう分野でAmazon Connectを扱った取り組みを紹介しようと思います。（基本的には平日に記事をあげることにしています。少人数なんで。。。w）

「実際使ってみたいけど時間かかって手出すのめんどくさい」みたいな自分でも1日ちゃんと時間作ればできたので、どういう動機付けでやったところから紹介しようかなーと思います。後、基本技術系の記事みると「こういう風にやるといいー」的なHowの要素多めで記述されていることが多いんですが、会社のAdventCalenderなのでもう少しWhyの部分とHowはHowでも技術以外に必要な部分（モチベーションとか、取り組み方みたいな？）ところを少しだけ散りばめて書こうかと思います。(Howに特化して知りたいのであれば他の記事の方がよくかけていてそちらの方が参考になるかと思います。)

# モチベーションを上げるために

何か新しいことに取り組むにはそれ相当にエネルギーもいるし、「何かがよくなる」みたいな結果を期待できないとやらない性分だったので、技術についてはインプットしつつ、やる機会を社内で伺っていました。~~でも正直Amzon Connectを仕事で使うことなんて当分ないだろって思ってましたけどね。~~

自社でサービスをいくつか展開しており、かれこれ今年でリリース３年目に突入しているんですが、もともと自社と協業していた会社の協業の座組み変更に伴い、「どの業務をどこの部門でやるか」に関して大きく変更が発生し、その際にカスタマーサポートの部門を正式に自社で用意する必要がありました。お客さんの問い合わせにおける通信手段が業界的に電話が多いことから、カスタマーサポートに電話番号を設置する話が出てきました。

これはタイミングがいい。

「Amazon Connect入れますか (￣ー￣)ﾆﾔﾘｯ」
「入れましょうヽ(‘ ∇‘ )ノ」

ということで導入が決定しました。何事も「備えあれば憂いなし」ですね。

# コールセンターにおける要件調整

CSチームを編成すると言っても、スタートアップで当時社員１５名程度、うち開発は社員３名程度。メンテがかかるようなシステムを扱う場合はあまり適しておらず、CSチームも社内リソースから割り振らなければならないので、設立当初はセールスチームが兼任することが必要不可欠でした（ちなみに現在ではちゃーんと正社員が増えてきてCS専任でのチーム編成になっています。）
となると、基本的にセールスチームが社内にずっといる状況は少ないので、電話対応は営業先でもどこでも受け付けられるような体制が必要であるため、「スタートアップだから仕方がない」といえばそれまでですが、少なくても窓口にいる人は会社に常駐するわけではなく、どこからでも対応できる要素が必要でした。
加えて、もともと電話番号はあるものの、電話番みたいな担当の人は存在せず、電話代行サービス（ビジネスアシスト）を利用していたので、製品の問い合わせに関しては可能な限りオンラインなコミュニケーションを実現する必要がありました。当然営業メンバーは営業中には電話は受け取れないので、「受け取れない場合は次の人、また受け取れない場合はその次の人」、のように１回のコールで順繰りで電話受け取りができるような仕組みが欲しいと要望がありました。それで応対できなかった場合はせめて折り返しができるようにしたいことも要望に含まれていました。もちろんですが、営業時間外に仕事の電話は極力受けないように自動制御し、時間外のコールに関しては翌日折り返すことをオペレーションとして組みました。

まとめると、

* 製品（アプリ）に関する電話受付用に番号を用意する
* 問い合わせにて１つのコールに対して、遠隔でかつ、ケータイから応答できるような仕組み
* 電話に出れなかった場合に１つのコールに対して順繰りで対応できるようにしたい
* 折り返しを後からできるようにしたい
* 営業時間を設定したい

というのが要件でした。

# 実際に作ってみる

## 兎にも角にも写経

新しい技術を学ぶ際、基本は０からやるのはしんどい。
こういう時こそ「先人の知恵」ということで、いくつかサンプル、サマっている記事をみるところから入りました。
(Google先生然り、基本的には情報の収集精度を上げるために４、５記事くらいは見るようにしています。)

https://qiita.com/kitagawamac/items/c9f89bd8109a974b5a55
https://qiita.com/kooohei/items/fec4e677cdfc3d2b35aa
https://qiita.com/hamham/items/a77ba2eaaec8aec69334
など

（この部分は解説は割愛します。）

## 基盤作り

素直にWebConsole上でポチポチ操作して作りました。
以下作業順に概要展開します。

### コールセンターサービスを起動
Amazon Connectの画面を開いて早速作っていきます。
今回はサービス内にオペレーションを行うユーザーを作成する形式で行いました。
ホストの先頭部分を編集して設定していきます。

![スクリーンショット 2019-12-02 14.09.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/8f71a733-78cc-3f11-f557-9c470490da90.png)

今回はあまり関係ないかもしれませんが、CCPでも着信/発信をできるようにしておきます）
CCPがなんなのかは下記なんかが参考になるかと思います。
https://docs.aws.amazon.com/ja_jp/connect/latest/adminguide/amazon-connect-contact-control-panel.html
https://dev.classmethod.jp/cloud/aws/how-to-dial-and-transfer-from-amazon-connect-ccp-contact-control-pannel/

![スクリーンショット 2019-12-02 14.10.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/3b9e072b-ca15-8856-d540-3fed07ee1338.png)


### 電話番号取得
電話番号取得しなければ何も始まらないので電話番号を取得します。（例はus-east-2でアメリカの番号を取得するケースです）
![スクリーンショット 2019-12-02 14.16.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/32d04548-9119-bf45-b42a-509e9af66ff6.png)
番号（ap-northeast-1は下記が扱えるようです。（ランダムに番号が生成されるので、番号を任意で選択はできないです。）
Direct Dial=DID,Free Toll=通話料無料番号と読み変えてください。
https://docs.aws.amazon.com/ja_jp/connect/latest/adminguide/connect-tokyo-region.html


### 問い合わせフロー

音声をオリジナルのものにしたいとか、色々やりたいことを増やしすぎるとパンクするので、基本的に機能だけに絞って作りました。


### 音声
なんとなくAmazonオリジナルの音声(Mizuki)で設定します。（普段利用するSaaSなどのカスタマーサービスセンターの自動音声の性別が男性・女性どちらに今まで多く遭遇していたか、くらいで決めました。将来的にはコール元の電話番号が自社サービス利用者だと特定できるなら男性に切り替えるのもありなのかもしれない。）（例はアメリカなのでJoannaになります。）
トップ画面より[5.問い合わせフローの表示]を選択して問い合わせフローを新規で作成を選び、設定より「音声の設定」をドラッグ＆ドロップして、エンドポイントからマウスを押しっぱなしにすると矢印がにょきにょきと出てくるのでそれを「音声の設定」ボックスにつなぎます。 GUIなんで要領さえ抑えればノンプログラミングでできます。
![スクリーンショット 2019-12-02 14.26.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/45796662-4b05-3954-a4a7-12bfefe8df24.png)


### 営業時間設定
営業時間を設定するためには「オペレーション時間の設定」から設定してやりました。
基本平日営業時間オープン、それ以外は受け付けない設定にして出しています。
ちなみに祝日等の設定はできないので、何らかの方法で営業時間を切り替える等の作業を手作業かそれ用に別途オペレーション時間設定を退避して切り替える、等のことが必要です。（祝日検知して自動でオペレーション時間を変更させてもいいかもしれないが、まだ慣れがないのもあるので未実装）
ダッシュボード画面より[2.問い合わせフローの表示]より新規作成を押して下記のように設定しちゃいます。
![スクリーンショット 2019-12-02 14.29.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/0f15ee05-8033-7534-fb82-8a54156ce1d0.png)

保存した後に、問い合わせフローでつなぎます。
ブランチより「オペレーション時間を確認する」をドラッグ＆ドロップして矢印をつなぎます。これで設定完了。
<img width="1084" alt="スクリーンショット 2019-12-02 14.39.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/ce73a5e1-0431-a711-2c94-4e16917808b8.png">



### 非通知・通知不可能な電話番号の対策
184とかでテストでかけてみるとわかりますが、どうも「anonymous」という文字列が対象になるようなのです。これも問い合わせフローでフィルタするようにします。（サービスを利用しているクライアント様にはお問い合わせ時に事前に 184等の設定を外してもらうような形にお願いしています。）  anonymousの問い合わせが来た場合は問い合わせフローを切り分けて設定を外して掛け直すように促します。

問い合わせ属性の電話番号がAWS側で判定してanonymousと文字列を出力するみたいなので、それを元に判定します。「ブランチ」より「問い合わせ属性を確認する」にて例の通りに設定します。

<img width="440" alt="スクリーンショット 2019-12-02 15.05.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/b3283031-bf45-0bc9-8e80-c2e5c2b88e9b.png">

あとは矢印をつなぐだけ。
<img width="1003" alt="スクリーンショット 2019-12-02 15.05.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/77cf8afd-ff36-7635-e260-fe04bacb211a.png">


下記記事が一番参考になりました。
https://dev.classmethod.jp/cloud/aws/amazon-connect-anonymous/

### 順繰り・携帯電話での対応の仕様
ここが本題、コールセンターからCCPにつなぐのではなく、携帯電話に転送し、応答がない等で判別して次の携帯電話に転送する、ってフローに組み替えて見ました。作り的にはあまり賢くはないんですが、コールセンター作るくらいの人員が増えればそれこそCCPで対応してもらったほうがいいので一旦はこの形にしています。

転送設定は「終了/転送」で「電話番号への転送」をドラッグ＆ドロップで複数個用意して繋ぎます。失敗した時に次の番号にかけてもらうような形をとります。

<img width="797" alt="スクリーンショット 2019-12-02 15.13.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/9c4d677a-2e56-f0ec-9fe5-91f59cbb9298.png">


### 電話番号をSlackに通知
流石にAmazon Connectだけでは対応できないのでここはSlackのWebhookとLambdaを活用します。
もともと製品のバックエンドはServerless Frameworkで構築したAPIなので、DynamoDBからクライアントの電話番号を検索して、そのクライアント名とセットでSlackに通知するようにしました。
アカウントのデータベースに電話番号も登録されているので、
電話番号をベースにどのクライアントからの電話かを特定できるようにしています。

（サンプルでは電話番号を単純に Slackに流す例です。）

```py
# 電話番号を取得（国際電話番号扱いなので+81を書き換え）
telnum = event["Details"]["ContactData"]["CustomerEndpoint"]["Address"].replace('+81', '0')

# 問い合わせ属性を設定したフローから設定した値を取得する（operatorというkeyで設定した場合）

contact_person = ''
if 'operator' in event["Details"]["ContactData"]["Attributes"].keys():
        contact_person = event["Details"]["ContactData"]["Attributes"]['operator']

message = ```
電話番号: %s
オペレーター： %s
``` % (telnum, contact_person)

# bodyを定義
send_data = {
    "username": {任意の文字列},           # Slack上のユーザー名
    "icon_emoji": {slackのemojiのコード等},　# Slack上のアイコン
    "text": message,
}

# urllibでリクエストを投げる準備
send_text = "payload=" + json.dumps(send_data)
request = urllib.request.Request(
    os.environ.get("slack_hook_url"),
    data=send_text.encode('utf-8'),
    method="POST"
)

# リクエストを投げてレスポンス内容を取得
with urllib.request.urlopen(request) as response:
    response_body = response.read().decode('utf-8')
```

上記はあくまでサンプルコードなんで、ちゃんと実行できるLambdaを用意したらarnをコピペ[統合]->[AWS Lambda関数を呼び出す]をドラッグ＆ドロップして
転送電話が成功したケース全てに問い合わせ属性とLambdaをつなぐ作業を行います。[設定]->[問い合わせ属性の設定]にてアサインするためのkey名と値を各転送電話のフローの成功時に設定されるように矢印をつなぎます。
<img width="1169" alt="スクリーンショット 2019-12-02 15.19.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/1e3485c9-b7d4-cb1b-bd1b-db9c4d830a9e.png">



### 整理整頓
問い合わせフローは、どの条件でも必ず[切断/ハングアップ]でおわわなければならないので、今の状態だとエラーのケースはフローが止まってしまうので、全てのケースを[切断/ハングアップ]にフローを完結させる必要があります。（ここが割と煩雑）
面倒なのですが、これをしないと完結しないのでここは時間をちゃんと使ってやります。（GUIで欠けているかどうかくらいはわかりますが、フローが多くなると大変、、、）
発話の設定など諸々入れるとだいたい下記のようになります。

<img width="672" alt="スクリーンショット 2019-12-02 15.28.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35126/0b237710-b8cd-dd2a-82a7-74bcae72ac9f.png">

問い合わせフローを図面ベースで作ると当然煩雑になるので、最後に整理整頓して、どのエリアが何のワークフローかくらいはまとめました。基本矢印まみれなので「ウッ」ってなりますが。~~見慣れるとなぜだろうか「僕はこんなに複雑なワークフローを見分けられるんだぞ！」みたいな少し優越感に浸れます（？）~~
本来的にはこういう問い合わせフローは「フィルタ部分」と「本フロー」みたいに問い合わせフローを分けて設計して、最後につなぎ合わせるように作ったほうがいいとは思います。（1日ではそこまで考えられないので、一度作り切ってから部分的に切り出すようにしたほうがいいんでしょうね。）サービスが伸びて、CSの業務フローが人員にまつわるところや負担の分配をコントロールするところを考えるようになると、設計を考える必要はあるんだろうなあとは思います。

### デプロイ・本番確認
問い合わせフローと電話番号紐づけておしまい。
デバッグ作業で電話をかけることになります。

# 悩ましいポイント

## 検証環境
本番運用の時に担当者を変えたいので電話番号を変更したいとなった時に、いわゆるステージング環境などを作ってデプロイするみたいなことをやるとすると電話番号をもう一個用意してかつ、同じ問い合わせフローを用意するみたいなことを考える必要があるので、開発環境とプロダクション環境のAWSアカを分けていると、

1. 開発環境で問い合わせフローを実装・確認
2. 電話をかけてデバッグ
3. 問い合わせフローをエクスポート
4. 本番環境に向けて問い合わせフローをインポート、転送先の電話番号を切り替える
5. 本番に問い合わせフローをデプロイ

みたいなことになるので、これの運用が悩ましいです。AWSアカウント分けなければいいって話でもいいんですが、人に触らせる・誰でも気兼ねなく改修することを考えるとやっぱり分けておきたい。インポートする部分に関してはちゃんと問い合わせフローを分けるだけですむ話にはなると思うのでそこまで悩んではいないんですが、エクスポート・インポートフローのフローは地味にいらない作業だよなあ。。。

## 非通知・通知不可能要件

非通知のanonymousの要件がどうも検索してもヒットしないので困っています。（AWSに問い合わせ中）
184をつけたり、iPhoneなど携帯電話で設定する箇所がある場合、CS側でその旨を伝えれば要件としては問題ないんですが、
どうも例外的にAnonymousになるケースが出ているようこともあるみたいです。（問い合わせ元のクライアントの電話端末が何かから調べないとラチがあかないのはもちろんですが）
AWS Forumでもいくつかそれらしいトピックがあることにはあるんですが、どうも根本的に解説しているようなものは発見でいませんでした。

# 運用してて感じたこと

## 不具合
割と要望少なく今の所これで何か不具合が確認されている状況ではないんですが、
報告ベースで問い合わせ時間のフィルタがなぜか有効でなく、営業時間過ぎた時に問い合わせで電話が掛かってきたくらいの不具合が一回ありましたが、原因は未だに不明です。

## ログ
通常のコールセンター作りなら、通話記録などはCCP挟めば確実に残るのでいいんですが、フローのログ（どのフローでこけたとかのシステムログ的な意味合い）が残らないようなこと調査しづらいので、問い合わせフローにLambdaを挟んでログを仕込む等々は今後必要になる気がしています。やるかどうかはわかりませんが。

## フローの変更差分コントロール

何かしら問い合わせフローを変更する場合に現行だとExportすれば一応設定用のjsonファイルを生成してくれますが、フロー変えた時の差分がjsonで表現されているので結局差分がわからないです（実際やってみたけど、結構がっつり変えられると差分の意味がもはやないかも）
一応WebGUIベースで保存されるたびにバージョン管理はされているのですが、せめて何変更したかのコメントくらいは付与しておきたいところです。

## 発話内容の入力
今回作ってみて、日本語を発話してくれない時があるので、これを一個一個やるのはやや骨が折れる作業です。でも適当に音声案内するとそれはそれでって話もあり・・・

# まとめ・展望

* 少数でCS兼任のチーム向けにAmazon Connectを使ってコールセンターを構築しました。
* 折り返しができるよう問い合わせ元番号の通知機能はLambda+Slackで対処
* ベストプラクティスが未だ見えないが、問い合わせフローをパズルのように組み合わせることが肝要

CSのメンバーが増えてきたらコールセンターっぽくCCPで対応みたいなことを検討する時期がくることを期待して締めさせてもらいます。ありがとうございました。