kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

AWS SSO環境でCodeCommitを使う

f:id:kun432:20210713025747p:plain

ちょっとメモ。

前提条件

  • AWS SSOを使っている
  • aws-vaultを使っている
  • AWS SSOのプロファイルは作成済みであること
  • アクセスキーとかシークレットアクセスキーは使わない

CodeCommitでレポジトリ作成

とりあえず空のレポジトリを作成。レポジトリ名は"testrepo"とした。

f:id:kun432:20210713030101p:plain

右上の「URLのクローン」から「HTTPSのクローン」をクリック

f:id:kun432:20210713030302p:plain

クリップボードにレポジトリのURLがコピーされる。今回のURLはhttps://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/testrepo

f:id:kun432:20210713030338p:plain

gitでclone/push

ではcloneする。ちょっとややこしいけど、Git 認証情報ヘルパーがaws-vault経由でAWS認証情報を使用するように設定する。

$ git clone \
--config 'credential.helper=!aws-vault exec SSOプロファイル名 -- aws codecommit credential-helper $@' \
--config 'credential.UseHttpPath=true' \
https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/testrepo

こんな感じでSSOのログインを促すメッセージが表示されて・・・

Cloning into 'testrepo'...
Opening the SSO authorization page in your default browser (use Ctrl-C to abort)
https://device.sso.us-east-1.amazonaws.com/?user_code=ZXQQ-LFXR

ブラウザが立ち上がるので"Sign In to AWS CLI"をクリックする

f:id:kun432:20210713032726p:plain

サインインできたらターミナルに戻る。

f:id:kun432:20210713032742p:plain

cloneされている。今回は空なのでwarningが出ているだけ。

warning: You appear to have cloned an empty repository.
$ ls
testrepo

cloneしたディレクトリで適当にpushする

$ cd testrepo
$ echo "# testrepo" >> README.md
$ git add .
$ git commit -m "initial commit"
[master (root-commit) XXXXXXX] initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 248 bytes | 248.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/testrepo
 * [new branch]      master -> master

CodeCommitを見てみるとちゃんとpushされている。

f:id:kun432:20210713033137p:plain

参考

ここがとても参考になった。

ここの「おまけ」にあるエラーは必ず経験するはず。Linuxの場合はpassとかを使うけど、認証情報のタイムラグみたいなことは同じように起きる。結構面倒。

#VUIchallenge #001 - Welcome Message

f:id:kun432:20210711001052p:plain

#VUIchallenge というのを知っていますか?

Amazon Alexa AutoのVUI&UXデザイナーであるJesús Martínさんによる企画で、毎日1問、VUIに関するお題が与えられて、それに対するデザインやソリューションを考えて、みんなでシェアしよう!という取り組みです。ちょっとおもしろそうなので、参加してみました。

ということで、第1回は「Welcome Message」。

お題

The challenge

Design a Welcome message for a service called TVguide for a Google Action or an Alexa Skill. The service provides information about what's is set on TV and allows customers to ask for the information they need.

Jesús' Tips

The welcome message is the first prompt your customers will receive from your interaction. Is the entrance door for your experience and will need to help customers understand what's your >experience all about while keeping them motivated to carry on.
Most welcome messages have 3 parts:

  • Greetings.
  • Scope explanation.
  • Call to action.

DeepLによる日本語訳

課題

Google ActionやAlexa Skill用のTVguideというサービスのWelcomeメッセージをデザインしてください。このサービスでは、テレビで放映されている番組の情報を提供し、お客様が必要な情報を求めることができます。

Jesúsのヒント

ウェルカムメッセージは、お客様があなたのインタラクションから受け取る最初のプロンプトです。あなたの体験の入り口となるもので、あなたの体験がどんなものかをお客様に理解してもらい、先に進みたいというモチベーションを維持する必要があります。
多くのウェルカムメッセージは3つの部分から成り立っています。

  • 挨拶。
  • 範囲の説明。
  • 行動への呼びかけ。

デザイン

Alexaであればスキル、Googleであればアクション(以降は「スキル」で統一します)が起動した一番最初のメッセージですね。ポイントは3つ。

  • スキルのあいさつ
  • スキルでできること
  • 呼びかけの例(ユーザはどういうふうに問いかければよいか?)

かんたんなようで難しいですね。とりあえず以下の2パターンで考えてみたいと思います。

  • 初めて起動するとき
  • 2回め以降で使い慣れたとき

初めて起動するとき

初めて起動するときのポイントは以下です。

  • ユーザは、スキルで何ができるのかをあまりわかっていない
  • ユーザは、スキルに対して、どう発話すればいいのかあまりわかっていない

つまり、スキルについて、そしてスキルの使い方についてある程度の説明が必要だということですね。したがって、以下のような感じになると思います。(スキルの機能については例なのであまり深く考えないことにします)

はじめまして、TVガイドスキルをご利用いただきありがとうございます。※スキルのあいさつ
このスキルでは、今放送されているテレビ番組名とその情報をお伝えします。※スキルでできること
例えば「今やっている番組をおしえて」とか「今◯チャンネルでやっている番組をおしえて」と言ってみてください。※呼びかけ例

スキルのあいさつは、まずスキル名を伝えるのは重要かなと思います。これがユーザにとっては今起動しているスキルを識別する唯一の情報だと思うので。その意味だと、呼び出し名とスキル名はなるべく一致しているほうがいいでしょうね。ただ、だいたいどのスキルもだいたい似通った感じになってしまっているので、このへんで工夫するのはなかなか難しいです。これについては後述します。

スキルでできることは、シンプルかつ簡潔に「何ができるか?」を伝えることですね。長すぎると聞いてられないし、短すぎてもわかりません。

それを補完するのが、

  • 呼びかけの例
  • ヘルプ

になるでしょう。

呼びかけの例は、いくつかのサンプルを提示することでユーザは何ができるか?どう使えばいいか?のイメージを持てますし、想定する発話にユーザを誘導することで「ごめんなさい、よくわかりません」を回避することにつながると思います(もちろんサンプル発話のバリエーションは大いに越したことはないです)。

ただし、呼びかけ例もあまり多く挙げると長くなりすぎてしまうので、それは「ヘルプ」におまかせするのがよいと思います。例えば、上記の最後に「それ以外の使い方についてはヘルプと言ってみてください」とつけてもいいかもしれませんね。個人的にはいちいちヘルプを呼び出すのも長くなりすぎて面倒なので、初回はチュートリアル的な雰囲気が伝わるようなフローになればいいのではないかと思います。例えばこういう感じ。

はじめまして、TVガイドスキルをご利用いただきありがとうございます。
このスキルでは、今放送されているテレビ番組名とその情報をお伝えします。
例えば「今やっている番組をおしえて」とか「今◯チャンネルでやっている番組をおしえて」と言ってみてください。

今やっている番組を教えて

午後9時現在、放送中の番組は、2チャンネルで「ニュース9時」、4チャンネルで「クイズ◯◯◯」、・・・です。それぞれの番組を詳しく知りたい場合は、例えば「4チャンネルについて詳しく教えて」と言ってみてください。

4チャンネルについて詳しく教えて

午後9時現在、4チャンネルで放送中の番組は「クイズ◯◯◯」です。出演者は✕✕✕、△△△、ゲストは▲▲▲です。

このような感じで、このスキルでは放送中の番組についてお伝えすることができます。次回以降は「アレクサ、TVガイドで4チャンネル」といってスキルを起動すればスピーディにお答えします。また、他にもできることがいろいろありますので、その場合は「ヘルプ」と言ってみてください。

現実はそんなにかんたんではないんですがまあサンプルなのでw。

スキルはそもそも起動してもらってナンボ、かつ、長すぎるとめんどくさくなって離脱、短すぎるとよくわからなくて離脱、というとても難しいものなので、最初はなるべくシンプルに使い方を体験してもらって、2回目以降によりスピーディに目的を達成できるやり方をお伝えしたり、もっと詳しい説明のためのヘルプを案内するという感じで、ユーザのスキルの使い方への理解を徐々に深めてもらうというのが良いのではないかなと思います。

2回め以降で使い慣れたとき

初回に対して2回目以降のポイントは、ユーザはすでにスキルの使い方をある程度理解している、ということです。この場合は、

  • 無駄なく目的をスピーディーに達成できる事が重要
  • ワンパターンなやり取りを回避するための工夫も必要

が重要になると思います。

音声インタフェースは、やりとりの間の集中力を結構要求します。初回ですら長いとめんどくさくなるのが、2回目以降も同じことを長々と繰り返し聞くと使う気がなくなります。すくなくとも初回よりはユーザは使い方を少し理解している状態ですので、2回目以降はさらに簡潔にすべきですね。例えば、

  • スキルの使い方の説明はしない。
  • ワンショット発話を使ってもらう

あたりが望ましいと思います。

そして、同じやり取りの繰り返しは、機械的になってしまってマンネリ化を生みます。ここは簡潔にするというところとのバランスも難しいと思うのですが、

  • 言い方のバリエーションを複数用意しておいてランダムに変える
  • パーソナライズ的な要素を組み込む
    • ユーザの名前をどこかで取得しておいて、発話に含める
    • 時間ごとに挨拶を変える
    • ユーザの過去の発話履歴からユーザが求めそうなものを解析して先んじて提案する(かなり大変だとは思いますが・・・)

ことで、いかにも機械が応答しているという感を減らすのが良いかと思います。

いくつかパターンを用意してみました。

呼び出し名だけでスキルを起動した場合

アレクサ、TVガイドをひらいて

TVガイドスキルです。午後9時現在、放送中の番組は、2チャンネルで「ニュース9時」、4チャンネルで「クイズ◯◯◯」、・・・です。詳細を知りたいチャンネル名を言ってください。

ワンショット発話の場合

アレクサ、TVガイドで4チャンネルについて教えて

(挨拶も省略)午後9時現在、4チャンネルで放送中の番組は「クイズ◯◯◯」です。出演者は✕✕✕、△△△、ゲストは▲▲▲です。(スキル終了)

パーソナライズ

アレクサ、TVガイドをひらいて

◯◯さん、こんばんは、TVガイドスキルです。午後9時現在、4チャンネルで放送中の番組は「クイズ◯◯◯」です。ちなみに8チャンネルで「プロ野球◯◯対△△」もやっていますよ(過去の履歴からプロ野球をよく見ている場合)

ここは色々なパターンがあると思いますので、工夫のしどころですね。

まとめ

色々かんがえてみましたが、かんたんなようでやっぱり難しいですね・・・概ねワンパワーンな感じになりがちですし、長すぎても短すぎてもダメ、その上で次回も使ってもらえるような工夫、と考えるとかなりハードルが高い。

起動時のメッセージは、あくまでもスキルの1要素にすぎないのですが、ユーザが必ず最初に触れる部分、つまりスキルの第一印象を決める部分でもあるので、手を抜かないようにしたいですね。

なお、上記でご紹介したような工夫については以下の記事でもご紹介していますので、興味があればごらんください。

シェルスクリプトでロック

シェルスクリプトでロックファイルを使った排他制御をする場合、普通にファイルだとうまく行かない場合がある。なぜならロックファイルの作成と確認がアトミックに行われないから。

でこれを回避する方法として、シンボリックリンクを使う方法が紹介されているけど、他にも色々あるみたいなので試してみた。

シンボリックリンク

とりあえず上記でも紹介されているシンボリックリンクを使うやり方。

#!/bin/bash

LOCK_FILE=./file.lock

## ロックファイルの確認と作成
if ! ln -s $$ $LOCK_FILE; then
    echo "LOCKED"
    exit 0
fi

## メインの処理
for ((i=0;$i<10;i=$i+1)); do
    echo $i
    sleep 1
done
echo OK

## ロックファイルの削除
rm -f $LOCK_FILE

exit 0

2つのターミナルで実行するとこうなる。

$ ./lock.sh
0
1
2
...(snip)...
7
8
9
OK
$ ./lock.sh
ln: ./file.lock: File exists
LOCKED

実行中は以下の通りシンボリックリンクが張られている。実際には実体が存在しないリンクになってるけど、問題ない。

$ ls -lt 
lrwxr-xr-x  1 xxxxx  xxxxx    5  7  6 02:16 file.lock -> 28128

ディレクトリを使う

使い方としてはどうよ?と思わないでもないけど、ディレクトリでもよい。個人的には昔はこれをよく使っていた。

#!/bin/bash

LOCK_DIR=./lock

## ロックファイルの確認と作成
if ! mkdir $LOCK_DIR; then
    echo "LOCKED"
    exit 0
fi

## メインの処理
for ((i=0;$i<10;i=$i+1)); do
    echo $i
    sleep 1
done
echo OK

## ロックファイルの削除
rmdir $LOCK_DIR

exit 0

実行結果

$ ./lock.sh
0
1
2
...(snip)...
7
8
9
OK
$ ./lock.sh
mkdir: ./lock: File exists
LOCKED

lockfileコマンド

procmailに含まれているlockfileコマンドを使う。個人的にこれは知らなかった。

Macの場合はhomebrewのprocmailパッケージに含まれている。

$ brew install procmail
$ which lockfile
/usr/local/bin/lockfile

スクリプトはこんな感じ。

#!/bin/bash

LOCK_FILE=./file.lock

## ロックファイルの確認と作成
if ! lockfile $LOCK_FILE; then
    echo "LOCKED"
    exit 0
fi

## メインの処理
for ((i=0;$i<10;i=$i+1)); do
    echo $i
    sleep 1
done
echo OK

## ロックファイルの削除
rm -f $LOCK_FILE

exit 0

これの面白いところは、オプションを指定せずに実行すると、ロックファイルが作れるようになるまで待つこと。

$ ./lock.sh
0
1
2
...(snip)...
7
8
9
OK
$ ./lock.sh
(1つ目の実行が終わるまでは停止したまま、1つ目の実行が終わると処理が進む)
0
1
2
...(snip)...

オプションでは、

  • 何秒間隔で何回リトライする
  • ロックファイルのタイムスタンプから何秒経っていたら強制的に削除する

というようなことができて、例えば以下のように指定すると、1秒間隔で2回リトライになる。

if ! lockfile -1 -r 2 $LOCK_FILE; then

ロック中はこんな感じになる。

$ ./lock.sh
lockfile: Sorry, giving up on "./file.lock"
LOCKED

ロックファイルが一定時間よりも古ければ強制的に削除する、というのは、エラーなどでロックが作られっぱなしになるようなケースを踏まえると役に立つかもしれない。

manはこちら

manpages.debian.org

まとめ

他にflock使う方法もある。個人的には、ロックを作成する場合はエラー処理をきちんとやらないと、ロックされっぱなしになると思うのでオプションも含めてlockfileコマンド、うまく使えたら良さげと思った。