For the complete documentation index, see llms.txt. This page is also available as Markdown.

dbt™ Model Query Cost Optimizer — Snowflake

Automate Snowflake dbt™ cost optimization with a DinoAI agent that identifies expensive models, rewrites Snowflake-specific anti-patterns, opens a PR, and posts a cost summary to Slack.

Automatically detect your most expensive dbt™ models, diagnose Snowflake-specific SQL anti-patterns in the model source files, apply targeted rewrites, and open a pull request — all triggered by a single DinoAI agent session. The agent reads Snowflake query performance stats to understand where credits and execution time are being wasted, makes the fixes directly in your repository, and posts the PR link to Slack without any manual intervention.

compass

Before You Start

Paradime

  • Your Paradime API endpoint, API key, and API secret — generate these under Workspace Settings → API. Make sure to enable DinoAI agent API capabilities. Requires Admin access.

Snowflake

  • Your Paradime workspace must have an active Snowflake connection with sufficient permissions to read query history metadata. The agent calls get_snowflake_query_performance_stats to retrieve performance stats for the Snowflake queries that each dbt™ model produced.

Model source

You need at least one of the following so the script knows which dbt™ models to analyse:

  • A Bolt schedule name — the script reads query IDs directly from the latest run's run_results.json artifact, which maps each dbt™ model to the Snowflake query it produced via adapter_response.query_id

  • One or more Snowflake query IDs supplied directly via CLI flag or environment variable, if you already know which models are expensive

Integrations

The following must already be connected in Paradime:

  • Slack — the agent posts the PR link and a cost summary to #finops via post_slack_message

What You'll Build

By the end of this guide you'll have:

  • A Poetry project wired to the Paradime SDK

  • A run_snowflake_query_optimizer.py trigger script that reads dbt™ model results from Bolt artifacts (or accepts Snowflake query IDs directly), filters to the top costly models by execution time, and hands the full context to the agent

  • A snowflake-query-optimizer DinoAI agent YAML that diagnoses Snowflake-specific anti-patterns, rewrites dbt™ model SQL and config, opens a PR, and notifies Slack

What the Agent Does

Once triggered, the agent works through five phases without stopping:

The agent never drops columns referenced by downstream models, never pushes directly to main, and never guesses — if a business decision is required it leaves a TODO comment in the SQL and calls it out in the PR description.

Anti-Patterns the Agent Detects and Fixes

Anti-Pattern
What It Means
Fix Applied

WAREHOUSE_OVERSIZED

Model runs on a warehouse larger than required for its data volume, burning unnecessary credits

Adds a snowflake_warehouse config targeting a smaller warehouse size

NO_CLUSTER_KEY

Large table filtered on high-cardinality columns but no clustering key defined, causing full micro-partition scans

Adds cluster_by to the dbt™ config block with the appropriate filter columns

WRONG_MATERIALIZATION

Frequently queried view causing repeated full recomputation on every downstream query

Changes to table or incremental materialization

INCREMENTAL_OPPORTUNITY

Full table rebuild on every run despite having a natural time-based key, consuming credits unnecessarily

Converts to incremental with an is_incremental() filter on the timestamp column

RESULT_CACHE_BYPASS

Model query is not benefiting from Snowflake's result cache due to non-deterministic functions (e.g. CURRENT_TIMESTAMP(), RANDOM()) used unnecessarily

Removes or replaces non-deterministic expressions with deterministic equivalents

SELECT_STAR

All columns selected from a source, scanning micro-partitions that contain unneeded data

Enumerates only the columns actually used by downstream models

CARTESIAN_FAN_OUT_JOIN

JOIN on a non-unique key causing row multiplication and credit waste

Adds QUALIFY ROW_NUMBER() OVER (PARTITION BY ...) = 1 deduplication before the join

REPEATED_CTE

A CTE is referenced more than twice in the same query; Snowflake re-evaluates it each time

Materialises the CTE as a separate intermediate dbt™ model

UNFILTERED_LARGE_TABLE

A large source() or ref() pulled with no predicate, scanning all micro-partitions

Pushes a WHERE clause into the CTE that reads that table

SEARCH_OPTIMISATION_CANDIDATE

Model supports frequent point lookups or selective equality filters but search_optimization is not enabled

Adds post-hook: ALTER TABLE {{ this }} ADD SEARCH OPTIMIZATION to the config block

Architecture Overview

How It Works

run_snowflake_query_optimizer.py loads configuration from environment variables, optionally reads the latest Bolt run's run_results.json artifact to extract the Snowflake query ID produced by each dbt™ model from adapter_response.query_id, filters to the top-N slowest models above a configurable execution time threshold (default: 30 seconds), then triggers a single snowflake-query-optimizer DinoAI agent session with the full model context pre-embedded in the opening message. The script polls every 20 seconds until the agent completes or times out (default: 60 minutes), then prints the full optimisation report to stdout. An optional follow-up drill-down on a single model can be sent within the same session.

Unlike the BigQuery version which filters by bytes processed, the Snowflake version filters by execution time in seconds — this is the most reliable proxy for credit consumption available in run_results.json without needing to call the Snowflake query history API upfront.

1

Set Up the Poetry Project

Create the following pyproject.toml at the same directory level as your dbt_project.yml. This is required so the agent can locate and rewrite dbt™ model files correctly during the optimisation pass.

Install dependencies by running:

The only two third-party dependencies are requests (for downloading Bolt artifacts) and paradime-io (the Paradime SDK). All other imports — argparse, os, sys, textwrap, time — are part of the Python standard library.

2

Create the Agent YAML

Create the following file in your repository at .dinoai/agents/snowflake-query-optimizer.yml. This defines the agent's role, five-phase goal, Snowflake-specific anti-pattern knowledge, guardrails, and Slack output channel.

tools.mode: allowlist restricts the agent to only the tools listed. This is important for a write-capable agent — it prevents accidental access to tools outside the intended optimisation workflow.

The Slack channel is set to #finops by default. Update slack.channel before committing if your team routes cost alerts to a different channel.

3

Create the Trigger Script

Create scripts/run_snowflake_query_optimizer.py. This script reads dbt™ model results from Bolt artifacts (or accepts Snowflake query IDs directly via CLI), extracts adapter_response.query_id from each result, filters to the top costly models by execution time, builds a structured opening message with all pre-fetched metadata, triggers the snowflake-query-optimizer agent, and polls until completion.

In Snowflake's run_results.json, the query ID is found at adapter_response.query_id — for example "01c4683c-0003-4ff0-0002-731a00bda6ea". This is different from BigQuery where the job ID is at adapter_response.job_id. The script reads this field specifically when parsing Bolt artifacts.

The script accepts --schedule-name or --query-ids but not both — they are mutually exclusive. When --schedule-name is used, the script reads run_results.json from the latest Bolt run and extracts adapter_response.query_id from each dbt™ model result. Models where query_id is absent (e.g. tests or snapshots) are automatically excluded.

When no models are found above the execution time threshold, or none have a captured query_id, the script exits cleanly with code 0 — safe to run on a schedule without generating noise.

4

Set Your Environment Variables

Store your secrets in Paradime before scheduling. Go to Workspace Settings → Environment Variables and add the following:

Variable
Description

PARADIME_API_ENDPOINT

Your Paradime API endpoint

PARADIME_API_KEY

Your Paradime API key

PARADIME_API_SECRET

Your Paradime API secret

BOLT_SCHEDULE_NAME

Name of the Bolt schedule whose run_results.json maps dbt™ models to their Snowflake query IDs

QUERY_OPTIMIZER_TOP_N

(optional) Max models per run — defaults to 5

QUERY_OPTIMIZER_MIN_EXEC_SECS

(optional) Execution time threshold in seconds — defaults to 30

QUERY_OPTIMIZER_DRILL_MODEL

(optional) Model name to drill into after the main pass

QUERY_OPTIMIZER_TIMEOUT

(optional) Max seconds to wait per session — defaults to 3600

QUERY_OPTIMIZER_QUERY_IDS

(optional) Comma-separated Snowflake query IDs — bypasses Bolt artifact fetch when you already know which dbt™ models are slow

Your Paradime API endpoint, key, and secret are available under Workspace Settings → API. Make sure the key has DinoAI agent API capabilities enabled.

5

Run the Optimizer Manually

Option A — Automatically detect costly dbt™ models from a Bolt schedule:

Option B — Supply Snowflake query IDs directly (when you already know which models are slow):

Option C — Include a follow-up drill-down on a specific dbt™ model:

You should see output like:

The agent then posts its full optimisation plan and PR link to stdout, and a credit reduction summary to #finops on Slack.

Schedule with Bolt

Run the optimizer automatically on a recurring cadence so costly dbt™ models are caught and fixed without any manual intervention. The schedule reads the latest Bolt run's run_results.json, identifies the top offending models by execution time, and triggers the agent to fix and PR them.

Add Environment Variables to Paradime

Go to Workspace Settings → Environment Variables and confirm the variables from the previous step are saved. Bolt schedules read from the same environment variable store, so no additional setup is needed.

Create the Bolt Schedule

Go to Bolt → Schedules and click New Schedule. Name it something like Snowflake Query Cost Optimizer and add the following two commands in order:

Replace your_dbt_production_schedule with the exact name of the Bolt schedule that runs your dbt™ models in production. The script reads that schedule's run_results.json artifact, which contains a record per dbt™ model including the Snowflake query ID captured at adapter_response.query_id.

poetry install runs first on every execution so that any dependency updates committed to pyproject.toml are picked up automatically — no manual intervention needed after a dependency bump.

Choose a Schedule Frequency

The right cadence depends on how frequently your production dbt™ models run and how quickly Snowflake credit costs accumulate. The table below covers the most common options:

Cadence
Cron expression
When it runs

Every day at 8 AM

0 8 * * *

Monday–Sunday, 8:00 AM

Weekdays at 8 AM

0 8 * * 1-5

Monday–Friday, 8:00 AM

Once a week (Monday 8 AM)

0 8 * * 1

Every Monday, 8:00 AM

Twice a week (Mon & Thu)

0 8 * * 1,4

Monday and Thursday, 8:00 AM

Every 12 hours

0 */12 * * *

Midnight and noon daily

For most teams, weekdays at 8 AM (0 8 * * 1-5) is a sensible default — it runs after overnight production dbt™ models have completed, so the latest run_results.json artifacts and Snowflake query history are always available. The script exits cleanly with code 0 when no dbt™ models exceed the execution time threshold, so there is no cost or noise on quiet days.

File Structure

Your repository should look like this after completing the setup:

pyproject.toml must sit at the same directory level as dbt_project.yml. The agent uses run_terminal_command, read_file, and write_file to navigate and rewrite your dbt™ project files, so the working directory at session start must be the repo root.

Last updated

Was this helpful?