Cortex Anlaystでリファレンス付きのネイティブアプリを作成する
はじめに
Cortex Agentsが出たりSnowflake Intelligenceが出たりして単体のCortex Analystの存在感はやや薄くなっていますが、ネイティブアプリやリファレンスと組み合わせると拡張性は高いのではと思っています。
前提事項
Cortex AnalystはSnowflakeのAI系の機能の一つで、構造化データに対してユーザーが自然言語で質問すると、内部でSQLクエリが実行され、データについての回答が返ってくるようなアプリケーションを構築できます。Cortex Analystのコアとなる機能は、LLMを用いて自然言語の入力からSQLのクエリを出力するText to SQLの部分で、この部分がREST APIとして提供されています。
ネイティブアプリは、正確にはSnowflake Native App Frameworkのことで、Snowflake内部でアプリを作成し、そのアプリを公開することのできるフレームワークを指します。Streamlit in Snowflakeでアプリを作成してもアカウント内部でしか利用できませんが、ネイティブアプリにすることで外部に公開することができ、プロダクトとして提供することができます。
リファレンスとはその名の通り参照のことで、これを使うことでアプリケーション外部のテーブルにアクセスすることができます。
Cortex Analyst × ネイティブアプリ × リファレンス の何が嬉しいのか
ネイティブアプリでアプリを他のアカウントに公開した時、そのアカウントにあるデータソースをアプリに紐づけることができます。
つまり、ユーザー環境(コンシューマー)のデータに問い合わせられるText to SQLのアプリを開発環境(プロバイダー)側で構築できるようになり、より拡張性高く顧客に提供できます。
Cortex Analystを使ったネイティブアプリの場合、これまではプロバイダー側でアプリが使うデータセットを全てパッケージ化して提供する方法しかありませんでしたが、リファレンスを使うことで「開発者はコンシューマーのデータをプロバイダーアカウント内に用意することなくアプリを開発・公開し、顧客が自社環境のデータに問い合わせるアプリを使える」ようになり、アプリ提供や技術検証の幅が広がります。
検証のストーリー

- 
プロバイダー(開発側) - あらかじめ顧客から開発用データ(サンプルデータやダミーデータ)を受領しておく
- セマンティックモデルを作る都合で最低限同じ形式のデータは必要
- セマンティックモデル≒Cortex Analystを使う際に必要なメタデータ
 
- アプリケーションパッケージ(アプリケーションに必要なデータコンテンツ、アプリケーションロジック、メタデータ、セットアップスクリプトをカプセル化したコンテナー)を作成
- アプリケーションパッケージからアプリケーションを作成
- アプリケーションパッケージをコンシューマーに公開
- 一般公開と特定のコンシューマーへの公開の2パターンがあるが、ここでは特定のコンシューマーに公開する想定
 
- あらかじめ顧客から開発用データ(サンプルデータやダミーデータ)を受領しておく
- 
コンシューマー(提供先): - 公開されたネイティブアプリをインストール
- その後本番用データ(顧客データ)をリファレンスで紐付けてアプリを利用
 ≒顧客は自社のデータをCortex Analystで使えるようになる
 
具体的な検証内容

大きく3つの手順で検証をしました。
- プロバイダー:検証用データ、ネイティブアプリ用のファイルの準備
- プロバイダー:アプリケーションを作成し公開
- コンシューマー:アプリケーションをインストールし利用
検証で用いたファイルはGitHubにもあげていますので参考にしてもらえればと思います。
ファイルのディレクトリツリー
.
├── native_app                        # ネイティブアプリ関連一式
│   ├── not_upload                    # アプリに同梱しないワークシート(手元で実行するクエリ)
│   │   ├── worksheet_consumer.sql    # プロバイダーでの処理
│   │   └── worksheet_provider.sql    # コンシューマーでの処理
│   └── upload_file                   # アプリに同梱するファイル群
│       ├── scripts
│       │   └── setup.sql             # アプリのセットアップスクリプト
│       ├── streamlit
│       │   ├── cortex_analyst_app.py # Streamlitアプリ本体(セマンティックモデル内包)
│       │   └── environment.yml 
│       ├── manifest.yml              # マニフェストファイル
│       └── readme.md
├── preparation                       # サンプルデータ準備
│   ├── data                          # 取込み用CSV
│   │   ├── daily_revenue.csv
│   │   ├── product.csv  
│   │   └── region.csv
│   └── load_data.sql                 # データのアップロード処理
└── README.md                         # プロジェクト概要・手順(クイックスタート/フォルダ説明)
今回の検証では権限周りをちゃんと考えるのが面倒だったのでサンドボックス環境でACCOUNTADMINを使用していますが、ちゃんとした環境で実行する時はロールを適切に払い出すなどをするべきなので注意が必要です。
1. プロバイダー:検証用データ、ネイティブアプリ用のファイルの準備
手順は簡潔に説明しつつ、検証のメインとなるファイルは内容も説明したいと思います。
データのアップロード
- 
Cortex Analsytのチュートリアルからデータソース、セマンティックモデル、アプリのソースコードを入手
- データソースはdaily_revenue、product_dim、region_dimの3つ
 
- データソースは
- データソースはそのままプロバイダー、コンシューマーの両方にアップロード
- 本来はプロバイダーが開発用データ、コンシューマーが顧客環境データだが、ここでは同じものを使用
- セマンティックモデルとアプリのソースコードはネイティブアプリ用に使うのでここではアップロードしない
 

ネイティブアプリ用のファイルの準備
基本的に上述のGitHubに格納しているファイルの通りなのですが、いくつかのファイルについては中身の説明をしようと思います。
ファイルの実装詳細についてはGitHubを適宜参照してもらえればと思います。
1. setup.sql:アプリケーションが作成されるときに自動で実行されるSQL文
ここではアプリ作成時に以下のオブジェクトが作られるようになっています。
- 開発用データ(daily_revenue、product_dim、region_dim)に対応する3つのビュー
- 本番用データをアプリケーションに紐づけられるようにするためのストアドプロシージャ
- 正確には、本番用データのテーブルから、アプリケーション内部にビューを作る機能
 
- Streamlitオブジェクト:アプリ本体
setup.sqlの中身(抜粋)
-- アプリケーションロールの作成とスキーマの作成
CREATE APPLICATION ROLE IF NOT EXISTS app_public;
-- (...略...)
-- ビューの作成
CREATE VIEW IF NOT EXISTS code_schema.daily_revenue_view
  AS SELECT *
  FROM shared_data.daily_revenue;
-- (...略...)
CREATE VIEW IF NOT EXISTS code_schema.product_dim_view
  AS SELECT *
  FROM shared_data.product_dim;
-- (...略...)
CREATE VIEW IF NOT EXISTS code_schema.region_dim_view
  AS SELECT *
  FROM shared_data.region_dim;
-- (...略...)
-- 各リファレンス用のコールバックストアドプロシージャを作成
-- prod_daily_revenue_view 用
CREATE OR REPLACE PROCEDURE code_schema.register_prod_daily_revenue_view(ref_name STRING, operation STRING, ref_or_alias STRING)
RETURNS STRING
LANGUAGE SQL
AS $$
-- (...略...)
$$;
-- (...略...)
-- prod_product_dim_view 用
CREATE OR REPLACE PROCEDURE code_schema.register_prod_product_dim_view(ref_name STRING, operation STRING, ref_or_alias STRING)
RETURNS STRING
LANGUAGE SQL
AS $$
-- (...略...)
$$;
-- (...略...)
-- prod_region_dim_view 用
CREATE OR REPLACE PROCEDURE code_schema.register_prod_region_dim_view(ref_name STRING, operation STRING, ref_or_alias STRING)
RETURNS STRING
LANGUAGE SQL
AS $$
-- (...略...)
$$;
-- (...略...)
-- Streamlitオブジェクトの追加
CREATE STREAMLIT IF NOT EXISTS code_schema.cortex_analyst_app
  FROM '/streamlit'
  MAIN_FILE = '/cortex_analyst_app.py'
;
-- (...略...)
2. manifest.yml:アプリを作成・管理するために必要な設定や情報を含むファイル
以下が含まれています。
- artifacts:アプリが使用するリソース
- configuration:アプリの設定関連
- references:コンシューマー側でアプリがリクエストする参照の名前
- セットアップスクリプトのストアドプロシージャをコンシューマーアカウントで使う際に必要
 
manifest.ymlの中身
manifest_version: 1
artifacts:
  setup_script: scripts/setup.sql
  readme: readme.md
  
configuration:
  log_level: debug
  trace_level: always
references:
  - prod_daily_revenue_view:
      label: "Product Daily Revenue View"
      description: "Product Daily Revenue View"
      privileges:
        - SELECT
      object_type: TABLE
      register_callback: code_schema.register_prod_daily_revenue_view
  - prod_product_dim_view:
      label: "Product Dimension View"
      description: "Product Dimension View"
      privileges:
        - SELECT
      object_type: TABLE
      register_callback: code_schema.register_prod_product_dim_view
  - prod_region_dim_view:
      label: "Region Dimension View"
      description: "Region Dimension View"
      privileges:
        - SELECT
      object_type: TABLE
      register_callback: code_schema.register_prod_region_dim_view
3. cortex_analyst_app.py:アプリ本体のファイル
チュートリアルで入手したセマンティックモデルをアプリのコードの中に埋め込み、少し修正したものを使用しています。
- 
semantic_model_provider、semantic_model_consumerという二つのセマンティックモデルをソースコード内に埋め込み- 開発環境:semantic_model_providerのみ使える
- 顧客環境:どちらも使える。本番データはsemantic_model_consumerに対応
 
- 開発環境:
- アプリ内でどちらのセマンティックモデルを使うかを選べる修正
- アプリはStreamlitで作成したチャットUI
cortex_analyst_app.pyの中身(抜粋)
# (...略...)
# セマンティックモデルを準備する(直接埋め込む)
semantic_model_provider = """
(...略...)
"""
semantic_model_consumer = """
(...略...)
"""
# (...略...)
# ------------------------------------ ページの構成要素を作る ------------------------------------
st.title("Cortex analyst")
# サイドバー
with st.sidebar:
    # セマンティックモデル選択
    selected_datasource = st.selectbox(
        "セマンティックモデルを選択してください:",
        ["semantic_model_provider", "semantic_model_consumer"],
        key="semantic_model_selector"
    )
# (...略...)
2. プロバイダー:アプリケーションを作成し公開
アプリケーションパッケージの作成
-- アプリケーションパッケージの作成
CREATE APPLICATION PACKAGE cortex_analyst_package_sample;
-- 確認
SHOW APPLICATION PACKAGES;
SHOWコマンドを実行すると作成できたことが確認できます。

このアプリケーションパッケージ内部にスキーマやステージ、テーブルを作成していきます。テーブルはあらかじめアップロードしていたものをクローンして作成しています。
-- スキーマを作成
CREATE SCHEMA stage_content;
-- 名前付きステージを作成
CREATE OR REPLACE STAGE cortex_analyst_package_sample.stage_content.cortex_analyst_stage
  FILE_FORMAT = (TYPE = 'csv' FIELD_DELIMITER = '|' SKIP_HEADER = 1);
-- 外部のデータソースと連携するための作業
-- アプリケーションパッケージにREFERENCE_USAGE権限を付与
GRANT REFERENCE_USAGE ON DATABASE sample_revenue_timeseries
  TO SHARE IN APPLICATION PACKAGE cortex_analyst_package_sample;
-- アプリケーションパッケージにスキーマを作成
CREATE SCHEMA IF NOT EXISTS shared_data;
-- テーブルを作成
CREATE TABLE IF NOT EXISTS cortex_analyst_package_sample.shared_data.daily_revenue
CLONE sample_revenue_timeseries.revenue_timeseries.daily_revenue;
CREATE TABLE IF NOT EXISTS cortex_analyst_package_sample.shared_data.product_dim
CLONE sample_revenue_timeseries.revenue_timeseries.product_dim;
CREATE TABLE IF NOT EXISTS cortex_analyst_package_sample.shared_data.region_dim
CLONE sample_revenue_timeseries.revenue_timeseries.region_dim;
-- スキーマとテーブルの使用権を付与
GRANT USAGE ON SCHEMA cortex_analyst_package_sample.shared_data 
    TO SHARE IN APPLICATION PACKAGE cortex_analyst_package_sample;
GRANT SELECT ON TABLE cortex_analyst_package_sample.shared_data.daily_revenue
  TO SHARE IN APPLICATION PACKAGE cortex_analyst_package_sample;
GRANT SELECT ON TABLE cortex_analyst_package_sample.shared_data.product_dim
  TO SHARE IN APPLICATION PACKAGE cortex_analyst_package_sample;
GRANT SELECT ON TABLE cortex_analyst_package_sample.shared_data.region_dim
  TO SHARE IN APPLICATION PACKAGE cortex_analyst_package_sample;
Snowsight上のデータエクスプローラーからも作成できたことが確認できます。

CORTEX_ANALYST_STAGEにupload_file/のファイル一式をアップロードしていきます。Snowsightで実行できます。

アプリケーションの作成
ここまでで用意したアプリケーションパッケージからアプリケーションを作成していきます。
コンシューマーにアプリケーションパッケージを公開するだけならプロバイダー内にアプリケーションを作る必要はないですが、動作確認などを考えるとプロバイダーでも作るのがよいと思います。
-- アプリケーションを作成(ファイルアップロード後)
CREATE APPLICATION cortex_analyst_app_sample
  FROM APPLICATION PACKAGE cortex_analyst_package_sample
  USING '@cortex_analyst_package_sample.stage_content.cortex_analyst_stage';
-- 作成されたことを確認
SHOW APPLICATIONS;
こちらもSHOWコマンドで作成されたことが確認できます。

データベースエクスプローラーからも確認できます。

アプリの動作確認
アプリケーションを作成したので、プロバイダー環境でアプリが利用できるようになります。
- カタログ→アプリから作成したアプリを押下
- 上部のタブを押下するとアプリを利用できる


semantic_model_providerにすると、必要なデータソースがアプリケーションに含まれているのでアプリが問題なく動作します。

一方コンシューマー環境のデータはまだないので、セマンティックモデルをsemantic_model_consumerにすると404エラーが出ます(意図通り)。

アプリケーション公開
ここの作業はSnowsight上で行います。
プロバイダーStudioを開き、リストを作成します。

リスト名をつけ、「指定したコンシューマーのみ」を選択して次へを押します
- 今回は特定のコンシューマーへの提供を想定
   
サイドバーのガイドに従って設定していきます。
- Cortex Analystを使う場合、タイトルは作成したアプリケーション名と揃える必要があるので注意!
- 正確には違うタイトルでも問題ないが、コンシューマー側でインストール時に行う作業が増える
  
  
   
右上の公開ボタンを押して公開します。

3. コンシューマー:アプリケーションをインストールし利用
アプリケーションのインストール
データ共有→プライベート共有を開くと、共有されているアプリが表示されるので、「取得」を押してインストールします。アンインストールしてしまっても、データ製品→プライベート共有から再びインストール可能です。

作成したアプリケーション名と同名になっていることを確認してから取得します。

データベースエクスプローラーを見ると、アプリケーションが入っていることが確認できます。この時点ではプロバイダー側のアプリケーションと全く同じ構成です。

リファレンスを付与
-- リファレンス設定を実行
CALL cortex_analyst_app_sample.code_schema.register_prod_daily_revenue_view(
  'prod_daily_revenue_view', 
  'ADD', 
  SYSTEM$REFERENCE('table', 'sample_revenue_timeseries.revenue_timeseries.daily_revenue', 'PERSISTENT', 'SELECT')
);
CALL cortex_analyst_app_sample.code_schema.register_prod_product_dim_view(
  'prod_product_dim_view', 
  'ADD', 
  SYSTEM$REFERENCE('table', 'sample_revenue_timeseries.revenue_timeseries.product_dim', 'PERSISTENT', 'SELECT')
);
CALL cortex_analyst_app_sample.code_schema.register_prod_region_dim_view(
  'prod_region_dim_view', 
  'ADD', 
  SYSTEM$REFERENCE('table', 'sample_revenue_timeseries.revenue_timeseries.region_dim', 'PERSISTENT', 'SELECT')
);
付与するとPROD_というビューが3つ追加されます(本番想定のデータ)。

アプリケーションの実行
実行方法はプロバイダー環境と同じです。
- カタログ→アプリから作成したアプリを押下
- 上部のタブを押下するとアプリを利用できる
semantic_model_consumerでも実行できるようになります。

おわりに
リファレンス機能とネイティブアプリを使うことで、顧客データを使ったアプリを開発して提供することが簡単になります。今回はそれがCortex Analystを使ったアプリでもできるようになった、という検証でした。
少し冷静に考えると「そもそもコンシューマー環境もおなじSnowflakeだから、顧客だけでアプリ開発まで完結すればいいのでは?」という気もしてくるのですが、Snowflakeを使ったソリューションやプロダクトを構想する時にプロバイダー側が顧客にアプローチする手段として今回のようなケースは出てくると思っており、ビジネスニーズまで視野を広げると今回の検証も意義があるんじゃないかなと思っています。
Cortex Analystだけでなく、今後拡張されていくであろうSnowflakeのAI系サービスを活用する際にもこの検証を参考にできるといいなと祈りつつ終わりたいと思います。




Discussion