カケトク wiki
Concepts

データモデル (PostgreSQL スキーマ設計)

データモデル (PostgreSQL スキーマ設計)

定義

カケトクの PostgreSQL スキーマ全体像と、主要テーブル間の関係・外部キー・設計上の論点をまとめた概念ページ。要件定義書 §6.1 の DDL を起点に、運用観点の注意点を追加する。

現在の理解

テーブル関係図 (概要)

tenants (1) ─┬─< users
             ├─< sessions ─┬─< transcripts
             │             ├─< escalations
             │             ├─< structured_outputs
             │             └─< usage_records
             ├─< campaigns ─< campaign_targets ──> sessions
             ├─< webhook_subscriptions ─< webhook_deliveries
             ├─< api_keys
             ├─< usage_aggregates
             └─< audit_logs

全テーブルに tenant_id が必須 (audit_logs は system_admin 操作のため NULL 可、users も system_admin は NULL 可)。RLS 適用対象。詳細は multi-tenant-rls

テーブル別ポイント

tenants (顧客企業)

  • plan: variable | fixed | hybrid — 請求モデル (§3.9 と対応)
  • settings JSONB: 時間帯制限・発信 concurrency 上限 等、テナント固有設定

users (Web ログインアカウント)

  • tenant_id NULL 可 (system_admin 用)
  • role: system_admin | tenant_admin | tenant_agent — 3 ロール ACL
  • zoom_phone_number + zoom_phone_extension: エスカレーション時の <Dial>
  • status: offline | available | on_call — Dispatcher の候補選定に使用
  • skills TEXT[]: Dispatcher のスキルベースルーティング (v1.1) で使用

sessions (通話セッション) — コアテーブル

  • twilio_call_sid UNIQUE: caller leg 不変 ID。転送後も不変 (call-session-continuity)
  • twilio_conference_sid: Conference 方式 (Warm transfer) 使用時のみ
  • direction: inbound | outbound
  • current_phase: ai | agent_a | agent_b | completed — 現在対応主体
  • assigned_agent_id: 担当エージェント (tenant_agent ロールの users.id)
  • campaign_id: outbound 時のみ、inbound は NULL

transcripts (発話)

  • phase: ai | agent_a | agent_b — どの Phase で発話されたか
  • speaker: caller | ai | agent_a | agent_b — 話者
  • track: inbound | outbound — Twilio Media Streams の track 由来 (物理話者分離の根拠)
  • is_committed: partial → committed の 2 段階 (Scribe v2 Realtime の仕様)
  • start_ms / end_ms: セッション開始からの相対タイムスタンプ

escalations (エスカレーション / 采配履歴)

  • from_actorto_actor の遷移記録 (ai → agent_a 等)
  • triggered_by: ai_auto | admin_manual | agent_request — 誰が発動したか (Dispatcher 監査要件)
  • twiml_update_status: pending | applied | failed — TwiML 更新の実行状態
  • 参照: escalation-flow

structured_outputs (構造化結果)

  • schema_version: 将来スキーマ進化時の互換管理
  • payload JSONB: 案件種別・顧客要望・合意事項[]・次アクション[]・重要度 等 (§3.8 最小スキーマ)
  • model: 使用した LLM モデル名 (Sonnet 4.6 / Opus 等) — 精度追跡に利用

campaigns / campaign_targets (アウトバウンド)

  • campaigns.script_config JSONB: AI 対話スクリプト設定 (紹介文・ヒアリング項目・エスカレーション条件)
  • campaigns.allowed_hours JSONB: [{"day": "mon", "from": "09:00", "to": "19:00"}, ...]
  • campaign_targets.status: pending | in_progress | completed | failed
  • campaign_targets.attempts: 再架電ポリシーと連動 (FR-OUT-8)
  • campaign_targets.session_id: 発信結果の sessions 参照 (多対一の可能性あり: 再架電で session が複数発生)

usage_records / usage_aggregates (利用状況)

  • usage_records.metric_type: call_duration_sec | twilio_cost_usd | scribe_seconds | tts_characters | claude_input_tokens | claude_output_tokens | media_streams_seconds
  • usage_aggregates は UNIQUE(tenant_id, period_type, period_start, metric_type) で冪等集計
  • estimated_cost_jpy: 単価マスタ適用後の概算
  • 参照: usage-cost-tracking

webhook_subscriptions / webhook_deliveries

  • subscriptions.events TEXT[]: session.started, session.phase_changed, session.escalated, session.completed, session.structured_ready, campaign.completed
  • deliveries.status + attempts + next_retry_at: 指数バックオフ (最大 24 時間)
  • response_code: HTTP ステータス保存で診断性を確保

api_keys

  • hashed_key: bcrypt/argon2 でハッシュ化 (NFR-SE-6)。生 Key は発行時のみ表示
  • revoked_at: ソフト削除

audit_logs

  • tenant_id NULL 可 (system_admin 操作用)
  • action: 正規化された操作名 (例: escalation.override, campaign.created)
  • details JSONB: 操作固有情報
  • ip_address / user_agent: 追跡性

RLS 設定 (§6.2)

全テーブルに以下のパターンでポリシー適用:

ALTER TABLE <t> ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON <t>
    USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
CREATE POLICY system_admin_all ON <t>
    USING (current_setting('app.current_role') = 'system_admin');

Drizzle ORM の middleware で SET app.current_tenant_id を自動実行する。

論点

  • transcripts の書込頻度: partial transcript をそのまま INSERT するか、committed のみに絞って partial は WS 中継のみにするか。前者は DB 書込負荷、後者は復旧時の欠損リスク
  • structured_outputs のスキーマ進化: schema_version で管理するが、過去通話の再構造化ポリシーは未定
  • campaign_targets → sessions の多重: 再架電で 1 target に複数 session が紐づく想定か、最新だけか。現状 session_id は単一カラムのため最新しか保持できない → 履歴保持が必要なら中間テーブル
  • usage_records の粒度: 通話ごと + metric_type ごとに 1 行だが、Claude の対話/構造化別に記録する場合は行数が膨らむ。パーティション設計が必要
  • escalations の twiml_update_status: 非同期 (TwiML 更新は Twilio REST コール) なので、applied 遷移は Twilio からの callback で行う想定の実装詳細が未定
  • audit_logs のリテンション: tenants.settings のデータ保存期間 (デフォルト 90 日、最大 1 年、NFR-CM-3) と整合を取る必要
  • webhook_deliveries の DB 肥大: 成功分のアーカイブ戦略未定。リテンションか cold storage 移行か

インデックス候補 (想像、要件には明記なし)

  • sessions (tenant_id, started_at DESC): テナント別最新セッション
  • sessions (twilio_call_sid): UNIQUE のため自動
  • transcripts (session_id, start_ms): セッション内時系列取得
  • escalations (session_id, created_at): セッション内采配履歴
  • usage_records (tenant_id, occurred_at): 集計ジョブ用
  • campaign_targets (campaign_id, status, next_retry_at): 発信ジョブのピックアップ
  • webhook_deliveries (status, next_retry_at): リトライジョブのピックアップ

根拠となる資料

関連する概念

関連する entity

  • 該当なし (現時点)

未解決の問い

  • partial transcript の永続化方針 (書込頻度と復旧性のトレードオフ)
  • campaign_targets ↔ sessions の多重性対応 (再架電履歴をどう持つか)
  • usage_records のパーティション戦略 (月次 RANGE か、テナント別 HASH か)
  • 構造化結果の再計算・スキーマ進化ワークフロー
  • 監査ログのリテンションと cold storage 設計

更新メモ

  • 2026-04-19: v3.0 要件定義書から初版作成。DDL 全体像と運用観点の論点を整理

On this page