
Snowflake Cortex Agents のバージョン管理および GitHub Actions での CI/CD を考えみた
こんにちは、データ事業本部のキタガワです。
最近 Snowflake の Cortex Agents に入門しました。
今回は Cortex Agents のバージョニング周りを、特に GitHub Actions を用いた CI/CD の観点から検証します。
なお、こちらで作成したソースコードは以下のリポジトリで公開しています。
Cortex Agents におけるバージョニング
Cortex Agents はバージョン管理ができます。
バージョンは Snowsight の AI & ML > Agents から該当の Cortex Agent を選択し、右上の時計アイコンから確認できます。

この Cortex Agent だと Version 1 から 4 まであり、未 Publish の Draft バージョンが 1 つある状態です。
バージョンには大きく分けて、 live バージョン と named バージョンの 2 種類があります。Draft は live バージョンに相当し、開発用として使うことが想定されています。開発が完了し、本番環境で使う場合には live バージョンをコミットして新しい named バージョンを作成します。named バージョンにはエイリアスやコメントをつけることができます。

named バージョン
named バージョンは Cortex Agent 作成時点で VERSION$1 が自動的にコミットされ、以後コミットごとに VERSION$2, VERSION$3, ..., VERSION$N というように増えていきます。コミット後に編集できるのはエイリアスやコメントのみで、Cortex Agent 自体の設定は変更できません。これにより安定したバージョン運用ができるようになっています。
live バージョンをコミットするには以下のような SQL を実行します。
-- Commit the live version to create a named version
ALTER AGENT my_agent COMMIT
COMMENT = 'Production release for Q1';
またステージから直接 named バージョンを作成することもできます。
-- Create a named version from a stage
ALTER AGENT my_agent ADD VERSION FROM @my_stage/agents/my_agent
COMMENT = 'Imported from feature branch';
エイリアス
エイリアスは開発者やプログラムが特定のバージョンを識別しやすくするために使うことができます。production や staging, canary, rollback などがよく使われます。エイリアスを付与することでバージョンの識別番号に依存することなく、API や SQL で特定のバージョンを指定することができるようになります。
エイリアスの付与には以下のような SQL を実行します。
-- Assign an alias to a named version
ALTER AGENT my_agent
MODIFY VERSION VERSION$3 SET ALIAS = production;
エイリアスは付与した後も別の named バージョンに付け直すことができるので、本番デプロイ後の切り戻しにも使用できます。
またバージョンショートカットとして以下のような識別子が別途用意されています。[1]
| Shortcut | Description |
|---|---|
| LIVE | The current live version (displayed as DRAFT in the UI) |
| FIRST | The first committed named version |
| LAST | The most recently committed named version |
| DEFAULT | The version set as the default for the agent |
特に DEFAULT は Snowflake CoWork でもルーティングされるバージョンなので GitHub Actions でのデプロイ後には、そのバージョンを DEFAULT に設定した方が良いと思います。[2]
特定のバージョンを DEFAULT に設定するには以下のような SQL を実行します。
-- Set the default version
ALTER AGENT my_agent
SET DEFAULT_VERSION = 'VERSION$3';
GitHub Actions での CI/CD 検討
ここまでの内容を踏まえて GitHub Actions での CI/CD を検討します。
バージョニング機能を利用した Cortex Agents の開発からデプロイまでの CI/CD のワークフローとしては次のように設定しました。
開発者の作業としては次のような流れを想定します。
- Snowsight で Cortex Agent を作成/編集する
GETコマンドにより指定したバージョンの設定が書かれている agent_spec.yaml を取得する- 取得した agent_spec.yaml を GitHub に Push する
GitHub Actions のワークフローとしては次のように検討しました。
- Push された agent_spec.yaml を
snow stage copyでステージにコピーする - ステージから
ADD VERSIONにより新しいバージョンを作成する - 作成されたバージョンに
MODIFY VERSIONでproductionエイリアスを付与する - 作成されたバージョンを
SET DEFAULT_VERSIONで DEFAULT に設定する
Cortex Agents の作成や編集を Snowsight に寄せているのは、現状 Snowsight の使い勝手がとても良いからです。作成・編集と動作確認のイテレーションを簡単に回すことができます。
また Cortex Agents を作るときは当然 Semantic View なども使うことになるので、そのあたりも Snowsight で完結させたいというのが理由です。今は Preview 段階ですが Workspace から Cortex Agents や Semantic View の作成ができるようになっているようですので、将来性を鑑みても Snowsight に寄せるのは有効な選択肢かなと思います。
GitHub Actions でデプロイする Cortex Agent は開発用の Cortex Agent とは別のオブジェクトとして作成する想定です。イメージとしては開発スキーマで開発用のエージェントを Snowsight で作成して、本番スキーマは GitHub Actions 経由のみでデプロイするという感じです。
簡単のため、ここでは開発用と本番用の Cortex Agent を同一データベースの別スキーマに置く想定です。実際には開発用と本番用の Cortex Agent は別データベースやアカウントに置くことが多いと思いますが、その場合でも大きくは変わらないと思います。流れは次の図のとおりです。
GitHub Actions での CI/CD 実装
前項で検討した内容の実装を行います。
ここでの前提として作業環境は以下で実施しました。
- macOS Tahoe バージョン 26.6.2
- Snowflake CLI version: 3.20.0
またここでは Cortex Agents の作成や WIF、Role の設定等については省略します。
agent_spec.yaml の取得
まず適当に作成した Cortex Agent の agent_spec.yaml を GitHub に Push します。KITAGAWA_TEST_DB.AGENT_DEV に GENERAL_AGENT という名前で Cortex Agent を作成しました。少し修正も加えたので現在は Version 2 まで進んでいます。

SHOW VERSIONS で agent_spec.yaml のディレクトリパスが確認できます。この SQL で取得できる spec_file_path を LIST に渡し、実際のファイル名を確認しています。
-- DEFAULT バージョンの agent_spec.yaml ディレクトリパスの取得
SHOW VERSIONS IN AGENT KITAGAWA_TEST_DB.AGENT_DEV.GENERAL_AGENT
->> SELECT "spec_file_path" FROM $1 WHERE "is_default" = 'true';
-- ファイル名の確認
-- SHOW VERSIONS で取得したディレクトリパスを LIST に渡す
LIST snow://agent/KITAGAWA_TEST_DB.AGENT_DEV.GENERAL_AGENT/versions/version$2/
->> SELECT "name" FROM $1;
ここでは name 列に /versions/version$2/agent_spec.yaml という値が入っていました。この値をもとに GET でファイルを取得します。
実行はローカルクライアントの Snowflake CLI から行います。
# Snowflake CLI から GET
snow sql -q "GET snow://agent/KITAGAWA_TEST_DB.AGENT_DEV.GENERAL_AGENT/versions/version\$2/agent_spec.yaml file:///tmp/;"
/tmp/ にファイルがダウンロードされているのでこれをリポジトリの適切なディレクトリに移動します。
mkdir -p ./cortex_agents/GENERAL_AGENT/
mv /tmp/agent_spec.yaml ./cortex_agents/GENERAL_AGENT/
GitHub Actions の設定
荒削りですが以下のようなワークフローを作成しました。
リポジトリの cortex_agents/<AGENT_NAME>/agent_spec.yaml を対象にデプロイを行います。変更があったものを対象に matrix で並列実行します。作成される Cortex Agent はディレクトリ名から導出するようにしました。
name: Deploy Cortex Agent
on:
push:
branches:
- main
paths:
- "cortex_agents/*/agent_spec.yaml"
workflow_dispatch:
inputs:
agent_name:
description: "Deploy target agent name (cortex_agents/<AGENT_NAME>)"
required: true
permissions:
id-token: write
contents: read
defaults:
run:
shell: bash
env:
# 接続情報
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
# デプロイ先
AGENT_DATABASE: KITAGAWA_TEST_DB
AGENT_SCHEMA: AGENT_PROD
STAGE_NAME: AGENT_SPECS
jobs:
detect:
runs-on: ubuntu-latest
outputs:
agents: ${{ steps.targets.outputs.agents }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Detect target agents
id: targets
env:
INPUT_AGENT_NAME: ${{ inputs.agent_name }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then
agents=$(jq -cn --arg a "${INPUT_AGENT_NAME}" '[$a]')
elif git cat-file -e "${BEFORE_SHA}" 2> /dev/null; then
# push 前のコミットと比較
agents=$(git diff --name-only --diff-filter=d "${BEFORE_SHA}" HEAD -- 'cortex_agents/*/agent_spec.yaml' \
| cut -d/ -f2 | sort -u | jq -Rnc '[inputs]')
else
# 比較元が無い場合、全エージェントを対象にする
agents=$(git ls-files 'cortex_agents/*/agent_spec.yaml' \
| cut -d/ -f2 | sort -u | jq -Rnc '[inputs]')
fi
echo "Target agents: ${agents}"
echo "agents=${agents}" >> "$GITHUB_OUTPUT"
deploy:
needs: detect
if: needs.detect.outputs.agents != '[]'
runs-on: ubuntu-latest
strategy:
matrix:
agent_name: ${{ fromJSON(needs.detect.outputs.agents) }}
env:
AGENT_NAME: ${{ matrix.agent_name }}
steps:
- uses: actions/checkout@v7
- name: Install Snowflake CLI
uses: snowflakedb/snowflake-actions@v3
with:
use-oidc: true
- name: Copy agent spec file to stage
run: |
snow stage copy \
"cortex_agents/${AGENT_NAME}/agent_spec.yaml" \
"@${AGENT_DATABASE}.${AGENT_SCHEMA}.${STAGE_NAME}/${AGENT_NAME}/" \
--overwrite \
--temporary-connection
- name: Check if agent exists
id: check
run: |
count=$(snow sql --temporary-connection --format json -q "
SHOW AGENTS LIKE '${AGENT_NAME}' IN SCHEMA ${AGENT_DATABASE}.${AGENT_SCHEMA};
" | jq 'length')
echo "exists=$([ "${count}" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
# 初回デプロイ: エージェント作成(VERSION$1 が自動コミットされる)
- name: Create agent (first deploy)
if: steps.check.outputs.exists == 'false'
run: |
{
echo "CREATE AGENT ${AGENT_DATABASE}.${AGENT_SCHEMA}.${AGENT_NAME}"
echo " FROM SPECIFICATION"
echo "\$\$"
cat "cortex_agents/${AGENT_NAME}/agent_spec.yaml"
echo "\$\$;"
} > create_agent.sql
snow sql --temporary-connection -f create_agent.sql
# 2回目以降: ステージから新バージョンを追加
- name: Add version
if: steps.check.outputs.exists == 'true'
run: |
# live バージョンが残っていると ADD VERSION が失敗するため、先に COMMIT する。
live=$(snow sql --temporary-connection --format json -q "
SHOW VERSIONS IN AGENT ${AGENT_DATABASE}.${AGENT_SCHEMA}.${AGENT_NAME};
" | jq 'map(select(.name == null)) | length')
if [ "${live}" -gt 0 ]; then
snow sql --temporary-connection -q "
ALTER AGENT ${AGENT_DATABASE}.${AGENT_SCHEMA}.${AGENT_NAME}
COMMIT COMMENT = 'Auto-commit leftover live version';
"
fi
snow sql --temporary-connection -q "
ALTER AGENT ${AGENT_DATABASE}.${AGENT_SCHEMA}.${AGENT_NAME}
ADD VERSION FROM @${AGENT_DATABASE}.${AGENT_SCHEMA}.${STAGE_NAME}/${AGENT_NAME}
COMMENT = 'Deployed from GitHub Actions (${GITHUB_SHA})';
"
- name: Set alias and default
run: |
# SHOW VERSIONS から最新の named バージョンを解決する
version=$(snow sql --temporary-connection --format json -q "
SHOW VERSIONS IN AGENT ${AGENT_DATABASE}.${AGENT_SCHEMA}.${AGENT_NAME};
" | jq -r 'map(select(.name != null)) | max_by(.created_on) | .name')
if [ -z "${version}" ] || [ "${version}" = "null" ]; then
echo "::error::No named version found for ${AGENT_NAME}"
exit 1
fi
echo "Latest version: ${version}"
snow sql --temporary-connection -q "
ALTER AGENT ${AGENT_DATABASE}.${AGENT_SCHEMA}.${AGENT_NAME}
MODIFY VERSION ${version} SET ALIAS = production;
ALTER AGENT ${AGENT_DATABASE}.${AGENT_SCHEMA}.${AGENT_NAME}
SET DEFAULT_VERSION = '${version}';
"
いろいろ処理を書いていますが、基本的には前項で検討した内容を実現するための周辺処理であり、メインは snow stage copy でステージにコピーし、そのステージから ADD VERSION で新しいバージョンを作成、 MODIFY VERSION でエイリアスを付与、 SET DEFAULT_VERSION で DEFAULT に設定するという流れです。
live バージョンについては Cortex Agent 作成時に自動的に作成されるもので、これが残っていると ADD VERSION が失敗するため、先に COMMIT するようにしています。
動作確認
GitHub Actions の動作確認を行います。
ワークフローを設定した状態で最初のプッシュを行います。


Cortex Agent が作成され、エイリアスの付与と DEFAULT の設定が行われたことがわかります。
Snowsight から該当の Cortex Agent を確認します。


こちらでも本番用のスキーマに作成されており、 production エイリアスが付与されていることが確認できました。
次に agent_spec.yaml を修正して再度 Push してみます。
--- a/cortex_agents/GENERAL_AGENT/agent_spec.yaml
+++ b/cortex_agents/GENERAL_AGENT/agent_spec.yaml
@@ -12,4 +12,4 @@ tools:
tool_resources:
code_toolset_all:
permission_policy:
- type: "always_allow"
+ type: "always_ask"
git add .
git commit -m "Update GENERAL_AGENT agent_spec.yaml"
git push
今度は意図通り Add version ジョブと Set alias and default ジョブが実行され、バージョンが更新されました。


同じように Snowsight からも確認します。

バージョンは3まで進み、このバージョンにエイリアスが付け替えられていることが確認できました。

SHOW VERSIONS でも確認します。
SHOW VERSIONS IN AGENT KITAGAWA_TEST_DB.AGENT_PROD.GENERAL_AGENT;

まとめ
GitHub Actions を使って Cortex Agents のバージョン管理とデプロイを自動化する方法について検討してみました。Snowsight の機能は積極的に使いつつ、ユーザーに影響のある本番環境は一定のルールのもと運用することができるようになったのではないかと思います。
バージョンとエイリアスを適切に管理することで、本番での切り戻しも即座に行うことができます。
Cortex Agents を運用する明確なベストプラクティスと呼べるものはまだまだない状況だと思いますが、この記事が参考になれば幸いです。
それではまた次の記事でお会いしましょう。
参考
https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-versioning#version-shortcuts ↩︎
このことについて明言したドキュメントは見つかりませんでしたが、Cortex Agents API の仕様および実際の挙動から推測しています。 ↩︎




