1) history に「必ず保存」する項目(必須フィールド)
route(例:SQL_ONLY/CYPHER_ONLY/CYPHER_THEN_SQL)sql_params(JSON。SQL生成/実行に使った“安全な”パラメータのみ。秘密情報は入れない)decikg_params(JSON。同上)execution_kind(後述)providers(後述。devportalとdecikgの mode を必ず持つ)timings_ms(後述。dummy/stubでも必ず計測して入れる)error_summary(失敗時に必須。成功時はnullか空)
✅ “接続なし”でも、Stub呼び出しとして上記は全部埋まるのが合格条件。
2) execution_kind(固定 enum)
以下の3値に固定します(将来拡張しても互換が壊れにくい)。
STUB:外部接続なし/禁止/未設定 → stub結果を返しつつ履歴は残すREAL:外部接続ありで実実行(devportal/decikg いずれか、または両方)DRY_RUN:外部には投げないが、生成物(SQL/Cypher/params)までは作って履歴に残す
3) providers(固定スキーマ)
必ずこの形で保存します(devportal/decikg 両方必須)。
{
"devportal": {"mode": "dummy" | "http"},
"decikg": {"mode": "dummy" | "http"}
}
- “接続なしでOK”の工程6では、最低でも
mode="dummy"を使えること。 - mode は 実際にそのリクエストで使った mode を保存(設定値ではなく実績)。
4) timings_ms(固定スキーマ + dummyでも計測)
必ず msの整数で保存し、最低限このキーを持ちます(増やすのはOK)。
{
"total": 1234,
"route": 12,
"build_sql": 210,
"build_decikg": 0,
"exec_devportal": 0,
"exec_decikg": 0,
"analysis": 350,
"proposal": 200
}
totalは必須(全体)- 未実行ステージは
0(nullにしない:集計しやすくするため)
5) error_summary(失敗時の固定スキーマ)
失敗時は必ず保存。DBには要約のみ(スタックトレースはログへ)。
{
"stage": "route|build_sql|exec_devportal|exec_decikg|analysis|proposal",
"provider": "devportal|decikg|null",
"type": "TimeoutError|HTTPError|ValidationError|...",
"message": "sanitized short message",
"retryable": true
}
stageは必須(どこで死んだか)providerは外部絡みなら必須(devportal/decikg)messageは 秘匿情報を含めない(URLのクエリやトークン等は落とす)
6) 「外部結果」についての扱い(工程6の最小決定)
工程6の目的にある「外部結果/失敗追跡」を満たすため、history には以下を最小で残します(接続なしでもOK)。
- 実行した/実行予定だったものの要約:
sql_text(生成したSQL。可能なら保存)decikg_query(生成したCypher等。可能なら保存)result_summary(例:行数、カラム、先頭N行のサンプル、ノード/リレーション件数など)
- 巨大な生データは保存しない(上限を決めてトランケート or 別ストレージ。工程6では“要約”で十分)
これで、工程6の狙い(「後から改善できる追跡」+「接続なしでもStubとして記録」)を、最小変更で満たせます。
新規ファイル
- 基本:なし
- 例外(どちらか)
- 既存DDLに追記できない運用なら:
sql/0xx_nlq_history.sqlなどの migration追加 が必要 - 既存SQLで管理しているなら:新規不要(既存DDLファイルを修正)
- 既存DDLに追記できない運用なら:
変更予定ファイル(工程6の最小セット)
1) api/app/config.py
- 工程6で必要な保存のため、すでに追加予定のENV(前回のやつ)を含めて providerのmode判定に使う
DEVPORTAL_URL,DEVPORTAL_SQL_PATHDECIGK_URL,DECIGK_MODE=dummy|httpNLQ_EXECUTE_STUB_ENABLED
- (必要なら)history保存のサイズ上限などの定数をここに置く
2) DBスキーマ定義(SQL or Alembic)
nlq_sessions_*もしくはhistory相当テーブルに以下を追加(工程6の必須)routesql_params/decikg_params(JSON)execution_kindproviders(JSON)timings_ms(JSON)error_summary(JSON, nullable)
3) api/app/schemas/*(Pydantic)
- history保存/返却用のスキーマに、上の必須フィールドを反映
Providers/TimingsMs/ErrorSummaryを型として切る(既存の書き方に合わせる)
4) api/app/repos/*(history/session upsert箇所)
- 履歴保存を1箇所に集約して、成功/失敗/Stubのどれでも同じ形で upsert できるようにする
- 既存で
nlq_sessions_*に upsert しているなら、そこに項目追加が最短
5) api/app/services/router.py
- すでに route/params を返している前提で、工程6の保存要件に合わせて
routeを必ず返すsql_params/decikg_paramsを “保存できる形” で必ず返す(空でも{})
6) api/app/services/*executor*.py(SQL/DeciKG実行層)
- provider mode 実績(dummy/http)を返せるようにする
- 実行時間を計測して
timings_ms.exec_devportal/timings_ms.exec_decikgを埋める - 接続なしの場合も stub扱いで timings を 0以外で埋める(開始/終了の差分でOK)
7) api/app/services/orchestrator.py(または相当の集約サービス)
- ここが工程6のコア:
execution_kind決定(STUB/REAL/DRY_RUN)providersを確定timings_ms.total含む全体タイムを集約- 例外時に
error_summaryを作って 必ず履歴保存(finallyで呼ぶ)
8) api/app/routers/*(NLQ実行エンドポイント)
- orchestrator から返ってきた history をレスポンスに含める/もしくは history_id を返す(既存設計に合わせる)
- 「接続なしでも stub として記録」をここで保証する
9) tests/*
- 既存テストに追記(新規ファイルなしでいけるなら追記)
- Stub時でも history に必須フィールドが全て入る
- 失敗時に
error_summaryが入る - timings_ms が常に入る
直す順番(おすすめ)
- DBスキーマ(保存先の形を先に確定)
- schemas(Pydantic)(API/Repo/Serviceの型を固定)
- repo(upsert)(保存処理を1箇所に集約)
- orchestrator(finallyで必ず保存)
- executor(providers/timing/error取り回し)
- router.py(route/paramsを必ず返す形に)
- API router(エンドポイントの返却整合)
- tests(Stub/失敗/成功の3パターン)
直す順番(この順が一番事故りにくい)
- DBスキーマ(DDL):保存先を先に確定
nlq_history_repo.py:新カラムに書けるようにするhistory_service.py:成功/失敗/Stub でも必ず保存できる形にするnlq_execute.py:execution_kind/providers/timings/error_summary を組み立てる- executors(
route_executor→sql_executor/cypher_executor→hybrid_executor):route/params を欠損させない - routers(
nlq_execute.py/history.py):レスポンスに反映 - tests(もしあれば):Stubでも必須項目が全部入る、失敗時 error_summary が入る
必要なら次の一手として、nlq_history_repo.py と history_service.py の中身(今の insert/update の引数と保存してる列)を貼ってくれれば、「変更が確定」リストを100%にして、どの関数に何を追加するかまで具体化できます。_
コメントを残す