セマンティックモデルとセマンティックレイヤーは何が違うのか
ここで田中氏は、混同されやすい2つの用語を切り分けた。業界でも会社によって解釈が異なると前置きした上で、本セッションでの定義を示している。
「セマンティックモデル」の実態は、指標や次元を記述したYAMLまたはJSONファイルである。「売上とは何か」「どう計算するか」といったルールをまとめた情報、いわばビジネス定義の設計図だ。Apache Ossieが標準化しているのは、こちらである。
対して「セマンティックレイヤー」は、データ基盤とBIツールやAIエージェントの間で実際に動く層を指す。モデルを読み込み、ユーザーの質問をSQLに変換してデータを返す、定義の実行エンジンにあたる。こちらは、リファレンス実装は存在するが、各社が自由に独自実装することも可能だと田中氏は位置づけた。
規格が定めるのは仕様書の書式まで。それを動かすエンジンは各社の実装に委ねられている、という住み分けである。
DDLのみとApache Ossieに準じたセマンティックモデル付きで、生成されるSQLはどう変わったか
田中氏のデモは、同じ質問を2系統のプロンプトに流して結果を並べる構成だった。片方はシステムプロンプトにDDLスキーマだけを入れた「without Ossie」、もう片方はそこにApache Ossie準拠のYAML定義書retail_model.yamlを加えた「with Ossie」である。
環境はLLMがLlama 3.1(70B)、データベースはDocker上のPostgreSQLで、17.7と18.4の両方を試したという。LLMの呼び出しにはSnowflakeのCortex REST APIを使ったが、「Snowflakeに限定した話ではありません」と田中氏は強調した。Ollamaでローカルで動かすことも、各社のAPIへ直接問い合わせることもできる。コードはllm-ossie-postgresで公開済みで、手元で追試できる。
前述のとおり、「6月の売上合計」と「ブランド別売上」ではOssieの有無で結果は変わらなかった。田中氏はこの点を隠さず、むしろ先に注意を促している。「最近の大規模言語モデルがかなり優秀になってきているので、結果に差が出にくくなってきているかもしれません」。
差が出たのは顧客LTVである。DDLのみの場合、生成されたSQLはSUM(ss_ext_sales_price)で止まった。売上を合計しただけで、これはLTVではない。一方、YAML定義書を添えた場合は、売上合計をCOUNT(DISTINCT ss_customer_sk)で割る式が生成された。顧客数で割らなければ全体のLTVにならないという計算ルールが、定義書を通じて伝わった結果だ。
====================================================================== Apache Ossie Demo: LLM SQL Generation Comparison Database: PostgreSQL (local) Semantic Model: retail_model.yaml LLM: cortex (llama3.1-70b) ====================================================================== ====================================================================== QUESTION: What is the total sales revenue for June? ====================================================================== --- WITHOUT Ossie (DDL only) --- SQL: SELECT SUM(ss_ext_sales_price) FROM store_sales INNER JOIN date_dim ON store_sales.ss_sold_date_sk = date_dim.d_date_sk WHERE date_dim.d_month_name = 'June' Result: sum ------- 1160.73 --- WITH Ossie (DDL + Semantic Model) --- SQL: SELECT SUM(T2.ss_ext_sales_price) FROM date_dim AS T1 INNER JOIN store_sales AS T2 ON T1.d_date_sk = T2.ss_sold_date_sk WHERE T1.d_month_name = 'June' Result: sum ------- 1160.73 ====================================================================== QUESTION: Show me sales by brand. ====================================================================== --- WITHOUT Ossie (DDL only) --- SQL: SELECT i.i_brand, SUM(ss.ss_ext_sales_price) AS total_sales FROM item i JOIN store_sales ss ON i.i_item_sk = ss.ss_item_sk GROUP BY i.i_brand Result: i_brand | total_sales ------------+------------ Lululemon | 272.00 Coach | 269.97 Nissin | 181.86 Anker | 239.84 Sony | 559.93 Nike | 649.95 Starbucks | 274.89 Hydro Flask | 244.93 --- WITH Ossie (DDL + Semantic Model) --- SQL: SELECT T2.i_brand, SUM(T1.ss_ext_sales_price) FROM store_sales AS T1 INNER JOIN item AS T2 ON T1.ss_item_sk = T2.i_item_sk GROUP BY T2.i_brand Result: i_brand | sum ------------+------- Lululemon | 272.00 Coach | 269.97 Nissin | 181.86 Anker | 239.84 Sony | 559.93 Nike | 649.95 Starbucks | 274.89 Hydro Flask | 244.93 ====================================================================== QUESTION: What is the customer lifetime value? ====================================================================== --- WITHOUT Ossie (DDL only) --- SQL: SELECT SUM(ss_ext_sales_price) FROM store_sales Result: sum ------- 2693.37 --- WITH Ossie (DDL + Semantic Model) --- SQL: SELECT SUM(T1.ss_ext_sales_price) / COUNT(DISTINCT T1.ss_customer_sk) FROM store_sales AS T1 Result: ?column? -------------------- 538.6740000000000000 ===================================
トレードオフもある。DDL全文に加えてYAML全文を送るため、プロンプトは確実に長くなる。それでもSQLの正確性はこちらが上だった、というのが検証の結論である。この長さの問題は、後述するセマンティックレイヤーの出番につながる。

