query-semantic-model について
組織のセマンティックモデルを `revos cubes` でクエリし、結果をチャットに表示するスキルです。メンバーの確認、結合経路の選択、クエリ実行、表やグラフでの表示を案内します。
主な機能
- 01キューブとクエリ可能なメンバーの確認
- 02ローカル YAML から結合経路を把握
- 03結合ヒント付き Cube.js クエリの作成
- 04`revos` コマンドラインでのクエリ実行
- 05結果の表・グラフ表示
活用シーン
- 01月別またはカテゴリ別の売上確認
- 02セマンティックモデルのディメンション間で指標を比較
- 03曖昧な結合による誤った結果の回避
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ revosai/ skills --skill query-semantic-modelインストール後、Claude Code で/query-semantic-modelを入力して読み込まれたか確認します。
詳細
セマンティックモデルへのクエリ
組織のセマンティックモデルにクエリを実行してビジネス上の質問に答え、その結果をチャットに直接表示します。ノートブックも UI の切り替えも不要です。必要なものはすべて CLI で利用できます。revos cubes meta で利用可能なキューブ/メジャー/ディメンションを一覧表示し、revos cubes query で Cube.js クエリを実行して行を取得できます。
このスキルは、Bronze → Silver → Gold → Cubes の構築と適用が済んだあとの「最後のひと手間」です。セマンティックモデルを、実際に確認できる形にします。
ステップ 1:クエリ可能な対象を確認する
メンバー名を推測する前に、組織のセマンティックモデルが実際に公開しているものを一覧表示します。
revos cubes meta --json
応答にはキューブの配列が含まれ、各キューブには measures、dimensions、segments、name があります。キューブのメンバーは <CubeName>.<member> で指定します。たとえば、gold_order_items_enriched.count や gold_order_items_enriched.order_date です。
ユーザーの質問に合ったキューブとメンバーを選びます。利用可能なメンバーでは質問に答えられない場合は、その旨を伝えてください。メンバー名を作り出してはいけません。
ローカルのキューブファイルから結合グラフを把握する
revos cubes meta で既存のメンバーが分かり、ローカルの cubes/ フォルダーでキューブ同士のつながり方が分かります。cubes/ 内のすべての *.yml ファイルを読み込み、各キューブの joins: ブロックを抽出します。ここに、ユーザー定義のセマンティックモデルの接続方法が記述されています。例:
# cubes/gold_order_items_enriched.yml
apiVersion: revos/v1
kind: Cube
metadata:
name: gold_order_items_enriched
spec:
name: gold_order_items_enriched
joins:
gold_users_with_order_stats:
relationship: many_to_one
sql: "${CUBE}.user_id = ${gold_users_with_order_stats}.user_id"
gold_product_performance:
relationship: many_to_one
sql: "${CUBE}.product_id = ${gold_product_performance}.product_id"
これをもとに、任意のキューブのペアについて、異なる結合経路がいくつあるかを計算できます。これにより、Cube.js に結合経路を推測させずに、ステップ 2 で適切な経路を選べます。
注意 — システムキューブは cubes/ にありません。 RevOS は、スコアリング、セグメント、モデルオーバーレイ、事前集計オーバーレイ用のキューブをサーバー側でいくつか生成します。これらはプロジェクト内の YAML ファイルとして現れることはありません。すべてのメンバー一覧を確認するには、revos cubes meta を使う必要があります。したがって:
cubes/フォルダーを使って、ユーザー定義の結合グラフを確認します。- システムキューブを含む、実際にクエリできるすべてのメンバーを確認するには、
revos cubes metaを使います。
ユーザーの質問がシステムキューブに関わる場合(例:「セグメント別のユーザーを表示して」「スコア上位のエンティティを表示して」)、cubes/ から分かる結合グラフは不完全です。経路がないと決めつけず、メタデータを一覧表示して確認してください。
ステップ 2:クエリを作成する — 必要に応じて結合経路を固定する
Cube.js クエリは JSON オブジェクトです。最もよく使う形式は次のとおりです。
{ "measures": ["gold_order_items_enriched.count"] }
{
"measures": ["gold_order_items_enriched.count"],
"dimensions": ["gold_order_items_enriched.traffic_source"],
"order": { "gold_order_items_enriched.count": "desc" },
"limit": 10
}
時系列(グラフに最も適した形式):
{
"measures": ["gold_order_items_enriched.revenue"],
"timeDimensions": [
{
"dimension": "gold_order_items_enriched.order_date",
"granularity": "month",
"dateRange": "last 12 months"
}
]
}
目安:
- メジャー 1 つ + ディメンション 1 つ → カテゴリ別の棒グラフ。
- メジャー 1 つ +
granularityを指定したtimeDimensions1 つ → 時系列の棒グラフ。 - 複数のメジャーまたは複数のディメンション → ASCII テーブルのみで表示します。グラフにしようとしないでください。
- 予期しないクエリが何百万行も返さないように、常に
limit(デフォルトは 20)を指定してください。
結合パス — ヒントで固定する方法
Cube.js は、measures / dimensions / filters に含まれるキューブから結合パスを推測します。複数のパスでそれらがつながっている場合、Cube.js はそのうちの 1 つを選びます。そのため、どのパスを選ぶかによって答えが変わります。 これは、セマンティックモデルへのクエリで「数値がおかしい」というバグが発生する最大の原因です。
メンバーの前に、Cube.js にたどらせたいキューブをドット区切りで付けて、パスを明示します。最後のセグメントが実際のメンバーで、それより前の部分が結合ヒントです。例:
# no hint — Cube.js picks a path
gold_users_with_order_stats.lifetime_revenue
# hint: reach lifetime_revenue via the fact spine, then the users mart
gold_order_items_enriched.gold_users_with_order_stats.lifetime_revenue
ヒントの構文は、メジャー、ディメンション、フィルターメンバーで同じように使えます。
ヒントが必要な場合(要注意の兆候):
- クエリ内の 2 つのキューブが、
cubes/内の複数のパスでつながっています。よくある構成は、ファクトの幹線が 2 つのディメンションキューブに結合し、そのディメンションキューブの一方が、もう一方にも結合しているケースです(例:orders → users、orders → products、last_purchaser_idを介した products → users)。ヒントがないと、Cube.js が直感に反するパスを選び、気づかないうちに数値が二重計上または過少計上されることがあります。 - ユーザーの質問でパスが暗に指定されています(「ユーザーの国別の注文アイテムあたりの売上」と「ユーザーの国別のユーザーあたりの売上」など)。粒度もパスも数値も異なります。
- 件数が最小のキューブで可能な値より小さい、または最大のファクトで可能な値より大きくなっています。Cube.js が誤った結合を介して行を増幅した兆候です。
- キューブ内の結合チェーンに多対多の関係があります。必ずパスを固定してください。
- ディメンションのセットを変えていないのに、メジャーを追加したら数値が大きく変わりました(約 10% を超える変化)。Cube.js が結合パスを切り替えた可能性があります。
例 — 同じ質問に対する 2 つの答え。
キューブ(cubes/ から):
gold_order_items_enriched ──many_to_one──▶ gold_users_with_order_stats
gold_order_items_enriched ──many_to_one──▶ gold_product_performance
gold_product_performance ──many_to_one──▶ gold_users_with_order_stats (last_purchaser_id)
質問:「ユーザーの国別の売上合計。」
// Path A — order items → users directly (CORRECT for per-item revenue)
{
"measures": ["gold_order_items_enriched.revenue"],
"dimensions": [
"gold_order_items_enriched.gold_users_with_order_stats.country"
]
}
// Path B — order items → product → last_purchaser (WRONG: revenue gets
// attributed to the last buyer's country, not the actual buyer's)
{
"measures": ["gold_order_items_enriched.revenue"],
"dimensions": [
"gold_order_items_enriched.gold_product_performance.gold_users_with_order_stats.country"
]
}
2 つのクエリでは、まったく異なる数値が返されます。グラフの下の説明で、使用したパスとその理由を必ず明記し、ユーザーが選択ミスに気づけるようにしてください。
例 — 特定のパスを介したフィルター。
{
"measures": ["gold_order_items_enriched.revenue"],
"dimensions": ["gold_order_items_enriched.traffic_source"],
"filters": [
{
"member": "gold_order_items_enriched.gold_users_with_order_stats.country",
"operator": "equals",
"values": ["DE"]
}
]
}
パスを選ぶ際のルール:
- 質問の粒度に自然に合うファクトの幹線を通る、最も短いパスをデフォルトにします(注文あたりの売上 → 幹線は
gold_order_items_enriched、X を持つユーザー → 幹線はgold_users_with_order_stats)。 - 代替パスを黙って選ばないでください。妥当なパスが 2 つある場合は、クエリを実行する前に、どの粒度を意図しているかユーザーに確認してください。
revos cubes metaがドットを含むメンバー名(例:cubeA.cubeB.foo)を返した場合、それが完全修飾形式です。プレフィックスを削除せず、そのまま貼り付けてください。
ステップ 3:クエリを実行する
revos cubes query --query '<INLINE_JSON>' --json
1 行を超えるクエリの場合は、JSON を一時ファイルに書き込み、@ を付けて渡します。
cat > /tmp/q.json <<'EOF'
{
"measures": ["gold_order_items_enriched.revenue"],
"timeDimensions": [
{
"dimension": "gold_order_items_enriched.order_date",
"granularity": "month",
"dateRange": "last 12 months"
}
]
}
EOF
revos cubes query --query @/tmp/q.json --json
レスポンスは Cube.js の生のペイロードです。data は行の配列で、各行はクエリで使ったものと同じメンバー名をキーとするフラットなオブジェクトです。
API がエラーを返した場合は、メッセージをそのまま提示してください。よくある原因は、メンバー名の誤記(revos cubes meta で再確認してください)、適用されていないキューブ(revos status / revos apply)、または時間ディメンションではない列に対する dateRange です。
ステップ 4:チャットに結果を表示する
アシスタントの返信には、2 つのブロックを出力してください。どちらも等幅フォントで表示されるよう、フェンス付きコードにします。
4a. ASCII テーブル — 必須
返されたすべての行を表示してください。数値列は右寄せ、それ以外は左寄せにします。数値には桁区切りを付け、整数でない場合は小数点以下 2 桁に丸めます。長い文字列値は 32 文字で切り詰め、末尾に … を付けます。
traffic_source count
───────────────── ───────
Search 142,318
Organic 88,204
Email 41,907
Facebook 22,015
Display 9,471
4b. ASCII 棒グラフ — データの形状が対応している場合
クエリの結果が、1 つのメジャーと 1 つのディメンション(カテゴリまたは時系列)のみの場合は、表の下に横棒グラフを描画します。結果セット内の最大値を基準に、棒の幅を40 文字に拡大縮小します。塗りつぶし部分には █ ブロック文字を使い、末尾に半角スペースを 1 つ付けます。
Search ████████████████████████████████████████ 142,318
Organic ████████████████████████▊ 88,204
Email ███████████▊ 41,907
Facebook ██████▏ 22,015
Display ██▋ 9,471
時系列の例(古いものから新しいものへ、上から下):
2025-06 ███████████▊ € 41,200
2025-07 █████████████████▏ € 59,800
2025-08 ████████████████████▋ € 72,400
…
次の場合はグラフを省略し、表のみ表示します。
- 結果セットが空の場合。
- クエリにメジャーが 2 つ以上、ディメンションが 2 つ以上、または単一のスカラー値(ディメンションなし)が含まれる場合。
- すべてのメジャー値がゼロまたは null の場合。
単一のスカラー値(1 行、1 列)の回答では、数値を文章で述べた後に 1 行の表を表示するだけにします。グラフは不要です。
ステップ 5:結果を簡潔に説明する
出力を表示した後、測定対象、最も高い/低い項目、明らかな異常(例:月が欠けている、1 つのカテゴリが合計の大部分を占めている)について、平易な英語で短い文を 1~3 文追加します。表に表示されている事実だけにとどめ、原因を推測しないでください。
ユーザーが詳細を掘り下げたい可能性が高い場合は、次に尋ねられる具体的な追加クエリを1 つ提案します(「国別に同じ内訳を確認しますか?」)。チャットを簡潔に保つため、2 つ以上は挙げないでください。
ルール
- キューブ名やメンバー名をハードコードしないでください。クエリを作成する前に、必ず
revos cubes metaで確認してください。 - 複数のキューブを使うクエリを作成する前に、
cubes/*.ymlのjoins:ブロックを確認してください。必要なキューブ間に複数の経路がある場合は、Cube.js に推測させず、ドット区切りの join ヒントで明示的に 1 つを選んでください。 - システムキューブ(スコアリング、セグメント、モデルオーバーレイ)は
cubes/ではなく、revos cubes metaにのみ存在することを忘れないでください。これらの join は YAML には表示されず、meta の出力にのみ表示されます。 - 常に表を表示してください。グラフはデータ形状が対応している場合にのみ表示してください。
limitを設定したままにしてください(デフォルトは 20)。「上位 N 件」の質問では、limitを N に設定し、メジャーにorderを追加してください。- シェル上でクエリ JSON を安全に引用符で囲んでください。引用符を含む長いインライン文字列より、一時ファイルを使った
@/tmp/q.jsonを優先してください。 - クエリ結果をディスクに書き込んだり、Python のプロッターを呼び出したりしないでください。このスキルはチャット内でのみ使用します。キューブグラフの静的 PNG 可視化には、
visualize-semantic-modelを使用してください。 revos cubes metaがキューブを返さない場合は停止し、まずrevos applyを実行するようユーザーに伝えてください。