❄️

SnowflakeでGemini APIを安全に叩く方法:Streamlit×生成AIアプリ開発の備忘録

に公開

0.前置き

この記事は、Streamlit in Snowflake(通称SiS)環境で、Gemini APIを安全に呼び出す方法をまとめた技術メモです。

2025年4月10日時点で同じ箇所で躓いた、あるいは悩んだ方がインターネットという広大な海の中では見つからなかったこと、そして疑問の解決にChatGPTを使っても期待した答えが得られなかったためこちらの記事を執筆することにしました。

なにか特別すごいことを学べる記事ではありませんが、小ネタ程度に流し読みしていただけると幸いです。なお、結論だけを読みたい方は3.感想へ進んでください。

1.対象読者

  • Snowflakeユーザー
  • Streamlit in Snowflakeユーザー
  • Snowflakeは触っていないけど、生成AIのAPIを叩いたことがあるユーザー

2.本題

2.1.「Snowflakeと生成AIの関係」

Snowflakeは「Snowflake Cortex AI」というサービスを提供しており、わざわざモデルをダウンロードしたり、API KEYを発行しなくても簡単に最新の生成AIを利用することができます。直近ではLlama4が利用可能となったニュースが発表され、サービス開始当初の「ひと世代前のモデルしかないなぁ・・・」みたいな不満は現状ほぼ解消されています。
https://www.snowflake.com/ja/blog/meta-llama-4-now-available-snowflake-cortex-ai/?utm_cta=website-workload-data-warehouse-self-service-analytics%3Fwtime%3Fwtime%3D{seek_to_second_number}&lang=es%3Fwtime%3Fwtime%3Fwtime

またオープンモデルだけでなくAnthropic社やMicrosoft社とのパートナーシップを締結・強化したことで、より性能の高いモデルを利用できるようになりました。Snowflakeのセキュアな環境に保存されたデータとこれらの性能の高いモデルを組み合わせることで、実務に直結した分析や、アプリ開発なども手軽に行えるようになりました。
https://www.snowflake.com/ja/news/press-releases/snowflake-and-anthropic-team-up-to-bring-claude-models-directly-to-the-ai-data-cloud/
https://www.snowflake.com/ja/news/press-releases/snowflake-microsoft-azure-openai-service/

しかし、一部のリージョンでしか使用できないモデルが存在したり(※クロスリージョン推論で運用回避可能)、コストが少々分かりづらい&厳密に計算してはいませんが若干割高という話を耳にしているので、万人におすすめできるかと言われるとケースバイケースかなぁ・・・というのが筆者の感想です。

https://www.snowflake.com/legal-files/CreditConsumptionTable.pdf

そもそもSnowflake社から提供されるLLMではなく、自社で別途契約しているAzure OpenAIやAmazon BedrockとSnowflake内のデータを組み合わせたいシーンも存在するはずです。そういったユーザー向けに、snowflake社から質の良いクイックスタートが提供されています。興味がある方は是非クイックスタートにそって手を動かすことをオススメします。
https://quickstarts.snowflake.com/guide/using_snowflake_and_azure_openai_for_a_rag_workflow/index.html?index=..%2F..index#0

https://quickstarts.snowflake.com/guide/getting_started_with_bedrock_streamlit_and_snowflake/index.html#0

2.2.「SnowflakeでGeminiのAPI使う方法がわからない」

さて、クイックスタートや有識者たちのブログを眺めていて「Geminiを使った事例がないな・・・」と感じました。Geminiは、ChatGPTやClaudeと同じようにブラウザベースでGoogleが提供する最新の生成AIモデルを試すことができます。加えて他のサービスと最も異なる点は、条件付きですが「API KEYを無料で利用できる点」です。API KEYを発行したい場合は、Google AI Studioへアクセスしてください。
https://ai.google.dev/aistudio?hl=ja

ちなみに「条件」とは、Gemini API の無料枠を使用する場合、データが学習に使われることを指します。またレートリミットも厳し目です。規約については常に最新のものを各自の責任で確認するようにしてください。
https://ai.google.dev/gemini-api/terms?hl=ja

生成AIアプリを個人開発する際、やはりネックになってくるのはAPI利用費です。条件付きではありますが、API利用が無料なGoogle AI Studioは神サービスだと思っています。私はプロダクト個人開発初期の試行錯誤している段階でお世話になっています(もちろんダミーデータを使って)。

話を戻しますが、Gemini APIをsnowflakeで使用する場合の各種設定情報がインターネット上に見つからなかったので、自分なりに試してうまくいった内容と試行中に感じた個人的な感想を共有します。

2.3.「クイックスタートを参考にGeminiを使ってみる」

SiSをすでに利用できるユーザー向けのおおまかな手順は以下のとおりです。

  1. NETWORK RULEを作成する
  2. SECRETを作成する
  3. 1, 2を使ってEXTERNAL ACCESS INTEGRATIONを作成する
  4. SiS中で使用するLLM FUNCTION(UDF)を作成する
  5. 作成したINTEGRATIONやFUNCTIONのUSAGE権限をCREATE STREAMLIT権限を持つロールに与える
  6. SiSのApp settings>External networksで3で作成したEXTERNAL ACCESS INTEGRATIONを有効にする
  7. SiS内で4で作成したUDFを呼び出す

なお、GeminiのAPIを利用する場合google-genaiパッケージで書く方が若干ですが書く量は減ります。ただし、LLMごとに異なるパッケージを使うのが面倒なので、今回は互換性のあるOpenAIライブラリを使用しました。

snowsight
-- 1.ネットワークルールオブジェクトを作成
CREATE OR REPLACE NETWORK RULE gemini_network_rule
 MODE = EGRESS
 TYPE = HOST_PORT
 VALUE_LIST = ('generativelanguage.googleapis.com'); -- "https://"は不要.

-- 2.シークレットオブジェクトを作成
CREATE OR REPLACE SECRET gemini_api_key
 TYPE = GENERIC_STRING
 SECRET_STRING = 'YOUR_GEMINI_API_KEY';

-- 3.外部アクセス統合オブジェクトを作成
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION gemini_integration
 ALLOWED_NETWORK_RULES = (gemini_network_rule)
 ALLOWED_AUTHENTICATION_SECRETS = (gemini_api_key)
 ENABLED=TRUE;

-- 4.LLM FUNCTION(UDF)を作成
CREATE OR REPLACE FUNCTION gemini(prompt STRING)
returns string
language python
runtime_version=3.11
handler = 'ask_gemini'
external_access_integrations=(gemini_integration)
packages = ('openai')
SECRETS = ('cred' = gemini_api_key )
as
$$
import _snowflake
from openai import OpenAI # google-genai パッケージでも可
client = OpenAI(
    api_key=_snowflake.get_generic_secret_string("cred"),
    base_url="https://generativelanguage.googleapis.com/v1beta/openai/"
    )

def ask_gemini(prompt):
    response = client.chat.completions.create(
        model="gemini-2.0-flash",
        n=1,
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": f"{prompt}"}
        ]
    )
    return response.choices[0].message.content
$$;

-- 5.USAGE権限をCREATE STREAMLIT権限を持つロールに付与
GRANT USAGE ON INTEGRATION GEMINI_INTEGRATION TO ROLE YOUR_ROLE_NAME;
GRANT USAGE ON FUNCTION GEMINI(STRING) TO YOUR_ROLE_NAME;

6.SiS上でEXTERNAL ACCESS INTEGRATIONを有効化

streamlit in snowflake
# 7.UDFを呼び出す
import streamlit as st
from snowflake.snowpark.context import get_active_session

session = get_active_session()

# 4で作成したUDF「gemini」にプロンプト「Explain to me how AI works within 100 words」を入力
result = session.sql(f"""SELECT gemini('Explain to me how AI works within 100 words')""").collect()[0][0]
st.write(result)

2.4.「私はLLMの呼び出しをSQLでやりたくないんだ!💢」

SnowflakeのLLMチュートリアルの出来は良いのですが、基本LLMを実行する際にSQLで呼び出しています。チュートリアル「Getting Started with Azure OpenAI, Streamlit and Snowflake」の該当部分を見てみましょう。「CHATGPT_IMAGE」というUDFをSiS中から「session.sql」で呼び出しています。

snowsight
CREATE OR REPLACE FUNCTION CHATGPT_IMAGE(instructions STRING, list STRING, user_context STRING)
returns string
language python
runtime_version=3.8
handler = 'ask_chatGPT'
external_access_integrations=(OPENAI_INTEGRATION)
packages = ('openai')
SECRETS = ('cred' = chatgpt_api_key )
as
$$
import _snowflake
import json
from openai import AzureOpenAI
client = AzureOpenAI(
    api_key=_snowflake.get_generic_secret_string("cred"),
    api_version='2023-12-01-preview',
    # Update Resource and Model to the base_url below
    base_url="https://{your-resource-name}.openai.azure.com/openai/deployments/{model}/extensions"
    )
def ask_chatGPT(instructions, list_, user_context):
    response = client.chat.completions.create(
    model='{model}', # Update your model/deployment name from Step 2
    messages = [
        {
            "role": "system",
            "content": json.dumps({
                "SYSTEM": f"Follow these: {instructions}",
                "CONTEXT_LIST": f"Use this list to select from {list_}",
                "USER_CONTEXT": f"Use this image for your response: {user_context}"
            })
        }
    ],
    max_tokens=2000 )
    return response.choices[0].message.content
$$;
streamlit in snowflake
# Use the job description to write the job to a table and run the function against it:
if(st.button('Ask ChatGPT')):
    result = session.sql(f"""SELECT chatgpt_image('{system}','{clothing_links}','{image_string}')""").collect()
    st.header('Answer')
    st.write(result[0][0].replace('"','')) 

https://quickstarts.snowflake.com/guide/getting_started_with_azure_openai_streamlit_and_snowflake_for_image_use_cases/index.html#0

正直、好みの範疇ですが私はStreamlitでダッシュボードやアプリを開発するのであれば極力pythonで書きたい人間です。ですのでクイックスタートで紹介されているようなUDFを作成せずに、SiS内で自由にPythonでLLMを試行錯誤できる方法を考えました。

つまり、やりたいことは以下のコードの書き方をSiS内で実現することです。

python
from openai import OpenAI
client = OpenAI(
    api_key="gemini_api_key",
    base_url="https://generativelanguage.googleapis.com/v1beta/" 
)

response = client.chat.completions.create(
    model="gemini-2.0-flash",
    n=1,
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {
            "role": "user",
            "content": "Explain to me how AI works within 100 words."
        }
    ]
)
 
print(response.choices[0].message)

2.5.「Streamlit in Snowflake上でapi keyを参照するベストプラクティスがわからない」

さて、ここでやっとタイトル回収です。上記の書き方をSiS内で実現するには引数api_keyに値を代入する必要があります。もちろん外部アクセス統合で使用したapi keyの値をハードコーディングすれば動きます。論外ですが。

次に思いつくのは、普段ローカルでLLMを触っている筆者的には「.envファイルからapi keyの値を読み込む」方法です。実はこれは簡単にできます。SiSの左のペインに.envファイルをアップロードするだけです。アップロード後はload_dotenv()などで読み込めばOKです。

streamlit in snowflake
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()
api_key = os.environ["gemini_api_key"]

client = OpenAI(
    api_key=api_key,
    base_url="https://generativelanguage.googleapis.com/v1beta/"
)

# 以下略

ただし、この方法はおそらく非推奨だと思われます。せっかくSECRETにAPI KEYを登録した意味がありません。ですので次はSECRETに登録したAPI KEYを呼び出す方法を考えます。

SECRETに登録したAPI KEYを呼び出すには、_snowflake.get_generic_secret_string(generic_string_secret_name)を使う必要があります。私が試した限りでは引数generic_string_secret_nameはチュートリアルのようにUDFを作成する時か、あるいは下記のようにstreamlitアプリに対してEXTERNAL_ACCESS_INTEGRATIONS プロパティを明示的にインテグレーションに設定した時にしか定義できなさそうでした。UDFをできるだけ作成しない方針でここまで進めてきたので、今回はALTER STREAMLITでSECRETSを定義しましょう。

snowsight
-- https://(中略)/streamlit-apps/<DB名>.<スキーマ名>.<Streamlitオブジェクト名>
-- ↑ブラウザに表示されている URLの末尾がオブジェクト名です。App名ではないことに注意。

-- Streamlit オブジェクトと外部アクセス統合のマッピング
ALTER STREAMLIT <DB名>.<スキーマ名>.<Streamlitオブジェクト名>
  SET EXTERNAL_ACCESS_INTEGRATIONS = (gemini_integration)
  SECRETS = ('cred' = <DB名>.<スキーマ名>.gemini_api_key);
streamlit in snowflake
from openai import OpenAI
import _snowflake # ←必須

# ALTER STREAMLITで設定したSECRETSのラベルcredを引数に指定
api_key = _snowflake.get_generic_secret_string('cred')

client = OpenAI(
    api_key=api_key,
    base_url="https://generativelanguage.googleapis.com/v1beta/"
)

# 以下略

以上が、普段ローカルでLLMを触っている環境に寄せた「SiSでGemini APIを叩く方法」です。

3.感想

この記事を書きながらふと「LLMを実験段階で関数化するのが嫌なだけでUDFが嫌いなわけではない。API KEYを返すだけのUDFを作れば楽なのではないか」と思いました。

ALTER STREAMLITを使ってAPI KEYをシークレットから呼ぶ場合、アプリごとにALTER STREAMLITをする必要があります。面倒です。

以下の書き方であれば、UDFを書く必要はありますが、アプリごとにALTER STREAMLITを実行せず、同じUDFを使いまわせます。LLM部分は関数化せず、普段の書き方で試行錯誤が可能となります。

snowsight
-- シークレットを返すためだけの関数を作成
CREATE OR REPLACE FUNCTION get_gemini_secret()
RETURNS STRING
LANGUAGE PYTHON
RUNTIME_VERSION = 3.11
HANDLER = 'get_secret'
EXTERNAL_ACCESS_INTEGRATIONS = (GEMINI_INTEGRATION)
SECRETS = ('cred' = GEMINI_API_KEY)
AS
$$
import _snowflake

def get_secret():
  secret = _snowflake.get_generic_secret_string('cred')
  return secret
$$;
streamlit in snowflake
from snowflake.snowpark.context import get_active_session
from openai import OpenAI

session = get_active_session()
api_key = session.sql("""select GET_GEMINI_SECRET()""").collect()[0][0]

client = OpenAI(
    api_key=api_key,
    base_url="https://generativelanguage.googleapis.com/v1beta/openai/"
)

# 以下略

今のところこの方法が一番手数が少なく、かつ(私にとって)普段通りにLLMを試行錯誤できる方法となります。

ただ、この方法に満足しているわけではなく、欲を言えば例えばGoogle ColaboratoryのようにSiSの画面上でシークレットや外部アクセス統合を操作できると良いなぁと思っています。シークレットの呼び出しも、ALTER STREAMLITやUDFの作成をせずとも直接SiS上からできると利便性が上がるのに・・・と思っています。

P.S.
どなたかもっと良い方法に心当たりがあれば、是非私のXの以下のポストにレスポンスください!
https://x.com/paru_tann0707/status/1911671017841528997

Discussion