> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shannon.run/llms.txt
> Use this file to discover all available pages before exploring further.

# データベーススキーマ

> Shannonのデータベーススキーマ

## 概要

Shannonは、永続的なストレージのためにPostgreSQLを使用しています。データベースは2つのスキーマに整理されています：

* `auth` - 認証、マルチテナンシー、およびセキュリティ
* `public` - コアアプリケーションテーブル（タスク、エージェント、セッション）

**テーブル数**: 25テーブル\
**拡張機能**: uuid-ossp, pg\_trgm, btree\_gin, pgcrypto

## エンティティリレーションシップ図

```
┌─────────────────┐
│  auth.tenants   │
└────────┬────────┘
         │
         │ 1:N
         ▼
┌─────────────────┐        ┌──────────────────┐
│   auth.users    │◄───────│  auth.api_keys   │
└────────┬────────┘   N:1  └──────────────────┘
         │
         │ 1:N
         ▼
┌─────────────────┐        ┌──────────────────┐
│   public.users  │◄───────│   sessions       │
└────────┬────────┘   1:N  └──────────────────┘
         │
         │ 1:N
         ▼
┌──────────────────────────┐
│   task_executions        │
│  (workflow_id = PK)      │
└──────────┬───────────────┘
           │
           │ 1:N
           ▼
┌──────────────────────────┐        ┌──────────────────┐
│   agent_executions       │◄───────│  tool_executions │
└──────────┬───────────────┘   1:N  └──────────────────┘
           │
           │ 1:N
           ▼
┌──────────────────────────┐
│     tool_calls           │
└──────────────────────────┘

┌──────────────────────────┐        ┌──────────────────┐
│   event_logs             │        │  token_usage     │
│  (workflow_id)           │        └──────────────────┘
└──────────────────────────┘

┌──────────────────────────┐        ┌───────────────────┐
│  usage_daily_aggregates  │        │  learning_cases   │
└──────────────────────────┘        └───────────────────┘
```

## スキーマ: `auth` (認証とマルチテナンシー)

### auth.tenants

**目的**: マルチテナントの組織管理

| カラム                    | 型            | 制約                              | 説明                                  |
| ---------------------- | ------------ | ------------------------------- | ----------------------------------- |
| id                     | UUID         | PK, DEFAULT gen\_random\_uuid() | テナント識別子                             |
| name                   | VARCHAR(255) | NOT NULL                        | 組織名                                 |
| slug                   | VARCHAR(100) | UNIQUE, NOT NULL                | URLセーフ識別子                           |
| plan                   | VARCHAR(50)  | DEFAULT 'free'                  | サブスクリプションプラン（free, pro, enterprise） |
| token\_limit           | INTEGER      | DEFAULT 10000                   | 月間トークン許容量                           |
| monthly\_token\_usage  | INTEGER      | DEFAULT 0                       | 現在の月の使用量                            |
| rate\_limit\_per\_hour | INTEGER      | DEFAULT 1000                    | APIレート制限                            |
| is\_active             | BOOLEAN      | DEFAULT true                    | テナントのステータス                          |
| created\_at            | TIMESTAMP    | DEFAULT NOW()                   | 作成時間                                |
| updated\_at            | TIMESTAMP    | DEFAULT NOW()                   | 最終更新時間                              |
| metadata               | JSONB        | DEFAULT '{}'                    | 追加のテナントデータ                          |

**インデックス**:

* `slug`に対する主キー（UNIQUE）

**例**:

```sql theme={null}
-- アクティブなテナントを取得
SELECT id, name, plan, token_limit
FROM auth.tenants
WHERE is_active = true;

-- トークン使用量を確認
SELECT name, monthly_token_usage, token_limit,
       (monthly_token_usage::FLOAT / token_limit * 100) as usage_percentage
FROM auth.tenants
WHERE monthly_token_usage > token_limit * 0.8;
```

### auth.users

**目的**: ユーザー認証とプロフィール管理

| カラム                 | 型            | 制約                              | 説明                     |
| ------------------- | ------------ | ------------------------------- | ---------------------- |
| id                  | UUID         | PK, DEFAULT gen\_random\_uuid() | ユーザー識別子                |
| email               | VARCHAR(255) | UNIQUE, NOT NULL                | ユーザーのメール               |
| username            | VARCHAR(100) | UNIQUE, NOT NULL                | ユーザー名                  |
| password\_hash      | VARCHAR(255) | NOT NULL                        | Bcryptパスワードハッシュ        |
| full\_name          | VARCHAR(255) |                                 | フルネーム                  |
| tenant\_id          | UUID         | FK → auth.tenants, NOT NULL     | 組織                     |
| role                | VARCHAR(50)  | DEFAULT 'user'                  | 役割（user, admin, owner） |
| is\_active          | BOOLEAN      | DEFAULT true                    | アカウントのステータス            |
| is\_verified        | BOOLEAN      | DEFAULT false                   | メール確認                  |
| email\_verified\_at | TIMESTAMP    |                                 | 確認タイムスタンプ              |
| created\_at         | TIMESTAMP    | DEFAULT NOW()                   | アカウント作成                |
| updated\_at         | TIMESTAMP    | DEFAULT NOW()                   | 最終更新                   |
| last\_login         | TIMESTAMP    |                                 | 最終ログイン時間               |
| metadata            | JSONB        | DEFAULT '{}'                    | 追加のユーザーデータ             |

**インデックス**:

* `idx_users_email` ON (email)
* `idx_users_tenant_id` ON (tenant\_id)

**例**:

```sql theme={null}
-- テナント内のすべてのユーザーを取得
SELECT u.email, u.username, u.role, u.last_login
FROM auth.users u
WHERE u.tenant_id = '...'
  AND u.is_active = true
ORDER BY u.last_login DESC NULLS LAST;

-- 非アクティブなユーザーを見つける
SELECT email, created_at, last_login
FROM auth.users
WHERE last_login < NOW() - INTERVAL '90 days'
   OR last_login IS NULL;
```

### auth.api\_keys

**目的**: プログラムによるアクセスのためのAPIキー管理

| カラム                    | 型            | 制約                                                                    | 説明            |
| ---------------------- | ------------ | --------------------------------------------------------------------- | ------------- |
| id                     | UUID         | PK, DEFAULT gen\_random\_uuid()                                       | キー識別子         |
| key\_hash              | VARCHAR(255) | UNIQUE, NOT NULL                                                      | キーのSHA256ハッシュ |
| key\_prefix            | VARCHAR(20)  | NOT NULL                                                              | 最初の8文字（表示用）   |
| user\_id               | UUID         | FK → auth.users                                                       | キーの所有者        |
| tenant\_id             | UUID         | FK → auth.tenants, NOT NULL                                           | 組織            |
| name                   | VARCHAR(100) | NOT NULL                                                              | キー名/説明        |
| description            | TEXT         |                                                                       | 詳細な説明         |
| scopes                 | TEXT\[]      | DEFAULT ARRAY\['workflows:read', 'workflows:write', 'agents:execute'] | 権限配列          |
| rate\_limit\_per\_hour | INTEGER      | DEFAULT 1000                                                          | キー固有のレート制限    |
| last\_used             | TIMESTAMP    |                                                                       | 最終使用タイムスタンプ   |
| expires\_at            | TIMESTAMP    |                                                                       | 有効期限          |
| is\_active             | BOOLEAN      | DEFAULT true                                                          | キーステータス       |
| created\_at            | TIMESTAMP    | DEFAULT NOW()                                                         | 作成時間          |
| metadata               | JSONB        | DEFAULT '{}'                                                          | 追加のキーデータ      |

**インデックス**:

* `idx_api_keys_tenant_id` ON (tenant\_id)
* `idx_api_keys_key_prefix` ON (key\_prefix)

**デフォルトスコープ**:

* `workflows:read`: ワークフロー/タスクのステータスと結果の読み取りを許可
* `workflows:write`: ワークフロー/タスクの送信と管理を許可
* `agents:execute`: エージェント実行を許可

**例**:

```sql theme={null}
-- アクティブなAPIキーのリスト
SELECT key_prefix, name, last_used, expires_at
FROM auth.api_keys
WHERE is_active = true
  AND (expires_at IS NULL OR expires_at > NOW())
ORDER BY last_used DESC NULLS LAST;

-- 未使用のキーを見つける
SELECT key_prefix, name, created_at, last_used
FROM auth.api_keys
WHERE last_used IS NULL
  AND created_at < NOW() - INTERVAL '30 days';

-- 期限切れのキーを確認
SELECT key_prefix, name, expires_at
FROM auth.api_keys
WHERE expires_at BETWEEN NOW() AND NOW() + INTERVAL '7 days'
  AND is_active = true;
```

### auth.refresh\_tokens

**目的**: JWTリフレッシュトークンの保存と取り消し

| カラム         | 型            | 制約                              | 説明               |
| ----------- | ------------ | ------------------------------- | ---------------- |
| id          | UUID         | PK, DEFAULT gen\_random\_uuid() | トークン識別子          |
| token\_hash | VARCHAR(255) | UNIQUE, NOT NULL                | SHA256ハッシュ       |
| user\_id    | UUID         | FK → auth.users                 | トークン所有者          |
| tenant\_id  | UUID         | FK → auth.tenants, NOT NULL     | 組織               |
| expires\_at | TIMESTAMP    | NOT NULL                        | 有効期限             |
| revoked     | BOOLEAN      | DEFAULT false                   | 取り消しステータス        |
| revoked\_at | TIMESTAMP    |                                 | 取り消し時間           |
| ip\_address | INET         |                                 | クライアントIP         |
| user\_agent | TEXT         |                                 | クライアントユーザーエージェント |
| created\_at | TIMESTAMP    | DEFAULT NOW()                   | 作成時間             |

**インデックス**:

* `idx_refresh_tokens_user_id` ON (user\_id)
* `idx_refresh_tokens_token_hash` ON (token\_hash)

**例**:

```sql theme={null}
-- ユーザーのすべてのトークンを取り消す
UPDATE auth.refresh_tokens
SET revoked = true, revoked_at = NOW()
WHERE user_id = '...'
  AND revoked = false;

-- 期限切れのトークンをクリーンアップ
DELETE FROM auth.refresh_tokens
WHERE expires_at < NOW() - INTERVAL '7 days';
```

### auth.audit\_logs

**目的**: セキュリティイベントの監査トレイル

| カラム         | 型            | 制約                              | 説明               |
| ----------- | ------------ | ------------------------------- | ---------------- |
| id          | UUID         | PK, DEFAULT gen\_random\_uuid() | ログエントリID         |
| event\_type | VARCHAR(100) | NOT NULL                        | イベントタイプ          |
| user\_id    | UUID         | FK → auth.users                 | ユーザー（nullable）   |
| tenant\_id  | UUID         | FK → auth.tenants               | テナント（nullable）   |
| ip\_address | INET         |                                 | クライアントIP         |
| user\_agent | TEXT         |                                 | クライアントユーザーエージェント |
| details     | JSONB        | DEFAULT '{}'                    | イベント詳細           |
| created\_at | TIMESTAMP    | DEFAULT NOW()                   | イベント時間           |

**インデックス**:

* `idx_audit_logs_user_id` ON (user\_id)
* `idx_audit_logs_tenant_id` ON (tenant\_id)
* `idx_audit_logs_event_type` ON (event\_type)
* `idx_audit_logs_created_at` ON (created\_at)

**イベントタイプ**:

* `login` - ユーザーログイン
* `logout` - ユーザーログアウト
* `api_key_created` - APIキー生成
* `permission_changed` - ロール/権限の変更

**例**:

```sql theme={null}
-- 最近のセキュリティイベント
SELECT event_type, user_id, ip_address, created_at
FROM auth.audit_logs
WHERE created_at > NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC
LIMIT 100;

-- 失敗したログイン試行
SELECT user_id, ip_address, COUNT(*) as attempts
FROM auth.audit_logs
WHERE event_type = 'login_failed'
  AND created_at > NOW() - INTERVAL '1 hour'
GROUP BY user_id, ip_address
HAVING COUNT(*) > 5;
```

## スキーマ: `public` (コアアプリケーション)

### users

**目的**: アプリケーションユーザープロファイル（レガシー、auth.usersにリンク）

| カラム          | 型            | 制約                               | 説明        |
| ------------ | ------------ | -------------------------------- | --------- |
| id           | UUID         | PK, DEFAULT uuid\_generate\_v4() | ユーザー識別子   |
| external\_id | VARCHAR(255) | UNIQUE, NOT NULL                 | 外部システムID  |
| email        | VARCHAR(255) |                                  | メールアドレス   |
| tenant\_id   | UUID         |                                  | テナント参照    |
| metadata     | JSONB        | DEFAULT '{}'                     | ユーザーメタデータ |
| created\_at  | TIMESTAMPTZ  | DEFAULT NOW()                    | 作成時間      |
| updated\_at  | TIMESTAMPTZ  | DEFAULT NOW()                    | 最終更新      |

**インデックス**:

* `idx_users_tenant_id` ON (tenant\_id)
* `idx_users_external_id` ON (external\_id)

**注意**: このテーブルは後方互換性のために存在します。新しいコードは`auth.users`を使用するべきです。

### sessions

**目的**: ユーザーセッション管理とコンテキスト

| カラム           | 型           | 制約                               | 説明             |
| ------------- | ----------- | -------------------------------- | -------------- |
| id            | UUID        | PK, DEFAULT uuid\_generate\_v4() | セッション識別子       |
| user\_id      | UUID        | FK → users                       | セッション所有者       |
| tenant\_id    | UUID        |                                  | テナント参照         |
| context       | JSONB       | DEFAULT '{}'                     | セッションコンテキストデータ |
| token\_budget | INTEGER     | DEFAULT 10000                    | トークン割り当て       |
| tokens\_used  | INTEGER     | DEFAULT 0                        | 消費トークン         |
| created\_at   | TIMESTAMPTZ | DEFAULT NOW()                    | セッション開始        |
| updated\_at   | TIMESTAMPTZ | DEFAULT NOW()                    | 最終活動           |
| expires\_at   | TIMESTAMPTZ |                                  | 有効期限           |
| deleted\_at   | TIMESTAMPTZ | DEFAULT NULL                     | ソフト削除のタイムスタンプ  |
| deleted\_by   | UUID        | DEFAULT NULL                     | セッションを削除したユーザー |

**インデックス**:

* `idx_sessions_user_id` ON (user\_id)
* `idx_sessions_tenant_id` ON (tenant\_id)
* `idx_sessions_expires_at` ON (expires\_at)
* `idx_sessions_external_id` ON ((context->>'external\_id')) WHERE context->>'external\_id' IS NOT NULL
* `idx_sessions_user_external_id` UNIQUE ON (user\_id, (context->>'external\_id')) WHERE context->>'external\_id' IS NOT NULL AND deleted\_at IS NULL
* `idx_sessions_not_deleted` ON (id) WHERE deleted\_at IS NULL
* `idx_sessions_deleted_at` ON (deleted\_at) WHERE deleted\_at IS NOT NULL

**注意**:

* 外部ID（非UUIDセッションID）は`context->>'external_id'`に保存され、二重IDの検索を可能にします。
* `deleted_at`カラムによるソフト削除のサポート - `deleted_at`がNULLでないセッションは削除されたと見なされます。

**例**:

```sql theme={null}
-- アクティブなセッション
SELECT id, user_id, tokens_used, token_budget, created_at
FROM sessions
WHERE expires_at > NOW()
  OR expires_at IS NULL
ORDER BY created_at DESC;

-- セッショントークンの使用状況
SELECT
    COUNT(*) as session_count,
    AVG(tokens_used) as avg_tokens,
    SUM(tokens_used) as total_tokens
FROM sessions
WHERE created_at > NOW() - INTERVAL '24 hours';
```

### task\_executions

**目的**: タスク/ワークフローの実行履歴とメトリクス

| カラム                | 型             | 制約                               | 説明                                |
| ------------------ | ------------- | -------------------------------- | --------------------------------- |
| id                 | UUID          | PK, DEFAULT uuid\_generate\_v4() | タスク識別子                            |
| workflow\_id       | VARCHAR(255)  | UNIQUE, NOT NULL                 | Temporal ワークフロー ID                |
| user\_id           | UUID          | FK → users                       | タスク作成者                            |
| tenant\_id         | UUID          |                                  | テナント参照                            |
| session\_id        | VARCHAR(255)  |                                  | セッション識別子                          |
| query              | TEXT          | NOT NULL                         | タスククエリ/プロンプト                      |
| mode               | VARCHAR(50)   |                                  | 実行モード (SIMPLE, STANDARD, COMPLEX) |
| status             | VARCHAR(50)   | NOT NULL                         | タスクのステータス                         |
| started\_at        | TIMESTAMPTZ   | NOT NULL, DEFAULT NOW()          | 開始時間                              |
| completed\_at      | TIMESTAMPTZ   |                                  | 完了時間                              |
| result             | TEXT          |                                  | 最終結果                              |
| response           | JSONB         | DEFAULT '{}'                     | 構造化されたレスポンス                       |
| error\_message     | TEXT          |                                  | エラー詳細                             |
| total\_tokens      | INTEGER       | DEFAULT 0                        | 使用トークン総数                          |
| prompt\_tokens     | INTEGER       | DEFAULT 0                        | 入力トークン                            |
| completion\_tokens | INTEGER       | DEFAULT 0                        | 出力トークン                            |
| total\_cost\_usd   | DECIMAL(10,6) | DEFAULT 0                        | 総コスト                              |
| duration\_ms       | INTEGER       |                                  | 実行時間 (ms)                         |
| agents\_used       | INTEGER       | DEFAULT 0                        | 使用されたエージェント数                      |
| tools\_invoked     | INTEGER       | DEFAULT 0                        | ツール呼び出し数                          |
| cache\_hits        | INTEGER       | DEFAULT 0                        | キャッシュヒット数                         |
| complexity\_score  | DECIMAL(3,2)  |                                  | 複雑さ (0.0-1.0)                     |
| metadata           | JSONB         |                                  | 追加メタデータ                           |
| created\_at        | TIMESTAMPTZ   | DEFAULT NOW()                    | レコード作成                            |

**インデックス**:

* `idx_task_user_session` ON (user\_id, session\_id)
* `idx_task_created_at` ON (created\_at DESC)
* `idx_task_status` ON (status)
* `idx_task_workflow_id` ON (workflow\_id)
* `idx_task_executions_tenant_id` ON (tenant\_id)
* `idx_task_executions_session_id` ON (session\_id)

**ステータス値**:

* `RUNNING` - タスク進行中
* `COMPLETED` - タスクが正常に完了
* `FAILED` - タスクがエラーで失敗
* `CANCELLED` - ユーザーによってタスクがキャンセル

**例**:

```sql theme={null}
-- ユーザーの最近のタスク
SELECT workflow_id, query, status, duration_ms, total_cost_usd
FROM task_executions
WHERE user_id = '...'
ORDER BY started_at DESC
LIMIT 20;

-- 失敗したタスクの分析
SELECT
    DATE(started_at) as date,
    COUNT(*) as failed_count,
    AVG(duration_ms) as avg_duration
FROM task_executions
WHERE status = 'FAILED'
  AND started_at > NOW() - INTERVAL '7 days'
GROUP BY DATE(started_at)
ORDER BY date DESC;

-- コスト分析
SELECT
    user_id,
    COUNT(*) as task_count,
    SUM(total_cost_usd) as total_cost,
    AVG(total_cost_usd) as avg_cost,
    SUM(total_tokens) as total_tokens
FROM task_executions
WHERE started_at > NOW() - INTERVAL '30 days'
GROUP BY user_id
ORDER BY total_cost DESC;
```

### agent\_executions

**目的**: タスク内の個々のエージェント実行の詳細

| カラム            | 型            | 制約                               | 説明                 |
| -------------- | ------------ | -------------------------------- | ------------------ |
| id             | UUID         | PK, DEFAULT uuid\_generate\_v4() | 実行 ID              |
| workflow\_id   | VARCHAR(255) |                                  | Temporal ワークフロー ID |
| task\_id       | VARCHAR(255) |                                  | タスク識別子             |
| agent\_id      | VARCHAR(255) | NOT NULL                         | エージェント識別子          |
| input          | TEXT         | NOT NULL                         | エージェント入力           |
| output         | TEXT         |                                  | エージェント出力           |
| state          | VARCHAR(50)  |                                  | FSM ステート           |
| tokens\_used   | INTEGER      | DEFAULT 0                        | 消費トークン             |
| model\_used    | VARCHAR(100) |                                  | LLM モデル            |
| duration\_ms   | INTEGER      |                                  | 実行時間               |
| error\_message | TEXT         |                                  | エラー詳細              |
| metadata       | JSONB        | DEFAULT '{}'                     | 追加メタデータ            |
| strategy       | VARCHAR(100) |                                  | 実行ストラテジー           |
| created\_at    | TIMESTAMPTZ  | DEFAULT NOW()                    | 開始時間               |
| updated\_at    | TIMESTAMPTZ  | DEFAULT NOW()                    | 最終更新               |

**インデックス**:

* `idx_agent_executions_workflow_id` ON (workflow\_id)
* `idx_agent_executions_created_at` ON (created\_at DESC)
* `idx_agent_executions_state` ON (state)

**ステート値**:

* `IDLE` - エージェントアイドル
* `ANALYZING` - 入力を分析中
* `PLANNING` - 実行を計画中
* `RETRIEVING` - 情報を取得中
* `EXECUTING` - タスクを実行中
* `VALIDATING` - 結果を検証中
* `SYNTHESIZING` - レスポンスを合成中
* `COMPLETED` - 実行完了
* `FAILED` - 実行失敗

**例**:

```sql theme={null}
-- タスクのためのエージェント実行チェーン
SELECT agent_id, state, duration_ms, tokens_used
FROM agent_executions
WHERE workflow_id = '...'
ORDER BY created_at;

-- エージェントパフォーマンスメトリクス
SELECT
    agent_id,
    COUNT(*) as execution_count,
    AVG(duration_ms) as avg_duration,
    AVG(tokens_used) as avg_tokens
FROM agent_executions
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY agent_id
ORDER BY execution_count DESC;
```

### tool\_executions

**目的**: ツール呼び出し履歴とパフォーマンス

| カラム                  | 型            | 制約                               | 説明                 |
| -------------------- | ------------ | -------------------------------- | ------------------ |
| id                   | UUID         | PK, DEFAULT uuid\_generate\_v4() | 実行 ID              |
| agent\_execution\_id | UUID         |                                  | 親エージェント実行          |
| workflow\_id         | VARCHAR(255) |                                  | Temporal ワークフロー ID |
| agent\_id            | VARCHAR(255) |                                  | エージェント識別子          |
| tool\_name           | VARCHAR(255) | NOT NULL                         | ツール識別子             |
| input\_params        | JSONB        |                                  | 入力パラメータ            |
| output               | TEXT         |                                  | ツール出力              |
| success              | BOOLEAN      | DEFAULT true                     | 成功ステータス            |
| error                | TEXT         |                                  | エラー詳細              |
| duration\_ms         | INTEGER      |                                  | 実行時間               |
| tokens\_consumed     | INTEGER      | DEFAULT 0                        | 使用トークン             |
| metadata             | JSONB        | DEFAULT '{}'                     | 追加メタデータ            |
| created\_at          | TIMESTAMPTZ  | DEFAULT NOW()                    | 作成時間               |

**インデックス**:

* `idx_tool_executions_workflow_id` ON (workflow\_id)
* `idx_tool_executions_tool_name` ON (tool\_name)
* `idx_tool_executions_created_at` ON (created\_at DESC)
* `idx_tool_executions_success` ON (success)

**例**:

```sql theme={null}
-- ツール使用統計
SELECT
    tool_name,
    COUNT(*) as invocation_count,
    COUNT(CASE WHEN success THEN 1 END) as successful,
    AVG(duration_ms) as avg_duration
FROM tool_executions
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY tool_name
ORDER BY invocation_count DESC;

-- 失敗したツール実行
SELECT tool_name, error, created_at
FROM tool_executions
WHERE success = false
  AND created_at > NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC;

-- ツールパフォーマンス
SELECT
    tool_name,
    AVG(duration_ms) as avg_ms,
    MAX(duration_ms) as max_ms,
    STDDEV(duration_ms) as stddev_ms
FROM tool_executions
WHERE success = true
GROUP BY tool_name
ORDER BY avg_ms DESC;
```

### event\_logs

**目的**: 監査およびリプレイのためのイベントストレージ

| カラム          | 型            | 制約                               | 説明              |
| ------------ | ------------ | -------------------------------- | --------------- |
| id           | UUID         | PK, DEFAULT uuid\_generate\_v4() | イベントID          |
| workflow\_id | VARCHAR(255) | NOT NULL                         | ワークフロー識別子       |
| task\_id     | UUID         |                                  | タスク参照（nullable） |
| type         | VARCHAR(100) | NOT NULL                         | イベントタイプ         |
| agent\_id    | VARCHAR(255) |                                  | エージェント識別子       |
| message      | TEXT         |                                  | イベントメッセージ       |
| payload      | JSONB        | DEFAULT '{}'                     | イベントペイロード       |
| timestamp    | TIMESTAMPTZ  | NOT NULL, DEFAULT NOW()          | イベント時間          |
| seq          | BIGINT       |                                  | シーケンス番号         |
| stream\_id   | VARCHAR(64)  |                                  | RedisストリームID    |
| created\_at  | TIMESTAMPTZ  | NOT NULL, DEFAULT NOW()          | レコード作成          |

**インデックス**:

* `idx_event_logs_workflow_id` ON (workflow\_id)
* `idx_event_logs_task_id` ON (task\_id)
* `idx_event_logs_type` ON (type)
* `idx_event_logs_ts` ON (timestamp DESC)
* `idx_event_logs_seq` ON (workflow\_id, seq)
* `idx_event_logs_workflow_ts` ON (workflow\_id, timestamp DESC)
* `uq_event_logs_wf_type_seq` UNIQUE ON (workflow\_id, type, seq) WHERE seq IS NOT NULL

**イベントタイプ**:

* `task.started` - タスク実行開始
* `task.completed` - タスク実行完了
* `agent.thinking` - エージェント処理中
* `tool.called` - ツール呼び出し

**例**:

```sql theme={null}
-- タスクのすべてのイベントを取得
SELECT type, agent_id, message, timestamp
FROM event_logs
WHERE workflow_id = '...'
ORDER BY timestamp;

-- イベント頻度分析
SELECT
    type,
    COUNT(*) as event_count,
    DATE_TRUNC('hour', timestamp) as hour
FROM event_logs
WHERE timestamp > NOW() - INTERVAL '24 hours'
GROUP BY type, hour
ORDER BY hour DESC, event_count DESC;
```

### token\_usage

**目的**: タスクごとの詳細なトークン使用状況の追跡

| カラム                | 型             | 制約                               | 説明        |
| ------------------ | ------------- | -------------------------------- | --------- |
| id                 | UUID          | PK, DEFAULT uuid\_generate\_v4() | レコードID    |
| user\_id           | UUID          | FK → users                       | ユーザー      |
| task\_id           | UUID          | FK → task\_executions            | タスク       |
| provider           | VARCHAR(50)   | NOT NULL                         | LLMプロバイダー |
| model              | VARCHAR(255)  | NOT NULL                         | モデル名      |
| prompt\_tokens     | INTEGER       | NOT NULL                         | 入力トークン    |
| completion\_tokens | INTEGER       | NOT NULL                         | 出力トークン    |
| total\_tokens      | INTEGER       | NOT NULL                         | 合計トークン    |
| cost\_usd          | DECIMAL(10,6) | NOT NULL                         | USDでのコスト  |
| created\_at        | TIMESTAMPTZ   | DEFAULT NOW()                    | レコード時間    |

**インデックス**:

* `idx_token_usage_user_id` ON (user\_id)
* `idx_token_usage_created_at` ON (created\_at DESC)
* `idx_token_usage_provider_model` ON (provider, model)
* `idx_token_usage_task_id` ON (task\_id)

**例**:

```sql theme={null}
-- プロバイダーごとのトークン使用状況
SELECT
    provider,
    model,
    COUNT(*) as call_count,
    SUM(total_tokens) as total_tokens,
    SUM(cost_usd) as total_cost
FROM token_usage
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY provider, model
ORDER BY total_cost DESC;

-- 日次トークントレンド
SELECT
    DATE(created_at) as date,
    SUM(total_tokens) as tokens,
    SUM(cost_usd) as cost
FROM token_usage
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date;
```

### usage\_daily\_aggregates

**目的**: 事前計算された日次使用統計

| カラム                | 型             | 制約                               | 説明        |
| ------------------ | ------------- | -------------------------------- | --------- |
| id                 | UUID          | PK, DEFAULT uuid\_generate\_v4() | レコードID    |
| user\_id           | UUID          | FK → users                       | ユーザー      |
| date               | DATE          | NOT NULL                         | 集計日       |
| total\_tasks       | INTEGER       | DEFAULT 0                        | 合計タスク数    |
| successful\_tasks  | INTEGER       | DEFAULT 0                        | 成功数       |
| failed\_tasks      | INTEGER       | DEFAULT 0                        | 失敗数       |
| total\_tokens      | INTEGER       | DEFAULT 0                        | 合計トークン    |
| total\_cost\_usd   | DECIMAL(10,6) | DEFAULT 0                        | 合計コスト     |
| model\_usage       | JSONB         |                                  | モデル分布     |
| tools\_invoked     | INTEGER       | DEFAULT 0                        | ツール呼び出し数  |
| tool\_distribution | JSONB         |                                  | ツール使用分布   |
| avg\_duration\_ms  | INTEGER       |                                  | 平均タスク時間   |
| cache\_hit\_rate   | DECIMAL(3,2)  |                                  | キャッシュヒット率 |
| created\_at        | TIMESTAMPTZ   | DEFAULT NOW()                    | レコード時間    |

**インデックス**:

* `idx_usage_daily_user_date` ON (user\_id, date)
* `idx_usage_daily_date` ON (date DESC)

**ユニーク制約**: (user\_id, date)

**例**:

```sql theme={null}
-- ユーザー使用状況の概要
SELECT date, total_tasks, successful_tasks, total_cost_usd
FROM usage_daily_aggregates
WHERE user_id = '...'
  AND date > CURRENT_DATE - INTERVAL '30 days'
ORDER BY date DESC;

-- 集計メトリクス
SELECT
    SUM(total_tasks) as total_tasks,
    SUM(total_cost_usd) as total_cost,
    AVG(cache_hit_rate) as avg_cache_rate
FROM usage_daily_aggregates
WHERE date > CURRENT_DATE - INTERVAL '7 days';
```

## 追加テーブル

### tool\_calls

**目的**: レガシーツール呼び出しの追跡（代わりに tool\_executions を使用）

### prompts

**目的**: プロンプトのバージョン管理とA/Bテスト

### learning\_cases

**目的**: 強化学習ケースのストレージ

### session\_archives

**目的**: Redisからの長期セッションスナップショット

### audit\_logs (public)

**目的**: アプリケーション監査トレイル（auth.audit\_logsとは別）

## データベース関数

### update\_updated\_at\_column()

**目的**: 行の変更時に `updated_at` を自動的に更新

**使用法**: `users` および `sessions` テーブルにトリガーとして添付

```sql theme={null}
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
```

### update\_daily\_aggregate(user\_id, date)

**目的**: ユーザーのために日次使用集計を更新または作成

**使用法**: タスク完了時にトリガーを介して自動的に呼び出される

```sql theme={null}
-- 手動呼び出し
SELECT update_daily_aggregate(
    '00000000-0000-0000-0000-000000000002',
    '2025-10-22'
);
```

### trigger\_update\_daily\_aggregate()

**目的**: タスクのステータス変更時に集計を更新するトリガー関数

**使用法**: task\_executions の INSERT/UPDATE 時に自動的に発火

## サンプルクエリ

### タスク分析

```sql theme={null}
-- モード別タスク成功率
SELECT
    mode,
    COUNT(*) as total,
    COUNT(CASE WHEN status = 'COMPLETED' THEN 1 END) as completed,
    ROUND(100.0 * COUNT(CASE WHEN status = 'COMPLETED' THEN 1 END) / COUNT(*), 2) as success_rate
FROM task_executions
WHERE started_at > NOW() - INTERVAL '7 days'
GROUP BY mode;

-- 複雑さ別平均実行時間
SELECT
    CASE
        WHEN complexity_score < 0.3 THEN 'Low'
        WHEN complexity_score < 0.7 THEN 'Medium'
        ELSE 'High'
    END as complexity,
    COUNT(*) as task_count,
    AVG(duration_ms) as avg_ms,
    AVG(total_cost_usd) as avg_cost
FROM task_executions
WHERE complexity_score IS NOT NULL
GROUP BY complexity;
```

### コスト分析

```sql theme={null}
-- コスト上位ユーザー
SELECT
    u.email,
    COUNT(t.id) as task_count,
    SUM(t.total_cost_usd) as total_cost,
    AVG(t.total_cost_usd) as avg_cost
FROM task_executions t
JOIN users u ON t.user_id = u.id
WHERE t.started_at > NOW() - INTERVAL '30 days'
GROUP BY u.email
ORDER BY total_cost DESC
LIMIT 10;

-- モデル別コスト内訳
SELECT
    tu.provider,
    tu.model,
    COUNT(*) as usage_count,
    SUM(tu.total_tokens) as total_tokens,
    SUM(tu.cost_usd) as total_cost,
    AVG(tu.cost_usd) as avg_cost_per_call
FROM token_usage tu
WHERE tu.created_at > NOW() - INTERVAL '7 days'
GROUP BY tu.provider, tu.model
ORDER BY total_cost DESC;
```

### パフォーマンス監視

```sql theme={null}
-- 遅いクエリ (タスク > 60秒)
SELECT
    workflow_id,
    query,
    duration_ms,
    agents_used,
    tools_invoked
FROM task_executions
WHERE duration_ms > 60000
  AND started_at > NOW() - INTERVAL '24 hours'
ORDER BY duration_ms DESC;

-- ツール実行成功率
SELECT
    tool_name,
    COUNT(*) as total,
    COUNT(CASE WHEN success THEN 1 END) as successful,
    ROUND(100.0 * COUNT(CASE WHEN success THEN 1 END) / COUNT(*), 2) as success_rate,
    AVG(duration_ms) as avg_duration
FROM tool_executions
GROUP BY tool_name
ORDER BY total DESC;
```

## ベストプラクティス

### 1. インデックス作成

**常にインデックスを使用するべき**:

* 外部キー列 (user\_id, tenant\_id, task\_execution\_id など)
* 頻繁にフィルタリングされる列 (status, created\_at, expires\_at)
* JSONB クエリ (context->>'external\_id')
* 時間ベースのクエリ (created\_at DESC)

**現在のインデックスカバレッジ**: 優秀 (すべての外部キーと一般的なクエリがインデックス化されている)

### 2. パーティショニング

高ボリュームのテーブルにはパーティショニングを検討:

```sql theme={null}
-- task_executions の月次パーティショニング
CREATE TABLE task_executions_2025_10 PARTITION OF task_executions
    FOR VALUES FROM ('2025-10-01') TO ('2025-11-01');
```

### 3. データ保持

**推奨ポリシー**:

* `event_logs`: 90日 (高ボリューム、アーカイブ可能)
* `task_executions`: 1年 (古いデータをアーカイブ)
* `audit_logs`: 2年 (コンプライアンス)
* `token_usage`: 日次集計、90日後に生データをアーカイブ

**クリーンアップスクリプト**:

```sql theme={null}
-- 古いイベントを削除 (毎月実行)
DELETE FROM event_logs
WHERE created_at < NOW() - INTERVAL '90 days';

-- 古いタスクをアーカイブ (毎年実行)
INSERT INTO task_executions_archive
SELECT * FROM task_executions
WHERE started_at < NOW() - INTERVAL '1 year';

DELETE FROM task_executions
WHERE started_at < NOW() - INTERVAL '1 year';
```

### 4. クエリ最適化

**EXPLAIN ANALYZE を使用**:

```sql theme={null}
EXPLAIN ANALYZE
SELECT * FROM task_executions
WHERE user_id = '...'
  AND started_at > NOW() - INTERVAL '7 days';
```

**カバリングインデックスで最適化**:

```sql theme={null}
CREATE INDEX idx_task_user_date_status
ON task_executions(user_id, started_at, status)
INCLUDE (total_cost_usd, duration_ms);
```

### 5. コネクションプーリング

**設定** (Shannon ですでに設定済み):

```bash theme={null}
DB_MAX_OPEN_CONNS=50
DB_MAX_IDLE_CONNS=10
```

## メンテナンス

### Vacuum と Analyze

```sql theme={null}
-- 手動バキューム (毎週実行)
VACUUM ANALYZE task_executions;
VACUUM ANALYZE event_logs;

-- 自動バキュームの設定
ALTER TABLE task_executions SET (autovacuum_vacuum_threshold = 1000);
```

### 統計

```sql theme={null}
-- 統計を更新
ANALYZE task_executions;

-- テーブルの膨張をチェック
SELECT
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
```

## 関連ドキュメント

<CardGroup cols={2}>
  <Card title="環境変数" icon="gear" href="/ja/deployment/environment-variables">
    データベース設定
  </Card>

  <Card title="パフォーマンス調整" icon="gauge" href="/ja/deployment/performance-tuning">
    クエリ最適化
  </Card>

  <Card title="監視" icon="chart-line" href="/ja/deployment/monitoring">
    データベース監視
  </Card>
</CardGroup>
