Skip to content

cortex

relationalai.agent

RelationalAI-Cortex Integration

Deploy Snowflake Cortex AI agents powered by RelationalAI semantic models for Snowflake Intelligence.

An agent answers questions from one model that you have already deployed with PyRel deploy mode (model.deploy() or rai models deploy). It reads that deployment and never deploys or refreshes the model.

Quickstart

from snowflake import snowpark
from relationalai.config import create_config, SnowflakeConnection
from relationalai.agent.cortex import (
AgentConfig, CortexAgentManager, DefaultTool, ModelDeployment, discover_imports)
# Create session using role for deployment
session: snowpark.Session = create_config().get_session(SnowflakeConnection)
# Configure manager
manager = CortexAgentManager(
session=session,
config=AgentConfig(
agent_name="MY_ASSISTANT", # Unique name for the agent
tools_schema="MY_DB.MY_TOOLS", # Schema for the agent's sprocs and stage (and the agent)
warehouse="COMPUTE_WH", # Warehouse for RAI tool execution (SI users need USAGE)
model_deployment=ModelDeployment(schema="MY_DB.CORE"))) # The model's deployment.schema
# Initialize RAI Tools
# - This function is executed during each sproc invocation.
# It must be self-contained — don't close over local
# runtime state (sessions, connections, dataframes, etc.).
def init_tools():
# IMPORTANT: import your RelationalAI Model and dependent code
# inside init_tools so it runs within the sproc session.
from my_project.model import core
return DefaultTool(
model=core.model, # The one model this agent serves
description="...")
# Deploy
manager.deploy(
init_tools=init_tools, # Initialize RAI Tools
imports=discover_imports()) # Specify local python modules to package into sproc
print(manager.status())

After deployment, you can find the Cortex Agent in the Snowflake UI under AI & ML > Agents. The UI allows you to chat with the agent, preview or add it to Snowflake Intelligence, and view traces of conversations the agent participates in.

For programmatic access (testing, automation):

chat = manager.chat()
response = chat.send("What can I ask about?")
print(response.full_text())

Conversations through the programmatic interface are persisted and available through the Monitoring tab for the agent in the Snowflake UI.

How It Works

The RelationalAI-Cortex integration

  1. operationalizes your RAI Model into Snowflake stored procedures
  2. configures a Cortex Agent with these tools, and instructions on how to explore your semantic model and answer questions about your data.

An agent serves exactly one model: init_tools returns one Tool for it, usually a DefaultTool. Deploy one agent per model; Snowflake Intelligence users can switch agents within a chat.

ToolPurpose
RAI_DISCOVER_MODELDescribe the agent’s model and its key concepts
RAI_VERBALIZE_MODELGet detailed model structure and relationships
RAI_EXPLAIN_CONCEPTUnderstand business rules for specific concepts
RAI_QUERY_MODELExecute catalog or dynamic queries (PREVIEW — requires allow_preview=True)

RAI_QUERY_MODEL runs both pre-defined catalog queries and — when a model is attached to the query catalog — dynamic queries the agent authors at runtime. See Queries (PREVIEW) below.

RBAC: Caller’s Rights

All stored procedures are created with CALLER’S RIGHTS — they execute under the SI user’s Snowflake role, not the deployer’s role. This means:

  • Data access is governed by the caller’s role. Users only see data their role can SELECT.
  • No privilege escalation. The agent never operates with more permissions than the person using it.

This makes Snowflake’s existing RBAC the single source of truth for data governance across all agent interactions.

Reading the Model Deployment

AgentConfig.model_deployment names where the model was deployed: its deployment.schema, plus its object_name_prefix and object_name_suffix when several models share that schema. Deploy the model with the default compiler and the oplog enabled, which are PyRel’s defaults.

deploy() and update() read the deployment’s identity marker, deploy state and oplog from its meta schema, <schema>_META. They write nothing to the deployment. They fail before registering any procedure when the deployment can’t serve the agent. update() and deploy(skip_preflight=True) raise the code below as the error. deploy() otherwise raises PreflightFailed, whose report lists the code:

CodeCause
ModelDeploymentNotFoundNo model is deployed at that schema with that prefix and suffix, the identity marker there belongs to another deployment (as in a cloned database), or your role can’t read the meta schema. A model deployed by a PyRel version that didn’t record an identity marker is reported here too, and redeploying it adds the marker
ModelDeploymentIncompleteThe model’s first deploy hasn’t finished, it was deployed with compiler.optimization: aggressive, or your role can’t read its deploy state
ModelDeploymentFailedThe model’s last deploy failed. The message carries the recorded error
ModelDeploymentHasNoOplogThe model was deployed with oplog.enabled: false, or your role can’t read its oplog

deploy() also replays the oplog into the deployed model and compares it with the model init_tools builds. A difference is a DeployedModelMismatch warning naming what changed, not an error, so an agent can be redeployed while a model rollout is under way.

Each tool call runs one SQL statement against the deployed tables and views, through the caller’s session, and needs no RAI engine.

The agent’s model runs with PyRel’s default settings plus what was read from the deployment: its location, and its preserve_column_case as recorded in the oplog. Nothing comes from raiconfig.yaml or from a config passed to the Model. The one exception is column-name casing: a model built with Model(..., config=...) keeps that config’s preserve_column_case, so build the model without a config, or give it the value the model was deployed with.

The procedures carry the deployment’s source-table columns and preserve_column_case as read at agent deploy. After you redeploy the model, run manager.update() so the agent picks up the new model.

Limitations:

  • The meta schema must be <schema>_META. A model deployed with another deployment.meta_schema is reported as ModelDeploymentNotFound.
  • A query that reads a relation the deployment lacks fails when the agent runs it. The DeployedModelMismatch warning names such relations at agent deploy.
  • Deploy and update the agent from a process with one open Snowpark session. With more than one, they fail with “No active Snowpark session found”.
  • For a branch deployment, the deployer also needs SELECT on the parent deployment’s identity marker and oplog. print_setup_sql doesn’t emit those grants.

Snowflake Privileges

The following permissions are required for two roles:

  1. deployer/admin who creates the Cortex Agent
  2. SI users who interact with it through the Snowflake Intelligence UI (or, programmatic users of the Cortex Agent)

Deployer / Admin

PrivilegePurpose
USAGE on the tools_schema database and schema, and on the warehouseAccess the deployment target
CREATE STAGE on tools_schemaStore sproc dependencies (if manage_stage=True)
CREATE PROCEDURE on tools_schemaRegister RAI tool sprocs
CREATE AGENT on tools_schema, or, if agent_schema is set, CREATE AGENT on it and USAGE on it and its databaseRegister the Cortex agent
USE AI FUNCTIONS on accountAccess Cortex AI functions (granted to PUBLIC by default)
database role snowflake.cortex_userAccess Cortex services (granted to PUBLIC by default)
database role snowflake.pypi_repository_userInstall Python packages in the sproc environment
application role snowflake.ai_observability_events_lookupMonitor AI observability events
USAGE on the model’s meta schema (<schema>_META) and its databaseRead the model deployment’s metadata
SELECT on the meta schema’s _INTERNAL_MODEL_IDENTITY, _INTERNAL_DEPLOY_STATE and _INTERNAL_OPLOG tables, named with the deployment’s prefix and suffixFind the deployment, check its last deploy, and replay its model

The deployer needs no access to the model’s source tables or deployed outputs.

SI Users

Because sprocs use CALLER’S RIGHTS, SI users need privileges on the resources the agent accesses at runtime:

PrivilegePurpose
USAGE on warehouseExecute sprocs (warehouse from AgentConfig, or session default)
USE AI FUNCTIONS on accountAccess Cortex AI functions (granted to PUBLIC by default)
database role snowflake.cortex_userAccess Cortex services (granted to PUBLIC by default)
USAGE on the databases and schemas of tools_schema and agent_schemaReach the sprocs and the agent
USAGE ON ALL PROCEDURES in tools_schemaInvoke RAI tools
USAGE on the agentChat with the agent
USAGE on the model’s schema and meta schema, and their databasesReach the model deployment
SELECT on all and future tables, dynamic tables and views in both schemasRead the deployed outputs, including outputs later model deploys add, and synthetic concepts, which are stored in the meta schema

SI users need no grants on the model’s source tables: deployed views read them with the view owner’s rights. The schema-wide grants on the meta schema also let SI users read the deployment’s internal _INTERNAL_* tables.

Grant the SI block after the agent is deployed, because it grants on the agent and on the procedures that exist then. update() keeps those grants.

Example Role Definitions

manager.print_setup_sql(deployer_role="MY_DEPLOYER", si_role="MY_SI_USER", create_role=True) prints this block for the quickstart’s AgentConfig:

-- Cortex deployer role setup
CREATE ROLE IF NOT EXISTS MY_DEPLOYER;
GRANT DATABASE ROLE snowflake.cortex_user TO ROLE MY_DEPLOYER;
GRANT DATABASE ROLE snowflake.pypi_repository_user TO ROLE MY_DEPLOYER;
GRANT APPLICATION ROLE snowflake.ai_observability_events_lookup TO ROLE MY_DEPLOYER;
GRANT USAGE ON DATABASE MY_DB TO ROLE MY_DEPLOYER;
GRANT USAGE ON SCHEMA MY_DB.MY_TOOLS TO ROLE MY_DEPLOYER;
GRANT CREATE STAGE ON SCHEMA MY_DB.MY_TOOLS TO ROLE MY_DEPLOYER;
GRANT CREATE PROCEDURE ON SCHEMA MY_DB.MY_TOOLS TO ROLE MY_DEPLOYER;
GRANT CREATE AGENT ON SCHEMA MY_DB.MY_TOOLS TO ROLE MY_DEPLOYER;
GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE MY_DEPLOYER;
-- Read the model deployment's identity, deploy state and oplog
GRANT USAGE ON SCHEMA MY_DB.CORE_META TO ROLE MY_DEPLOYER;
GRANT SELECT ON TABLE MY_DB.CORE_META._INTERNAL_MODEL_IDENTITY TO ROLE MY_DEPLOYER;
GRANT SELECT ON TABLE MY_DB.CORE_META._INTERNAL_DEPLOY_STATE TO ROLE MY_DEPLOYER;
GRANT SELECT ON TABLE MY_DB.CORE_META._INTERNAL_OPLOG TO ROLE MY_DEPLOYER;
-- Cortex SI-user role setup
CREATE ROLE IF NOT EXISTS MY_SI_USER;
GRANT DATABASE ROLE snowflake.cortex_user TO ROLE MY_SI_USER;
GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE MY_SI_USER;
GRANT USAGE ON DATABASE MY_DB TO ROLE MY_SI_USER;
GRANT USAGE ON SCHEMA MY_DB.MY_TOOLS TO ROLE MY_SI_USER;
GRANT USAGE ON ALL PROCEDURES IN SCHEMA MY_DB.MY_TOOLS TO ROLE MY_SI_USER;
GRANT USAGE ON AGENT MY_DB.MY_TOOLS.MY_ASSISTANT TO ROLE MY_SI_USER;
-- Read the model's deployment, including outputs later deploys add; synthetic concepts are stored in its meta schema
GRANT USAGE ON SCHEMA MY_DB.CORE TO ROLE MY_SI_USER;
GRANT SELECT ON ALL TABLES IN SCHEMA MY_DB.CORE TO ROLE MY_SI_USER;
GRANT SELECT ON ALL DYNAMIC TABLES IN SCHEMA MY_DB.CORE TO ROLE MY_SI_USER;
GRANT SELECT ON ALL VIEWS IN SCHEMA MY_DB.CORE TO ROLE MY_SI_USER;
GRANT SELECT ON FUTURE TABLES IN SCHEMA MY_DB.CORE TO ROLE MY_SI_USER;
GRANT SELECT ON FUTURE DYNAMIC TABLES IN SCHEMA MY_DB.CORE TO ROLE MY_SI_USER;
GRANT SELECT ON FUTURE VIEWS IN SCHEMA MY_DB.CORE TO ROLE MY_SI_USER;
GRANT USAGE ON SCHEMA MY_DB.CORE_META TO ROLE MY_SI_USER;
GRANT SELECT ON ALL TABLES IN SCHEMA MY_DB.CORE_META TO ROLE MY_SI_USER;
GRANT SELECT ON ALL DYNAMIC TABLES IN SCHEMA MY_DB.CORE_META TO ROLE MY_SI_USER;
GRANT SELECT ON ALL VIEWS IN SCHEMA MY_DB.CORE_META TO ROLE MY_SI_USER;
GRANT SELECT ON FUTURE TABLES IN SCHEMA MY_DB.CORE_META TO ROLE MY_SI_USER;
GRANT SELECT ON FUTURE DYNAMIC TABLES IN SCHEMA MY_DB.CORE_META TO ROLE MY_SI_USER;
GRANT SELECT ON FUTURE VIEWS IN SCHEMA MY_DB.CORE_META TO ROLE MY_SI_USER;

Diagnostics

Cortex deployments depend on a stack of grants that are easy to misconfigure. The diagnostics module turns cryptic Snowflake errors (“Object X does not exist or not authorized”) into one upfront list of named GRANTs, with a paste-ready SQL block.

Preflight

manager.deploy() runs manager.preflight() automatically. To inspect findings without raising:

report = manager.preflight()
print(report.format(config=manager.config, role=report.role))
if report.has_errors:
print(manager.print_setup_sql(deployer_role="MY_ROLE"))

Preflight probes are read-only (SHOW/DESCRIBE/SELECT):

CheckSeverity
tools_schema database and schema, and warehouse, exist + USAGEerror
snowflake.pypi_repository_user granted (primary or secondary role)error
snowflake.cortex_user grantederror
CREATE STAGE/PROCEDURE/AGENT on schema (direct grants)warning
Agent schema (if agent_schema set)error/warning
Model deployment can serve the agent (see Reading the Model Deployment); ModelDeploymentUnreadable if reading it fails otherwiseerror
The model init_tools builds matches the deployed model (DeployedModelMismatch)warning
DISCOVER / VERBALIZE / EXPLAIN payloads fit the response budget (Truncated*Payload)warning

The last two checks run only when init_tools is given, as deploy() does. Of the warnings, deploy() shows only DeployedModelMismatch; call manager.preflight(init_tools) to see the rest.

The database-role check uses IS_DATABASE_ROLE_IN_SESSION plus a fallback that walks CURRENT_AVAILABLE_ROLES() × SHOW GRANTS OF DATABASE ROLE, so secondary-role grants are recognized.

Setup SQL

Generate a GRANT block parameterized with this manager’s config:

print(manager.print_setup_sql(
deployer_role="MY_DEPLOYER",
si_role="MY_SI", # optional — also emit SI-user block
create_role=False, # prepend CREATE ROLE IF NOT EXISTS
))

The block is the deployer-friendly version of the privilege table above — hand it to a Snowflake admin verbatim instead of writing it by hand.

Skip preflight

For fast iteration once you know the environment is correctly granted:

manager.deploy(init_tools=init_tools, imports=imports, skip_preflight=True)

Skipping preflight skips the grant probes and the model comparison. Deploy still checks the model deployment before registering any procedure, and raises the ModelDeployment* code itself instead of PreflightFailed.

If deploy still fails, the underlying Snowflake error is post-processed: the diagnostic message includes the named GRANT that fixes the most common cases (missing pypi_repository_user, missing CREATE PROCEDURE, etc.).

Command-line diagnostics

For ad-hoc probing without writing Python:

Terminal window
python -m relationalai.agent.cortex.diagnostics \
--account MY_ACCT --user me@example.com \
--role MY_ROLE --warehouse MY_WH \
--tools-schema MY_DB.MY_TOOLS --model-schema MY_DB.CORE \
--agent-name MY_AGENT \
[--model-object-name-prefix SALES_] [--model-object-name-suffix _V2] \
[--preflight-only] # skip the deeper SQL/REST sweep
[--no-rest] # skip Cortex REST API probes
[--json-out FILE] # write the full report as JSON

Read-only — never creates or drops resources.

Configuration

AgentConfig

ParameterRequiredDefaultDescription
agent_nameYes-Unique name for the Cortex agent within its schema
tools_schemaYes-Fully-qualified DATABASE.SCHEMA for the agent’s sprocs and stage, and the agent unless agent_schema is set. Keep it separate from the model’s deployment schema
model_deploymentYes-ModelDeployment(schema=..., object_name_prefix=..., object_name_suffix=...) naming where the model of the Tool returned by init_tools was deployed with PyRel deploy mode. The model runs against it with default settings; its own config and raiconfig.yaml are not used. deploy() and update() also point the in-process model init_tools returns at it
warehouseNoNoneSnowflake warehouse the Cortex agent will use when executing RAI tools. SI users need USAGE on this warehouse. If omitted, tools use the caller’s session warehouse. Stored procedures are created with CALLER’S RIGHTS
stage_nameNo"rai_sprocs"Name of the Snowflake stage for storing sproc dependencies
manage_stageNoTrueIf True, automatically create/drop the stage during deploy/cleanup. Set to False if using a pre-existing stage
llmNo"claude-opus-4-6"Language model for agent orchestration. Must be available in Snowflake Cortex
query_timeout_sNo300Timeout in seconds for stored procedure execution
budget_secondsNoNoneTime budget in seconds for agent execution. Can be used with budget_tokens
budget_tokensNoNoneToken budget for model consumption. Can be used with budget_seconds
artifact_repositoryNo"snowflake.snowpark.pypi_shared_repository"Artifact repository for Python packages
allow_previewNoFalseAllow Preview capabilities (e.g., queries)
agent_schemaNoNoneFully-qualified DATABASE.SCHEMA where the agent is created. Sprocs and stage remain in tools_schema. Use SNOWFLAKE_INTELLIGENCE.AGENTS to expose the agent in SI. If not set, agent is created alongside sprocs
procedure_prefixNoNonePrefix for the stored-procedure names, so several agents can share one tools_schema. Agent-facing tool names don’t change

ModelDeployment

ParameterRequiredDefaultDescription
schemaYes-Fully-qualified DATABASE.SCHEMA the model was deployed to: its deployment.schema
object_name_prefixNo""The deployment’s deployment.object_name_prefix
object_name_suffixNo""The deployment’s deployment.object_name_suffix

Verbalizers

Verbalizers control how model structure is presented to the agent.

ModelVerbalizer (Default)

Returns relationship readings extracted from the RAI model:

Customer has many Orders
Order has one Customer
Order contains many OrderItems
...

This is the default — no configuration needed.

SourceCodeVerbalizer

Extends ModelVerbalizer: explain_model returns the standard relationship readings, while explain_concept returns the Python source code from the modules you provide, filtered to those that reference the requested concept.

Pass the model as the first argument, followed by your model definition modules:

from relationalai.agent.cortex import DefaultTool, SourceCodeVerbalizer
def init_tools():
from my_project.model import core, computed
return DefaultTool(
model=core.model,
description="Customers and orders",
verbalizer=SourceCodeVerbalizer(core.model, core, computed)
)

The agent sees actual Python code for specific concepts, including business logic and computed properties.

Comments are included, so clarifications and justifications of your code will benefit the agent as well.

Queries (PREVIEW)

> Note: The queries capability is in PREVIEW. Deployment requires allow_preview=True. Please contact us first!

Two complementary surfaces ship under one capability:

  • Catalog queries — pre-defined, vetted analytical queries built by domain experts. The agent picks one by id and supplies arguments.
  • Dynamic queries — the agent authors a JSON query spec at runtime against the model’s queryable schema. Use for narrow exploratory analysis when no catalog query matches.

Both are exposed through RAI_QUERY_MODEL. Catalog queries are preferred when a question matches one; dynamic queries are the fallback. You opt into either or both via the queries= parameter on DefaultTool.

Catalog queries

Define queries as module-level functions returning a rai.Fragment:

my_project/model/queries.py
import relationalai.semantics as rai
from my_project.model.core import model, Customer, Order
def segment_summary() -> rai.Fragment:
# Topline metrics per customer value segment
customer = Customer.ref()
segment = Customer.ValueSegment.ref()
order = Order.ref()
g = rai.per(segment)
return model.select(
segment.name.alias("segment"),
g.sum(customer.ltv).alias("revenue"),
g.sum(order.profit)
.where(customer.order(order))
.alias("profit")
).where(
customer.value_segment(segment)
)

Register them with QueryCatalog. The function name becomes the query id and the agent picks it when a user’s question matches.

from relationalai.agent.cortex import (
AgentConfig, CortexAgentManager, ModelDeployment, discover_imports,
DefaultTool, SourceCodeVerbalizer, QueryCatalog,
)
def init_tools():
from my_project.model import core, computed, queries
return DefaultTool(
model=core.model,
description="Customers and orders",
verbalizer=SourceCodeVerbalizer(core.model, core, computed),
queries=QueryCatalog(queries.segment_summary),
)
# Deploy with preview enabled — set allow_preview on AgentConfig
manager = CortexAgentManager(
session=session,
config=AgentConfig(
agent_name="MY_ASSISTANT",
tools_schema="EXAMPLE.CORTEX",
warehouse="COMPUTE_WH",
model_deployment=ModelDeployment(schema="EXAMPLE.CUSTOMERS"),
allow_preview=True,
)
)
manager.deploy(
init_tools=init_tools,
imports=discover_imports(),
)

Type-annotated parameters become the agent-visible args schema. Function names must not be "dynamic" (reserved for dynamic queries).

Dynamic queries

Enable dynamic queries by passing model= to QueryCatalog. Discovery then advertises a dynamic path alongside the catalog, and the agent authors small queries at runtime against the model’s queryable schema whenever no catalog query fits the user’s question.

def init_tools():
from my_project.model import core, queries
return DefaultTool(
model=core.model,
description="Customers and orders",
# Pass model= to enable dynamic queries alongside the catalog.
queries=QueryCatalog(queries.segment_summary, model=core.model),
)

Catalog-only (QueryCatalog(queries.segment_summary)) and dynamic-only (DynamicQueries(model)) tools are both supported. Use CompositeQueries for advanced layouts (e.g. multiple catalog providers).

Dynamic queries are intentionally a small-result, exploratory surface: narrow filters, counts, simple aggregations. They complement — they do not replace — catalog queries. Catalog queries should still cover the vetted analytical workload; dynamic queries are the fallback for ad-hoc questions the catalog doesn’t anticipate. The validator emits structured self-correction envelopes for common authoring mistakes so the agent can recover without a round-trip to a human.

Packaging Code for Deployment

When deploying, you need to provide code that runs inside Snowflake stored procedures. There are two parameters for this:

imports - Your Project Code

The imports parameter packages your local Python files (model definitions, queries, etc.) and uploads them to Snowflake. Use discover_imports() to automatically find all modules imported by your init_tools function:

manager.deploy(
init_tools=init_tools,
imports=discover_imports() # Packages your project code
)

discover_imports() recursively discovers all local imports starting from the calling file. It excludes standard library and installed packages.

extra_packages - PyPI Dependencies

The extra_packages parameter specifies PyPI packages that Snowflake will install in the stored procedure environment. Use this for third-party libraries your code depends on:

manager.deploy(
init_tools=init_tools,
imports=discover_imports(),
extra_packages=["pandas==2.0.0", "numpy"] # Installed by Snowflake
)

Note: relationalai is included automatically.

Updating Agents

Update tool definitions without recreating the agent:

def init_tools_v2():
# Updated tool definitions
...
manager.update(init_tools=init_tools_v2, imports=discover_imports())

Run update() after each model redeploy too, so the procedures read the deployment as it is now.

Lifecycle

Check deployment status or tear down all resources:

print(manager.status()) # Reports what exists (agent, stage, sprocs)
manager.cleanup() # Drops agent, sprocs, and stage — permanently loses SI conversation history

Attributes

Attributes exposed by this module.

Functions

Functions exposed by this module.

Classes

Classes exposed by this module.

Modules and Subpackages

Submodules and subpackages available under this namespace.