音声で制御するローカルAIエージェントを構築する:音声認識(Speech-to-Text)とツール活用への道のり

Dev.to / 2026/4/11

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical Usage

要点

  • この記事では、マイク入力またはアップロードした音声を「話し言葉の指示」に変換し、その後、ファイルシステムの変更、コード生成、テキスト要約といったローカルのアクションを実行する「音声で制御するローカルAIエージェント」を紹介する。
  • システムは3段階のパイプライン――Speech-to-Text、LLMによる意図分類を構造化JSONに変換、そしてローカルPython関数を対応付けたツール実行――を採用し、速度と信頼性を両立している。
  • STT(音声認識)では、Groq経由でWhisper-large-v3を選択し、秒未満の書き起こしレイテンシを実現している。この点は、自然な音声対話体験において重要だと位置づけている。
  • 「脳」の部分では、厳密なスキーマ/構造化出力への準拠を優先するためにGPT-4o-miniと、低レイテンシの推論を重視するためにLlama-3.1-8b-instantの両方に対応しており、ローカル環境での応答性を高めている。
  • 重要な課題は、意図パースのために厳密なJSONスキーマを強制することだ。著者は、OpenAIの構造化出力要件に加えて、Pydanticの設定変更(余分なフィールドを許可しない)を組み合わせることで、この問題に対処している。

大規模言語モデル(LLM)の時代において、「AIと雑談する」ことと「自分のコンピュータを操作する」ことの間にあるギャップは急速に縮まっています。最近、ユーザーが自然な発話で自分のファイルシステムを管理し、コードを生成し、テキストを要約できる音声制御ローカルAIエージェントを作るプロジェクトに着手しました。

この記事では、アーキテクチャ、私が選んだ高性能モデル、そして道中で直面し解決した独自の課題を順を追って紹介します。

ビジョン

目的はシンプルでしたが野心的でもあります。音声入力(マイクまたはファイルアップロード経由)を受け取り、ユーザーの意図を理解し、適切なローカルツール(ファイル作成やPythonスクリプトの作成など)を実行できる専用エージェントを作ることです。

アーキテクチャ

このエージェントは、速度と信頼性のために設計された「Three-Step Pipeline(3段階パイプライン)」に基づいて構築されています:

  1. Speech-to-Text(STT): 生の音声を、クリーンで実行可能なテキストへ変換します。
  2. Intent Classification(意図分類): LLMを使ってテキストを「構造化されたJSONオブジェクト(Intent + Arguments)」に「解析」します。
  3. Tool Execution(ツール実行): 分類された意図を、ファイルシステムと連携するローカルのPython関数へマッピングします。

フロントエンドにはStreamlitを選びました。音声入力ウィジェットの迅速なプロトタイピングと、ユーザー向けのリアルタイムなステータス更新を可能にする、洗練されたダークテーマのUIを提供してくれました。

モデル選定:ボリュームより速度を優先

ローカルのハードウェアは、WhisperやLlama-3のような重いモデルではボトルネックになりがちです。そのため、エージェントが「瞬時」であると感じられるようにするため、私はAPI-first(APIを優先する)アプローチのハイブリッド構成を採用しました。

1. Speech-to-Text:OpenAI Whisper-large-v3(Groq経由)

私はGroqによるWhisper-large-v3の実装を選びました。今日利用可能なSTT APIの中でも、最速クラスだと言ってよいでしょう。音声の文字起こしは秒未満の時間で行われます。これは音声エージェントにとって極めて重要です。文字起こしで少しでも遅延があると、やり取りがもたついてぎこちなく感じられるからです。

2. 脳:GPT-4o-mini & Llama-3.1-8b

論理的な「脳」には、2つのプロバイダをサポートしました:

  • GPT-4o-mini: 「Structured Outputs(構造化出力)」と厳密なJSONスキーマに従う点で非常に優れています。
  • Llama-3.1-8b-instant: Groq上での実測の速さと効率性を重視し、結果として「考える」プロセスがほぼユーザーに見えない状態になります。

直面した課題(そして解決)

1. 「厳密スキーマ」の壁

最大のハードルの1つは、OpenAIで厳密なJSONスキーマを実装することでした。OpenAIの最新の構造化出力機能では、スキーマ内のすべてのオブジェクトで追加プロパティを明示的に禁止する必要があります(additionalProperties: false)。

  • 解決策: 私はPydanticのConfigDict(extra='forbid')を活用し、モデルを「generic(汎用)の“argument”ディクショナリ」から、明示的で型付けされたフィールドへと移行するよう再設計しました。これにより400番台のAPIエラーが解消され、ツール呼び出しが大幅に堅牢になりました。

2. マルチプロバイダのオーケストレーション

異なるAPI(OpenAIとGroq)を扱うということは、異なるSDKとパースロジックに対処する必要があることを意味します。OpenAIはPydantic向けの便利な.parse()メソッドをサポートしていますが、GroqではJSONモードを使った手動のフォールバックが必要です。

  • 解決策: 私は、これらの違いを抽象化する統一されたVoiceAgentクラスを構築し、UI上で1クリックするだけで「スピード(Groq)」と「標準(OpenAI)」を切り替えられるようにしました。

3. ローカルの安全性

AIにドライブへファイルを書き込ませることは、本質的にリスクを伴います。

  • 解決策: 私は厳密な「Clamping(クランプ)」ポリシーを実装しました。すべてのツール実行は専用のoutput/フォルダに制限され、エージェントが誤ってシステムファイルを上書きしたり、サンドボックスの外へ逃げたりできないようにしています。

結果

最初は単純な音声からテキストへのプロジェクトでしたが、より洗練されたローカルアシスタントへと進化しました。UIにStreamlit、処理の重さを担うのにGroq/OpenAI、構造化されたロジックにPydanticを組み合わせることで、エージェントは「フィボナッチ数列のPythonファイルを作成して」のような発話を、2秒未満で保存済みのスクリプトへ変換できるようになりました。

今後の作業

次のステップはFunction Calling(関数呼び出し)の機能を追加し、エージェントがWebを閲覧したりローカルのデータベースとやり取りしたりできるようにすることです。ローカルエージェントの未来は、「聞くこと」だけではありません。つまり「やること」にあります。

面白そうですか?リポジトリで完全なソースコードとドキュメントを確認できます。 REPO LINK