著者について Ctrip の技術専門家である Yu Xiu 氏は、電話の音声およびビデオ通信やインテリジェントな顧客サービス ロボットなどの分野に重点を置いています。 1. はじめにCtrip には、数万人のカスタマー サービス担当者を擁する巨大なコール センターがあり、航空券、ホテル、鉄道チケット、休暇、その他の製品ラインの販売前および販売後のサービスを担当し、毎日の電話取引量は 100 万件を超えています。近年、通信技術、人工知能技術、スマート端末は絶えず革新を続けており、当社も将来的に膨大な顧客ニーズに応えるために、よりインテリジェントで自動化されたコールセンターを構築し、安定した高品質のサービスを提供する方法について考えてきました。 Ctrip のコールセンターのインテリジェンスには、さまざまな側面が含まれます。 - ユーザー側:インテリジェントオンラインチャットロボット(IM)、インテリジェント音声ナビゲーション/インテリジェント音声カスタマーサービスロボット/インテリジェントレビュー招待プラグイン(電話)
- 顧客サービス側:インテリジェントな作業指示およびスケジュールシステム、インテリジェントな品質検査システム、インテリジェントな顧客リソース管理システム、インテリジェントなサービスチャネル
- システムインフラストラクチャ: インテリジェントなプラットフォームの展開とインテリジェントなビジネス監視
この記事の目的は、Ctrip がコール センターのインテリジェント音声顧客サービス ロボットを実装するために使用するインフラストラクチャ サービスである音声認識サービス (ASR) のバランスのとれた進化とベスト プラクティスを探ることです。 II. 背景人工知能技術の発展に伴い、コールセンターサービスでは、従来のIVR(対話型音声応答)ボタンナビゲーションモードが徐々にIVRインテリジェントカスタマーサービスロボット(顧客がIVRロボットとの音声会話を通じてビジネスを行う)へと変化してきました。 CtripのコールセンターシステムにおけるIVR業務も、電話インテリジェント音声ロボットへの変革が続いています。現在、Ctripの国内ホテル、航空券、鉄道チケットのIVRインバウンド業務、およびIBU国際英語航空券のIVRインバウンド業務は、すべて電話インテリジェント音声ロボットによって提供され、顧客にセルフサービスを提供しています。 次の図は、国内のホテルビジネスシナリオで顧客が Ctrip サービス ホットラインにダイヤルした後の顧客と電話ロボット間の音声通信の記録です。顧客が「注文キャンセル」ビジネス プロセスを正常に完了したことがわかります。 インテリジェントな顧客サービスロボットが上の図に示すようなインタラクティブな効果を実現するには、ASR サービスの使用と完全かつ安定したコールセンター システムのサポートが不可欠です。 Ctrip のコールセンターのプラットフォーム全体は、CC-Gateway (音声ゲートウェイ)、SBC (セッション ボーダー コントロール サービス)、REG (内線登録サービス)、SM (セッション管理サービス)、RS (コール ルーティング サービス)、CM (FreeSWITCH に基づくコール管理サービス)、ASR (音声認識サービス) などの多くのコンポーネントに依存しています。 次の図は、顧客が Ctrip のコール センターのインテリジェント カスタマー サービス ロボットに電話をかけ、音声セルフ サービス業務を実行するときに関係するいくつかのコア コンポーネントのアーキテクチャ図です。 上図からわかるように、Ctrip のコールセンター システムの基盤となるレイヤー (FreeSWITCH など) は、リアルタイム ASR を呼び出して、MRCP プロトコルに基づいて音声認識を完了します。上の図では、ASR の使用に関係するコンポーネントの相互作用を簡略化し、次の 3 つのコンポーネントが含まれていると結論付けています。 - MRCPクライアント: FreeSWITCH(以下FS)などのRTPおよびSIP/MRCPを送信するイニシエーター
- MRCPサーバー: MRCP/SIPシグナリングを処理し、RTPを受信して転送する
- ASRエンジン: RTPを解析し、音声をテキストに変換してMRCPサーバーに返します。
コール センターの ASR 発信者にとっては、MRCP サーバーに接続する方法だけを気にすればよく、ASR エンジン部分には注意を払う必要がないことがわかります。実際の使用では、プライベート展開用にサードパーティの ASR システム (iFlytek ASR、Baidu ASR など) を購入する場合、MRCP サーバーと ASR エンジンは通常一緒にパッケージ化され、同じマシンに展開されます。 しかし、クラスター展開用にどの ASR 製品を購入しても、製造元は ASR 負荷分散ソリューションを提供しないため、顧客が自分で解決する必要があります。 ASR エンジンの可用性を高めるために、Ctrip はマルチクラスター、マルチ IDC、マルチサプライヤーの ASR 製品 (Ctrip 独自の製品、Baidu、Alibaba、Microsoft など) を使用してサービスを提供しています。非常に多くのクラスターと ASR 製品があるため、ASR リソースを合理的かつ効果的に利用するためのスケジューリング戦略と負荷分散ソリューションを設計することが非常に重要です。 3. ソリューションの探索目標が明確になったので、ASR の呼び出しに関連する技術的なポイントを分析して、目標を達成する方法を見てみましょう。 MRCP サーバーの呼び出しには、SIP (UDP/TCP)、MRCP (TCP)、および RTP (UDP) の 3 つの部分が含まれます。MRCP と RTP のサーバー アドレスは、SIP INVITE の 200 OK 応答で SDP によって指定されます (以下を参照)。したがって、SIP の負荷分散が完了すれば、他の 2 つは解決できます。MRCP サーバーの負荷分散を行うには、SIP (UDP/TCP) の負荷分散を行う必要があります。 負荷分散効果は次のようになると予想されます。MRCP-Server サーバー クラスターの下に複数のマシンがある限り、クライアントが 1 つしかない場合でも、負荷分散デバイスはサーバーの各メンバーに要求を均等に分散できます。 従来の負荷分散ソリューションは、A10 (AX)、F5、NetScaler などのハードウェア負荷分散デバイス、または LVS、Nginx などのソフト負荷に基づいています。しかし、これらの従来の方法では、MRCP サーバーの負荷分散を真に実現することはできません。 Ctrip のインフラサービスには、AX、Netscaler、TDLB 関連の負荷分散サービスが含まれているため、これらのインフラサービスをベースに検証テストを実施しましたが、残念ながら最終結果は満足のいくものではありませんでした。 例として、FS を MRCP クライアント、AX を負荷分散デバイスとして使用します。 FS デバイスが 1 台、AX デバイスが 1 台、MRCP サーバー デバイスが 4 台あるとします。 FS から 4 つの要求を順番に開始するか、4 つの要求を同時に開始して、最終的に ASR 常駐同時実行性を 4 に到達させます。 上の図では、左側の「売り手ショー」が期待される効果であり、右側の「買い手ショー」が実験の実際の効果であることを示しています。すべてのリクエストは同じ MRCP サーバー マシンに割り当てられており、クラスターの下の各メンバーに均等に分散することはできません。理想は満ち溢れているが、現実は貧弱すぎる。 では、AX デバイスが均一な分散を実現できなかった理由は何でしょうか? FS が AX を MRCP の負荷として使用する場合の欠点は何ですか? まず、FS と AX デバイスが比較的固定されている場合、SIP 要求の IP 4 つ (送信元 IP、送信元ポート、宛先 IP、宛先ポート) は変更されません。これは、FS が MRCP サーバーに接続されると、クライアントとサーバーの IP/ポートが MRCP 構成ファイルで指定されるためです。したがって、AX によって FS に割り当てられる MRCP サーバーは毎回同じであり、明らかに負荷分散の期待を満たしていません。 次に、電話のシナリオでは、200 OK を受信した後、最大 30 分間 SIP のやり取りが行われない場合があります。この期間中、MRCP と RTP は MRCP クライアントと MRCP サーバーの間で直接やり取りされ、AX デバイスをまったく通過しません。AX デバイスのデフォルトのセッション期間は 120 秒です。この時間を超えると、AX によって SIP チャネルが閉じられ、後続の SIP が配信されなくなります。 このアプローチは実行不可能なので、他の解決策を検討する必要があります。徹底的な調査とさまざまな試行の結果、次の 2 つの解決策がより適切であると考えていますが、それぞれに長所と短所があります。 - ソリューションA: FreeSWITCHのディストリビュータモジュールを通じて実装
- ソリューションB: OpenSIPs経由で実装
| アドバンテージ | 欠点 | プランA | 1. サードパーティの負荷分散コンポーネントに頼る必要がない | 1. 設定が複雑 2. MRCP サーバー ノードの追加と削除には FS 構成ファイルの調整が必要であり、ASR サービスがない場合にのみロードして有効になります。 3. 消費されるポート数が多い(各MRCPサーバに個別のポートセグメントを割り当てる必要がある) 4. 負荷分散戦略は比較的単純で、比例割り当てのみをサポートします。そして、単一のマシンの最小比率は0未満にはならない | プランB | 1. シンプルな構成 2. MRCP サーバー ノードを追加または削除するには、OpenSIPs DB を調整するだけです。ASR が呼び出されると、変更も可能になり、リアルタイムで有効になります。 3. ポート消費量が少ない(MRCP プロファイル ファイルを 1 つだけ設定する必要があり、複数の MRCP サーバーが同じポート セグメントを共有します) 4. 比例やポーリングなどの複数の方法をサポートするさまざまな負荷分散ソリューションがあります。 | 1. サードパーティの負荷分散コンポーネントOpenSIPsに依存する必要がある |
最終的に、2 つのソリューションを組み合わせて負荷分散を実現しました。FS でディストリビュータ モジュールを使用して OpenSIP を負荷分散し、その後 OpenSIP で MRCP サーバーの負荷分散を行いました。結果は次のとおりです。 1) IDC 優先順位と最も近いアクセスは、FS、OpenSIPs、および MRCP-Server の 3 つのコンポーネント間で実装されます (前述のように、FS は 100% 最も近いアクセスを実現できません)。 2) 同じ IDC のすべてのダウンストリーム サービスが利用できない場合、トラフィックは自動的に他の IDC に割り当てられます。次の図に示すように、IDC-A の FS の ASR 要求は IDC-A の OpenSIP に優先的に要求され、その後、IDC-A の OpenSIP は割り当て戦略に従って、IDC-A の MRCP サーバーに要求を優先的に割り当てます。IDC-A のすべての MRCP サーバーがダウンしている場合は、IDC-B の MRCP サーバーに自動的に割り当てられます。 3) 負荷分散サービスは、下流クラスタの各メンバーの状態を自動的に検出し、メンバーサービスが利用できない場合は自動的にプルアウトし、サービス状態が回復した後に自動的に再度プルインすることができます。 FS と OpenSIP はどちらも、SIP OPTION を送信することでダウンストリーム サービスのステータスを自動的に検出します。 IV. ソリューションの実践次に、各ソリューションがどのように実装されるかを詳しく見てみましょう。以下のソリューションは、CentOS 7.6、FreeSWITCH 1.6.20、OpenSIPs 2.4.2 の環境で実行されます。 この記事では、各方法の実装原理を詳しく説明するのではなく、ソリューションのみを紹介します。興味のある学生は、FS と OpenSIP の関連機能を自分で学習できます。ここにいくつかのリンクがあります。 - mod_unimrcp
- mod_distributor
- mod_dptools: 音声認識と再生の検出
- ロードバランサモジュール
- ディスパッチャモジュール
- ダイヤルプランモジュール
MRCP クライアントとして FS が 1 つだけあり、MRCP サーバー クラスターに mrcp1 と mrcp2 という 2 つのサーバーがあると仮定します。FS が各コールに対して ASR コマンドを実行すると、要求が 2 つの MRCP サーバーに均等に分散されることを期待します。 4.1 FSディストリビュータモジュールに基づくMRCPサーバのLBの実装このプログラムの中心的なアイデアは次のとおりです。 1) FSはMRCPサーバーに直接接続し、MRCPサーバークラスターの各メンバーのプロファイルを設定します。 2) MRCP サーバー クラスターのすべてのメンバーを FS ゲートウェイとして設定し、ゲートウェイの SIP OPTION 検出機能を有効にします。同時に、ゲートウェイの名前が mrcp_profile ファイル内のプロファイルの名前と一致していることを確認します。 3) FSディストリビュータモジュールを介してこれらのMRCPゲートウェイの負荷分散戦略を構成する 4) 最後に、ASR コマンドが実際に実行されるときは、まず expand eval ${distributor mrcp ${sofia profile external gwlist down}} を使用して、負荷分散分散によって利用可能な MRCP サーバー プロファイルの名前を取得し、次に MRCP プロファイルの名前を FS play_and_detect_speech ASR コマンドのパラメーターとして使用します。 詳細な設定手順は次のとおりです。 ステップ1: FSをMRCPサーバーに接続する 在/usr/local/freeswitch/conf/mrcp_profiles/下配置FS对接MRCP Server的文件tree /usr/local/freeswitch/conf/mrcp_profiles ├── mrcp1.xml └── mrcp2.xml
以下に、mrcp1.xml のコア設定の一部のみを示します。mrcp1.xml と mrcp2.xml の client-port、rtp-port-min、および rtp-port-max 設定が異なることを確認する必要があります。 ここで、MRCP サーバー クラスターに多数のマシンがある場合、ここの RTP ポート セグメントでは不十分な場合があることがわかります。通話には、ASR 解析用に 2 つのポートが必要です。1 つは RTP 送信用、もう 1 つは RTCP 送信用です。 <include> <profile name="mrcp1" versinotallow="2"> 【每个MRCP Server这里配置的名称都不一样,但一定有一个相同名称的网关】 <param name="client-ip" value="192.168.1.99"/> <param name="client-port" value="client-port-1"/> <param name="server-ip" value="server-ip-1"/> <param name="server-port" value="8060"/> <param name="sip-transport" value="tcp"/> 【也可以是UDP哦】 <param name="rtp-ip" value="192.168.1.99"/> <param name="rtp-port-min" value="min-port-1"/> <param name="rtp-port-max" value="max-port-1"/> <param name="ua-name" value="FreeSWITCH"/> </profile> </include> ステップ2: FSゲートウェイを構成する 在/usr/local/freeswitch/conf/sip_profiles/external/下配置网关对接文件/usr/local/freeswitch/conf/sip_profiles/external ├── mrcp1.xml └── mrcp2.xml
mrcp1.xml の詳細な構成は次のとおりです。 <gateway name="mrcp1"> 【gateway的name要与mrcp_profile文件中profile的name一致,或可以按照某种规则转换】 <param name="username" value=""/> <param name="proxy" value="mrcp1-server-ip:8060"/> 【当然这里端口可能是其他值】 <param name="realm" value="mrcp1-server-ip"/> <param name="register" value="false"/> <param name="rtp-autofix-timing" value="false"/> <param name="caller-id-in-from" value="true"/> <param name="ping" value="10"/> 【FS给proxy对应地址发送探测的周期】 <param name="ping-max" value="5"/> <param name="ping-min" value="2"/> </gateway> ステップ3: FSディストリビュータモジュールを構成する vim /usr/local/freeswitch/conf/autoload_configs/distributor.conf.xml <configuration name="distributor.conf" descriptinotallow="Distributor Configuration"> <lists> <list name="mrcp"> 【权重配置成一样,相当于两个MCRP server 按1:1 分配】 <node name="mrcp1" weight="5"/> 【node name值与sip gateway 名称相同】 <node name="mrcp2" weight="5"/> </list> </lists> </configuration>
最後に、使用効果を見てみましょう。上記の構成によると、mrcp2 サービスをシャットダウンした後、負荷分散を実行した場合の効果は次のようになります。 freeswitch@LPT0596> sofia profile external gwlist down 【获取宕机的网关】 mrcp2 freeswitch@LPT0596> expand eval ${distributor mrcp ${sofia profile external gwlist down}} 【将宕机的网关排除在外后,获取分配的SM节点】 mrcp1 freeswitch@LPT0596> expand eval ${distributor mrcp ${sofia profile external gwlist down}} 【如果mrcp2没有宕机,这里将返回mrcp2】 mrcp1使用得到的MRCP Server Profile名称执行ASR命令:play_and_detect_speech /usr/local/freeswitch/sounds/ivr_prompt_voice.wav detect:unimrcp:mrcp1 {start-input-timers=false,no-input-timeout=10000,recognition-timeout=10000}ahlt_ats その他の FS 関連コマンド: - reload mod_unimrcp: FS を MRCP サーバーに接続するファイルを変更した後、有効にするにはリロードします [リロードは、現在実行中の ASR 操作がない場合にのみ可能です]
- sofia プロファイル外部再スキャン: FS ゲートウェイ構成を再読み込み
4.2 OpenSIPsに基づくMRCPサーバのLBの実装4.2.1 コアアイデア FS は MRCP サーバーに直接接続するのではなく、OpenSIP に接続します。接続方法は、OpenSIPs を MRCP プロファイルとして設定し、ファイル内の server-ip アドレスと server-port アドレスを OpenSIPS のサービス アドレスに設定します。 FS が ASR コマンドを実行すると、まず OpenSIP に SIP 要求が送信され、次に OpenSIP がそれを MRCP サーバー クラスターのメンバーに負荷分散します。相互作用のシーケンス図は次のとおりです。 4.2.2 ソリューション分析 OpenSIP を介して MRCP の負荷分散を実装するには、次の問題に対処する必要があります。 質問 1: 受信した INVITE 要求が ASR コマンドを実行するものか、通常の通話コマンドを実行するものかをどのように判断すればよいですか? 質問 2: ASR コマンドが実行されたことを確認した後、MRCP サーバーを選択して割り当てを行うにはどうすればよいでしょうか? 質問 3: Baidu MRCP と Alibaba MRCP など、複数の MRCP サーバー クラスターがあり、クライアントが使用するエンジンを指定したい場合、この問題を解決するにはどうすればよいでしょうか。 問題点が特定されたので、1 つずつ分析してみましょう。各問題点の解決策は次のとおりです。 FS から OpenSIPs に送信され、MRCP ロード バランシングの実行を要求する SIP INVITE メッセージを見てみましょう。192.168.1.99 は FS、192.168.1.18 は OpenSIPs です。 INVITE sip:192.168.1.18:5070 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.99:5102;rport;branch=z9hG4bKQ21yZS46ytrgF Max-Forwards: 70 From: <sip:192.168.1.99:5102>;tag=4B8SvQe66FNvc To: <sip:192.168.1.18:5070> Call-ID: ed9f5f6b-0673-123b-199a-fa163e72d95e CSeq: 47770741 INVITE Contact: <sip:192.168.1.99:5102> User-Agent: FreeSWITCH Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE Supported: timer, 100rel Content-Type: application/sdp Content-Disposition: session Content-Length: 306 v=0 o=FreeSWITCH 2480643166757753319 6144298267054033408 IN IP4 192.168.1.99 s=- c=IN IP4 192.168.1.99 t=0 0 m=application 9 TCP/MRCPv2 1 a=setup:active a=connection:existing a=resource:speechrecog a=cmid:1 m=audio 31799 RTP/AVP 0 8 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=sendonly a=mid:1 上記のシグナリングから、FS によって開始された INVITE には発信者番号と着信者番号の情報はなく、FS と OpenSIP の IP とポートの情報のみが含まれていることがわかります。 OpenSIPs を MRCP サーバーの負荷分散にのみ使用する場合は、非常に簡単です。INVITE 要求を受信すると、ASR コマンドを実行する要求とみなされ、MRCP サーバーに割り当てられます。しかし、MRCP サーバーだけをロードするのは OpenSIP の無駄ではないでしょうか? したがって、実際にはこのようには使用しません。OpenSIPs は通常、FreeSWITCH の LB、音声ゲートウェイ、内線登録サービスなど、他のコールセンター コンポーネントの負荷分散も実行します。 OpenSIP はさまざまなコンポーネントから SIP INVITE 要求を受信します。では、受信した INVITE が ASR コマンドを実行するためのものか、他のサービスのためのものかをどのように判断するのでしょうか? 従来の考え方では、OpenSIPs は INVITE の SIP メッセージ ヘッダーを分析し、そこから判断を下します。ただし、FS の mod_unimrcp モジュールの制限により、FS が ASR コマンドを実行する場合、送信された SIP INVITE にカスタム SIP メッセージ ヘッダーを追加することはサポートされていないため、標準の SIP メッセージ ヘッダーからのみマイニングできます。 - INVITE 要求の送信元 IP に基づく: 同じ送信元 IP が複数の INVITE 要求を開始する可能性があるため、これは実現可能ではありません。たとえば、FS は ASR の実行や携帯電話への呼び出しを要求する場合があります。また、実現可能であっても、送信元 IP を維持するのは簡単ではありません。
- INVITE リクエストの宛先 IP に基づく: 実行不可能。この値はすべての INVITE リクエストで同じです。
- INVITE リクエストの User-Agent ヘッダーによると、これは可能です。OpenSIP は $ua を通じて値を取得できます。各 INVITE ごとに異なる UA ヘッダーをカスタマイズすることはできませんが、FS-to-MRCP サーバーのプロファイルで統一された User-Agent ヘッダーを指定できます。デフォルトは FreeSWITCH です。
- INVITE リクエストの SDP 情報の 'm' ヘッダーによると、これは可能です。OpenSIP は $(rb{sdp.line,m}) を通じて値を取得できます。例えば、上記のメッセージでは、「m=application 9 TCP/MRCPv2 1」にMRCPv2が含まれているので、ASRが実行されていると判断できます。
区別には User-Agent ヘッダーを使用することをお勧めします。これは便利で効率的です。したがって、FS が OpenSIPs に接続されると、MRCP プロファイルの設定時に ASR_MRCP_CLIENT_FS などの特別な User-Agent が指定されます。OpenSIPs は INVITE 要求を受信すると、まず UA 情報を判別します。それが ASR_MRCP_CLIENT_FS である場合は、ASR コマンドが実行されます。 OpenSIPS の load_balancer または dispatcher モジュールを使用して、MRCP サーバーで負荷分散を実装できます。 2 つの方法の特徴は次のとおりです。 MRCP サーバー クラスターのメンバーが異なる数の同時接続をサポートでき、利用可能なリソースが最も多いマシンを優先し、各メンバーの利用可能なリソースが同じ場合にラウンドロビン モードでリソースを割り当てる場合は、load_balancer モジュールを使用して負荷分散を実現できます。 MRCP サーバー クラスターのメンバーによってサポートされる同時要求の数がまったく同じである場合は、ディスパッチャ モジュールを使用して負荷分散を実行し、要求を各サーバーに均等に分散することをお勧めします。
| アドバンテージ | 欠点 | ロードバランサー | 各MRCPサーバーの最大同時実行数を制御できます 各MRCPサーバーに割り当てられたリアルタイム同時実行の監視をサポート | 単一割り当て戦略: アイドル優先度割り当てと比例割り当ての 2 つの戦略のみがサポートされています。メモリのローテーションはサポートされていません。その結果、MRCP サーバー クラスターに新しいメンバーが追加されると、すべてのトラフィックが新しいマシンに割り当てられます。この場合、新しいマシンへの負荷が急激に増加する可能性があります。 | ディスパッチャ | メモリラウンドロビン、ハッシュ割り当てなどのサポートなど、さまざまな割り当て戦略があります。
| 各 MRCP サーバの最大同時接続数を制御できないため、通話量が急増すると、雪崩が発生する恐れがあります。 各MRCPサーバーに割り当てられたリアルタイムの同時実行性を監視することはできません(ただし、これはOpenSIPの他のモジュールを通じて実現できます)。 |
FS では、各 MRCP サーバー クラスターの MRCP プロファイルを設定し、それらを OpenSIPs にポイントしますが、User-Agent 値は異なるように設定します。OpenSIPs は、さまざまな UA に基づいて、LB として使用するクラスターを選択します。 baidu_mrcp_lb.xml 下面只给出特有配置,其他配置被省略了<include> <profile name="baidu_mrcp_lb" versinotallow="2"> 【阿里的配置,name为ali_mrcp_lb】 <param name="server-ip" value="opensips-ip"/> <param name="ua-name" value="ASR_MRCP_CLIENT_FS_BAIDU"/> 【阿里的配置,ua-name为ASR_MRCP_CLIENT_FS_ALI】 <param name="sdp-origin" value="FS_MRCP"/> </profile> </include> MRCP サーバーの OpenSIPs 負荷分散のプロセス フロー チャートは次のとおりです。LB を実行するモジュールを選択するのはダイヤルプラン モジュールによって異なります。 4.2.3 具体的な実装 OpenSIPs 自体もクラスターに展開されている場合は、この記事のセクション 3.1 の方法を使用して OpenSIPs の負荷分散を実現できます。 次のコードでは、OpenSIPs の dialplan、dispatcher、load_balancer モジュールが使用されています。この記事では、これらのモジュールの使用方法については説明しません。 例: 1) Baidu と Alibaba の 2 つの MRCP サーバー クラスターが以下のように構成され、各クラスターは 2 つの IDC (IDC_A と IDC_B) に展開されます。 2) OpenSIPs は、ダイヤルプランのダイヤルプランに基づいて、Alibaba と Baidu の負荷分散方法を選択します。ダイヤルプラン テーブルのフィールド「attrs」の構成ロジックは、[MRCP クラスターの最初のルートのクラスター ID: 負荷分散の実装方法: クラスター名] です。たとえば、「90:DS:ASR_MRCP_SERVER_CTRIP_ALI」は、Alibaba MRCP の最初のルートのクラスター ID が 90 であり、ディスパッチャー モジュールを使用して LB を実装することを意味します。「90:DS:ASR_MRCP_SERVER_CTRIP_ALI」は、Baidu MRCP の最初のルートのクラスター ID が 91 であり、ロード バランサー モジュールを使用して LB を実装することを意味します。 3) ディスパッチャとロードバランサの両方に、単一の IDC の下での負荷分散に基づくエスケープ ルーティング機能が構成されます。最初のルートのクラスター ID は 90/91、2 番目のルートのクラスター ID は 10090/10091 です。 dialplan的attrs字段被赋予了特殊用途INSERT INTO `dialplan`(`dpid`,`pr`,`match_op`,`match_exp`,`match_flags`,`subst_exp`,`repl_exp`,`timerec`,`disabled`,`attrs`) VALUES (90,1000,1,'^ASR_MRCP_CLIENT_CTRIP_FS_ALI$',0,NULL,NULL,NULL,0,'90:DS:ASR_MRCP_SERVER_CTRIP_ALI'), (90,1000,1,'^ASR_MRCP_CLIENT_CTRIP_FS_BAIDU$',0,NULL,NULL,NULL,0,'91:LB:ASR_MRCP_SERVER_BAIDU'); dispatcher的attrs字段没有实际作用INSERT INTO `dispatcher` (`setid`, `destination`, `state`, `weight`, `priority`, `attrs`, `description`) VALUES (90, 'sip:192.168.1.190:8060', 0, 1, 100, 'pstn=100', 'IDC_A:ASR_MRCP_SEVER_ALI'), (90, 'sip:192.168.1.191:8060', 0, 1, 100, 'pstn=100', 'IDC_A:ASR_MRCP_SEVER_ALI'), (10090, 'sip:192.168.2.198:8060', 0, 1, 100, 'pstn=100', 'IDC_B:ASR_MRCP_SEVER_ALI'); load_balancer的resources字段可以控制最大并发数INSERT INTO `load_balancer`(`group_id`,`dst_uri`,`resources`,`probe_mode`,`description`) VALUES (91,'sip:192.168.1.180:8060','pstn=50',2,'IDC_A:ASR_MRCP_SEVER_BAIDU'), (91,'sip:192.168.1.181:8060','pstn=50',2,'IDC_A:ASR_MRCP_SEVER_BAIDU'), (10091,'sip:192.168.2.188:8060','pstn=50',2,'IDC_B:ASR_MRCP_SEVER_BAIDU'); 設定後、クラスター内の MRCP メンバーのステータスを表示できます。 sudo /usr/local/opensips/sbin/opensipsctl fifo lb_list Destination:: sip:192.168.1.180:8060 id=1 group=90 enabled=yes auto-reenable=on Resources:: Resource:: pstn max=50 load=50 Destination:: sip:192.168.1.180:8060 id=2 group=90 enabled=no auto-reenable=on Resources:: Resource:: pstn max=50 load=0 Destination:: sip:192.168.2.188:8060 id=3 group=90 enabled=yes auto-reenable=on Resources:: Resource:: pstn max=50 load=10 sudo /usr/local/opensips/sbin/opensipsctl fifo ds_list PARTITION:: default SET:: 90 URI:: sip:192.168.1.190:5080 state=Active first_hit_counter=8 attr:: pstn=500 URI:: sip:192.168.1.191:5080 state=Inactive first_hit_counter=0 attr:: pstn=500 SET:: 10090 URI:: sip:192.168.2.198:8060 state=Active first_hit_counter=0 attr:: pstn=100 route{ #省略N多代码... # check sip INVITE message source ip and port if (is_method("INVITE")) { xlog("ua = $ua , callid = $ci, fu = $fu , tu = $tu , ru = $ru , du =$du src:$si, $(rb{sdp.line,m}))"); $var(dlgPingTag) = "Pp"; if ( $ua == "ASR_MRCP_CLIENT_FS" ) { #to_asr_mrcp_server $var(dlgPingTag) = ""; # ASR 的SIP通道不能做OPTION探测} if ( !create_dialog("$var(dlgPingTag)") ) { route(PRINT_LOG, "create_dialog error : Internal Server Error"); send_reply("500","SM Internal Server Error"); exit(); } if ( $ua =~ "^ASR_MRCP_CLIENT_CTRIP_FS*" ) { #to_asr_mrcp_server 【需要修改FS mrcp client配置文件,<param name="ua-name" value="ASR_MRCP_CLIENT_FS..."/>】 if ( dp_translate("90", "$ua/$avp(dest)", "$var(attrs)") ) { #拨号方案判断route(exeLb, $(var(attrs){s.int}), "pstn", $(var(attrs){s.select, 1,:}), $(var(attrs){s.select, 2,:})); } } else { #处理其他呼叫类型,如呼叫手机等#省略N多代码... } } exit(); } #usage : route(exeLb, lb_group_id, resource_type, node_type, lb_method) #eg route(exeLb, 90, "pstn", "ASR_MRCP_SERVER_ALI", "LB") #eg route(exeLb, 90, "pstn", "ASR_MRCP_SERVER_ALI", "DS") route[exeLb]{ $var(lb_group_id) = $param(1); $var(lb_group_id_bak) = $param(1) + 10000; $var(resource_type) = $param(2); $var(node_type) = $param(3); $var(lb_method) = $param(4); xlog("[$fU->$rU] Route $rU to '$var(node_type)' by load_balancer group_id : '$var(lb_group_id)' [back_group_id:'$var(lb_group_id_bak)'], resource_type : '$var(resource_type)', node_type : '$var(node_type)' [ci:$ci] [xcid:$hdr(X-CID)]"); $var(lbRst) = 0; if( $var(lb_method) == "DS" ) { $var(lbRst) = ds_select_dst("$var(lb_group_id)", "4"); if($var(lbRst) == -1) { xlog("[exeLb4CM] [$fU->$rU] Failed --->lbRst=$var(lbRst) Route $rU to '$var(node_type)' by dispatcher group_id : '$var(lb_group_id)', resource_type : '$var(resource_type)' [ci:$ci]"); $var(lbRst) = ds_select_dst("$var(lb_group_id_bak)", "4"); if(!$var(lbRst)) { xlog("[exeLb4CM] [$fU->$rU] Failed ===>lbRst=$var(lbRst) Route $rU to '$var(node_type)' by dispatcher [back_group_id:'$var(lb_group_id_bak)'], resource_type : '$var(resource_type)' [ci:$ci]"); } } } else { $var(lbRst) = lb_start_or_next("$var(lb_group_id)", "$var(resource_type)", "s"); if( $var(lbRst) < 0) { xlog("[$fU->$rU] Failed --->lbRst=$var(lbRst) Route $rU to '$var(node_type)' by load_balancer group_id : '$var(lb_group_id)', resource_type : '$var(resource_type)' [ci:$ci]"); $var(lbRst) = lb_start("$var(lb_group_id_bak)", "$var(resource_type)", "s"); if( $var(lbRst) < 0) { xlog("[$fU->$rU] Failed ===>lbRst=$var(lbRst) Route $rU to '$var(node_type)' by load_balancer [back_group_id:'$var(lb_group_id_bak)'], resource_type : '$var(resource_type)' [ci:$ci]"); } } } if ( $var(lbRst) > 0) { if ( $rU == null ) { #对于FS 发起的MRCP INVITE 请求, $rU 为null, 而不设置$rU 将导致Load balancer 失败,所以需要初始化一个值xlog("[$fU->$rU] rU is null, then initialize to 'Null2Sm' [ci:$ci] [xcid:$hdr(X-CID)]"); #$rU = "Null2SM"; $ru = "sip:" + $(du{uri.host}) + ":" + $dp; } else { $ru = "sip:" + $rU + "@" + $(du{uri.host}) + ":" + $dp; } xlog("[$fU->$rU] Route to '$var(node_type)' --> [$du] [ci:$ci] [xcid:$hdr(X-CID)]"); route(relay); } else { xlog("[$fU->$rU] No available '$var(node_type)' now [ci:$ci] [xcid:$hdr(X-CID)]"); t_reply("480", "$var(node_type) Unavailable"); exit(); } }
上記のスクリプトに従って $ru = "sip:" + $rU + "@" + $(du{uri.host}) + ":" + $dp; を実行し、$rU== null であり、$rU="Null2SM" またはその他の null 以外の値を設定しないと、次のエラーが報告されます。 Feb 12 22:27:35 fat5410 /usr/local/opensips/sbin/opensips[3710]: ERROR:core:parse_uri: bad char '@' in state 0 parsed: <sip:> (4) / <sip:@192.168.1.190:8060> (20) Feb 12 22:27:35 fat5410 /usr/local/opensips/sbin/opensips[3710]: ERROR:core:parse_sip_msg_uri: bad uri <sip:@192.168.1.190:8060> Feb 12 22:27:35 fat5410 /usr/local/opensips/sbin/opensips[3710]: ERROR:core:pv_get_ruri_attr: failed to parse the R-URI 解決: 1) $rUを空でない値に設定する 2) $ruの値を直接変更しないでください 3) $ru = "sip:" + $(du{uri.host}) + ":" + $dp; を変更します。 4.2.4 シグナリングレコード: 2022-02-13 13:50:53 +0800 : 192.168.1.99:5221 -> 192.168.1.18:5070 INVITE sip:192.168.1.18:5070 SIP/2.0 你可以看到,这里没有被叫号码,所以到了OpenSIPs 后$rU是null Via: SIP/2.0/UDP 192.168.1.99:5221;rport;branch=z9hG4bKFUXeX5r0Q0gXp Max-Forwards: 70 From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS To: <sip:192.168.1.18:5070> Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01 CSeq: 38878534 INVITE Contact: <sip:192.168.1.99:5221> User-Agent: ASR_MRCP_CLIENT_FS_ALI Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE Supported: timer, 100rel Content-Type: application/sdp Content-Disposition: session Content-Length: 299 v=0 o=FS_MRCP 1321904497415698834 1659019553944433241 IN IP4 192.168.1.99 s=- c=IN IP4 192.168.1.99 t=0 0 m=application 9 TCP/MRCPv2 1 a=setup:active a=connection:new a=resource:speechrecog a=cmid:1 m=audio 16416 RTP/AVP 0 8 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=sendonly a=mid:1
- OpenSIPSは、MRCPサーバーへの招待を転送します
2022-02-13 13:50:53 +0800 : 192.168.1.18:5070 -> 192.168.1.190:8060 INVITE sip:192.168.1.18:5070 SIP/2.0 [如果修改$rU, 这里就是INVITE sip:[email protected]:5070 SIP/2.0] Record-Route: <sip:192.168.1.18:5070;lr;did=de7.2517abb1> Via: SIP/2.0/UDP 192.168.1.18:5070;branch=z9hG4bK9443.c1265465.0 Via: SIP/2.0/UDP 192.168.1.99:5221;received=192.168.1.99;rport=5221;branch=z9hG4bKFUXeX5r0Q0gXp Max-Forwards: 69 From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS To: <sip:192.168.1.18:5070> Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01 CSeq: 38878534 INVITE Contact: <sip:192.168.1.99:5221> User-Agent:ASR_MRCP_CLIENT_FS_ALI Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE Supported: timer, 100rel Content-Type: application/sdp Content-Disposition: session Content-Length: 299 X-UUI: &XCID=0dc0196031626864653EXCIDEND X-CID: 0dc0196031626864653EXCIDEND v=0 o=FS_MRCP 1321904497415698834 1659019553944433241 IN IP4 192.168.1.99 s=- c=IN IP4 192.168.1.99 t=0 0 m=application 9 TCP/MRCPv2 1 a=setup:active a=connection:new a=resource:speechrecog a=cmid:1 m=audio 16416 RTP/AVP 0 8 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=sendonly a=mid:1
- MRCPサーバーは200 OKに返信し、その後のRTP受信のために実際のアドレスを返します
2022-02-13 13:50:53 +0800 : 192.168.1.190:8060 -> 192.168.1.18:5070 SIP/2.0 200 OK Via: SIP/2.0/UDP 192.168.1.18:5070;branch=z9hG4bK9443.c1265465.0 Via: SIP/2.0/UDP 192.168.1.99:5221;received=192.168.1.99;rport=5221;branch=z9hG4bKFUXeX5r0Q0gXp Record-Route: <sip:192.168.1.18:5070;lr;did=de7.2517abb1> From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS To: <sip:192.168.1.18:5070>;tag=45D4K1DvpQQXK Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01 CSeq: 38878534 INVITE Contact: <sip:192.168.1.190:8060> User-Agent: BaiduSpeech SofiaSIP 1.5.0 Accept: application/sdp Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE Supported: timer, 100rel Session-Expires: 600;refresher=uac Min-SE: 120 Content-Type: application/sdp Content-Disposition: session Content-Length: 303 v=0 o=BaiduSpeechServer 8512797916186481341 4497985761629564802 IN IP4 192.168.1.190 【接收RTP的IP】 s=- c=IN IP4 192.168.1.190 t=0 0 m=application 1544 TCP/MRCPv2 1 a=setup:passive a=connection:new a=channel:b250b76cea1011eb@speechrecog a=cmid:1 m=audio 18380 RTP/AVP 0 【接收RTP的端口】 a=rtpmap:0 PCMU/8000 a=recvonly a=mid:1
2022-02-13 13:50:53 +0800 : 192.168.1.99:5221 -> 192.168.1.18:5070 ACK sip:192.168.1.190:8060 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.99:5221;rport;branch=z9hG4bKFUXeX5r0Q0gXp Route: <sip:192.168.1.18:5070;lr;did=355.53f8e331> Max-Forwards: 70 From: <sip:192.168.1.99:5221>;tag=3pU7FrrQBQ1NS To: <sip:192.168.1.18:5070> Call-ID: 5c97aeaf-64b4-123a-02b4-fa163ea03f01 CSeq: 38878534 ACK Contact: <sip:192.168.1.99:5221> Content-Length: 0
- 最後に、OpenSipsはACKをMRCPサーバーに転送します
V. 結論CTRIPコールセンターのカスタマーコールインのコアコンポーネントアーキテクチャ図から、上記のインテリジェントなカスタマーサービスロボットシナリオへのコールインは、ASRエンジンの負荷分散は、Ctripコールセンタープラットフォームのコンポーネントの小さな機能ポイントにすぎないことがわかりますが、それは不可欠な部分でもあります。 この技術的なソリューションの実装のおかげで、複数のクラスター、複数のデータセンター、複数のサプライヤーからのASR製品を十分に統合でき、Cthepheallの電話のインテリジェントカスタマーサービスロボットビジネスの安定した運用のための優れた技術サポートを提供し、Ctripの顧客の通話体験を改善します。 この記事では、主にASRエンジンの負荷分散の設計と実装について説明し、インテリジェントコールセンターの分野で働いたり研究している学生に助けを提供したいと考えています。 |