# 機能設計書 209-音声トランスクリプション

## 概要

本ドキュメントは、Chatwootの音声トランスクリプション機能について記載する。メッセージに添付された音声ファイルをOpenAI Whisper APIを使用してテキストに変換するEnterprise機能である。

### 本機能の処理概要

音声トランスクリプション機能は、メッセージに添付された音声ファイル（ボイスメッセージなど）をOpenAI Whisper APIを使用してテキストに変換（文字起こし）する機能である。Captain AI統合機能の一部として提供され、Enterprise版でのみ利用可能である。変換されたテキストは添付ファイルのメタデータに保存され、検索やAI処理に活用される。

**業務上の目的・背景**：カスタマーサポートにおいて、音声メッセージで問い合わせが来ることがある（特にWhatsAppやTelegramなどのチャネル）。音声メッセージは検索できず、エージェントが一つずつ聴く必要があるため、効率が悪い。本機能により音声をテキスト化することで、検索可能にし、AIによる自動処理も可能になる。

**機能の利用シーン**：
- WhatsAppやTelegramから音声メッセージが送られた場合の自動文字起こし
- 音声メッセージの内容をAIアシスタントが理解する場合
- 会話の全文検索で音声メッセージの内容も検索対象にする場合

**主要な処理内容**：
1. 添付ファイルから音声ファイルを取得
2. Captain統合機能の有効性と利用上限を確認
3. OpenAI Whisper APIに音声ファイルを送信
4. 変換されたテキストを添付ファイルのメタデータに保存
5. メッセージの更新イベントを発行
6. アカウントの利用回数をインクリメント
7. 高度な検索が有効な場合はメッセージを再インデックス

**関連システム・外部連携**：OpenAI Whisper API（whisper-1モデル）。Captain AI統合機能の一部として動作。

**権限による制御**：Enterprise版のみ。アカウントでcaptain_integration機能フラグが有効かつ、audio_transcriptions設定が有効な場合のみ利用可能。利用回数制限あり。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | 会話詳細画面 | 結果表示画面 | 文字起こし結果の表示 |

## 機能種別

AI処理 / データ変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| attachment | Attachment | Yes | 音声ファイル添付 | - |

### 入力データソース

- attachmentsテーブル（添付ファイル情報）
- messagesテーブル（メッセージ情報）
- accountsテーブル（利用上限情報）
- ActiveStorage（音声ファイル本体）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| transcribed_text | String | 文字起こしされたテキスト |

### 出力先

- attachments.meta['transcribed_text']（JSONB）
- Searchkickインデックス（高度な検索有効時）

## 処理フロー

### 処理シーケンス

```
1. サービス初期化
   └─ attachment, message, accountを設定
2. 前提条件チェック
   ├─ captain_integration機能フラグ
   ├─ audio_transcriptions設定
   └─ 利用上限残高
3. 既存の文字起こし確認
   └─ meta['transcribed_text']が既にあればそれを返す
4. 音声ファイル取得
   └─ 一時ファイルにダウンロード
5. Whisper API呼び出し
   └─ client.audio.transcribe
6. 結果保存
   └─ attachment.meta['transcribed_text']を更新
7. イベント発行
   └─ message.send_update_event
8. 利用回数更新
   └─ account.increment_response_usage
9. 再インデックス（オプション）
   └─ message.reindex
10. 一時ファイル削除
```

### フローチャート

```mermaid
flowchart TD
    A[サービス開始] --> B{captain_integration有効?}
    B -->|No| C[エラー返却]
    B -->|Yes| D{audio_transcriptions設定?}
    D -->|No| C
    D -->|Yes| E{利用上限残高あり?}
    E -->|No| F[制限超過エラー]
    E -->|Yes| G{既存の文字起こしあり?}
    G -->|Yes| H[既存テキスト返却]
    G -->|No| I[音声ファイル取得]
    I --> J[Whisper API呼び出し]
    J --> K[結果保存]
    K --> L[イベント発行]
    L --> M[利用回数更新]
    M --> N{高度な検索有効?}
    N -->|Yes| O[再インデックス]
    N -->|No| P[処理完了]
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | Enterprise限定 | Enterprise版でのみ利用可能 | 常時 |
| BR-002 | 機能フラグ必須 | captain_integration機能フラグが必要 | 常時 |
| BR-003 | 設定必須 | audio_transcriptions設定が必要 | 常時 |
| BR-004 | 利用上限 | responses利用上限内でのみ実行可能 | 常時 |
| BR-005 | キャッシュ | 既に文字起こし済みの場合は再実行しない | 常時 |

### 計算ロジック

利用可能判定ロジック：
```ruby
def can_transcribe?
  return false unless account.feature_enabled?('captain_integration')
  return false if account.audio_transcriptions.blank?
  account.usage_limits[:captain][:responses][:current_available].positive?
end
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メタデータ更新 | attachments | UPDATE | transcribed_textを保存 |
| 利用回数更新 | accounts | UPDATE | response_usageをインクリメント |

### テーブル別操作詳細

#### attachments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | meta | transcribed_text追加 | JSONBマージ |

#### accounts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | - | increment_response_usage | 内部カウンター |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 制限超過 | 利用上限を超えた場合 | エラーレスポンス返却 |
| - | メッセージなし | messageが存在しない場合 | エラーレスポンス返却 |
| - | API呼び出しエラー | Whisper API失敗 | 例外発生 |

### リトライ仕様

本機能に明示的なリトライ処理は実装されていない。

## トランザクション仕様

添付ファイルのメタデータ更新は単一レコードの操作のため、明示的なトランザクション制御は不要。

## パフォーマンス要件

- 音声ファイルの長さに応じてAPI応答時間が変動
- 一時ファイルはtmp/uploads/audio-transcriptionsに保存
- 処理完了後に一時ファイルを削除

## セキュリティ考慮事項

- Enterprise版限定機能
- OpenAI APIキーの安全な管理が必要
- 音声データが外部APIに送信される点に注意
- 一時ファイルの適切な削除

## 備考

- Llm::LegacyBaseOpenAiServiceを継承
- Whisper APIモデルはwhisper-1を使用
- temperature: 0.4で呼び出し
- 高度な検索（Searchkick）有効時はメッセージを再インデックス

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: サービス層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | audio_transcription_service.rb | `enterprise/app/services/messages/audio_transcription_service.rb` | メイン処理ロジック |

**読解のコツ**: Enterprise版機能のため、enterpriseディレクトリ内に配置。Llm::LegacyBaseOpenAiServiceを継承してOpenAIクライアントを使用。

**主要処理フロー**:
1. **8-13行目**: initializeでattachment, message, accountを設定
2. **15-21行目**: performメソッドで前提条件チェックと文字起こし実行
3. **25-31行目**: can_transcribe?で実行可否を判定
4. **33-45行目**: fetch_audio_fileで一時ファイルにダウンロード
5. **47-70行目**: transcribe_audioでWhisper API呼び出し
6. **82-92行目**: update_transcriptionで結果保存とイベント発行

### プログラム呼び出し階層図

```
Messages::AudioTranscriptionService#perform
    │
    ├─ can_transcribe?
    │      ├─ account.feature_enabled?('captain_integration')
    │      ├─ account.audio_transcriptions.blank?
    │      └─ account.usage_limits[:captain][:responses][:current_available]
    │
    ├─ transcribe_audio
    │      ├─ attachment.meta['transcribed_text'] (キャッシュ確認)
    │      │
    │      ├─ fetch_audio_file
    │      │      └─ attachment.file.blob.open
    │      │
    │      ├─ @client.audio.transcribe
    │      │      └─ Whisper API (whisper-1)
    │      │
    │      └─ update_transcription
    │             ├─ attachment.update!(meta: ...)
    │             ├─ message.send_update_event
    │             ├─ account.increment_response_usage
    │             └─ message.reindex (オプション)
    │
    └─ FileUtils.rm_f(temp_file_path)
```

### データフロー図

```
[入力]               [処理]                    [出力]

Attachment ─────────▶ AudioTranscriptionService ──▶ transcribed_text
    │                       │
    │                  ┌────┴────┐
    │                  ▼         ▼
    │           前提条件チェック   キャッシュ確認
    │                  │              │
    │                  └────┬─────────┘
    │                       ▼
    │                  音声ファイル取得
    │                       │
    │                       ▼
    │              Whisper API呼び出し
    │                       │
    │                       ▼
    │              テキスト取得
    │                       │
    └───────────────────────┘
                            │
                            ▼
                    attachment.meta更新
                            │
                            ▼
                    Searchkick再インデックス
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| audio_transcription_service.rb | `enterprise/app/services/messages/audio_transcription_service.rb` | サービス | 文字起こし処理 |
| legacy_base_open_ai_service.rb | `enterprise/app/services/llm/legacy_base_open_ai_service.rb` | 基底クラス | OpenAIクライアント管理 |
| account.rb | `app/models/account.rb` | モデル | 設定・利用上限管理 |
| attachment.rb | `app/models/attachment.rb` | モデル | 添付ファイル管理 |
| message.rb | `app/models/message.rb` | モデル | メッセージ管理 |
