- 画面構成
- 左:チャット風の履歴(ユーザー自然文/生成SQL/結果サマリ)
- 中:SQLプレビュー+実行結果テーブル(先頭100行)+Metabase埋め込み(
url/iframe_src) - 右:AI分析(
analysis.summary,key_findings,sql_improvements,anomalies,suggestions) - 上部:進行ステップバー(
stats.timelineのguard→sql→metabase→analysisを表示)
- フロー
/nlq/planを叩いて候補SQL・display_hintを提示- 「実行」押下で
/nlq/execute(analyze_mode選択可:off/quick/deep) - レスポンスの
timelineをステップバーに反映、429時はRetry-AfterをUIでカウントダウン
- エラーハンドリング
- 400(ガード): ステップバーの guard に
note:error:...、メッセージをチップ表示 - 429(deep): トースト+自動再試行ボタン(
Retry-After秒) - 500系: スナック表示+リトライ
- 400(ガード): ステップバーの guard に
- 追加機能(任意)
sql_improvements[].revised_sqlを「置換して再実行」ボタン/nlq/historyを使い、会話ごとにsession_idを紐付けた履歴一覧
実装方針(最短ルート)
- Stack: Vite + React + TypeScript + Tailwind(軽量・1ファイル配信でOK)
- 依存:なし(shadcn/ui はお好みで)
- コンテナ追加(
docker-compose.yml例)web: build: context: ./web dockerfile: Dockerfile environment: - VITE_API_BASE=http://localhost:8081 ports: - "5173:5173" depends_on: - api - 呼び出し例(超要点だけ)
// fetch plan → execute const plan = await fetch(`${API}/nlq/plan`, { method:'POST', body: JSON.stringify({...})}).then(r=>r.json()); const ex = await fetch(`${API}/nlq/execute`, { method:'POST', body: JSON.stringify({...})}).then(r=> { if (r.status === 429) { const retryAfter = Number(r.headers.get('Retry-After') || 60); // UIでカウントダウン → 再試行 } return r.json(); }); // timeline をそのまま描画 // ex.stats.timeline: [{phase:'guard'|'sql'|'metabase'|'analysis', start_ms, elapsed_ms, note}] - Metabaseの表示
metabase.mode === 'public'→urlを<iframe src=...>metabase.mode === 'signed'→iframe_srcをそのまま<iframe src=...>
これで「動きのトレース」が可能
- サーバ側は P10までで必要情報が出揃っているので、P13はフロントのみで成立します。
- 仕様変更が必要になれば、
stats.timelineのphase/note値を拡張(例:history、cache_hitなど)すればOK。
Go/No-Go 判定(P14への事前チェック)
**Go(緑)**にするための最小4点だけ:
- CORS設定
- Allowed Origins に
http://localhost:5173を追加 - Allowed Headers に
Authorization, Content-Type - Expose Headers に
Retry-After(JSからヘッダ読めるように)
- Allowed Origins に
- Metabase 埋め込み確認
- dev: Public sharing ON/
METABASE_PUBLIC_URL到達OK - prod想定: Embedding ON+Secret設定/frame-ancestors で
webのオリジン許可
- dev: Public sharing ON/
- OpenAPIパッチ反映済み
ExecuteStats.columns/column_map追加ChromaPackageInput(入力用; collection任意)ChromaUpsertResponse.items[].doc_idを常に返却
- 認証トークン流し込み
- フロントから
Authorization: Bearer …が通る(OPTIONS→200、POST→200/429 まで確認)
- フロントから
上記4点OKなら、P14はブロッカーなしで着工できます。
画面構成 ↔ APIフィールド対応
- 左(チャット履歴)
- ユーザー自然文:
PlanRequest.message/HistoryItem.input_message - 生成SQL:
PlanResponse.validated_sql(候補)→ 実行後はExecuteResponse.sql - 結果サマリ:
ExecuteStats.analysis.summary(quick/deep時)
- ユーザー自然文:
- 中(SQLプレビュー+結果テーブル+Metabase)
- SQLプレビュー:
PlanResponse.validated_sql(未実行時)、ExecuteResponse.sql(実行後) - 結果テーブル(先頭100行):
ExecuteRequest.mode="sample"、ExecuteResponse.rows - 列名:
ExecuteStats.columns(正規化後)またはcolumn_mapで表示名整える - Metabase埋め込み:
- dev(public):
stats.metabase.urlを<iframe src> - prod(signed):
stats.metabase.iframe_srcを<iframe src>
- dev(public):
- SQLプレビュー:
- 右(AI分析)
ExecuteStats.analysis.{key_findings, sql_improvements, anomalies, suggestions}- 「置換して再実行」:
sql_improvements[].revised_sqlをエディタへコピーして/nlq/execute
- 上部(ステップバー)
ExecuteStats.timeline: [{phase:'guard'|'sql'|'metabase'|'analysis', start_ms, elapsed_ms, note}]- 429時:HTTPヘッダ
Retry-Afterを読み、カウントダウン表示→再試行
UIフロー(最短ルート・イベント駆動)
session_idをlocalStorageに永続(無ければ UUID 生成)- Plan:
POST /nlq/plan→ SQL候補・display_hintを左/中に出す - Execute:
POST /nlq/execute(analyze_mode選択)- 200:中ペインにテーブル・Metabase、右ペインに分析、上部は timeline 反映
- 429:
Retry-After秒をカウントダウンして再実行ボタン活性 - 400:guard ステップに
note:"error:..."、チップで理由表示 - 500:スナック+再試行
- 履歴:
POST /nlq/history(同session_idで保存)/GET /nlq/history?session_id=...で左ペイン再構築
作業手順(リポ構成と最小タスク)
web(Vite+React+TS+Tailwind) を追加するだけでOK。compose例は提示どおりで十分。
- 新規
web/(Vite アプリ)- 主要コンポーネント:
ChatPanel(左)/SqlAndResultPanel(中:SQLエディタ・テーブル・MetabaseFrame)/AnalysisPanel(右)/StepBar(上)api.ts(fetchラッパ:ベアラートークン注入・429読取り・JSON整形)types.ts(OpenAPIに沿った最低限の型)
- 既存(API側の“設定”だけ)
- CORS(allow origin 5173/expose
Retry-After)
-(任意)レスポンスヘッダCache-Control: no-store(signed埋め込みのTTL誤用防止)
- CORS(allow origin 5173/expose
DoD(P14)
- 機能
- Plan→Execute 一連の操作が1画面で完結(SQL→表→Metabase→分析)
- timeline が guard→sql→metabase→analysis の4ステップで表示
- 429 を UI カウントダウンで可視化し、ワンクリック再実行可能
- 「改善SQLの置換→再実行」ができる
- 履歴一覧(session_id単位)からの復元表示
- 品質
- dev 環境で public / prod 想定で signed の両パスを手動検証
- 400/429/500 それぞれのハンドリング確認(トースト/スナック/バナー)
- LCP 1.5s 以内(ローカル)、JSバンドル < 300KB(初期版の目安)
- 運用
.env:VITE_API_BASE,VITE_DEFAULT_ANALYZE_MODE,VITE_METABASE_HEIGHT- README に起動手順(compose)と既知の制限(deepの制限・signedのTTL)が記載
つまずきポイント(先回りで潰す)
- Retry-After が読めない → CORS の
Access-Control-Expose-Headers: Retry-Afterを忘れずに - Metabase が iframe 拒否 → Embedding ON/
frame-ancestorsにhttp://localhost:5173を含める - 列名が崩れる →
ExecuteStats.columns/column_mapを優先使用(無ければ rows[0] のキー配列) - 429ループ → リトライ上限+指数バックオフ(例:
retryAfter*(1+0.2*rand)) - 認証 → dev は固定Bearer、prod は JWKS を想定。フロントはトークン取得を env/ローカル設定で注入
コメントを残す