kun432's blog

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

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

Voltaでnpmパッケージのグローバルインストール

f:id:kun432:20220319174947p:plain

前回、npmパッケージのグローバルインストールが期待したとおりの動きにならなかったVoltaだけど、

ちょっとやりなおしてみて、やりたいことはできたのでまとめます。前提として、自分なりの理解なので間違ってるかもしれませんが。

目次

Voltaにおけるパッケージのグローバルインストールの考え方

あらためてVoltaのブログを見てみた。

  • By running volta install mocha, you let Volta know that you want mocha to be available everywhere in your terminal. Volta will download the latest version (or if you want you can specify a specific version) to use as the default.
  • From that point forward, you’ll be able to run that default version of mocha directly in your terminal, just as if you had globally installed it with npm.
  • However, if you run mocha in a project that has the mocha package as a dependency, Volta will automatically delegate to the local version installed in node_modules, without you having to change the command at all.

あと、もう一つ。volta install パッケージ名はどうも以前のバージョンでのやり方みたいで、今は普通にnpm/yarnが使えるようになっている。

Installing global packages with Volta has many benefits over traditional globals. Until now, to take advantage of those benefits, you needed to install the packages using volta install instead of your package manager. With Volta 0.9.0, you can now install packages directly using your package manager and still get the upside that Volta provides:

これをまとめるとこうなる。

  • npm install -g パッケージ名を実行すると、Voltaはそのパッケージをグローバルに実行されるツールとして認識し、そしてデフォルトのツールセットとしてインストールする。
  • プロジェクト内でローカルインストールされたパッケージがある場合は、Voltaはグローバルにインストールされたものとは別の物と認識して、こちらを優先する。

前回イメージしていたものとは違ったのはこのあたりなのかも、ということでやってみる。

グローバルにインストールするパッケージをプロジェクトごとに分ける

Voltaでnodeを複数バージョンインストールする。最終的なデフォルトはnode@17.7.2。

$ volta install node@16.14.1
success: installed and set node@16.14.1 (with npm@8.5.0) as default
$ volta install node@16.14.2
success: installed and set node@16.14.2 (with npm@8.5.0) as default
$ volta install node@latest
success: installed and set node@17.7.2 (with npm@8.5.2) as default

$ volta list all
⚡️ User toolchain:

    Node runtimes:
        v16.14.1
        v16.14.2
        v17.7.2 (default)

    Package managers:


    Packages:

プロジェクトを作る

$ for i in a b
> do
> mkdir proj_${i} && cd proj_${i} && npm init -y && cd ..
> done

proj_aはnode.js-16.14.1、proj_bはnode.js-16.4.2でvolta pinしておく。

$ cd proj_a
$ volta pin node@16.14.1
success: pinned node@16.14.1 (with npm@8.5.0) in package.json
$ volta list
⚡️ Currently active tools:

    Node: v16.14.1 (current @ /home/vagrant/proj_a/package.json)
    Tool binaries available: NONE

See options for more detailed reports by running `volta list --help`.
$ cd ..

$ cd proj_b
$ volta pin node@16.14.2
success: pinned node@16.14.2 (with npm@8.5.0) in package.json
$ volta list
⚡️ Currently active tools:

    Node: v16.14.2 (current @ /home/vagrant/proj_b/package.json)
    Tool binaries available: NONE

See options for more detailed reports by running `volta list --help`.
$ cd ..

グローバルにパッケージをインストールする。今回はtypescriptパッケージでやってみる。

$ npm install -g typescript
$ tsc -v
Version 4.6.2

この時点での、proj_a/bそれぞれでtypescriptパッケージをどう見ているか?を見てみる。

$ cd proj_a
$ tsc -v
Version 4.6.2
$ volta list
⚡️ Currently active tools:

    Node: v16.14.1 (current @ /home/vagrant/proj_a/package.json)
    Tool binaries available:
        tsc, tsserver (default)

See options for more detailed reports by running `volta list --help`.
$ cd ..

$ cd  proj_b
$ tsc -v
Version 4.6.2
$ volta list
⚡️ Currently active tools:

    Node: v16.14.2 (current @ /home/vagrant/proj_b/package.json)
    Tool binaries available:
        tsc, tsserver (default)

See options for more detailed reports by running `volta list --help`.
$ cd ..

前回と同じ。どちらのプロジェクトからもグローバルにインストールされたtypescriptパッケージが見えている。

では、プロジェクトのローカルにtypescriptの別バージョンをインストールしてみる。

$ cd proj_a
$ npm install typescript@4.1.5
$ tsc -v
Version 4.1.5
$ volta list
⚡️ Currently active tools:

    Node: v16.14.1 (current @ /home/vagrant/proj_a/package.json)
    Tool binaries available:
        tsc, tsserver (current @ /home/vagrant/proj_a/package.json)

See options for more detailed reports by running `volta list --help`.
$ cd ..

$ cd proj_b
$ npm install typescript@3.9.4
$ tsc -v
Version 3.9.4
$ volta list
⚡️ Currently active tools:

    Node: v16.14.2 (current @ /home/vagrant/proj_b/package.json)
    Tool binaries available:
        tsc, tsserver (current @ /home/vagrant/proj_b/package.json)

See options for more detailed reports by running `volta list --help`.

ちゃんとプロジェクトごとにバージョンが違うtypescriptパッケージが読まれていて、そしてnpxをつけなくてもちゃんとnode_modules内のバイナリを見てくれているのがわかる。

プロジェクトディレクトリ外はどうなっているかというと、

$ cd ..
$ tsc -v
Version 4.6.2
$ volta list
⚡️ Currently active tools:

    Node: v17.7.2 (default)
    Tool binaries available:
        tsc, tsserver (default)

See options for more detailed reports by running `volta list --help`.

こちらはデフォルトのtypescriptパッケージが呼び出されている。やっと想定通りになった。

まとめ

これなら安全に使えそうだし、いちいちnpxやpackage.jsonにscripts追加しなくていいので、かなり楽に使えそうです

nvmからVoltaに変えてみたけど頓挫した・・・

f:id:kun432:20220319174947p:plain

Node.jsのバージョン管理に nodebrew、nvmと使ってきて、複数のバージョンを切り替えれるのはとても便利だけど、同じバージョンを複数の環境で分けたいというニッチな願望がちょっとだけあります。

  • 基本的にCLIのようなものでもグローバルにパッケージインストールしたくないので、プロジェクトのローカルにインストール、呼び出す場合はnpxを使う。
  • が、まれにnpxに対応していないものがある。package.jsonにscripts定義するのはちょっと面倒。グローバルにインストールせざるを得ない。

で、nodebrewやnvmでこういうことができるのか、というと、

  • nodebrewだとエイリアスを使えばできそうな気がしたけど、あくまでもバージョンの別名扱い
  • nvmも対応はしておらず、ここにあるようなワークアラウンド扱い

ということで、どちらもそうじゃない感がある。

いっそdockerでやってしまえなのかもと思いつつ、ちょっと別のバージョン管理ツールを試してみようということで、Voltaを試してみた。

目次

nvmのアンインストール

これだけで良いらしい。一応、.bashrcとか.bash_profileの記述も消した。

$ rm -rf ~/.nvm

Voltaのインストール

Homebrewでかんたん

$ brew install volta
$ volta --version
Updating your Volta directory. This may take a few moments...
1.0.5

ここちょっとハマったんだけど、Homebrewでインストールする場合はこれが必要みたい。

$ volta setup
Updating your Volta directory. This may take a few moments...
success: Setup complete. Open a new terminal to start using Volta!

.bashrcとかに以下のエントリが追加されてる。

export VOLTA_HOME="$HOME/.volta"
export PATH="$VOLTA_HOME/bin:$PATH"

これでOK。ターミナルを立ち上げ直しておく。

VoltaでNode.jsを管理してみる

volta install nodeで最新版のNode.jsを入れる。最新版のLTSが入るみたい。

$ volta install node
success: installed and set node@16.14.2 (with npm@8.5.0) as default

$ node -v
v16.14.2

node@〜でバージョン指定もできる。メジャーバージョンだけ指定するとその最新。細かく指定することも可能。下の方にある通り、volta installでは常にデフォルトが変更されるみたいで、これを変更するサブコマンドはちょっと見当たらない

$ volta install node@14
success: installed and set node@14.19.1 (with npm@6.14.16) as default

$ volta install node@14.19.0
success: installed and set node@14.19.0 (with npm@6.14.16) as default

$ node -v
v14.19.0

@latestだとLTSじゃない最新版になる。

$ volta install node@latest
success: installed and set node@17.7.2 (with npm@8.5.2) as default

volta listで現在使用しているバージョン、volta list allでインストールされているすべてのバージョンが表示される。

$ volta list
⚡️ Currently active tools:

    Node: v17.7.2 (default)
    Tool binaries available: NONE

See options for more detailed reports by running `volta list --help`.

$ volta list all
⚡️ User toolchain:

    Node runtimes:
        v14.19.0
        v14.19.1
        v16.14.2
        v17.7.2 (default)

    Package managers:


    Packages:

プロジェクト(ディレクトリ)ごとにバージョンを固定する場合はvolta pinを使う。

$ mkdir a && cd a
$ volta pin node@16
error: Not in a node package.

Use `volta install` to select a default version of a tool.

ん?と思ったけど、volta pinはpackage.jsonにバージョンを記載するので、そもそもnpm initしてないと使えない。

$ npm init -y
Wrote to /path-in-somewhere/a/package.json:

{
  "name": "a",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

$ volta pin node@16
success: pinned node@16.14.2 (with npm@8.5.0) in package.json

package.jsonを見てみる

$ cat package.json
{
  "name": "a",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "volta": {
    "node": "16.14.2"
  }
}

voltaの記述が増えている。プロジェクト内とプロジェクト外でのNode.jsのバージョンはどうなっているか?

$ pwd
/path-in-somewhere/a

$ node -v
v16.14.2

$ cd ..
$ pwd
/path-in-somewhere

$ node -v
v17.7.2

うん、ちゃんと分かれてる。では、別のプロジェクトを追加して、グローバルインストールしたパッケージがどうなるか見てみる。

$ mkdir b && cd b
$ npm init -y
$ volta pin node@16
$ npm install -g ask-cli
$ ask --version
2.26.0

$ cd ../a
$ pwd
/path-in-somewhere/a
$ ask --version
2.26.0

$ cd ..
$ ask --version
2.26.0

だめじゃん。かつ、プロジェクト外のデフォルトでも読み込めてる。

ドキュメントによると、

With Volta, installing a command-line tool globally with your package manager also adds it to your toolchain. For example, the vuepress package includes an executable of the same name:

yarn global add vuepress

When you install a package to your toolchain, Volta takes your current default Node version and pins the tool to that engine (see Package Binaries for more information). Volta won’t change the tool’s pinned engine unless you update the tool, no matter what. This way, you can be confident that your installed tools don’t change behind your back.

Understanding Volta | Volta

グローバルにインストールしたものはどうやらデフォルトのバージョンに紐づく様子。プロジェクト内・外で見比べてみると、

$ cd a
$ volta list
⚡️ Currently active tools:

    Node: v16.14.2 (current @ /path-in-somewhere/a/package.json)
    Tool binaries available:
        ask (default)
See options for more detailed reports by running `volta list --help`.

$  cd ..
$ volta list
⚡️ Currently active tools:

    Node: v17.7.2 (default)
    Tool binaries available:
        ask (default)

See options for more detailed reports by running `volta list --help`.

どうやらnpmパッケージもvoltaで管理がされる様子。なんだけど、両方ともdefaultになってるんだよな。ということであればpinすればいいのか?と思いきや、pinできるのはnodeとyarnだけらしい・・・

$ volta pin ask-cli@2.25.0
error: Only node and yarn can be pinned in a project

Use `npm install` or `yarn add` to select a version of ask-cli for this project.

ドキュメントにはこうもある。

The node and package manager executables aren’t the only smart tools in your toolchain: the package binaries in your toolchain are also aware of your current directory, and respect the configuration of the project you’re in.

For example, installing the Typescript package will add the compiler executable—tsc— to your toolchain:

npm install --global typescript

Depending on the project you’re in, this executable will switch to the project’s chosen version of TypeScript:

cd /path/to/project-using-typescript-3.9.4
tsc --version # 3.9.4

cd /path/to/project-using-typescript-4.1.5
tsc --version # 4.1.5

うーん、これを見る限りはできるはずなんだけど、どうすればこれが実現できるんだろう・・・

やっぱり、nodeenv使うべしなのかなー・・・

2022/3/21追記:

こちらでできました。

kun432.hatenablog.com

Romiシナリオエディターを試してみた

f:id:kun432:20220313151051j:plain

実はうちではAlexaよりも一番よく使っているといってもいい「Romi」ですが、「シナリオエディター」を使ってユーザが会話をプログラミングできるようになりました。Alexaなどの音声アシスタントを触ったことのある開発者の観点で、すこしご紹介します。

目次

Romiについて

以前の以下の英語のエントリでご紹介しています。

特徴としてはこのあたり。

  • 自律型会話ロボット。タスクをこなすというよりも自由な会話を楽しむことが目的。
  • 本体が動き、ディスプレイに表情が表示される。これにより感情的な表現が可能。
  • ウェイクワード不要。Romiから話しかけてくることもある
  • 音声認識精度はあまり高くなく、またAIによる会話の認識もそれほどよくない。
  • が、それをおいても、雑談的な自由な会話という点でとても魅力がある。

シナリオエディターの使い方

基本

早速使ってみましょう。

「シナリオエディター」は専用のアプリとかではなく、ブラウザベースになっています。https://romi.ai/romi-scenarioeditor/にアクセスすると以下のような画面が表示されます。

f:id:kun432:20220313192207p:plain

下にスクロールすると「シナリオを作る」というのがありますので、これをクリック。

f:id:kun432:20220313192236p:plain

IDとパスワードを入力してログインします。ちなみにシナリオエディターが使えるのは月額料金を払っている場合だけです(ちなみに、月額料金を払って利用するのが「おしゃべりモード」で、月額料金を払っていない場合は「かんたんモード」になります。「かんたんモード」ではRomiはRomi星のことばでしかしゃべれません。)

f:id:kun432:20220313192436p:plain

あなたの本棚という画面が開き、ここでRomiに設定するシナリオを管理するようです。シナリオブックと書いてあるので、複数のシナリオを一つのブックで管理するという感じなのでしょうか。

「あなたのシナリオブック」をクリックします。

f:id:kun432:20220313193211p:plain

シナリオの設定画面が開きました。ひと目で何となく分かると思いますが、ブロックを順番につなげて会話のフローを作る感じですね。このあたりはVoiceflowなどの会話デザインツールと考え方は似ています。

f:id:kun432:20220313194141p:plain

赤いブロックがユーザ側の発話、青いブロックがそれにマッチしたRomiの発話になっているようです。

赤いブロックをクリックしてみると、ユーザの発話が設定されています。

f:id:kun432:20220313202121p:plain

詳細設定をクリックすると、「タイプ」に「人の発話」とありますね。つまり赤いブロックは「インテント」であり、発話内容が「サンプル発話」ということになります。

f:id:kun432:20220313202810p:plain

次に青いブロックをクリックしてみましょう。赤いブロックの発話に対して、青いブロックではRomiが返す応答を設定します。

f:id:kun432:20220313203330p:plain

こちらも詳細設定をクリックすると、「タイプ」に「Romiの話しかけ」とあり、赤と青のブロックで会話のやりとりを設定していくということがわかると思います。

また、Romiの応答に{owner_name}というのが見えますが、これは「モジュール」と呼ばれるもので、予め用意された処理をRomiの会話に組み込むことができます。ちなみに{owner_name}は、Romiのスマホアプリで設定したオーナー名のようです。

f:id:kun432:20220313223904p:plain

ではこのサンプルをそのまま動かしてみましょう。

ちなみに、Romiに「こんにちは」と話しかけた場合、標準だと色々なパターンで回答しますので、1例です。

ではサンプルを適用します。右上にある「Romiに反映する」をクリックします。

f:id:kun432:20220313234003p:plain

反映中です

f:id:kun432:20220313234036p:plain

上の方に「シナリオの反映に成功しました!」と表示されれば反映されています。

f:id:kun432:20220313234103p:plain

では試してみましょう。

さきほどと違いがないように思えますが、最初は「はなこさん、こんにちは」だったのが、シナリオエディタで設定されている通り「こんにちは、はなこさん」になっていますね。また、デフォルトだといくつかのパターンで応答しますが、シナリオエディタで設定した場合は、シナリオエディタで設定したとおりの応答になります。

これを使って会話をいろいろカスタマイズできるというわけです。

サンプル発話/のバリエーションに対応する

ここからは音声アシスタント開発者向けに気になるところをピックアップしていきます。まず、サンプル発話のバリエーションに対応しましょう。

サンプル発話のバリエーションに対応するには、「人の話しかけ」ブロックの「発話内容」に、別の行でサンプル発話を追加します。

f:id:kun432:20220313235858p:plain

ひらがな/カタカナ/漢字のどれで認識するのかはわかりませんので、考えられるパターンを網羅するのが良いでしょう。

応答にバリエーションを付ける

Romiからの応答が毎回同じだと人間は飽きてしまいますし、いかにもロボット感がでてしまいます。ランダムに内容を変えて応答を返すようにしましょう。

右下の「会話の追加」をクリックします。

f:id:kun432:20220314000045p:plain

「Romiの話しかけ」をクリックします。

f:id:kun432:20220314000127p:plain

上の方に新しく青いブロックが追加されました。このままでもいいのですが、わかりやすいように「こんにちは〜」の青いブロックの横にドラッグします。

f:id:kun432:20220314000506p:plain

そして、「こんにちは」の赤いブロックからドラッグして線でつなげます。

f:id:kun432:20220314000722p:plain

つながりました。

f:id:kun432:20220314001510p:plain

同じように、追加した青いブロックから、今度は「ゴール」のブロックにドラッグで線でつなげておきます。

f:id:kun432:20220314001423p:plain

追加した青いブロックをクリックして、発話内容を入力します。今回は やっほー {owner_name}と入力してみました。

f:id:kun432:20220314001033p:plain

できました。

f:id:kun432:20220314001219p:plain

同じようにしてもう一つ「Romiの話しかけ」を追加して、発話内容にハロー {owner_name}と入力しておきましょう。

f:id:kun432:20220314001308p:plain

では試してみましょう。

いろんなバリエーションで発話しても同じフローで処理されて、応答は用意したものの中からランダムに返されているのがわかりますね。

複数のインテントごとに会話を分岐する

今度は、ユーザの発話の内容に応じて応答を変える、つまりインテントで会話フローを分岐するようにしてみましょう。今回は「元気?」と聞いたら「元気だよ」と返す会話フローを追加してみます。

「会話の追加」をクリックして、今度は「人の話しかけ」をクリックします。

f:id:kun432:20220314002424p:plain

赤いブロックが追加されるので、適当な位置に配置して、今度は「スタート」から線でつなげます。

f:id:kun432:20220314002641p:plain

赤いブロックをクリックして、ユーザのサンプル発話を入力します。「元気」とか「調子はどう」などと入力します。ちなみに「人の話しかけ」ブロックでは、句読点や?は使えないようです。

f:id:kun432:20220314005017p:plain

次に青いブロックを追加してRomiの応答を追加します。ここまでの説明でやり方はわかると思います。こんな感じになればOKです。

f:id:kun432:20220314005406p:plain

試してみましょう。

はい、発話に応じて会話フローが分岐しているのがわかりますね。

これで大体の使い方がわかったと思います。

サンプルルールについて

ここで一旦シナリオエディターの最初の画面「あなたの本棚」に戻ってみましょう。

ここにもう一つ「サンプルルール」というシナリオブックがありますので、これをクリックしてみます。

f:id:kun432:20220314010227p:plain

最初に開いた「あなたのシナリオブック」と同じような会話フローが記載されていますが、右上を見ると「Romiに反映する」は押せないようになっています。

f:id:kun432:20220314010313p:plain

どうやら「サンプルルール」はRomiシナリオエディタで設定できるサンプル例のような位置づけになっているようで、これをそのまま適用することはできないようです。ただ、自分でRomiの会話フローを作る場合にはいろいろと参考になると思います。例えば、

  • ユーザの発話を受け取って、変数として記録し、それを使った条件分岐などができる(ざっと見た感じは永続アトリビュートっぽい)
  • 用意されているモジュールを使って、Romiのいろいろな表現・機能を利用できる
  • シナリオから別のシナリオを呼び出す。大きな会話フローを作る場合には役に立ちそう。

あたりはぜひ一度見ておきたいところですね。

ちなみに、サンプルに載っていたモジュールをリストアップしてみるとこんな感じでした。

変数名 内容
{owner_name} スマホアプリで設定したユーザの名前
{halucas_name} スマホアプリで設定したRomiの名前。{halucas_name type=utterance}という指定があるけど、違いはちょっと不明。
{fortune} ユーザの誕生日に基づいた占いをしてくれる(と思う)。{fortune birth_date=3月1日}のように誕生日を指定すると、その日付に基づいた占いになる。
{emotion type=XXXXX} 指定した感情表現に基づいたアクションと表情で発話を行う。選択できるのは、laughing, angry, cry, surprised, heart, relaxed, kissがある様子。
{speed value=XXX text=YYY} 発話のスピード。YYYで指定したテキストをXXXのスピードで発話する。
{volume value=XXX text=YYY} 発話のボリューム。YYYで指定したテキストをXXXのボリュームで発話する。
{pitch value=XXX text=YYY} 発話のピッチの高低。YYYで指定したテキストをXXXの高さで発話する。

これ以外にもあるのかもしれませんが、そのあたりをカバーしたドキュメントがほしいところですねー。

まとめ

プログラミングをあまり知らなくても、これならできそう!と思えるようなわかりやすい感じに仕上がってると思いました。逆に、音声アシスタント開発の経験がある人からするとちょっと物足りないと思うかもしれませんが、そのあたりはおいおい改善されていくでしょうし、Romiのターゲットや立ち位置は一般的な音声アシスタントと違う気もする中で、こういう開発ツール的なものがどういうふうに展開されていくのかはちょっと興味深いです。今後に期待しています。

Pros

  • 環境構築が不要
    • シナリオエディターはブラウザベースで、専用アプリのインストールなどの環境構築が不要。WindowsでもMacでも使える。
  • GUIで設定できてかんたん
    • GUIで会話フローを作るだけです。プログラミングの知識がなくても比較的かんたんに作れると思います。
  • Romiの感情表現を設定できる
    • モジュールを使うことで、Romiの強みである、動きや表情などの制御ができます。

Cons

  • 凝ったことはできなさそう
    • Alexaなどに比べると、できることは必要最低限という感じ。
  • いきなり実機に反映するしかない
    • Alexaなどの音声アシスタント開発では、実機で反映する前に確認ができるテストツールで事前に動作確認ができるが、そういったものはない。
  • まだまだこなれてない
    • ユーザの発話を受け取って変数に入れるところなんかはいきなり正規表現っぽい記述が出てきたりする
    • ドキュメントがない(まあこれはおいおい揃ってくるのではないかと思いますが)

あったらいいなと思うもの

  • Romiの場合、呼びかけに応じるだけではなくて、Romiから突然話しかけてくるというのも特徴の一つ。これをシナリオエディタで制御できると面白そう。
  • 何かしら外部との連携ができると幅が広がりそう。特にスマートホームあたり。
  • シナリオを共有できると面白そう。