はじめに
本稿では、Copilot の支援のみを活用し、0 から動作確認可能なコールセンター音声分析デモアプリを作成した経験をシェアします。
Azure サービスと OpenAI を組み合わせ、実用性を意識した最小構成のデモを短期間で構築しました。
本取り組みを通じて特に重要だと感じたポイントは、以下の点です。
- 要件定義の精密化(最重要)
🎯 アプリ概要
ニーズ
複数のコールセンター音声ファイル(WAV形式)から、以下を 自動抽出 したい:
- 主要インテント(解約、料金照会、クレームなど)
- 通話内容の要約
- 顧客の質問と対応内容
- オペレーターへのアドバイス
アプローチ
WAV ファイル
↓
Azure AI Speech(日本語文字起こし)
↓
Azure OpenAI(インテント抽出・分析)
↓
Streamlit(UI表示)
成果物
- Streamlit アプリ ← 完全動作
- 3つの結果ビュー ← UX設計済み
- 18個のユニットテスト ← すべてグリーン
- 詳細ドキュメント ← 実装ガイド付き
🏗️ 要件の精密化
AIとの対話プロセス
初期の要件指定は「曖昧さ」を含みがちです。実装前に 以下を明確化 しました:
❌ 曖昧な要件
「複数の音声ファイルを分析し、インテント抽出して結果を表示したい」
✅ 明確な要件に変換
ファイル入力:
• WAV 形式のみ対応
• 最大 200MB(1ファイル)
• 複数選択可能
• エラーハンドリング明記
処理パイプライン:
• speech.py で Azure Speech を呼び出し(ja-JP)
• llm.py で Azure OpenAI を呼び出し(GPT-4等)
• parser.py で LLM 出力を構造化パース
結果表示:
• セクション A: 各音声の要約(3カラム)
• セクション B: インテント一覧表(DataFrame)
• セクション C: 詳細分析(expander + 音声プレイヤー + 2カラム)
エラー処理:
• ファイル不正 → st.warning() で表示
• 音声認識失敗 → 「無音またはサポートされていない形式」
• LLM API エラー → ユーザーフレンドリーなメッセージ
技術スタック選定
| レイヤー | 選択技術 | 理由 |
|---|---|---|
| UI | Streamlit | 迅速なPrototyping、デプロイ容易 |
| 音声認識 | Azure AI Speech | 日本語精度、ストリーミング対応 |
| LLM | Azure OpenAI | 長文出力対応、日本語性能 |
| データ処理 | Pandas | テーブル表示が簡単 |
| テスト | pytest | モック対応、CI/CD 統合 |
ディレクトリ構造の決定
call-analytics/
├── main.py # Streamlit エントリ
├── models.py # データクラス
├── services/ # ビジネスロジック
│ ├── speech.py
│ ├── llm.py
│ └── parser.py
├── tests/ # ユニットテスト
│ ├── test_parser.py
│ └── test_prompt.py
├── requirements.txt
├── .env.example
└── README.md
判断基準:
-
services/で外部API呼び出しを集約 → テスト容易 -
models.pyでデータモデルを統一 → 型安全性 -
tests/でテストを分離 → 保守性
実装依頼
実行環境
Chat Editor > Agent > Claude Haiku 4.5
依頼内容(プロンプト)
あなたは熟練のフルスタックPythonエンジニアです。次の要件で、Streamlitベースのデモアプリを“新規リポジトリとして”実装してください。成果物は動作するコード一式、README、.env.example、単体テスト(pytest)を含みます。
# 1. ゴール(アプリの目的)
コールセンター音声(WAV)を複数まとめてアップロードし、各音声を
(1) Azure AI Speech で日本語文字起こし(ja-JP)
(2) Azure OpenAI で「主要インテント・要約・質問内容・回答内容・詳細・オペレーターへのアドバイス」を抽出
し、結果を一覧と詳細で表示する。
# 2. 技術スタック/制約
- 言語: Python
- UI: Streamlit
- 音声認識: Azure AI Speech SDK(azure-cognitiveservices-speech)
- LLM: Azure OpenAI(openai>=2.26.0 の AzureOpenAI クライアント)
- dotenvで環境変数を読み込む
- 1ファイルあたり最大200MB、拡張子.wavのみ、複数選択可
- ローカル起動: `streamlit run main.py` で http://localhost:8501 にアクセス(READMEにも明記)
- Secrets(API Key等)は絶対にコミットしない(.env / .env.example 運用)
環境変数名は以下を使用する(.env.exampleも同名で作る):
SPEECH_KEY
SPEECH_REGION
AZURE_OPENAI_ENDPOINT
AZURE_OPENAI_API_KEY
AZURE_OPENAI_DEPLOYMENT_NAME
AZURE_OPENAI_API_VERSION (省略時デフォルト: 2025-04-01-preview)
# 3. 画面要件(UI)
Streamlitで次の構成を実装する:
## 3.1 ヘッダー
- タイトル: 「📞 コールセンター音声 インテント抽出PoC」
- 説明文: 「複数のWAV形式の音声ファイルを一括でアップロードし、分析・比較できます。」
## 3.2 アップロード
- file_uploader:
- ラベル: 「音声ファイル(.wav)をアップロードしてください(複数選択可)」
- type=["wav"], accept_multiple_files=True
- 200MB制限を超えるファイルがあればエラー表示して除外(もしくは処理停止)
- 選択された件数を info 表示(例: 「3件のファイルが選択されています。」)
## 3.3 一括解析ボタン
- primaryボタン: 「一括解析を実行する」
- 押下で処理開始し、進捗バーとステータステキストを表示:
- 「処理中 (i/N): filename ...」
- 全完了時: 「すべてのファイルの解析が完了しました!」を success 表示
## 3.4 結果表示(3セクション)
A) 「📋 各音声の要約」
- 各ファイルごとに
- 要約
- 質問内容
- 回答内容
B) 「📊 インテント抽出結果一覧」
- DataFrameで表表示(columns: ファイル名, 主要インテント, 分析結果)
- hide_index=True, use_container_width=True
C) 「📄 各音声の詳細分析」
- 各ファイルを expander で表示:
- 見出し: 「📁 {ファイル名} (インテント: {主要インテント})」
- st.audio で音声プレイヤー
- 2カラム:
- 左: 「📝 文字起こし結果」全文
- 右: 「💡 分析結果」全文(LLMの生出力を表示)
# 4. 処理要件(パイプライン)
各アップロード音声に対し:
## 4.1 一時ファイル化
- tempfile.NamedTemporaryFile(delete=False, suffix=".wav") を使って保存
- 終了時に必ず削除(finallyでos.remove)
## 4.2 Azure AI Speechで文字起こし
- SpeechConfig(subscription=SPEECH_KEY, region=SPEECH_REGION)
- speech_recognition_language="ja-JP"
- SpeechRecognizer + start_continuous_recognition_async を使い、
recognizedイベントでテキストをlistにappendし、session_stopped/canceledで終了
- 認識できない/無音等の場合は分かるメッセージ(例: 「音声が認識できませんでした。または無音でした。」)
## 4.3 Azure OpenAIで分析
- AzureOpenAI(azure_endpoint=AZURE_OPENAI_ENDPOINT.rstrip("/"), api_key=AZURE_OPENAI_API_KEY, api_version=AZURE_OPENAI_API_VERSION)
- chat.completions.create を使い model=AZURE_OPENAI_DEPLOYMENT_NAME
- system: 「あなたはコールセンターの優秀なアナリスト。客観的かつ簡潔に分析」
- user: 以下の出力フォーマットで必ず返すよう指示(日本語):
- インテント: [解約 / 料金照会 / クレーム / 使い方への質問 など]
- 要約: [通話全体の簡単な要約]
- 質問内容: [顧客の質問や要望]
- 回答内容: [オペレーターの対応・回答内容(オペレーターの発言がない場合は「なし」)]
- 詳細: [顧客が具体的に何を求めているか]
- オペレーターへのアドバイス: [対応時の心構えや提案内容]
- max_completion_tokens は十分大きく(例: 16384)
- 例外時は分かるエラーメッセージ(OpenAI APIエラーなど)を結果に反映
## 4.4 解析結果のパース
- LLMの出力テキストから以下を抽出する関数を作る:
- main_intent
- summary
- question
- answer
- 行頭の「- インテント:」「- 要約:」「- 質問内容:」「- 回答内容:」でパース
- 取れない場合はフォールバック("不明" 等)
- 「詳細」「オペレーターへのアドバイス」は“全文表示”で見られるので、最低限は表/要約表示に不要(ただし将来拡張しやすい構造に)
# 5. データ構造
analysis_results は list[dict] で保持し、最低限以下キーを持つ:
- ファイル名
- 主要インテント
- 要約
- 質問内容
- 回答内容
- 分析結果全文
- 文字起こし
- 音声データ(uploaded_fileのバイナリ/StreamlitのUploadedFile)
# 6. リポジトリ構成(提案: 1ファイルでもOKだが、テストしやすく分割推奨)
例:
call-analytics/
main.py # Streamlitエントリ
services/
speech.py # speech_to_text
llm.py # extract_intent, prompt生成
parser.py # parse_analysis_result
models.py # dataclass等(任意)
tests/
test_parser.py
test_prompt.py
requirements.txt or pyproject.toml(どちらでも可。READMEに手順を書く)
※依存関係は最低限:
azure-cognitiveservices-speech>=1.48.2
openai>=2.26.0
pandas>=2.3.3
python-dotenv>=1.2.2
streamlit>=1.55.0
pytest(テスト用)
pytest-mock(任意)
# 7. 単体テスト要件(pytest)
- parse_analysis_result が正しく値を抜けること
- 正常ケース(全項目あり)
- 一部欠落ケース("不明"にフォールバック)
- prompt生成が指定フォーマットを含むこと
- llm呼び出し/speech呼び出しは外部I/Oなので、モック前提のテストにする(実APIに接続しない)
# 8. README要件
README.md に以下を必ず含める:
- プロジェクト概要(何ができるか)
- 前提(WAV/200MB/ローカルURL)
- セットアップ手順
- git clone
- .env作成(.env.exampleからコピー)
- pip install -r requirements.txt(またはuv/pip)
- streamlit run main.py
- 使い方(アップロード→一括解析→3つの結果セクションの見方)
- 注意事項(個人情報が含まれうる、取り扱い注意)
# 9. 受け入れ基準(Acceptance Criteria)
- 複数wavをアップロードして一括解析できる
- 進捗が表示され、完了メッセージが出る
- 「各音声の要約」「一覧表」「詳細(音声プレイヤー+2カラム)」が表示される
- .env.example と README が揃っている
- pytest がローカルで実行できる(外部I/Oはモック)
- secretsがリポジトリに含まれない
上記を満たす形で、必要なファイルを全て生成し、コードを実装してください。
📊 成果物の全体像
ファイル一覧
callcenter-audio-intent-analyzer/
├── main.py # 495行 Streamlit UI
├── models.py # 45行 dataclass
├── services/
│ ├── speech.py # 75行 Azure Speech
│ ├── llm.py # 70行 Azure OpenAI
│ └── parser.py # 65行 パース処理
├── tests/
│ ├── test_parser.py # 85行 7個テスト
│ └── test_prompt.py # 140行 11個テスト
├── requirements.txt # 7パッケージ
├── .env.example # 環境変数テンプレ
├── README.md # セットアップガイド
├── ARCHITECTURE.md # システム設計書
├── API_REFERENCE.md # API 仕様書
└── IMPLEMENTATION_GUIDE.md # 実装ガイド
コード行数: 約 950行(テスト含む)
ドキュメント: 1,500行以上
実現した機能
| 機能 | 実装状況 |
|---|---|
| 複数 WAV ファイル一括アップロード | ✅ 完成 |
| 200MB ファイルサイズ制限 | ✅ 完成 |
| 日本語文字起こし(Azure Speech) | ✅ 完成 |
| インテント抽出(Azure OpenAI) | ✅ 完成 |
| 3セクション結果表示 | ✅ 完成 |
| エラーハンドリング | ✅ 完成 |
| ユニットテスト(18個) | ✅ 完成 |
| ドキュメント完備 | ✅ 完成 |
💡 AI との協調が成功した理由
1. 要件の明確化
AI に「何をするのか」を正確に指示することが最重要。曖昧な要件は、曖昧なコードを産む。
(良い例)
- ファイル: .wav のみ、最大 200MB
- 処理: speech.py で ja-JP 文字起こし
- テスト: pytest で 18個のテストケース
- エラー: ユーザーフレンドリーなメッセージ
(悪い例)
- 「音声を分析するアプリを作ってほしい」
2. 段階的なアプローチ
一度にすべてを実装するのではなく、段階的に進めることで、修正が容易。
Phase 1: コアロジック(services/)
↓
Phase 2: UI(main.py)
↓
Phase 3: テスト(tests/)
↓
Phase 4: ドキュメント
↓
Phase 5: バグ修正
3. エラー駆動開発
エラーが出たら、そこから何かを学ぶ機会。AI に相談して、ドキュメントで学び、修正。
エラー出現
↓
原因究明(ドキュメント参照)
↓
AI による複数の修正案検討
↓
ベストプラクティスの選択と実装
↓
テストで検証
4. テスト駆動開発
テストを先に書くことで、実装の方向性が明確になり、バグが減った。
テスト設計(何をテストするか)
↓
実装(テストが成功するように)
↓
本番環境での検証