# 機能設計書 94-タスク関連機能

## 概要

本ドキュメントは、Chatwoot の Captain AI 機能におけるタスク関連機能（返信提案・要約・ラベル提案等）の設計を記載する。

### 本機能の処理概要

タスク関連機能は、AIを活用してエージェントの業務を支援する各種タスクを提供する機能である。会話の要約生成、返信提案、ラベル提案、文章のリライト（トーン変更・スペル修正）、フォローアップ対話などの機能を提供する。

**業務上の目的・背景**：カスタマーサポートエージェントは、多数の問い合わせに効率的に対応する必要がある。AIによる返信提案や会話要約により、エージェントの作業負荷を軽減し、対応品質と速度を向上させることができる。また、ラベル提案により会話の分類を自動化し、データ分析の精度を向上させる。

**機能の利用シーン**：
- 長い会話履歴を要約して素早く状況を把握する
- AIの返信提案をベースに顧客への回答を作成する
- 会話の内容に基づいて適切なラベルを提案してもらう
- エージェントが書いた返信文のトーンを調整する
- スペル・文法のミスを自動修正する

**主要な処理内容**：
1. 会話要約（summarize）- 会話履歴をAIで要約
2. 返信提案（reply_suggestion）- AIによる返信案の生成
3. ラベル提案（label_suggestion）- 会話内容に基づくラベル推奨
4. リライト（rewrite）- トーン変更、スペル修正、文章改善
5. フォローアップ（follow_up）- AI回答の追加調整

**関連システム・外部連携**：
- OpenAI API（GPT-4o-mini）
- Redis（ラベル提案のキャッシュ）

**権限による制御**：Captain::TasksPolicy により、すべてのアクションはログインユーザーに許可される。captain_tasks機能フラグが有効である必要がある。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | 会話詳細画面 | 主画面 | 各タスク機能の呼び出し |
| 88 | Captain設定画面 | 設定画面 | Captain機能の有効化設定 |

## 機能種別

AI処理 / テキスト生成 / キャッシュ活用

## 入力仕様

### 入力パラメータ（各アクション別）

#### summarize（会話要約）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| conversation_display_id | Integer | Yes | 会話のdisplay_id | 必須 |

#### reply_suggestion（返信提案）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| conversation_display_id | Integer | Yes | 会話のdisplay_id | 必須 |

#### label_suggestion（ラベル提案）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| conversation_display_id | Integer | Yes | 会話のdisplay_id | 必須 |

#### rewrite（リライト）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| content | String | Yes | リライト対象のテキスト | 必須 |
| operation | String | Yes | 操作種別 | fix_spelling_grammar/improve/casual/professional/friendly/confident/straightforward |
| conversation_display_id | Integer | No | 会話のdisplay_id | improve操作時に使用 |

#### follow_up（フォローアップ）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| message | String | Yes | ユーザーからの追加指示 | 必須 |
| follow_up_context | Object | Yes | 前回のコンテキスト | event_name/original_context/last_response必須 |
| conversation_display_id | Integer | No | 会話のdisplay_id | - |

### 入力データソース

- 画面入力（API経由）
- 会話データ（Conversationモデル）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message | String | AIが生成したテキスト |
| follow_up_context | Object | フォローアップ用のコンテキスト（該当する場合） |
| error | String | エラーメッセージ（エラー時） |

### 出力先

API レスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ TasksControllerがリクエストを受信
2. 認可チェック
   └─ Captain::TasksPolicyによる権限確認
3. 機能・API設定確認
   ├─ captain_tasks機能フラグ確認
   └─ OpenAI APIキー確認
4. 各タスクサービス実行
   ├─ SummaryService
   ├─ ReplySuggestionService
   ├─ LabelSuggestionService
   ├─ RewriteService
   └─ FollowUpService
5. OpenAI API呼び出し
   └─ GPT-4o-miniモデルでテキスト生成
6. レスポンス整形・返却
   └─ follow_up_contextの付与（該当する場合）
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{認可チェック}
    B -->|失敗| C[403エラー]
    B -->|成功| D{機能フラグ確認}
    D -->|無効| E[403エラー: captain.disabled]
    D -->|有効| F{APIキー確認}
    F -->|なし| G[401エラー: api_key_missing]
    F -->|あり| H{アクション判定}
    H -->|summarize| I[SummaryService]
    H -->|reply_suggestion| J[ReplySuggestionService]
    H -->|label_suggestion| K[LabelSuggestionService]
    H -->|rewrite| L[RewriteService]
    H -->|follow_up| M[FollowUpService]
    I --> N[OpenAI API呼び出し]
    J --> N
    K --> O{キャッシュ確認}
    O -->|ヒット| P[キャッシュ返却]
    O -->|ミス| N
    L --> N
    M --> N
    N --> Q[レスポンス整形]
    P --> Q
    Q --> R[follow_up_context付与]
    R --> S[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-94-01 | 機能フラグ必須 | captain_tasks機能フラグが有効である必要 | 全アクション |
| BR-94-02 | APIキー必須 | OpenAI APIキーが設定されている必要 | 全アクション |
| BR-94-03 | トークン制限 | 会話コンテキストは400,000文字以下 | 全アクション |
| BR-94-04 | ラベル提案キャッシュ | 同一会話の結果はキャッシュ（last_activity_at基準） | label_suggestion |
| BR-94-05 | ラベル提案条件 | incoming>=3件、total<=100件、last_activity条件 | label_suggestion |
| BR-94-06 | リライト操作種別 | 指定された操作のみ許可 | rewrite |
| BR-94-07 | フォローアップコンテキスト | 必須キーが揃っている必要 | follow_up |

### 計算ロジック

トークン制限計算：
```
TOKEN_LIMIT = 400,000文字
# GPT-4o-miniは128,000トークンサポート
# 1トークン ≒ 4文字として算出
```

ラベル提案キャッシュキー：
```
captain_label_suggestion_{conversation_id}_{last_activity_at_unix}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 全アクション | conversations | SELECT | 会話データ取得 |
| 全アクション | messages | SELECT | メッセージ取得 |
| label_suggestion | labels | SELECT | ラベル一覧取得 |
| reply_suggestion | users | SELECT | エージェント情報取得 |
| reply_suggestion | inboxes | SELECT | チャネル情報取得 |

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

このAPIはデータの参照のみを行い、データベースへの書き込みは発生しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | Unauthorized | APIキーが未設定 | OpenAI APIキーを設定 |
| 403 | Forbidden | 機能フラグ無効 | captain_tasks機能を有効化 |
| 400 | Bad Request | フォローアップコンテキスト不正 | 正しいコンテキストを指定 |
| 422 | Unprocessable Entity | 無効なリライト操作 | 許可された操作を指定 |
| 500 | Internal Server Error | OpenAI API エラー | APIステータス確認、リトライ |

### リトライ仕様

OpenAI API呼び出しでエラーが発生した場合、自動リトライは行わない。エラーメッセージをクライアントに返却し、ユーザーによる再試行を促す。

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

データベースへの書き込みは発生しないため、トランザクション管理は不要。

## パフォーマンス要件

- ラベル提案はRedisキャッシュを活用してAPIコール削減
- 会話メッセージは400,000文字制限で切り詰め
- OpenAI API応答時間に依存（通常2-5秒）

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

- Captain::TasksPolicy による認可
- OpenAI APIキーはInstallationConfigまたはIntegration Hookで管理
- 会話内容がOpenAI APIに送信される点をユーザーに通知必要

## 備考

APIキーはInstallationConfig（CAPTAIN_OPEN_AI_API_KEY）またはOpenAI Integration Hookから取得される。Enterprise版では追加のチェックが行われる。

---

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

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

### 推奨読解順序

#### Step 1: ベースクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | base_task_service.rb | `lib/captain/base_task_service.rb` | 全タスクサービスの基底クラス |

**読解のコツ**:
- **8-9行目**: TOKEN_LIMIT, GPT_MODELの定数定義
- **38-56行目**: make_api_call メソッドでAPIコール前のチェックと実行
- **58-74行目**: execute_ruby_llm_request でRubyLLM経由のAPI呼び出し
- **134-140行目**: captain_tasks_enabled?, api_key_configured? のチェック
- **154-156行目**: prompt_from_file でプロンプトテンプレート読み込み

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | tasks_controller.rb | `enterprise/app/controllers/api/v1/accounts/captain/tasks_controller.rb` | APIエンドポイントの実装 |

**主要処理フロー**:
1. **2行目**: before_action で認可チェック
2. **4-12行目**: rewrite アクション - RewriteService呼び出し
3. **15-22行目**: summarize アクション - SummaryService呼び出し
4. **24-32行目**: reply_suggestion アクション - ReplySuggestionService呼び出し
5. **34-41行目**: label_suggestion アクション - LabelSuggestionService呼び出し
6. **43-52行目**: follow_up アクション - FollowUpService呼び出し
7. **56-66行目**: render_result でレスポンス整形

#### Step 3: 各タスクサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | summary_service.rb | `lib/captain/summary_service.rb` | 会話要約サービス |
| 3-2 | reply_suggestion_service.rb | `lib/captain/reply_suggestion_service.rb` | 返信提案サービス |
| 3-3 | label_suggestion_service.rb | `lib/captain/label_suggestion_service.rb` | ラベル提案サービス |
| 3-4 | rewrite_service.rb | `lib/captain/rewrite_service.rb` | リライトサービス |
| 3-5 | follow_up_service.rb | `lib/captain/follow_up_service.rb` | フォローアップサービス |

**主要処理フロー（LabelSuggestionService）**:
- **5-7行目**: キャッシュ確認
- **14-20行目**: API呼び出し
- **27-28行目**: キャッシュ保存
- **58-67行目**: labels_with_messages でプロンプト組み立て

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

```
Api::V1::Accounts::Captain::TasksController
    │
    ├─ before_action :check_authorization
    │      └─ Captain::TasksPolicy (全アクションtrue)
    │
    ├─ rewrite
    │      └─ Captain::RewriteService.new(...).perform
    │             ├─ fix_spelling_grammar / improve / tone操作
    │             └─ make_api_call (BaseTaskService)
    │
    ├─ summarize
    │      └─ Captain::SummaryService.new(...).perform
    │             └─ make_api_call (BaseTaskService)
    │
    ├─ reply_suggestion
    │      └─ Captain::ReplySuggestionService.new(...).perform
    │             └─ make_api_call (BaseTaskService)
    │
    ├─ label_suggestion
    │      └─ Captain::LabelSuggestionService.new(...).perform
    │             ├─ read_from_cache
    │             ├─ make_api_call (キャッシュミス時)
    │             └─ write_to_cache
    │
    └─ follow_up
           └─ Captain::FollowUpService.new(...).perform
                  └─ make_api_call (BaseTaskService)
                         │
                         └─ Llm::Config.with_api_key
                                └─ context.chat(model: GPT_MODEL)
                                       └─ OpenAI API
```

### データフロー図

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

conversation_display_id ──┐
                          ├──▶ TasksController
content (rewrite) ────────┤           │
                          │           ▼
operation (rewrite) ──────┤    各種TaskService
                          │           │
follow_up_context ────────┘           ├──▶ conversation.to_llm_text
                                      │           │
                                      │           ▼
                                      │    prompt_from_file (Liquidテンプレート)
                                      │           │
                                      │           ▼
                                      └──▶ Llm::Config.with_api_key
                                                  │
                                                  ▼
                                           OpenAI API
                                                  │
                                                  ▼
                                           {message, follow_up_context}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| tasks_controller.rb | `enterprise/app/controllers/api/v1/accounts/captain/tasks_controller.rb` | コントローラー | APIエンドポイント |
| tasks_policy.rb | `app/policies/captain/tasks_policy.rb` | ポリシー | 認可制御 |
| base_task_service.rb | `lib/captain/base_task_service.rb` | サービス | タスク基底クラス |
| summary_service.rb | `lib/captain/summary_service.rb` | サービス | 会話要約 |
| reply_suggestion_service.rb | `lib/captain/reply_suggestion_service.rb` | サービス | 返信提案 |
| label_suggestion_service.rb | `lib/captain/label_suggestion_service.rb` | サービス | ラベル提案 |
| rewrite_service.rb | `lib/captain/rewrite_service.rb` | サービス | リライト |
| follow_up_service.rb | `lib/captain/follow_up_service.rb` | サービス | フォローアップ |
| summary.liquid | `lib/integrations/openai/openai_prompts/summary.liquid` | テンプレート | 要約プロンプト |
| reply.liquid | `lib/integrations/openai/openai_prompts/reply.liquid` | テンプレート | 返信プロンプト |
