# 機能設計書 93-アシスタントレスポンス

## 概要

本ドキュメントは、Chatwoot の Captain AI 機能におけるアシスタントレスポンス（FAQ）管理機能の設計を記載する。

### 本機能の処理概要

アシスタントレスポンス機能は、AIアシスタントが顧客からの質問に回答するための質問-回答ペア（FAQ）を管理する機能である。ドキュメントからの自動生成や会話からの学習、手動入力により FAQ を蓄積し、ベクトル検索により類似質問に対する回答を提供する。

**業務上の目的・背景**：AIアシスタントが一貫性のある正確な回答を行うためには、事前に定義された FAQ データが重要である。本機能により、FAQ を体系的に管理し、AIの回答品質を向上させることができる。また、保留中（pending）の回答を承認フローに乗せることで、不適切な回答の自動生成を防止できる。

**機能の利用シーン**：
- ドキュメントクロール後に自動生成された FAQ を確認・承認する
- 手動で FAQ を追加してAIの回答精度を向上させる
- 不要な FAQ を削除または編集して回答品質を維持する

**主要な処理内容**：
1. FAQレスポンス一覧の取得（フィルタ・検索・ページネーション対応）
2. FAQレスポンスの詳細表示
3. 新規FAQレスポンスの作成
4. 既存FAQレスポンスの更新（質問・回答・ステータス）
5. FAQレスポンスの削除
6. ベクトル埋め込みの自動更新

**関連システム・外部連携**：
- OpenAI API（埋め込みベクトル生成）
- pgvector（ベクトル類似検索）

**権限による制御**：Captain::Assistant ポリシーに基づく認可。アシスタント管理権限を持つユーザーが操作可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 104 | FAQ一覧画面 | 主画面 | FAQの一覧表示・編集・削除 |
| 105 | 保留中FAQ一覧 | 主画面 | 保留中FAQの確認・承認 |

## 機能種別

CRUD操作 / ベクトル検索 / 非同期処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| question | String | Yes | 質問文 | 必須 |
| answer | Text | Yes | 回答文 | 必須 |
| assistant_id | Integer | Yes | 紐付けるアシスタントのID | 必須 |
| status | String | No | ステータス（pending/approved） | デフォルト: approved |

### フィルタパラメータ（一覧取得時）

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|-----|------|
| assistant_id | Integer | No | アシスタントIDでフィルタ |
| document_id | Integer | No | ドキュメントIDでフィルタ |
| status | String | No | ステータスでフィルタ |
| search | String | No | 質問・回答の部分一致検索 |
| page | Integer | No | ページ番号 |

### 入力データソース

- 画面入力（API経由）
- ドキュメントからの自動生成（ResponseBuilderJob）
- 会話からの学習

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | レスポンスID |
| account_id | Integer | アカウントID |
| assistant_id | Integer | アシスタントID |
| assistant | Object | アシスタント情報（ネスト） |
| question | String | 質問文 |
| answer | Text | 回答文 |
| status | String | ステータス（pending/approved） |
| documentable | Object | 出典情報（Document/Conversation/User） |
| created_at | Integer | 作成日時（UNIX時間） |
| updated_at | Integer | 更新日時（UNIX時間） |

### 出力先

API レスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ APIコントローラーがリクエストを受信
2. 認証・認可チェック
   └─ Captain::Assistantポリシーによる権限確認
3. 各アクション実行
   ├─ index: レスポンス一覧取得（フィルタ・検索・ページネーション）
   ├─ show: 指定IDのレスポンス取得
   ├─ create: 新規レスポンス作成
   │      └─ documentable = Current.user（手動作成時）
   ├─ update: レスポンス更新
   └─ destroy: レスポンス削除
4. 埋め込みベクトル更新（非同期）
   └─ question/answer変更時にUpdateEmbeddingJob実行
5. レスポンス返却
   └─ jbuilderによるJSON整形
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{認可チェック}
    B -->|失敗| C[403エラー]
    B -->|成功| D{アクション判定}
    D -->|index| E[フィルタ適用]
    D -->|create| F[レスポンス作成]
    D -->|update| G[レスポンス更新]
    D -->|destroy| H[レスポンス削除]
    E --> I[ページネーション]
    I --> J[レスポンス返却]
    F --> K[documentable設定]
    K --> L[保存]
    L --> M{question/answer変更?}
    M -->|Yes| N[UpdateEmbeddingJob登録]
    M -->|No| J
    N --> J
    G --> M
    H --> O[204 No Content]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-93-01 | ステータス初期値 | 新規作成時のステータスはapproved | 手動作成時 |
| BR-93-02 | 出典記録 | 手動作成時はCurrent.userがdocumentableに設定 | 手動作成時 |
| BR-93-03 | 埋め込み自動更新 | question/answer変更時に埋め込みベクトルを再計算 | 更新時 |
| BR-93-04 | 部分一致検索 | searchパラメータはquestion/answerに対してILIKE検索 | 検索時 |
| BR-93-05 | account_id自動設定 | assistant.account_idから自動設定 | 作成時 |

### 計算ロジック

ベクトル類似検索：
```ruby
# 質問からの類似検索
embedding = Captain::Llm::EmbeddingService.new(account_id: account_id).get_embedding(query)
nearest_neighbors(:embedding, embedding, distance: 'cosine').limit(5)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| index | captain_assistant_responses | SELECT | レスポンス一覧取得 |
| show | captain_assistant_responses | SELECT | 指定IDのレスポンス取得 |
| create | captain_assistant_responses | INSERT | 新規レスポンス作成 |
| update | captain_assistant_responses | UPDATE | レスポンス更新 |
| destroy | captain_assistant_responses | DELETE | レスポンス削除 |

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

#### captain_assistant_responses

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT (index) | * | account_id = current_account.id | フィルタ・検索・ページネーション適用 |
| SELECT (show) | * | id = params[:id] | - |
| INSERT | question, answer, assistant_id, documentable_type, documentable_id, status | 入力値 | account_id自動設定 |
| UPDATE | question, answer, status | 入力値 | 埋め込み更新トリガー |
| DELETE | - | id = params[:id] | 物理削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 権限不足 | 適切な権限を付与 |
| 404 | Not Found | 指定IDのレスポンスが存在しない | 正しいIDを指定 |
| 422 | Unprocessable Entity | questionが空 | 質問を入力 |
| 422 | Unprocessable Entity | answerが空 | 回答を入力 |

### リトライ仕様

UpdateEmbeddingJobは非同期で実行され、失敗した場合はSidekiqのリトライ機構により自動リトライされる。

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

create/update/destroyは各レコード単位でトランザクション管理される。埋め込み更新はafter_commitで非同期実行されるため、メイン処理のトランザクションには影響しない。

## パフォーマンス要件

- 一覧取得は25件/ページでページネーション
- ベクトル検索はpgvectorのIVFFLATインデックスを使用
- account_id, assistant_id, statusにインデックス設定済み

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

- Captain::Assistant ポリシーによるアクセス制御
- ポリモーフィック関連（documentable）の型検証

## 備考

Enterprise Edition機能として提供される。

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | assistant_response.rb | `enterprise/app/models/captain/assistant_response.rb` | レスポンスのスキーマ定義、バリデーション、埋め込み処理 |

**読解のコツ**:
- **25-31行目**: 関連定義（belongs_to :assistant, :account, :documentable）
- **31行目**: has_neighbors :embedding でベクトル検索機能を定義
- **33-34行目**: バリデーション（question, answer必須）
- **45行目**: statusのenum定義（pending: 0, approved: 1）
- **47-50行目**: searchメソッドでベクトル類似検索を実装
- **62-66行目**: 埋め込み更新のafter_commitコールバック

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

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

**主要処理フロー**:
1. **3行目**: before_action で Captain::Assistant ポリシーによる認可チェック
2. **12-15行目**: index アクション - フィルタ適用・ページネーション
3. **20-24行目**: create アクション - documentable = Current.user 設定
4. **26-28行目**: update アクション
5. **37-58行目**: apply_filters メソッド - 各種フィルタ条件の適用

#### Step 3: フィルタロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | assistant_responses_controller.rb | `enterprise/app/controllers/api/v1/accounts/captain/assistant_responses_controller.rb` | apply_filtersメソッド |

**主要処理フロー**:
- **38行目**: assistant_idフィルタ
- **40-45行目**: document_idフィルタ（ポリモーフィック対応）
- **47行目**: statusフィルタ
- **49-55行目**: searchフィルタ（ILIKE検索）

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

```
Api::V1::Accounts::Captain::AssistantResponsesController
    │
    ├─ before_action -> { check_authorization(Captain::Assistant) }
    │
    ├─ index
    │      └─ Current.account.captain_assistant_responses
    │             │
    │             ├─ includes(:assistant, :documentable)
    │             ├─ apply_filters
    │             │      ├─ where(assistant_id:)
    │             │      ├─ where(documentable_id:, documentable_type:)
    │             │      ├─ where(status:)
    │             │      └─ where(ILIKE search)
    │             └─ page().per(25)
    │
    ├─ create
    │      └─ captain_assistant_responses.new(response_params)
    │             │
    │             ├─ documentable = Current.user
    │             ├─ before_validation :ensure_account
    │             └─ after_commit :update_response_embedding
    │                    └─ Captain::Llm::UpdateEmbeddingJob.perform_later
    │
    └─ update
           └─ @response.update!(response_params)
                  └─ after_commit :update_response_embedding
```

### データフロー図

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

question ───────────┐
                    ├──▶ AssistantResponsesController#create
answer ─────────────┤               │
                    │               ▼
assistant_id ───────┘    Captain::AssistantResponse.save
                                    │
                    ┌───────────────┼───────────────┐
                    ▼               ▼               ▼
         captain_assistant     documentable    embedding
           _responses           = User         (非同期更新)
            (INSERT)                               │
                                                   ▼
                                    Captain::Llm::UpdateEmbeddingJob
                                                   │
                                                   ▼
                                    EmbeddingService.get_embedding
                                                   │
                                                   ▼
                                         UPDATE embedding
                                         (pgvector)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| assistant_responses_controller.rb | `enterprise/app/controllers/api/v1/accounts/captain/assistant_responses_controller.rb` | コントローラー | APIエンドポイント |
| assistant_response.rb | `enterprise/app/models/captain/assistant_response.rb` | モデル | データ定義・バリデーション・埋め込み |
| _assistant_response.json.jbuilder | `enterprise/app/views/api/v1/models/captain/_assistant_response.json.jbuilder` | ビュー | JSON出力 |
| index.json.jbuilder | `enterprise/app/views/api/v1/accounts/captain/assistant_responses/index.json.jbuilder` | ビュー | 一覧JSON出力 |
| embedding_service.rb | `enterprise/app/services/captain/llm/embedding_service.rb` | サービス | 埋め込みベクトル生成 |
| update_embedding_job.rb | `enterprise/app/jobs/captain/llm/update_embedding_job.rb` | ジョブ | 埋め込み更新非同期処理 |
| response_builder_job.rb | `enterprise/app/jobs/captain/documents/response_builder_job.rb` | ジョブ | ドキュメントからのFAQ自動生成 |
