クラウドインテグレーションサービス「雲斗」のブログ

芝公園にある創研情報株式会社がAWS を 中心にクラウドの基本から便利な使いかたまでをお伝えしていきます。

AWS Lambda 人工知能・機械学習

AWS LambdaでIBM WatsonのTokenを定期的に取得してみる

IBM Bluemix Watsonで音声認識(Speech to Text)や音声合成(Text to Speech)を利用するコードを書く機会があった。(長いのでIBM Bluemix WatsonはWatson、Speech to TextはSTT、Text to SpeechはTTSと記述)

これらのAPIは用途によりWebSocketやHTTP呼び出しを使い分けるようなのだが、リアルタイム性がキモだったのでWebSocketを選んでみた。(TTSも同じようにWebSocketしてみたが、音声データ変換終了と同時にSocketがCloseされる仕様であまり意味なかった...。)

WebSocketを呼び出すためにはWatsonが発行するトークンが必要となるのだが、ブラウザのクライアントJSからWatsonへトークンをGetしに行くとアクセス元のオリジンの問題(セキュリティ上の問題)で取り出せない。トークンの寿命は1時間(※1)、Getの処理自体は数ステップ、ならばAWSのLambdaでサクッと行けるのでは?と考えた。(ローカルPCにWebサーバ立てれば?というツッコミはなしで...。)

利用したサービス・API

IBM Bluemix Watson承認API(Authentication"認証"APIではなくAuthorization"承認"API)
AWS Lambda、CloudWatch、(IAM、S3)

事前準備

IBM Bluemixでアカウントを登録する。30日間フリートライアルで利用できる。 アカウントを登録後、ダッシュボードから「サービスの作成」でサービス > WatsonからSTTとTTSのサービスを作成する。忘れずに「サービス資格情報」をコピーしておく。なお、フリートライアル以降もAPIの利用には無料枠が設定されている。(STT:最初の1,000分、TTS:最初の100万文字)

speech_to_text

AWSアカウントを登録する。こちらは1年無料。

開発言語・環境

Java8
Eclipse Neon(4.6.1)
AWSツールキット(Eclipseにインストール、アクセスキーを設定)

aws_toolkit_setting

AWSツールキットでJavaプロジェクトのひな形を生成

ツールキットのオレンジ色のアイコンをクリックするとメニューが表示されるので「New AWS Lambda Java Project...」を選ぶ。
aws_toolkit_for_eclipse
スケジュール起動なので入力タイプは「Custom」を設定した。他はよしなに入力。
create_a_new_aws_lambda_java_project
自動的に生成してくれるプロジェクトは以下のような構成となる。
lambda_java_project_01

プロジェクトへ参照ライブラリ設定

LambaからWatsonへのHTTPアクセスのためokhttpを参照ライブラリ設定する。okhttp(okhttp-3.4.2.jar)とokio(okio-1.11.0.jar)の2つが必要となる。
lambda_java_project_02

Lambda上で実行されるJavaコードを作成

先にメモしておいたSTT/TTSのサービス資格情報をコード中の[username]、[password]に埋め込む。

LambdaLogger logger = null;
protected void _log(String input) {
  this.logger.log(input+"\n");
}
@Override
public Object handleRequest(Object input, Context context) {
  this.logger = context.getLogger();

  try{
    Map<String, OkHttpClient> connMap = new HashMap<String, OkHttpClient>();
    Map<String, UrlInfo> accMap = new HashMap<String, UrlInfo>();
    accMap.put("TTStoken", new UrlInfo("https://stream.watsonplatform.net/authorization/api/v1/token?url=https%3A%2F%2Fstream.watsonplatform.net%2Ftext-to-speech%2Fapi","[username]", "[password]"));
    accMap.put("STTtoken", new UrlInfo("https://stream.watsonplatform.net/authorization/api/v1/token?url=https%3A%2F%2Fstream.watsonplatform.net%2Fspeech-to-text%2Fapi","[username]", "[password]"));

    for(Map.Entry<String, UrlInfo> elem : accMap.entrySet()) {
        String key = elem.getKey();
        UrlInfo urlInfo = elem.getValue();

        connMap.put( key, new OkHttpClient() );
        Request request = new Request.Builder().url(urlInfo.getUrl())
            .header("Authorization", Credentials.basic(urlInfo.getAccessKey(),urlInfo.getAccessSecret()))
            .build();
        Response response = connMap.get(key).newCall(request).execute();
        if (response.isSuccessful()) {
            String token = response.body().string();
            _log("token["+token+"]");
        } else {
            _log("response error["+response.message()+"]");
        }
    }
  }catch (Exception e){
    _log(e.toString());
  }
  return null;
}

UrlInfoクラス(蛇足)

 public class UrlInfo {
  String url="";
  String accessKey="";
  String accessSecret="";
  public UrlInfo(){}
  public UrlInfo( String url, String ak, String as ){
    this.url=url;
    this.accessKey=ak;
    this.accessSecret=as;
  }
  public String getUrl() {    return url;  }
  public void setUrl(String url) {    this.url = url;  }
  public String getAccessKey() {    return accessKey;  }
  public void setAccessKey(String accessKey) {    this.accessKey = accessKey;  }
  public String getAccessSecret() {    return accessSecret;  }
  public void setAccessSecret(String accessSecret) {    this.accessSecret = accessSecret;  }
}

アップロード

できあがったプログラムをEclipse/AWSツールキットからアップロードする。プロジェクトを右クリックし、「Amazon Webサービス」から「Upload function to AWS Lambda...」を選ぶ。
aws_toolkit_for_eclipse_context

アップ先のリージョンやLambda Function名を聞かれる。よしなに入力。
upload-function-to-aws-lambda_01

IAMロールやS3バケツを聞かれる。よしなに入力(作成)。
upload-function-to-aws-lambda_02
AWSのドキュメントでは"バケット"と記述されることが多いような気がするがここでは"バケツ"。弱冠の違和感。
無事アップロードできるとAWS LambdaのダッシュボードからFunctionが登録されたことを確認できる。
lambda_dashboard_01

Lambda Functionの実行

AWSツールキットからLambda Functionを実行する。プロジェクトを右クリック、「Amazon Webサービス」から「Run function on AWS Lambda...」を選ぶ。Functionへのインプットパラメータはないので、空欄のままで「Show live log」にチェックを入れ「Invoke」する。
run_function_on_aws_lambda
Lambda Functionが問題なく実行されると、コード中で出力したログがEclipseのコンソールで確認できる。
eclipse_console
同じログがAWSのCloudWatchログに出力されている事も確認できる。
cloudwatch_logs_01

定期実行

Lambda Functionは問題ないので、CloudWatch Events - Scheduleで一定間隔でFunctionを起動するよう設定する。ダッシュボードの「Triggers」タブで「Add trigger」を選択する。lambda_dashboard_03
Lambdaアイコン左側の点線枠をクリックする。
lambda_add_trigger_01
トリガとして指定できるサービスがリスト表示されるのでCloudWatch Events - Scheduleを選択する。
lambda_add_trigger_02
ルール名や起動スケジュールを聞かれるので適宜入力。若干の余裕をみて58分間隔で起動するように"rate(58 minutes)"を指定した。
lambda_add_trigger_03

スケジュールにより起動されていることをCloudWatchログで確認する。
cloudwatch_logs_02

 

これで無事Watsonからトークンを定期的にGetできた...がSTT/TTSのWebSocketに接続するプログラム(ブラウザのJS)はどうやって利用するのか?
というわけで、次回はこのトークンをAWS DynamoDB経由でブラウザのJSと連携する方法をとりあげる。

次回タイトル:Amazon DynamoDBにクライアントサイドJSでアクセスしてみる
(JS->AWS API Gateway->AWS Lambda->Watsonで毎回取得すれば?というツッコミはありそうですが。)

参考サイト

※1 WatsonトークンのTTLは1時間

https://www.ibm.com/watson/developercloud/doc/getting_started/gs-tokens.shtml

Tokens have a time to live (TTL) of one hour, after which you can no longer use them to establish a connection with the service.

◆クラウドインテグレーションサービス「雲斗」以下のページからアクセス出来ます。

-AWS Lambda, 人工知能・機械学習

Bitnami