
2026/07/14 3:18
Show HN: Strands エージェントを活用した AI パワーズビッグマウス・ビリーバス、BillAI Bass
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
本テキストは、「BillAI Bass Builder Guide」を提示しており、Strands Agents を雙方向ストリーミング(BidiAgent)技術と組み合わせ、Raspberry Pi 5 および Amazon Bedrock を通じた Amazon Nova 2 Sonic と連携させることで、Big Mouth Billy Bass の実装をリアルタイム AI ボイスアシスタントへと変換します。元々は
billy-b-assistant プロジェクトから再構築されており、このガイドによってユーザーは、ロボティクスまたは溶接の経験がない者であっても、リアルタイムオーディオストリームに直接動作を連動させた、頭部の左右回転、口パク同期、尻尾の振れ運動を含む機能を備えた動作可能な玩具を作成することができます。
このビルドには、約 240 ドルの総投資が必要であり、必要な部品には Raspberry Pi 5(8GB)、USB マイク、スピーカー、モータードライブ、および必要な電子機器が含まれます。主な設定ステップには、Nova モデルにおける雙方向ストリーミングのために AWS IAM パーミッションを設定すること、SD カードに Linux イメージをフラッシュすること、MX1508 ドライバ経由で Pi とモーター間の接続を溶接すること、オーディオコールバックおよびモーター制御を処理する「脳」ソフトウェアの組み立てが含まれます。
複雑な組立プロセスを簡素化するために、このガイドは段階的な実行、デバッグ、ハードウェア特定のために AI コーディングアシスタントの使用を推奨しています。動作した後には、魚の個性をカスタマイズし、ウェイクワード検出(例:"Hey Billy")を実装したり、systemd を通じた起動自動化を行ったり、AWS IoT Core X.509 証明書を用いてセキュリティを向上させるなどすることができます。その結果として、このプロジェクトは高度な技術的な前提条件なしにリアルタイム AI オーディオストリーミングおよびハードウェア統合の試行を行うための、アクセスしやすい低コストな教育プラットフォームとして機能します。
本文
BillAI Bass Builder ガイド:双方向ストリーミング機能付きの実装
Strands Agents の双方向ストリーミング機能と Amazon Nova 2 Sonic を搭載、ラズベリーパイ上で動作する「話せる魚」プロジェクトです。
🦈 プロジェクトの概要
- コンセプト: ビッグマウス・ビリー・バスをリアルタイムの音声アシスタントに変換する。
- 動作内容:
- ユーザーが話しかけると、魚も応答します。
- 頭を振り回し、口は自分の声に**リプシンク(唇の動き同期)**を行います。
- 尾をふって強調表現を加えます。
- 技術スタック:
- ハードウェア:ラズベリーパイ 5 (Strands Agents の BidiAgent を実行)
- AI モデル:Amazon Nova 2 Sonic (Amazon Bedrock上のリアルタイムオーディオ処理)
- 初心者向け: 経験不要。溶接やロボティクス知識も不要です。
- 基盤: 優れた
プロジェクトを Strands 上で再構築。billy-b-assistant
📦 リポジトリの構成要素
| ファイル名 | 内容と役割 |
|---|---|
| 本ガイドです。上から順に作業してください。 |
| 完成したスクリプト。話せる魚を起動します。 |
| モーターテスト用。配線検証のために必ず先に実行してください。 |
| ALSA 構成ファイル。USB ミックとスピーカーをデフォルトに設定します。 |
| 依存関係の固定版。 で正確な環境を作成してください。 |
| オプション(セキュリティ向上用)。X.509 証明書認証の構成テンプレートなど。 |
🤝 AI アシスタントとの共作方法
このプロジェクトは AI コードアシスタントと共同作成することを強く推奨します。Claude Code などを使っている場合、以下の指示を AI に与えて進めてください。
指示例: "私はこれを作ろうとしています。一つずつステップ带我ってください。次のステップを与える前に、私が確認することを待ってください。"
🤝 AI による構築支援のメリット
AI は以下の点であなたのパートナーになります:
- エラーの解決: 恐ろしい長さの Python Traceback でも、意味のある一行だけでエラーを特定してくれます。
- 画像の確認: モーターや配線の写真を撮って送信できます(例:
)。~/Downloads/IMG_1234.jpg - わからないことは尋ねる: 「X について何も知りません」と返ってきたら、AI は後退して解説してくれます。
- 事前確認: 溶接や切断など自信がないことを事前に聞いてくれます。
ガイドの隙間に出会った場合は AI に相談し、エラーメッセージがあれば乗り越えられます。
🛒 買い物リスト
| アイテム | 用途 | 参考価格 |
|---|---|---|
| ビッグマウス・ビリー・バス | 魚本体(現在販売中の"Gemmy"バージョン)。モダン版の 2 モーター構成を前提。 | ~$30 |
| ラズベリーパイ 5, 8GB | 脳。4GB でも動作します。 | ~$80 |
| 27W USB-C PD ポワーサプライ | 電源。スマホ充電器では不可(電力制限防止)。公式または RasTech GaN 推奨。 | ~$15 |
| ラズベリーパイ 5 アクティブクーラー | ヒートシンク&ファン。熱対策必須。 | ~$10 |
| MicroSD カード, 64GB | Pi のストレージ(Transcend など)。 | ~$12 |
| USB ミニスピーカー | ビリーの声(例:HONKYOB)。 | ~$15 |
| USB ネッキングマイク | ビリーの耳(例:CGS-M1)。 | ~$13 |
| MX1508 デュアル H ブリッジモータードライバー | Pi がモーターを安全にドライブするインターフェース。予備も有用。 | ~$9 |
| Dupont ジャンパーワイヤーキット (120pcs) | ドライバーボードと Pi を接続(実際は female-to-female 約 6 本使用)。 | ~$7 |
| ハンダ付けアイロンキット (60W) | 合計 10 の接合部を固定するために必要。 | ~$20 |
| フラットカッター | ワイヤーの切断と被覆剥離(例:KATA)。 | ~$9 |
| JST XH コネクタキット | オプション。整理整頓には有用ですが必須ではない。 | ~$9 |
⚠️ 別途必要なもの
- PC: SD カードをフォーマット・イメージ焼き込み用(Mac/Windows/Linux)。企業向けラップトップは USB ストレージブロックされる場合があるためホームマシンの使用推奨。
- SD アダプター: microSD を PC に接続するために。
- AWS アカウントと支払い手段: 個人利用用。IAM ユーザー作成時に必要。
- 工具・消耗品: テープ(電気用またはスコッチ)、小型プラスドライバー、家庭 WiFi。
✅ プレリクワイス(ビルド当日の前)
1. AWS / Amazon Bedrock のセットアップ(最小権限)
💸 AWS アカウントがない場合の対応
- 新規アカウントには通常 $100 クレジットが付与され、Bedrock はフリープランで使用可能です。
- 注意点: 音声チャットは安価(分あたり約 1 セント)ですが、クォータ制限や有効期限に注意してください。
⚠️ フリープランの重要な注意点
- クォータ制限: 新しいアカウントは初期クォータが「0」の場合あり。
エラーが出たら AWS サポートへ要望を出すか有料プランへのアップグレードが必要です。ValidationException: Operation not allowed - 有効期限: アカウントは 6 ヶ月後に自動終了します。常駐用途の場合は有料プランへ早期アップグレードしてください。
🔐 セキュリティの重要性
- AWS キーを Pi の平文ファイルとして保存します。
- デモ用や貸し出し用として放置された場合、キーが漏洩すると他人があなたのアカウントでチャットしたり(悪用)するリスクがあります。
🛠️ セットアップ手順
ステップ 1:モデルアクセスの有効化
- AWS コンソールのリージョンを
(バージニア州北部) に設定。us-east-1 - Amazon Bedrock → Model access で、**Nova 2 Sonic (
)**へのアクセス権限を取得。amazon.nova-2-sonic-v1:0
ステップ 2:専用 IAM ユーザーの作成
- IAM > Users > Create user を実行(例:
)。billy-bass - 「ユーザーに AWS マネジメントコンソールのアクセスを提供」は選択しない。 (ロボットはログインしません)。
ステップ 3:Nova Sonic のみ許可するポリシーの作成
- ユーザー > Permissions > Create inline policy で JSON を設定。
- 貼り付ける内容:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "BillyTalksToNovaSonicOnly", "Effect": "Allow", "Action": "bedrock:InvokeModelWithBidirectionalStream", "Resource": "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-2-sonic-v1:0" } ] } - ポリシー名を
と付け、保存。billy-nova-sonic-only
解説: これにより、S3 や EC2 などへのアクセスは禁止され、Nova Sonic のみの利用に制限されます。
ステップ 4:アクセスキーの作成
- Security credentials > Create access key を実行(Application running outside AWS)。
- Key ID と Secret Access Key は安全な場所にコピー。後で Pi にアップロードします。
ステップ 5:使用予算アラートの設定(推奨)
- Billing > Budgets で $10/月の予算を作成し、メールアラートを設定します。漏洩防止のためです。
2. コードペースト時の注意点
SSH でコードを貼り付ける際によくあるエラーと対策:
| エラータイプ | 原因 | 修復方法 |
|---|---|---|
| IndentationError | ペースト時に先行スペース(インデント)が余計に追加された | または再貼り付け |
| SyntaxError | カンマの欠落や「smart quotes」への文字化け | 手動で修正、または文書と照合 |
✅ コード実行前のチェック習慣
Python ファイルを作成後、実行する前に以下のコマンドを必ず確認してください:
python -m py_compile yourfile.py && echo OK
と表示されない場合はエラーあり。 エラーメッセージを AI に貼り付けて相談してください。OK
第 1 部 — 脳(ラズベリーパイの設定)
💡 戦略:先にソフトウェア構築
魚を開く前に、裸の Pi で音声アシスタントが動作するか確認することにより、デバッグを容易にします。
1.1 SD カードのフラッシュ
PC から Raspberry Pi Imager を使用してイメージを焼きます。
- Device: Raspberry Pi 5
- OS: Raspberry Pi OS Lite (64-bit) ⚠️
- "Trixie"ベースがPython 3.13(Nova Sonic 推奨)。
- 古い"Bookworm"は Python 3.11(非推奨)。
- 理由: "Lite"版は GUI と Wayfire パネルがないため、
が破損する問題を回避できます。~/.asoundrc
OS カスタマイズ設定(イメージ焼込み時の選択肢)
- ホスト名:
billy - ユーザー名/パスワード: 覚えましょう
- WiFi: ネットワーク名とパスワードを正確に入力
- Services: SSH を有効化
1.2 最初の起動
- MicroSD を Pi に挿入し、USB-C 電源を接続。
- 2-3 分待機(OS リサイズと WiFi 接続完了)。
- PC ターミナルで SSH 接続:
ssh yourusername@billy.local
注意点:
- 初回接続はリセットされる場合があります(設定完了のため)。1-2 分待ってから再接続。
と出る場合は、SSH キーの再確認が必要ですが、通常は期待される動作です。REMOTE HOST IDENTIFICATION HAS CHANGED
1.3 システムパッケージの更新
sudo apt update && sudo apt full-upgrade -y sudo apt install -y python3-dev portaudio19-dev swig git
: マイク/スピーカーアクセス用portaudio19-dev
: GPIO ライブラリビルド用(後続エラー防止のため)swig
1.4 オーディオデバイスの設定
USB メックとスピーカーを接続後、以下のコマンドでデバイスを確認・設定します。
arecord -l # マイクロフォンの一覧確認(カード名をメモ) aplay -l # スピーカーの一覧確認
⚠️ 重要な構成ルール
- カード名を使用し、番号は使用しないでください(再起動でシャッフルするため)。
- 設定ファイル (
) を作成:~/.asoundrcpcm.!default { type asym playback.pcm "plug:hw:UACDemoV10" # スピーカー名に書き換える capture.pcm "plug:hw:Device" # マイック名に書き換える } ctl.!default { type hw card UACDemoV10 # スピーカーのカード ID に変更 } - 保存後、内容を確認(
)。cat ~/.asoundrc - テスト録音:
arecord -d 3 -f S16_LE -r 16000 test.wav && aplay test.wav- 16000 Hz は Nova Sonic のレートに合わせるため意図的。
1.5 Python 環境と Strands のインストール
python3 -m venv ~/billy/.venv source ~/billy/.venv/bin/activate pip install "strands-agents[bidi,bidi-io]" gpiozero lgpio
- プロンプトに
が出るまで環境が有効であることを確認。(.venv) - SSH で接続し直した場合は、毎回
コマンドを実行する必要があります。source
1.6 AWS 資格情報の設定(平文ファイル化)
個人アカウントのキーは絶対に使わないでください。専用ユーザーのキーのみ使用します。環境変数より永続的なファイルを推奨します。
mkdir -p ~/.aws nano ~/.aws/credentials
貼り付ける内容:
[default] aws_access_key_id = YOUR_KEY_ID aws_secret_access_key = YOUR_SECRET [default] region = us-east-1
- ファイル権限を
に変更します。chmod 600
1.7 最初の会話テスト 🎉
nano ~/billy/billy.py
(以下のコードをコピー&ペースト)
import asyncio from strands.experimental.bidi import BidiAgent, BidiAudioIO from strands.experimental.bidi.models import BidiNovaSonicModel model = BidiNovaSonicModel( model_id="amazon.nova-2-sonic-v1:0", provider_config={ "audio": { "input_rate": 16000, "output_rate": 16000, "voice": "matthew", "channels": 1, "format": "pcm", } }, ) agent = BidiAgent( model=model, system_prompt=( "You are Billy, a wisecracking animatronic singing bass mounted on a " "wooden plaque. Keep responses short, punchy, and conversational." ), ) audio_io = BidiAudioIO() asyncio.run(agent.run(inputs=[audio_io.input()], outputs=[audio_io.output()]))
実行:
python -m py_compile ~/billy/billy.py && echo OK python ~/billy/billy.py
エラー対策:
: Bedrock 権限不足またはリージョン違い (AccessDeniedException確認)。us-east-1 : ステップ 1.6 の設定漏れ。no AWS credentials found : ASoundRC の不備(カード名確認)。OSError: Invalid sample rate- ビリーが自分を中断する:マイクをスピーカーから離し、音量を下げる(物理配置の問題)。
第 2 部 — 身体(魚を開く)
2.1 サルジー 101(開腹準備)
- バックプレートのネジをはずし、内部構造を確認。
- モーター: 2 つのモーター搭載タイプを想定(口用 1 つ、回転用兼用 1 つ)。
- 不要部品: オリジナルスピーカー、ピエゾブザー、コントロールボード、センサー類は外します。
2.2 切断とラベル付け
- ワイヤーと元の配線を解放し、「MOUTH(口)」、「BODY(体/回転)」「BUTTON(ボタン)」 とテープで即座にラベル付け。
- ボード自体は外しますが、ワイヤーの接続状態を明確にするためです。
第 3 部 — 脳と身体の接続
3.1 モータードライバーへの配線
Pi の GPIO ピンは直接モーター駆動できず、MX1508 ドライバーボードが必要です。
- 2 チャンネル(A, B)あり。
- 合計 10 箇所のハンダ接合が必要(溶接初心者でも 3 分の動画で完了します)。
3.2 ワイヤーの色マップと準備
以下に対応させるための配線図を厳守してください:
| ワイヤー色 | ドライバー端子 | 機能 |
|---|---|---|
| Yellow | + | 5V パワー |
| Black | − | グランド |
| White | IN1 | 口モーター(方向 A) |
| Grey | IN2 | 口モーター(方向 B) |
| Purple | IN3 | 体/回転モーター(A) |
| Blue | IN4 | 体/回転モーター(B) |
3.3 溶接と品質管理
- ワイヤーをピン穴に通し、先を曲げて保持。
- ハンダで固定後、ワイヤー自体も熱して再固定します(完全な密閉)。
- テスト: 強く引っ張ったり揺らしたりして、ハンダが剥がれないか確認。
3.4 Pi への接続 (GPIO ピン割り当て)
Pi の GPIO ヘッダー上のピン番号:
| ワイヤー | ピン番号 | 場所 |
|---|---|---|
| Yellow (+) | pin 4 | エッジ行(2 箇所目) |
| Black (−) | pin 6 | エッジ行(3 箇所目) |
| White (IN1) | pin 9 | 内側行(5 箇所目) |
| Grey (IN2) | pin 11 | 内側行(6 箇所目) |
| Blue (IN4) | pin 13 | 内側行(7 箇所目) |
| Purple (IN3) | pin 15 | 内側行(8 箇所目) |
パターン確認:
が外側、Yellow-Blackが内側。これで正しい。White-Grey-Blue-Purple
3.5 アクティブクーラーの取り付け
- サーマルパッドを外し、ヒートシンクとファンを Pi に固定。
- ファンケーブルを FAN ソケットに接続。
で温度確認(60°C 未満)。vcgencmd measure_temp
3.6 モーターテスト
sudo shutdown now # 起動後 SSH 接続し、venv を有効化 nano ~/billy/motors.py
(以下のコードをコピー&ペースト)
import time from gpiozero import OutputDevice, PWMOutputDevice mouth = PWMOutputDevice(17) head = OutputDevice(22) tail = OutputDevice(27) print("m = mouth (1s) h = head (1s) t = tail b = both q = quit") while True: cmd = input("> ").strip().lower() if cmd == "q": break elif cmd == "m": mouth.value = 1.0; time.sleep(1.0); mouth.value = 0 elif cmd == "h": head.on(); time.sleep(1.0); head.off() elif cmd == "t": tail.on(); time.sleep(0.3); tail.off() elif cmd == "b": head.on(); time.sleep(0.3) for _ in range(4): mouth.value = 1.0; time.sleep(0.25); mouth.value = 0; time.sleep(0.15) time.sleep(0.2); head.off() mouth.value = 0; head.off(); tail.off()
,m
,h
を入力し、それぞれモーターが動くか確認。t
トラブル対策:
- 動作しないものがある: ワイヤーとピン番号の対応が違う(ソフトウェア側で番号を書き換える)。
- 「頭」と「尾」が同じ物: 逆転している場合あり。両方動けば OK。
- 口が弱く、尾が強すぎる: モーター特性の問題。Pi ヘッダー上の口モーター用ワイヤーを交換することで改善(強い方向に合わせる)。
第 4 部 — 融合(話せる魚)
リプシンクさせるためのコード:
- 問題: Nova Sonic は速いため、データ到着時に口を開けても間に合わない。
- 解決: オーディオ再生のコールバック内で音量レベルを検知し、それに応じて口を動かす。頭は「話している間」だけ上げ、尾は強調部分で振る。
🚀 完成スクリプト (billy_final.py
)
billy_final.pyimport asyncio import math import time from array import array from gpiozero import OutputDevice, PWMOutputDevice from strands.experimental.bidi import BidiAgent, BidiAudioIO from strands.experimental.bidi.io.audio import _BidiAudioOutput from strands.experimental.bidi.models import BidiNovaSonicModel from strands.experimental.bidi.types.events import ( BidiInterruptionEvent, BidiResponseCompleteEvent, BidiResponseStartEvent, ) # モーター設定 mouth = PWMOutputDevice(17) head = OutputDevice(22) tail = OutputDevice(27) # 調整パラメータ(必要に応じて変更) MOUTH_OPEN = 0.04 # 口が開く音量閾値 EMPHASIS = 0.3 # 尾を振るための強調音量閾値 COOLDOWN = 1.2 # 強調振りの間隔 SILENCE = 1.5 # 話が終わってから頭が下がるまでの時間 class BillyBody(_BidiAudioOutput): """オーディオ再生と身体制御を統合したクラス""" def __init__(self, config): super().__init__(config) self.level = 0.0 self.last_loud = 0.0 self._tail_until = 0.0 async def __call__(self, event): await super().__call__(event) # 会話イベントが発生したら尾部振りを開始(または終了) if isinstance(event, (BidiResponseStartEvent, BidiResponseCompleteEvent, BidiInterruptionEvent)): self.flap() def flap(self, seconds=0.4): self._tail_until = time.monotonic() + seconds @property def tail_now(self): return time.monotonic() < self._tail_until # オーディオボリュームからレベルを算出 def _callback(self, in_data, frame_count, *args): data, flag = super()._callback(in_data, frame_count, *args) samples = array("h", data) if samples: self.level = math.sqrt(sum(s * s for s in samples) / len(samples)) / 32768.0 else: self.level = 0.0 return (data, flag) # AI モデル設定 model = BidiNovaSonicModel( model_id="amazon.nova-2-sonic-v1:0", provider_config={ "audio": { "input_rate": 16000, "output_rate": 16000, "voice": "matthew", "channels": 1, "format": "pcm", } }, ) agent = BidiAgent( model=model, system_prompt=( "You are Billy, a wisecracking animatronic singing bass on a wall plaque. RULE ONE: never say more than one short sentence at a time. 5 to 12 words, then stop and let the human talk. Fish puns are your love language - work them in shamelessly and often." ), ) audio_io = BidiAudioIO() body = BillyBody({}) # 本体的なループ処理 async def body_loop(): last_flap = 0.0 smoothed = 0.0 while True: now = time.monotonic() # 口の開閉制御(音量に合わせて滑らかに) smoothed = 0.6 * smoothed + 0.4 * body.level if smoothed > MOUTH_OPEN: mouth.value = min(1.0, 0.5 + smoothed * 3) body.last_loud = now else: mouth.value = 0 speaking = (now - body.last_loud) < SILENCE # 強調振り(音量ピーク時に) if body.level > EMPHASIS and now - last_flap > COOLDOWN: body.flap() last_flap = now # 頭と尾の制御 if speaking: head.on() tail.off() elif body.tail_now: head.off() tail.on() else: head.off() tail.off() await asyncio.sleep(0.05) async def main(): print("Billy is ALIVE... (Ctrl+C to stop)") try: await asyncio.gather( agent.run(inputs=[audio_io.input()], outputs=[body]), body_loop(), ) finally: mouth.value = 0 head.off() tail.off() asyncio.run(main())
実行:
python -m py_compile ~/billy/billy_final.py && echo OK python ~/billy/billy_final.py
動作確認ポイント:
- 応答時:頭が上がり、口が開いてリプシンクし、終了後 1.5 秒後に頭が下がる。
- 強調時:尾をふって動きが激しくなる。
- トラブル対応:
- 「頭が上がらない」:
の値を下げる(判定時間短縮)。SILENCE- 「尾が振らない」:
を下げる(閾値引き下げ)。EMPHASIS- 「口が休むときも動く」:
を上げる。MOUTH_OPEN- 「ビリーが長文になる」: システムプロンプトを厳格に。
🎨 発展とカスタマイズ
- 人格変更: システムプロンプトを書き換えて、海賊や IT ズーマンなどに設定可能。
- ツールの追加: Strands ドキュメントを見て、天気を聞くなどのツールを実装可能。
- ボタン実装: GPIO ピン(黄色/緑ワイヤー)を接続し、起動/停止ボタン化。
- ウェイクワード: Porcupine を使用し、「Hey Billy」で起動。
🔐 高度なセキュリティオプション:AWS IoT Core
現在の実装は平文アクセスキーを使用していますが、プロダクション環境ではX.509 証明書による認証が推奨されます(
iot-identity/ フォルダ参照)。
- 利点: 盗難された Pi でも IAM クレデンシャルは期限切れとなり、再発行で即座に無効化可能。
- セットアップ: CloudFormation テンプレートと IoT SETUP ワークフローを使用。
🧯 トラブルシューティング速查
| 症状 | 原因 | 修復方法 |
|---|---|---|
| ペースト時のスペース追加 | |
解決できない | WiFi パスワード错误或 mDNS 無効 | ルーターから IP 取得して SSH |
| Bedrock モデルアクセス未許可 | AWS コンソールで権限追加 |
| オディオ不発 | の欠落またはカード名错误 | で名前確認後再設定 |
| モーターが動かない | GPIO ピン配線错误 | でピンの交換テスト |
| Pi が過熱 | アクティブクーラー未設置 | 冷却システムをインストール |