./docs/シーケンス図.md

# 時代考証アシストAIシステム - シーケンス図

## 非同期ジョブモード(画像入力)

```mermaid
---
title: 時代考証システム - 非同期ジョブモード(画像入力)
---
sequenceDiagram
    participant U as ユーザー
    participant F as フロントエンド
    participant API as FastAPI
    participant S3In as S3 (inputs/)
    participant Lambda as Lambda Worker
    participant LA as 文字・物体列挙
    participant LU as 検証観点リスト
    participant V as 検証エージェント
    participant LLM as LLMモデル群
    participant Tool as 検索ツール
    participant S3Out as S3 (results/)

    U->>F: 画像アップロード + 年代入力
    F->>API: POST /api/analyze

    API->>S3In: 画像保存<br/>inputs/{job_id}_image
    S3In-->>API: S3キー
    API->>Lambda: 非同期Invoke (Event)<br/>job_id + S3キー + era_year
    API-->>F: 202 Accepted + job_id
    F-->>U: ジョブID表示

    Lambda->>S3In: 画像取得
    S3In-->>Lambda: 画像データ
    Lambda->>LA: 文字・物体列挙要求
    LA->>LLM: Vision LLM呼び出し(画像)
    LLM-->>LA: JSON配列(文字・物体)
    LA->>LLM: 文字専用で再実行
    LLM-->>LA: JSON配列(文字のみ)
    LA-->>Lambda: enumerated_items
    Lambda->>S3Out: 部分結果保存<br/>results/{job_id}.json

    Lambda->>LU: 検証観点リスト要求
    LU->>LLM: Vision LLM呼び出し(画像+列挙結果)
    LLM-->>LU: JSON配列(観点10-15件)
    LU-->>Lambda: observations
    Lambda->>S3Out: 部分結果保存<br/>results/{job_id}.json

    Lambda->>V: 並列検証要求(全観点)

    par 観点1
        V->>LLM: Phase1: 曖昧さ判定
        LLM-->>V: ambiguity_score
        alt 曖昧さ高い
            V->>LLM: Phase2: ReActエージェント
            LLM->>Tool: Web検索
            Tool-->>LLM: 検索結果
            LLM->>Tool: 画像検索(必要なら)
            Tool-->>LLM: 画像検索結果
            LLM-->>V: 検証結果
        end
        V->>LLM: アンサンブル判定(複数モデル並列)
        LLM-->>V: 各モデルの判定
    and 観点2〜15
        V->>LLM: Phase1-2 + アンサンブル
        LLM-->>V: 判定結果
    end

    V-->>Lambda: verifications(全観点分)
    Lambda->>S3Out: 最終結果保存<br/>results/{job_id}.json

    loop ロングポーリング(最大20秒)
        F->>API: GET /api/jobs/{job_id}?wait=20
        API->>S3Out: 結果確認
        alt 最終結果あり
            S3Out-->>API: 結果データ(summary完了)
            API-->>F: 200 + status=completed + result
            F-->>U: 検証結果表示
        else 部分結果あり
            S3Out-->>API: 結果データ(verifications未完了)
            API-->>F: 200 + status=partial + result
            F-->>U: 途中経過表示
            F->>API: 再度GET(リトライ)
        else 結果なし(タイムアウト)
            API-->>F: 200 + status=pending
            F->>API: 再度GET(リトライ)
        end
    end
```

## 処理フローの詳細

### 1. ジョブ投入フェーズ

#### 1.1. リクエスト受信

1. ユーザーが画像/テキスト + 年代を入力
2. フロントエンドが `POST /api/analyze` を送信

#### 1.2. S3への保存

1. FastAPIが画像/テキストをS3の `inputs/` に保存
   - 画像: `inputs/{job_id}_image`
   - テキスト: `inputs/{job_id}_text`
2. S3キーを取得

#### 1.3. Lambda Invoke

1. Lambda を非同期モード(InvocationType='Event')で起動
2. ペイロードに以下を含める:
   - `job_id`: ジョブID(UUID)
   - `era_year`: 考証対象年
   - `image_s3_bucket` / `image_s3_key`: 画像のS3参照
   - `text_s3_bucket` / `text_s3_key`: テキストのS3参照

#### 1.4. 即座にレスポンス

1. FastAPIが `202 Accepted` と `job_id` を返却
2. フロントエンドがジョブIDを表示

### 2. Lambda処理フェーズ

#### 2.1. 入力データ取得

1. LambdaがS3から画像/テキストを取得
2. base64デコード

#### 2.2. エージェント実行

1. LangGraphを起動
2. 各エージェント(list_all → list_up → verify)を順次実行

#### 2.3. 部分結果の保存

1. 各エージェント完了ごとに、部分結果をS3に保存
   - `results/{job_id}.json`
2. フロントエンドが部分結果を取得できるようにする

#### 2.4. 最終結果の保存

1. 全エージェント完了後、最終結果をS3に保存
   - `results/{job_id}.json`
2. `summary` フィールドを含める

### 3. ロングポーリングフェーズ

#### 3.1. ジョブ結果取得

1. フロントエンドが `GET /api/jobs/{job_id}?wait=20` を送信
2. FastAPIがS3の `results/{job_id}.json` を確認

#### 3.2. 結果の判定

- **結果あり(verifications + summary 完了)**:
  - `200 OK` + `status=completed` + `result`
- **部分結果あり(verifications 未完了)**:
  - `200 OK` + `status=partial` + `result`
- **結果なし**:
  - 最大20秒までロングポーリング(1秒間隔)
  - タイムアウト後: `200 OK` + `status=pending`
  - フロントエンドが再度GET(リトライ)

## 環境変数

### FastAPI (app)

```bash
USE_ASYNC_JOBS=true  # 非同期ジョブモードを有効化
AWS_REGION=ap-northeast-1
AWS_ENDPOINT_URL=http://localstack:4566  # LocalStackのエンドポイント
JOB_LAMBDA_FUNCTION_NAME=historical-accuracy-worker
JOB_RESULT_BUCKET=historical-accuracy-results
```

### LocalStack (localstack-init)

```bash
JOB_RESULT_BUCKET=historical-accuracy-results
JOB_LAMBDA_FUNCTION_NAME=historical-accuracy-worker
```

## LocalStackのセットアップ

### 1. Lambda関数のzip作成

```bash
make lambda-zip
```

### 2. Docker Composeで起動

```bash
docker compose up --build
```

### 3. LocalStack初期化スクリプト

`scripts/localstack-init.sh` が以下を実行:

1. S3バケット作成: `historical-accuracy-results`
2. Lambda関数作成: `historical-accuracy-worker`
3. Lambda関数のzipをアップロード

## 主な特徴

- **非同期処理**: 重い処理をバックグラウンドで実行
- **ロングポーリング**: 最大20秒まで結果を待機(Next.jsプロキシのタイムアウト対策)
- **部分結果対応**: 各エージェント完了ごとに部分結果を返却
- **S3によるペイロードサイズ回避**: 画像は256KB制限を避けるためS3経由
- **LocalStack対応**: ローカル開発環境でAWS Lambda/S3をエミュレート

## 同期モードとの違い

| 項目             | 同期モード           | 非同期モード               |
| ---------------- | -------------------- | -------------------------- |
| レスポンス形式   | NDJSONストリーミング | 202 Accepted + job_id      |
| 処理場所         | FastAPI内            | Lambda Worker              |
| 処理時間         | 即座                 | バックグラウンド           |
| タイムアウト     | なし                 | Lambda最大15分             |
| スケーラビリティ | 低い                 | **高い**(Lambda並列実行) |
| コスト           | 低い                 | Lambda実行コスト           |

## ユースケース

- **大量の画像を処理**: 複数のジョブを並列投入
- **長時間処理**: 観点数が多い場合
- **高負荷対策**: FastAPIの負荷を減らす
- **本番環境**: AWS Lambda/S3を使った本番デプロイ