Google ASRを使用した顧客セルフサービス

それはすべて、時代に遅れずに、実際の操作員を魂のないロボットに部分的に置き換えたい、つまり電話でメーターの測定値を受信したいというクライアントの要求から始まりました。 DTMFを介した入力では、すべてが明確であるように思われ、これにハイライトはありません。 音声入力が必要でした。



カットの下に、Google ASRを箱入りのアスタリスクオプションのいずれかにねじ込むことに関する小さなマニュアル。



いくつかのインターネット監視の後、MDGテクノロジーに基づくいくつかのターンキーソリューションが見つかりました。 たぶんいいかもしれませんが、値札は150キロルーブルから始まったので、行きます(こんにちは、ヒキガエル)。



彼らは裸のアスタリスクを取りませんでした、私はある種の男が欲しいので、FreePBXは1つの大きなカスタムコンテキストに変わるので森に入りました。 、ASR、およびHTTP APIを介したデータベース。 私たちは無料のもの(10同時通話の制限がある有料のものとは異なります)を使用します。このバージョンは、2つのPSTN回線と2つの3Gホイッスル用です。 原則として、同じswitchvoxに対して、ねじ込みはほぼ同じです。



行こう:



サイトまたはミラー (安定しているが、 ここに少し古いアスタリスクが付いている )から、VMWare Player用のすぐに使用できるイメージを含む大きなtar.bz2アーカイブをダウンロードします。 または、古いバージョンがある場合は、 更新を配置します



VMWareプレーヤーで解凍して実行し、ESXiに変換するか、実際のハードウェアにダンプします。 複雑なことは何もありません。 サウンドファイルが解凍され、更新がチェックされるため、最初の起動は少し長くなります。



叙情的な余談:

このシステムは仮想PBXホスティングを作成するために考案されましたが、私たちはそれを単独で使用するため、管理者およびユーザーです。 一般に、これはスズメと戦うための小さな「大砲」ではありません。 ただし、追加の利点があります。たとえば、すぐに使用できる複数のQAを作成できます。



管理インターフェイスに入り、ユーザーを作成し、DIDを追加し、言語をロシア語(ロシア語)に変更します(これはGoogle TTSです)-この仮想PBXはセルフサービスシステムになります。 おそらく、設定について詳しく説明する価値はないでしょう。彼らはすでにここに書いています。



次に、コンソールに踏み込んで、Googleに送信されるcgiスクリプトを作成します。 CGIパラメータを理解するために例を挙げて少し修正すると、次のようになります。



#!/usr/bin/perl ######################################################################## use strict; use CGI qw(:standard); use LWP::UserAgent; use JSON::XS; my $lang = param('lang'); my $file = param('file'); my $var_name = param('var') || 'ASR_RESULT'; print "Content-type: text/plain; charset=utf-8\n\n"; my $url; if ( lc($lang) eq 'ru' ) { $url = "http://www.google.com/speech-api/v1/recognize?xjerr=1&client=chromium&lang=ru-RU"; } else { $url = "http://www.google.com/speech-api/v1/recognize?xjerr=1&client=chromium&lang=en-US"; } my $new_file = "/tmp/google-asr-$$.wav"; open(TMP,">$new_file"); my $buffer; while ( !eof($file) ) { read( $file, $buffer, 16384 ); print TMP $buffer; } system "ffmpeg -y -i $new_file $new_file.flac 2>/dev/null"; if ( $? ) { print "$var_name=\n"; die "Can't convert file $new_file to $new_file.flac: $?\n"; } else { $new_file .= '.flac'; } my $file_info = `file $new_file`; if ( $file_info =~ /FLAC audio.*\s([\d.]+)\s*kHz/ ) { $file_info = $1 * 1000; } else { unlink $new_file; print "$var_name=\n"; die "Incorrect FLAC file: $file_info\n"; } unless ( open( FILE, "<$new_file" ) ) { print "$var_name=\n"; die "Can't open input file[$file]: $!\n"; } undef $/; my $audio = <FILE>; $/ = "\n"; close(FILE); unlink $new_file; my $ua = LWP::UserAgent->new( debug => 1 ); my $response = $ua->post($url, Content_Type => "audio/x-flac; rate=$file_info", Content => $audio); if ( $response->is_success ) { my $h_ref; eval { my $json = JSON::XS->new(); $h_ref = $json->decode($response->content()); }; if ( ref $h_ref eq 'HASH' and exists $h_ref->{'hypotheses'} ) { my $data = $h_ref->{'hypotheses'}->[0]->{'utterance'}; my $map = { 'o' => 0, 'o' => 1, 'a' => 2, '' => 3, '' => 4, '' => 5, '' => 6, '' => 7, 'o' => 8, '' => 9, '' => 10, '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', '' => '', }; my $result = ''; foreach my $ch ( split(/\s+/,$data) ) { $result .= length($map->{$ch}) ? $map->{$ch} : $ch; } print "$var_name=$result\n"; exit; } } print "$var_name=\n";
      
      







CGIパラメータの読み取りに加えて、「1」、「2」などの形式の文字列からの「数値への変換」、およびカウンターの種類と確認の名前のいくつかの「エイリアス」も追加されました(yes == yes || next || true)一部の行では、「さらに」は「はい」よりもよく認識されます。



入力で、スクリプトはwav形式のファイルと値を書き込む変数の名前を受け取ります(これはXVBで送信され、ダイヤルプランで使用されます)。さらに2つのスクリプトを作成します。







すべてをそこにあるデータベースに依存するので、私はそれらをここに持ってきません。 3番目のスクリプトは、個人アカウント番号/インジケーションのタイプ/およびインジケーションを受け取り、それらをデータベースに書き込み、差を返します。 ユーザーに発音します。

これらのスクリプトを同じマシン(または別のマシン)の/ var / www / cgi-bin /に保存します。



次に、ほとんど同一の部分からの退屈なダイヤルプランのセットアップが行われます。







つまり、各インディケータに対して次のことを行います。







ご覧のとおり、アクションは非常に退屈なので、すべてを手動で行うのではなく、XML構成スクリプトを生成しました

(XVBは、XMLファイルからの構成のロード/アンロードをサポートしています)。 起こったことの小さな部分:



 <opt> <IVR name="0" EXT_NUMBER="0" NAME="." GREET_REPEAT_CNT="1" GREETING="     ." GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="V5_2*0" TYPE="1" WAITEXTENSION="0"> </IVR> <IVR name="error" EXT_NUMBER="error" NAME="WEB " GREETING=" .   ." GREET_REPEAT_CNT="1" GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="hangup" TYPE="1" WAITEXTENSION="0"> </IVR> <IVR name="V5_2*0" EXT_NUMBER="V5_2*0" NAME="  -  --------------" GREET_REPEAT_CNT="0" GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="V5_2*1" TYPE="1" WAITEXTENSION="0"> </IVR> <IVR name="V5_2*1" EXT_NUMBER="V5_2*1" NAME="  -  /  " GOTO_IF_FAIL="error" GREETING="    " GREET_REPEAT_DELAY="0.00" GREET_REPEAT_CNT="1" MAX_MSG_DURATION="10" MAX_SILENCE="2" NEED_VOICE="1" NEXTEXTENSION="V5_2*2" TYPE="20" WAITEXTENSION="0" WEBVAR_URL="http://localhost/cgi-bin/gv.pl?lang=ru&file=% VAR:FILE_DATA %&var=ASR_RESULTV5_2"> </IVR> <IVR name="V5_2*2" EXT_NUMBER="V5_2*2" NAME="  -  /  " GREET_REPEAT_CNT="0" GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="V5_2*3" TYPE="21" WAITEXTENSION="0"> <_VB_DATA COND="==" FUNC="strlen" PRIORITY="5" REDIRECT_TO="V5_2*2*1" VAR_NAME="ASR_RESULTV5_2" VAR_VALUE="0"/> </IVR> <IVR name="V5_2*2*1" EXT_NUMBER="V5_2*2*1" NAME="  -  /  " GREETING="    ,    ." GREET_REPEAT_CNT="1" GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="V5_2*1" TYPE="1" WAITEXTENSION="0"> </IVR> <IVR name="V5_2*3" EXT_NUMBER="V5_2*3" NAME="  -  /  " GREETING=" " GREET_REPEAT_CNT="1" GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="V5_2*4" SAY_PATTERN="char" SAY_PATTERN_ID="0" TEXT_STR="% VAR:ASR_RESULTV5_2 %" TYPE="25" WAITEXTENSION="0"> </IVR> <IVR name="V5_2*4" EXT_NUMBER="V5_2*4" NAME="  -  / " GOTO_IF_FAIL="error" GREETING=",    ." GREET_REPEAT_DELAY="0.00" GREET_REPEAT_CNT="1" MAX_MSG_DURATION="3" MAX_SILENCE="2" NEED_PARAMS="0" NEED_VOICE="1" NEXTEXTENSION="V5_2*4*1" TYPE="20" WAITEXTENSION="0" WEBVAR_URL="http://localhost/cgi-bin/gv.pl?lang=ru&file=% VAR:FILE_DATA %"> </IVR> <IVR name="V5_2*4*1" EXT_NUMBER="V5_2*4*1" NAME="  -  /  " GREET_REPEAT_CNT="0" GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="V5_2*3" TYPE="21" WAITEXTENSION="0"> <_VB_DATA COND="==" FUNC="value" PRIORITY="5" REDIRECT_TO="V5_2*0" VAR_NAME="ASR_RESULT" VAR_VALUE=""/> <_VB_DATA COND="==" FUNC="value" PRIORITY="5" REDIRECT_TO="V5_2*1" VAR_NAME="ASR_RESULT" VAR_VALUE=""/> </IVR> <IVR name="V5_3*9" EXT_NUMBER="V5_3*9" NAME="  -  / " GREET_REPEAT_CNT="0" GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="V5_3*9*1" TYPE="6" WAITEXTENSION="0" QUIET_MODE="1" GOTO_IF_FAIL="error" TTS_URL="http://127.0.0.1/cgi-bin/commit.pl?level=V5_3&account=% VAR:ASR_RESULTV1 %&value1=% VAR:ASR_RESULTV5_1 %&value2=% VAR:ASR_RESULTV5_2 %&value3=% VAR:ASR_RESULTV5_3 %"> </IVR> <IVR name="V5_3*9*1" EXT_NUMBER="V5_3*9*1" NAME="-----------------------" GREET_REPEAT_CNT="1" GREETING="  . .       ." GREET_REPEAT_DELAY="0.00" NEXTEXTENSION="0" TYPE="1" WAITEXTENSION="0"> </IVR> </opt>
      
      







上記の構成をxxx.xmlに保存し、構成復元アイテムを介してユーザープロファイルに入力できます。



ウェブでは、次のような結果になりました。







実際、音声およびDTMFを介して証言を収集するための最も単純な作業プロトタイプ(VirtualPBXにDTMFを入力することによりセルフサービスを編成する機能は、箱から出してすぐなので、ここでは詳しく説明しませんでした)。 次に、スクリプトを研磨し、通常のサウンドグリーティングを作成します。





PS DTMFの音声を好む人の数を見てみましょう。私にとっては、DTMFは、DTMFを介して入力できる場合、よりシンプル(より馴染みのある)で、より高速であるように見えます。



All Articles