1) 方針:新規骨格+既存資産を“箱”としてぶら下げる
- ✅ 新規:質問受付 → 分岐決定 → ワークフロー呼び出し の骨格
- ✅ 既存流用:SQL実行(Dev-Portal/Metabase)・コメント(analysis_comment_service)・DeciKGクライアント/スタブ
- ✅ 段階移行:古い import を一気に直さず、互換レイヤ(re-export)で徐々に寄せる
これで「削除の迷い」がなくなります。
2) おすすめフォルダ設計(あなたの区分そのまま)
api/app/ 配下を “機能境界” で切ります(Pythonパッケージとして成立するように __init__.py 前提)。
api/app/
nlq/ # 質問受付・分岐・全体の流れ(ここが中心)
routers/
nlq_execute.py # POST /nlq/execute (入口1つ)
nlq_plan.py # (必要なら)将来用。今は無しでもOK
schemas/
request.py # ExecuteRequest / Options / Headers
response.py # ExecuteResponse(v0.2互換)
common.py # RouteName / ReasonCode etc
routing/
decider.py # route決定ロジック(SQL_ONLY / CYPHER_ONLY)
policy.py # 将来のポリシー(今は薄くてOK)
workflows/
sql_workflow.py # SQL: 実行→コメントまで“一気通貫”
cypher_workflow.py # Cypher: resolve→(解決ならコメント) / (未解決なら途中結果)
services/
orchestrator.py # request→route→workflow を束ねる薄い箱(任意)
utils/
timeline.py # trace/timeline
model_dump.py # dict/pydantic/dataclass 統一dump
errors.py # Problem+JSON/ExecuteError生成 sql/ # “SQL部分(Metabase含む)”
devportal/
client.py # DevPortalClient(既存を移動 or wrap)
schemas.py # TabularResult
metabase/
client.py # Metabaseの呼び出し/iframe/embed等(あるなら)
schemas.py
guards/
sql_guard.py
sql_normalize.py cypher/ # “Cypher部分(DeciKG)”
decikg/
client.py # decikg_client
client_stub.py # decikg_client_stub
execute.py # decikg_execute(ここが唯一の入口箱)
schemas.py # GraphResult, Resolved, Candidates 等
resolver/
resolver.py # 名前解決の判定/変換(search/fuzzy_matchに寄せるならここ) analysis/ # “コメント(分析)”
comment/
service.py # analysis_comment_service
prompt_templates/
...
schemas.py # CommentResult etc legacy/ # 互換レイヤ(段階移行のため)
services/
route_executor.py # 旧 import の受け皿(中身は新へ委譲)
nlq_execute.py
routers/
nlq.py
なぜこの分け方が良いか
- nlq/ が「司令塔」
- sql/ と cypher/ は “外部I/F(実行)” の箱
- analysis/ は “コメント生成” の箱
- legacy/ は移行中だけ存在。最終的に削除するのが明確
3) ルート分岐の設計(あなたの希望を固定化)
SQL_WORKFLOW(完結)
- Dev-Portal/Metabase実行(どっちでも)
- その結果をコメント生成へ渡す
- 1回の
/nlq/executeで完結
CYPHER_WORKFLOW(2段階)
- まず 名前解決(resolver)
- 未解決なら 途中結果だけ返す(候補・need_user_choice)
- 解決できたら コメント生成へ直行(図表なし)
これを nlq/workflows/ に置くと、「何を残して何を捨てるか」が一目瞭然になります。
4) “新規で作った方が変更が容易”の判断基準
あなたのケースだと 新規骨格を作るメリットが大きいです。理由:
- もう **2択(SQL/CYPHER)**に決めた → 旧 plan/hybrid/parallel がノイズ
- Cypherは “resolve → comment” の2段階 → 旧 executor 設計(全部 execute 結果を返す前提)とズレる
- “あとで削除するとき迷う” は構造問題 → フォルダ境界の整理が一番効く
ただし全面リライトはリスクなので、「骨格だけ新規」が最適。
5) 移行手順(迷わない、壊さない)
Step 0: まず新規フォルダだけ作る(中身空でもOK)
nlq/,sql/,cypher/,analysis/,legacy/
Step 1: 入口を新規へ寄せる(最重要)
POST /nlq/executeの router をnlq/routers/nlq_execute.pyに作る- 既存 router は
legacy/routers/に移すか、薄い re-export にする
Step 2: route_decider を新規で作る
nlq/routing/decider.py- SQL_ONLY / CYPHER_ONLY しか返さない(他は存在しない世界にする)
Step 3: workflows を新規で作る
sql_workflow.pyは既存の “SQL→コメント” を呼ぶだけでまず動かすcypher_workflow.pyは “resolve→途中結果 or コメント” だけ作る
※DeciKG実行で図表を返すのはやらない(あなたの方針通り)
Step 4: 中身の実装を段階的に移す
app/services/decikg_execute.py→cypher/decikg/execute.py(まずは wrapper でOK)analysis_comment_service.py→analysis/comment/service.py(wrapper)DevPortalClient→sql/devportal/client.py(wrapper)
Step 5: legacy を削る(最後)
- 使われなくなったら
legacy/を丸ごと削除すれば終わり
6) 互換(v0.2 add-only)も守りやすくなる
あなたの “追加のみ” コントラクトは、入口とレスポンススキーマを nlq/schemas/ に固定すれば守れます。
- 新旧実装が混在しても、レスポンスの形だけは同じにできる
- 内部のフォルダ移動は自由(API契約に影響しない)
7) 迷いを最小化する命名ルール(おすすめ)
- 入口は 常に1つ:
nlq/routers/nlq_execute.py - ルーティングは 常に1つ:
nlq/routing/decider.py - SQLワークフローは 完結:
sql_workflow.execute_and_comment() - Cypherワークフローは 2段階:
cypher_workflow.resolve_then_comment()
この “1つだけ” ルールが、後から見返す時に効きます。
もしこの方針でいくなら、次のターンでこちらから
nlq/routing/decider.py(フルファイル)nlq/workflows/sql_workflow.py(フルファイル:既存呼び出しだけ)nlq/workflows/cypher_workflow.py(フルファイル:resolve→途中結果/コメント)nlq/routers/nlq_execute.py(フルファイル:入口で2択呼び分け)
を 新ディレクトリ前提で一式出せます。
そのために、いまの実装で「SQLはすでにコメントまでできている」と言っていたので、次は その“SQL→コメント”の呼び出し元だけ見せてください:
api/app/services/nlq_execute.py(現状版) または/nlq/executeの router ハンドラ
これが分かると、既存資産を壊さずに新骨格へそのまま接続できます。
コメントを残す