いまのSQLが60点になっている主因はだいたいこの3つです:
- 件数が二重計上になり得る:明細JOINしてるのに
COUNT(so.id)(→本当はCOUNT(DISTINCT so.id)が安全) - LIMITがズレる:直近12ヶ月なのに
LIMIT 200 - 並び順がズレる:月次は普通
DESCで “直近が上” が期待されがち(あなたも上位12ヶ月と言ってる)
なので、プロンプトで「月次の定型」「ヘッダ×明細の数え方」「LIMIT/ORDER」を“規約”にしてしまうのが一番効きます。
追加するプロンプト(そのまま貼れる)
llm_sql.py の system(もしくは developer)メッセージに、既存ルールの下へ追記してください。
① まず汎用ルール(全ドメイン共通で効く)
- Use only the tables in
scope_tableswhen provided or inferred. Do not invent other tables. - Use only columns that exist in
allowed_fields(or can be derived from them). Do not guess columns. - If a column name contains
/or special characters, you MUST double-quote it.
Example:sol."order_id/id" - For month time series:
month := date_trunc('month', <date_column>)GROUP BY 1- Default sort:
ORDER BY 1 DESC
- If the user says “直近12ヶ月 / 上位12ヶ月” then
LIMIT 12(not 200).
② ヘッダ×明細の“数え方”ルール(今回の30点)
- If joining a header table and a line table (1-to-many) and the user asks for order count / header count:
- Use
COUNT(DISTINCT <header_pk>)to avoid double counting. - Example:
COUNT(DISTINCT so.id) AS order_count
- Use
- If the user asks for line count / 明細件数:
- Use
COUNT(*)(orCOUNT(sol.id)).
- Use
③ SAPB1のドメインルール(短くて強い)
sapb1_sales_ordersis header (1 row per order, pkid).sapb1_sales_order_linesis lines (many rows per order).- Join condition:
so.id = sol."order_id/id". - Total amount should be
SUM(sol.line_total).
これを入れるだけで、弱い質問でも「amount_total を生やす」「COUNTが爆増」「LIMIT 200」みたいなブレがかなり減ります。
期待される“満点”SQL(あなたの例の質問)
この質問:
「受注データで、月別の受注件数と合計金額を出して。直近12ヶ月で。」
なら、プロンプトが効くと最終形はこう寄せられます:
SELECT
DATE_TRUNC('month', so.posting_date) AS month,
COUNT(DISTINCT so.id) AS order_count,
COALESCE(SUM(sol.line_total), 0) AS total_amount
FROM sapb1_sales_orders so
LEFT JOIN sapb1_sales_order_lines sol
ON so.id = sol."order_id/id"
WHERE so.posting_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY 1
ORDER BY 1 DESC
LIMIT 12
ポイントは COUNT(DISTINCT) と LIMIT 12 と ORDER BY DESC です。
実装の置き場所(最短で効く場所)
join_planner.py ではなく、LLMに渡す最終プロンプトを作ってる場所です。
/analytics/query→generate_sql()(app/services/analytics/llm_sql.py)
ここに “上の規約” を入れるのが一番効きます。
さらに精度を上げるなら:
allowed_models(sample)がsapb1.sales.order*を含む場合だけ
“SAPB1ルール” を追記する(=条件付きプロンプト)
追加で1つだけやるなら(超効く)
プロンプトだけでも良いですが、最後の保険として SQL後処理を1個だけ入れると安定します:
- 明細JOINが存在して、
order_countがCOUNT(so.id)になっていたら
→COUNT(DISTINCT so.id)に置換(安全側)
これは「LLMがうっかり」を潰す保険になります。
この方針でいくなら、次にこちらから **llm_sql.pyの “messages組み立て部分” を前提に、どこにどう追記するか(パッチ形式)**で書けます。
いまの llm_sql.py を貼ってくれれば、そこに合わせて最短の差分を出します。
コメントを残す