はじめに
IBM Bobは、タスクの途中でユーザーへの確認や選択を求める「フォローアップ質問」を投げかけることがあります。しかし他の作業をしているとチャット画面を見ていないことも多いです。気が付いたら、Bobが答えを待って佇んでいる
みたいな経験はないでしょうか。
Bob: 次の手順で進めますが、どちらにしますか?
A. ○○ B. ××
(10分後……あれ、Bobが止まってる?)
この記事では、バージョン2.0.2から実装された Lifecycle Hooks を使い、Bob がフォローアップ質問をしようとしたタイミングに音声で通知する仕組みを実装します。
この記事で分かること
- Lifecycle Hooks の仕組みと、フォローアップ質問の検知に適したフックの選び方
- Hooksの定義方法
- Windows環境での音声通知指定
検証環境
| 項目 | 内容 |
|---|---|
| OS | Windows 11 |
| IBM Bob | 2.0.3 |
| VOICEVOX CORE | 0.17.0(VOICEVOX利用の場合は必要) |
| Python | 3.10以降(VOICEVOX利用の場合は必要) |
Lifecycle Hooks とは
IBM Bob には、セッションの重要なタイミングでシェルコマンドを自動実行する「Lifecycle Hooks」機能があります。設定は settings.json の hooks キーに記述します。
5つのフックの特性
Lifecycle Hooks には5種類の実行タイミングがあります。
また PreToolUse / PostToolUse では、フック対象のツールを matcher で絞り込むことも可能です。
このタイミングに合わせてシェルコマンドを実行することができます。
| フック | 実行タイミング |
|---|---|
SessionStart |
セッション開始時に1回 |
UserPromptSubmit |
プロンプト送信のたびに |
PreToolUse |
対象ツール実行前 |
PostToolUse |
対象ツール完了後 |
Stop |
エージェント停止時 |
設定ファイルの場所
| スコープ | ファイルパス |
|---|---|
| グローバル(全ワークスペース共通) | %USERPROFILE%\.bob\settings\settings.json |
| ワークスペース(現在のプロジェクトのみ) | .bob\settings.json |
この記事では グローバル設定 に記載することで、どのプロジェクトでも音声通知が機能するようにします。
フォローアップ質問の検知
検知には、PreToolUseフックを使用します。
Bob がユーザーへ質問を投げかける際には、内部で ask_followup_question ツールが呼び出されます。
PreToolUse の matcher フィールドでは、正規表現で対象ツールを絞り込むことができます。
この記事ではフォローアップ質問を検知するために ask_followup_question を対象としています。
また、スキル実行時に利用許可を求められるケースも想定し、 サンプルとして use_skill も通知対象に含めています。複数ツールを列挙する際の参考にしてください。
どのタイミングで通知を行うかは、ご自身の許可設定などに合わせて適宜設定してください。
[ユーザーがプロンプトを送信]
↓
Bob が処理
↓
ask_followup_question を呼び出そうとする
↓
PreToolUse フックが発火 → 音声通知スクリプトを実行
↓
チャット画面に質問が表示される
フックの設定
%USERPROFILE%\.bob\settings\settings.json に以下を追加します(既存設定がある場合は hooks キーの部分を既存ファイルにマージしてください)。
notepad ~\.bob\settings\settings.json
Bob設定画面の 一般 > 設定ファイルを開く でも編集を開始できます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "^(use_skill|ask_followup_question)$",
"hooks": [
{
"type": "command",
"command": "cmd /c %USERPROFILE%\\.bob\\hooks\\notify-voice.bat \"ボブから質問があります\"",
"timeout": 15
}
]
}
]
}
}
この記事ではWindows環境での実行を想定していますが、MacOSの場合はcommandの箇所を次のように指定することで簡単に実現できます。
"command": "say 'ボブから質問があります'",
Windows環境での音声通知
MacOSではsayコマンドを使用することで簡単に音声出力ができますが、Windowsに同じ機能を持ったコマンドは存在しません。そのため、音声出力を個別に実装する必要があります。
2パターン提示しますので、ご自身の環境に合わせて選択してください。
パターン1: System.Speech
Windows 標準の .NET System.Speech を PowerShell から呼び出す方法です。こちらのほうが簡単に実装できます。
スクリプトの作成
%USERPROFILE%\.bob\hooks\notify-voice.bat を以下の内容で作成します。
New-Item -ItemType Directory -Path "$env:USERPROFILE\.bob\hooks" -Force
notepad ~\.bob\hooks\notify-voice.bat
@echo off
setlocal
set "TEXT=%~1"
powershell -Command "(New-Object Media.SoundPlayer 'C:\Windows\Media\Windows Notify.wav').PlaySync()"
powershell -Command "Add-Type -AssemblyName System.Speech; $s = New-Object System.Speech.Synthesis.SpeechSynthesizer; $s.Speak('%TEXT%')"
exit 0
第1引数に渡したテキストを読み上げます。
準備ができたら、コマンドプロンプト上でスクリプト単体を実行して確認します。
~\.bob\hooks\notify-voice.bat "ボブから質問があります"
「ボブから質問があります」という音声が再生されれば成功です。
パターン2: VOICEVOX
VOICEVOX CORE は HTTP サーバーを立てずに Python スクリプトから直接音声合成できるライブラリです。
VOICEVOX アプリを起動していなくても動作するため、常時起動の手間がないのが利点です。
VOICEVOX CORE のライセンスや利用規約については、公式ドキュメントをご確認ください。
VOICEVOX CORE は Windows だけでなく macOS(Intel / Apple Silicon)・Linux 向けの Downloader と Python wheel も提供されています(リリースページ)。macOS や Linux でも同様の手順で利用できます。
キャラクターを選ぶ
VOICEVOXでは、複数のキャラクターから声を選択できます。製品ページのサンプルから、あなたがイメージするBobに合ったキャラクターを予め決めておいてください。また、キャラクター毎に利用規約が存在するため、確認するようにしてください。
以下のサンプルでは VOICEVOX:東北ずん子 を利用させていただきました。
インストール
まず、インストールファイルを展開するためのフォルダを任意の場所に作成して下さい(以下、voicevoxフォルダと記載します)。
VOICEVOX CORE の最新リリース から次の2つのインストールファイルをダウンロードし、voicevoxフォルダに配置します。その後、次のコマンドでインストールします。
- Windows x64 用の Downloader(
download-windows-x64.exe) - 環境にあわせたPython Wheel(
voicevox_core-0.17.0-cp310-abi3-win_amd64.whl)
cd <voicevoxフォルダ>
./download-windows-x64.exe --exclude c-api
pip install voicevox_core-0.17.0-cp310-abi3-win_amd64.whl
スクリプトの作成
次の3つのスクリプトを作成して、各々実行します。
- キャラクターとライブラリファイルを特定するためのスクリプト(list-styles.py)
- 音声出力用スクリプト (notify-voice.py)
- 音声出力用バッチファイル (notify-voice.bat)
【キャラクター定義の確認】
まず選択したキャラクターを示すIDと、データが格納されたファイルを特定します。
list-styles.py を作成してください。その際、VOICEVOX_CORE_DIR は実際の環境にあわせて書き換えてください。
実行すると、キャラクターのIDと格納vvmのリストが出力されます。事前に選択したキャラクターのIDとvvmファイル名は、次のステップで使用します。
【VOICEVOX実行用pythonコードの作成、テスト】
このIDとvvmファイル名を控えておき、notify-voice.py を作成します。
コードの修正箇所は次の3つです。
| 宣言 | 値 |
|---|---|
| VOICEVOX_CORE_DIR | の部分を書き換え |
| SPEAKER_STYLE_ID | (選択したキャラクターのID) |
| VVM_FILE | (選択したキャラクターのvvmファイル名) |
ファイルの作成後に、指定したキャラクターで音声出力が実行できているか確認してください。
【Hooks用バッチファイルの作成、テスト】
最後に、Hooksから呼び出すためのバッチファイルを作成して、こちらも実行して音声出力を確認してください。
New-Item -ItemType Directory -Path "$env:USERPROFILE\.bob\hooks" -Force
# キャラクターの確認
notepad ~\.bob\hooks\list-styles.py
python ~\.bob\hooks\list-styles.py
# 出力スクリプトの作成と確認
notepad ~\.bob\hooks\notify-voice.py
python ~\.bob\hooks\notify-voice.py "ボブから、質問があるよ"
# 出力バッチファイルの作成と確認
notepad ~\.bob\hooks\notify-voice.bat
~\.bob\hooks\notify-voice.bat "ボブから、質問があるよ"
# list-styles.py — 利用可能なスタイル ID を一覧表示する
from pathlib import Path
from voicevox_core.blocking import Onnxruntime, OpenJtalk, Synthesizer, VoiceModelFile
VOICEVOX_CORE_DIR = Path("<voicevoxフォルダ>/voicevox_core") # 環境に合わせて変更
ONNXRUNTIME_LIB = VOICEVOX_CORE_DIR / "onnxruntime" / "lib"
OPEN_JTALK_DICT = VOICEVOX_CORE_DIR / "dict" / "open_jtalk_dic_utf_8-1.11"
VVM_DIR = VOICEVOX_CORE_DIR / "models" / "vvms"
ort = Onnxruntime.load_once(filename=str(ONNXRUNTIME_LIB / Onnxruntime.LIB_RECOMMENDED_VERSIONED_FILENAME))
synthesizer = Synthesizer(ort, OpenJtalk(str(OPEN_JTALK_DICT)))
for vvm_path in sorted(VVM_DIR.glob("*.vvm")):
with VoiceModelFile.open(str(vvm_path)) as model:
synthesizer.load_voice_model(model)
for speaker in model.metas:
for style in speaker.styles:
print(f"ID {style.id:4d} {speaker.name} — {style.name} [{vvm_path.name}]")
# notify-voice.py
# VOICEVOX CORE を直接使って音声通知する
# IBM Bob の PreToolUse フック(ask_followup_question)から呼ばれる想定
#
# 使い方:
# python notify-voice.py "読み上げテキスト" # 第1引数のテキストで再生
import sys
import tempfile
import os
from pathlib import Path
VOICEVOX_CORE_DIR = Path("<voicevoxフォルダ>/voicevox_core") # 環境に合わせて変更
OPEN_JTALK_DICT = VOICEVOX_CORE_DIR / "dict" / "open_jtalk_dic_utf_8-1.11"
ONNXRUNTIME_LIB = VOICEVOX_CORE_DIR / "onnxruntime" / "lib"
VVM_DIR = VOICEVOX_CORE_DIR / "models" / "vvms"
SPEAKER_STYLE_ID = 107
VVM_FILE = VVM_DIR / "21.vvm"
DEFAULT_TEXT = "ボブから質問があります"
def speak(text: str) -> None:
try:
from voicevox_core.blocking import Onnxruntime, OpenJtalk, Synthesizer, VoiceModelFile
ort_filename = ONNXRUNTIME_LIB / Onnxruntime.LIB_RECOMMENDED_VERSIONED_FILENAME
synthesizer = Synthesizer(
Onnxruntime.load_once(filename=str(ort_filename)),
OpenJtalk(str(OPEN_JTALK_DICT))
)
# VVM_FILE 指定あり → そのファイルだけロード(高速)
# VVM_FILE 指定なし → 全ファイルをロード(低速・スタイルID確認前の初期状態向け)
if VVM_FILE is not None:
targets = [VVM_FILE]
else:
targets = sorted(VVM_DIR.glob("*.vvm"))
if not targets:
raise FileNotFoundError(f"VVM ファイルが見つかりません: {VVM_DIR}")
for vvm_path in targets:
with VoiceModelFile.open(str(vvm_path)) as model:
synthesizer.load_voice_model(model)
wav_bytes = synthesizer.tts(text, SPEAKER_STYLE_ID)
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
tmp.write(wav_bytes)
tmp.close()
os.system(f'powershell -Command "(New-Object Media.SoundPlayer \'{tmp.name}\').PlaySync()"')
# MacOSの場合
# os.system(f'afplay "{tmp.name}"')
except Exception as e:
print(f"[notify-voice] VOICEVOX CORE エラー: {e}", file=sys.stderr)
raise
if __name__ == "__main__":
text = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_TEXT
speak(text)
@echo off
setlocal
set "TEXT=%~1"
powershell -Command "(New-Object Media.SoundPlayer 'C:\Windows\Media\Windows Notify.wav').PlaySync()"
python "%USERPROFILE%\.bob\hooks\notify-voice.py" "%TEXT%"
exit 0
まとめ
今回はBobで発生しやすいコミュニケーションの課題を、Lifecycle Hooksを使用して改善を試みました。
Hooksの機能を使うことで、これ以外にもAIの外部で必要な処理(ログ収集、監査、Slack通知、外部ツール連携 など)をあなたのタスクに組み込むことができます。応用することで、より円滑に仕事を進める可能性が見えてきますね。
まずは今回の通知機能を追加して、Bobと効率的かつフレンドリーに仕事をしてみてください ![]()
参考リンク