Twilio.TaskRouterを調べて使ってみた【長文】

こんにちは、@24guchia です。

掲題通り、便利そうだけどなんか難しそうだな・・・

と思って避けていたTwilio.Taskrouter導入しました。

 

最初にお伝えします。

理解が難しいですが、とても便利な機能です。

顧客を待たせることになる受電、手を抜かずにやりきりましょう。

 

2017/11/04 追記

なんと、Twilio.TaskrouterのプロマネからTwitterでコメントありました。

後ほど、まとめますが、気になる方は僕のTwitterアカウント参照してください。

@24guchia

2017/11/06 追記

一部、不明点やアドバイス頂いた点を追記しました。

Twilio.Syncで受電割り振りすると言ったな

Twilio Developer Meetup 2017 – LT用スライド

あれは嘘だ。

すいません、Syncの使い方間違えてました。

Sync自体は在席管理で使い続けます。Syncも便利です。

Sync → Taskrouter変更の経緯

そもそもSyncでデータの中身に対して、クエリを発行する方法がない。

そのため、全件取得して中身を取り出して、該当する人だったら

その人に通話を割り振るとしていたが、500件mapに入れたら検索だけで10秒位かかった。

遅すぎて流石に厳しいので、ACDに特化したTaskrouterについて調べることにしました。

Taskrouter概要

理解が必要な概念は下記の通り。

  • Workspace
    • 以下概念を入れるための入れ物
  • Workflow
    • ACDとして、どの呼をどのTaskqueue(後述)に入れるかのルール設定ができる
    • 挙動としては、一般的なSwitch文に似ている。JSON形式で記述方法はSQLライクな文法で設定することが出来る
  • Taskqueue
    • 呼を入れて、待たせる。保留と同等なので、架電側には保留音が流れる(電話の場合)
    • FIFOとLIFOが選べる
    • このTaskqueueに入っているTask(後述)をどの属性を持ったWorker(後述)が対応可能かフィルタできる
    • 一般的な使い方はTaskqueueにセールス部門、サポート部門、全社員というような使い分けを行う
  • Task
    • 受電した呼に属性をJSONで持たせたもの
    • Taskが持っている属性でWorkflowの評価が行われ、Taskqueueへキューされる
    • 受電以外に問い合わせメールとかにも使えるらしいので、Taskという名前らしい
  • Worker
    • Taskを対応する人。オペレーターや営業など
    • Task同様、JSONで属性を持つ。Taskqueueのフィルタの際に評価される
    • 例えば、Aさんにセールス部門という属性をもたせ、Bさんにサポート部門という属性をもたせて、得意なTaskをこなせるように設定する
    • Task対応できるかどうかの属性(Available)やどういう状況(Activity)を持たせる
  • その他
    • Workflowによって、タスクキューという通話一覧にキューされる。
    • 優先度と時間によってエスカレートする仕組みがある。
    • タイムアウトの時間設定ができるので何秒か対応できなかったら、自動でエスカレートし、次の評価されたQueueに入る。

概念はこのようになっています。

振り分け処理について

先述の通り、Workflowsに設定しているJSONで振り分けを行っている。

task_routingに設定されているfiltersはSwitch文のように動作する。

filtersのexpressionに合致するタスクは、そのfilter内のtargetsに沿って通話振り分けが行われる。

いずれにも一致しない場合、default_filterのキューに入れられる。

switch caseとdefaultと同じです。

expressionの文法

SQLライク。JSONのキーバリューを走査してくれる。

ANDとORで接続する。一般的な比較ができる、==,!=,>,<,HAS,CONTAINS,IN,NOT INが使える。

型はString,Numbers(int and float),Booleanがある。配列も使える。

タスクにJSONで属性つけるとexpressionでの評価対象になる。

ここらへんはドキュメント読んでください。

Taskrouterってどうやって作るの

いつも通り、管理コンソールかREST APIで作る。

https://jp.twilio.com/console/taskrouter/workspaces/create

で、Twimlでどうやって使うの

Enqueueでworkflowに入れる。

そのときにTask名詞でexpressionの評価対象になる属性をJSON形式で設定できる。

で、クライアントではどう使うの

Taskrouter.jsというJavaScriptSDKが用意されているのでそれを使う。

トークン生成が必要なので、サーバ必須。

ここのところ激プッシュしてるFunctionsでもOKらしい。動画参照

https://www.youtube.com/watch?v=XMg5ytgyn1E

FunctionsだとCRM連携ってどうやるんだろう?

Taskrouterを使う場合の受電の流れ

  1. 受電するとTwilioの受電時Webhookが呼ばれる
    1. この画面で管理してるやつ → https://jp.twilio.com/console/phone-numbers/incoming
  2. 1で呼び出されたWebhook内でTaskをあるWorkflowにenqueueするtwimlを生成する
  3. Workflowの処理が呼び出だされTaskの属性とWorkerの属性によってタスク割り振りが決定し、予約が作成する
  4. 割り振りされたら、コールバックが呼ばれる
    1. この画面で管理してるやつ → https://jp.twilio.com/console/taskrouter/workspaces/WSxxx/workflows/WWxxx
  5. 4で呼び出されたコールバック内で、タスクをどうするかを決める処理を行う
    1. 今のところ使っているのはVoiceのみのため、基本的に全部callでWorkerのcontact_uriに電話かける
    2. Voice以外のタスクを取り扱うようになったら、適切なチャネルに流れるように設計する必要がある
  6. クライアントに電話がかかる
    1. 具体的にはTwilio.Device.incomingとworkerのイベントでresevation.createdが並行して呼ばれる。
    2. 並行に呼ばれるのはかなり厄介なので、他はどうしてるか知りたい

受電のフローはこうです。

TaskRouterの勘所

Taskとあるが、Queue(保留)の仕組みを組み合わせているだけ

これが理解できておらず、苦労しました。

Taskrouterで割り振られた通話の親通話がなぜか取れない

callAPIの受電では親通話に紐付いて、

クライアント側は子通話が生成されるが、Queueと同じ仕組みのため、

割り振られた受電でも、Queueに入っている通話に対して架電しているので、

親子関係が生成されない。

追記:親子関係が生成されない件について

下記ドキュメントにあるようにinstruction:conferenceを使用することで親子関係が生成されるようです。

https://www.twilio.com/docs/api/taskrouter/reservations#conference

現在はdequeueでクライアントにかけているため、

conferenceに変更し、どう挙動するか後ほどまとめます。

 

ACDって要するにロードバランサーだよね

Automatic Call Distributor/着信呼自動分配装置って、名前いかついし

そんなのWebで似た概念ないよな・・・って思ってたけど、

ロードバランサーと同じってのに気づくと、よく分からないものから

なんとなくイメージが付けやすくなると思います。

良いところ

  • 振り分け、タイムアウト、エスカレートできる
    • 振り分けはともかく、タイムアウトとエスカレートの処理を書くのはなかなかしんどいと思う
  • ソースを修正せずに、管理コンソールだけで受電振り分け設定を変更できる
    • 注意として、管理コンソールだけでは使用できない設定があり、更にAPIで設定しても管理コンソールに表示されないようなので、この事情を知らない人が勝手に変えると壊れる可能性があります
  • 受電したとき、clientだとcurrentの親callを取らないどこからの受電分からないが、taskrouterだとreservation.task.attributesに入ってる
    • 2017/11/06追記: conferenceでTaskを割り振りすることで親受電を取れるようです。現在、確認中
  • taskの生成を行う際、JSONになんでも情報を持たせることができ、クライアントでtask.attributesで参照できる
    • task.attibutesはめちゃくちゃ便利です!DBで取得した値を設定することで、クライアント側に受電と同時に取得した値の参照が出来るようになります。
    • API呼び出しとAPI保守費用が下がるので、めちゃくちゃ単純な割り振りしかないからいいやって思わずに、使うと良いです

困りどころ

  • dequeue/callを使うと、クライアントの受電とアサインされたtaskの受電で2つとも同じ処理(Twilio.Device.incoming)を通るので、処理の切り分けが必要
    • dequeueだと、connection.parameters.ApiVersionがなぜかないが判断基準にするには微妙(そもそもない事自体がバグなのでは?)
  • dequeue/callを使うと、Twilio.Device.incomingがreservation.createdより先に発火する事が多い
    • 電話は鳴るが、アサインされたtaskが参照できないので若干無駄な瞬間がある
    • たまにreservationの方が速かったりするので、reservation.createdを必ず待つことができない
  • ワーカーのavailabilityを見て、割り振りするかの設定であるskip_ifが管理コンソールで変更できない
    • API呼び出しで解決しました
    • APIでskip_if付けても、管理コンソールに表示されないっぽい?
    • 挙動確認したところ、ドキュメント通りなので反映はされているらしい
    • StackOverflowで聞いてるけど、回答が来なくて悲しかったのはちょっと前の話
    • https://stackoverflow.com/questions/46884250/how-do-i-configure-skip-if-on-twilio-taskrouter-workflow
    • 2017/11/06追記:↑先日、回答がありました。現在はAPIで更新/参照の必要があります
    • なんとドキュメントに見れない件がNoteとして記載されていました。嬉しい:)
    • https://www.twilio.com/docs/api/taskrouter/worker-presence
  • TaskQueueに対応可能Workerがいなくても、一旦キューに入ってタイムアウトまで待つ
    • 対応可能Workerがいない場合、次のキュー判定に入るかどうかの設定がほしい
  • TaskAttributesがマルチバイト文字が未サポート
    • 下記、気合のエンコードとデコードで解決
    • サーバ側(Php)でurlencode()でエンコードして、クライアント側(Taskrouter.js)でdecodeURIComponent(reservation.task.attributes.name)でデコードする
    • Syncではマルチバイト文字対応してるのになんでだろう
    • 2017/11/06追記:Taskの割り振りに関係ないため、対応していないらしい。現在、対応を希望していますが、同上の理由により優先度を上げて対応は難しいとのこと。

REST APIではまったとこ(Php)

https://www.twilio.com/docs/api/taskrouter/workers

  • Workerを作るAPIのオプショナルパラメータでドキュメントのFIELDがプログラムで使う連想配列のキーと一致しない
    • ActivitySidはactivitySid、Attributesはattributesをキーにする必要がある。ドキュメント仕事してくれ
    • パラメータ変換するだけのクラス作りました
  • Attributesに設定するのは単純な配列じゃなくて、JSON化する必要がある
    • JSON化はTwilioのライブラリ側でやるでも良いような気がする
    • json_encode,json_decodeしないで文字列でゴリゴリJSONを扱うってあんまりしないし
  • Worker削除しようとする時に、そのWorkerのavailableがtrueだと削除できない。
    • 対応として、削除する関数にforce引数を付けて、force=trueだったら削除前にavailableがfalseのactivityに変えている。
  • QuickStartのドキュメントは更新されてないので、APIは別でドキュメントを読む必要あり

Taskrouter.jsではまったとこ(クライアント側)

  • Workerのトークン生成でreservation更新を許可しておらず、403が出た
    • エラーメッセージがちょっと分かりづらかった
    • 403 Policies defined such that we cannot access the given resource
    • dequeueでかかってきた通話をconnectするとreservationが更新されたのでより混乱してしまった
  • Worker生成のオプションでdisconnectActivitySid設定しても、アクティビティが変わらない?

まとめ

困りどころ多いですね!

ただそれを差し引いても便利機能なので、がんばって実装しましょう。

後で使いそうなドキュメント