Skip to content

Get started with a coding agent

RAI skills give your coding agent instructions for building, querying, and deploying PyRel models. Choose your agent below and install the skills. For other options, see Install RAI agent skills.

In Claude Code, add the RelationalAI marketplace and install the rai plugin:

/plugin marketplace add RelationalAI/rai-agent-skills
/plugin install rai@RelationalAI

Start a new agent session after installation so the new skills are available. Check that your agent lists the RAI skills, including /rai-ontology and /rai-deployment. If they aren’t available, check the installation for your agent in Install RAI agent skills before continuing.

The starter project contains sample data, model files, a query script, and connection settings:

  • Directorydata/ Sample CSV files
    • …
  • Directorymodel Starter PyRel semantic model package
    • __init__.py
    • schema.py Defines the model’s schema
    • sources.py Maps source data to the schema
  • load_data.py Creates Snowflake tables from sample data
  • pyproject.toml Declares project metadata and dependencies
  • queries.py Contains example queries
  • raiconfig.yaml Stores the RAI settings for this project

You’ll edit these files as you build the model. First, download the project and install its dependencies.

  1. Download and extract the ZIP file.

    Copy this prompt:

    Create a directory called rai-agent-skills-tutorial. Download https://docs.relational.ai/rai-starter-project.zip into it and extract the archive. Remove only the downloaded ZIP after extraction succeeds. Use the extracted rai-starter-project directory, which contains pyproject.toml, as the project root for the remaining steps.

    Confirm that your working directory contains pyproject.toml, raiconfig.yaml, and model/. Use this directory for all remaining commands and agent tasks. If extraction fails or these files are missing, download and extract the ZIP again before continuing.

  2. Set up the project environment.

    uv manages the project’s Python environment and dependencies. Use PyRel 1.38.0 for this tutorial.

    Copy this prompt:

    Install uv if needed, then run uv add "relationalai==1.38.0" in the extracted project root. Confirm that installation succeeds and PyRel 1.38.0 is selected.

    Confirm that installation succeeds and pyproject.toml lists relationalai==1.38.0. If installation fails, ask your agent to resolve the reported error before continuing.

Connect PyRel to the Snowflake account containing your tutorial data. The starter is configured for a programmatic access token, or PAT, the recommended option for most use cases. Choose the other tab if you need a different authentication method.

  1. Gather your Snowflake connection details.

    Gather these details before editing the configuration:

    DetailWhere to find it
    AccountLog in to the Snowflake account that has the RAI Native App installed. Execute the following SQL command in a worksheet to find your account name:
    SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME() AS account_identifier;
    UserThe Snowflake username you will use to connect. This user must have the RAI_DEVELOPER role.
    TokenThe programmatic access token (PAT) you use to authenticate with Snowflake. If you don’t have one, ask a Snowflake admin to follow the steps in Configure programmatic access token authentication to create one.
    WarehouseThe name of the Snowflake warehouse you want to use to execute SQL queries. Can be any existing X-SMALL or larger warehouse.
  2. Edit the raiconfig.yaml file.

    Open raiconfig.yaml and replace the placeholders for account, warehouse, and user with your Snowflake connection details. Keep role: RAI_DEVELOPER unless your administrator has specified a different role.

    Replace the token placeholder with an environment-variable reference:

    token: "{{ env_var('SNOWFLAKE_PROGRAMMATIC_ACCESS_TOKEN') }}"

    Set SNOWFLAKE_PROGRAMMATIC_ACCESS_TOKEN locally in the environment where you run PyRel before validating the connection. See Configure your connection for secret-handling guidance and the PAT configuration example. Don’t paste your token into agent chat or commit it to source control.

After setting up either method, check the connection:

Terminal window
uv run rai connect

A success message confirms that PyRel can authenticate and connect to Snowflake. It doesn’t confirm permission to deploy or refresh the model; you’ll check that at deployment. If the connection fails, follow Validate your connection before continuing.

Use the starter’s load_data.py script to load the sample CSVs into Snowflake tables. Choose a dedicated source schema, such as RAI_TUTORIAL.ENERGY_GRID, separate from the schemas where you’ll deploy the model.

How do I create a sample database and schema for this tutorial? Use a dedicated schema for the tutorial’s source data in a database where you have permission to work.

If you need a source schema, ask an administrator to run this SQL using ACCOUNTADMIN or another role with permission to create databases, create schemas, and grant privileges:

CREATE DATABASE IF NOT EXISTS RAI_TUTORIAL;
CREATE SCHEMA IF NOT EXISTS RAI_TUTORIAL.ENERGY_GRID;
GRANT USAGE ON DATABASE RAI_TUTORIAL TO ROLE rai_developer;
GRANT USAGE ON SCHEMA RAI_TUTORIAL.ENERGY_GRID TO ROLE rai_developer;
GRANT CREATE TABLE ON SCHEMA RAI_TUTORIAL.ENERGY_GRID TO ROLE rai_developer;

Use RAI_TUTORIAL.ENERGY_GRID as the source schema in the steps below. These grants allow you to load source data; they don’t establish the privileges required to deploy the model. See Set the deployment target for deployment role requirements.

What is in the sample data? The sample data describes Texas electric-grid substations, generators, load zones, and transmission lines. It also includes requests to connect new data centers, load readings, demand forecasts, maintenance windows, and grid upgrades.

Load the data with your agent or follow the manual steps. After a successful load, remove the local CSVs and loader so the agent uses the Snowflake tables when building the model.

Copy this prompt:

Ask me to confirm a dedicated source schema in DATABASE.SCHEMA form. Warn that the loader replaces matching tutorial tables and overwrites their data.
Run uv run load_data.py <DATABASE.SCHEMA> from the project root. If all sample tables load successfully, remove only this starter project's data/ directory and load_data.py. If loading fails, stop and help me fix the error without deleting files.

If loading fails, keep the local files and resolve the error before continuing. For authentication errors, see Validate your connection. For privilege errors, ask your administrator to check access to the source schema. Rerun the loader only after confirming that it’s safe to replace the tutorial tables.

Ask your agent to turn the Snowflake tables into a semantic model. You’ll review the model’s structure here and query its results after deployment.

  1. Ask the agent to build the model.

    Copy this prompt:

    /rai-ontology Build an electric-grid and data-center capacity-planning model from my Snowflake sample tables. Ask me for the source schema in DATABASE.SCHEMA form.
    Define concepts and relationships in model/schema.py, map the tables in model/sources.py, and load both through model/__init__.py.
    Keep model imports free of queries, result inspection, and data printing. Prepare a data-center request count query in the root queries.py under an if __name__ == "__main__" guard, but don't run it until after deployment. You may inspect source tables and model schema metadata now.

    The model should cover substations, load zones, generators, transmission lines, demand, forecasts, upgrades, maintenance, customers, and data-center requests. Source mappings connect those concepts to Snowflake tables using PyRel Table objects and definitions.

  2. Enter your Snowflake schema.

    Provide the source schema you loaded earlier, such as RAI_TUTORIAL.ENERGY_GRID—not the deployment schema.

  3. Choose one-shot mode.

    If the agent offers a mode choice, choose one-shot mode to generate the starter model in one pass.

    For your own projects, guided mode lets you discuss modeling choices with the agent as it works.

  4. Review the generated model.

    Ask the agent to walk you through these files:

    FileWhat to check
    model/schema.pyDeclares the concepts, properties, and relationships using a PyRel Model.
    model/sources.pyMaps the intended Snowflake source tables to those concepts.
    model/__init__.pyLoads both modules without running queries, inspecting results, or printing data.
    queries.pyContains the count query under an if __name__ == "__main__" guard, so importing it won’t run the query.

    Ask the agent to correct missing definitions, mappings, or imports before deployment.

    The following files illustrate one possible model; your agent’s names and structure may differ. This review checks structure, not deployed results. Don’t run queries.py until the deployment step below succeeds.

    Click to expand the sample output from /rai-ontology
    """Semantic model for electric-grid and data-center capacity planning."""
    from relationalai.semantics import Date, Float, Integer, Model, String
    model = Model("EnergyGridCapacityPlanning")
    LoadZone = model.Concept("LoadZone", identify_by={"id": String})
    LoadZone.name = model.Property(f"{LoadZone} has {String:name}")
    LoadZone.peak_demand_mw = model.Property(f"{LoadZone} has {Float:peak_demand_mw}")
    LoadZone.base_demand_mw = model.Property(f"{LoadZone} has {Float:base_demand_mw}")
    Customer = model.Concept("Customer", identify_by={"id": String})
    Customer.name = model.Property(f"{Customer} has {String:name}")
    Customer.load_zone = model.Property(f"{Customer} is served by {LoadZone:load_zone}")
    Customer.contracted_demand_mw = model.Property(
    f"{Customer} has {Float:contracted_demand_mw}"
    )
    Customer.flexibility_pct = model.Property(f"{Customer} has {Float:flexibility_pct}")
    Customer.curtailment_cost_per_mwh = model.Property(
    f"{Customer} has {Float:curtailment_cost_per_mwh}"
    )
    Substation = model.Concept("Substation", identify_by={"id": String})
    Substation.name = model.Property(f"{Substation} has {String:name}")
    Substation.voltage_kv = model.Property(f"{Substation} has {Integer:voltage_kv}")
    Substation.max_capacity_mw = model.Property(f"{Substation} has {Float:max_capacity_mw}")
    Substation.current_load_mw = model.Property(f"{Substation} has {Float:current_load_mw}")
    DataCenterRequest = model.Concept("DataCenterRequest", identify_by={"id": String})
    DataCenterRequest.name = model.Property(f"{DataCenterRequest} has {String:name}")
    DataCenterRequest.hyperscaler = model.Property(
    f"{DataCenterRequest} has {String:hyperscaler}"
    )
    DataCenterRequest.substation = model.Property(
    f"{DataCenterRequest} interconnects at {Substation:substation}"
    )
    DataCenterRequest.requested_mw = model.Property(f"{DataCenterRequest} has {Float:requested_mw}")
    DataCenterRequest.annual_revenue_per_mw = model.Property(
    f"{DataCenterRequest} has {Float:annual_revenue_per_mw}"
    )
    DataCenterRequest.pue = model.Property(f"{DataCenterRequest} has {Float:pue}")
    DataCenterRequest.low_carbon_requirement_pct = model.Property(
    f"{DataCenterRequest} has {Float:low_carbon_requirement_pct}"
    )
    DataCenterRequest.queue_position = model.Property(
    f"{DataCenterRequest} has {Integer:queue_position}"
    )
    DataCenterRequest.status = model.Property(f"{DataCenterRequest} has {String:status}")
    DataCenterRequest.is_ai_workload = model.Relationship(f"{DataCenterRequest} is AI workload")
    DemandPeriod = model.Concept("DemandPeriod", identify_by={"id": String})
    DemandPeriod.load_zone = model.Property(f"{DemandPeriod} is for {LoadZone:load_zone}")
    DemandPeriod.period = model.Property(f"{DemandPeriod} has {Integer:period}")
    DemandPeriod.demand_mw = model.Property(f"{DemandPeriod} has {Float:demand_mw}")
    DemandPeriod.price_per_mwh = model.Property(f"{DemandPeriod} has {Float:price_per_mwh}")
    DemandForecast = model.Concept("DemandForecast", identify_by={"id": String})
    DemandForecast.substation = model.Property(f"{DemandForecast} is for {Substation:substation}")
    DemandForecast.forecast_period = model.Property(
    f"{DemandForecast} has {Integer:forecast_period}"
    )
    DemandForecast.predicted_load_mw = model.Property(
    f"{DemandForecast} has {Float:predicted_load_mw}"
    )
    DemandForecast.confidence = model.Property(f"{DemandForecast} has {Float:confidence}")
    DemandForecast.includes_dc_growth = model.Relationship(
    f"{DemandForecast} includes data center growth"
    )
    Generator = model.Concept("Generator", identify_by={"id": String})
    Generator.name = model.Property(f"{Generator} has {String:name}")
    Generator.gen_type = model.Property(f"{Generator} has {String:gen_type}")
    Generator.substation = model.Property(f"{Generator} connects at {Substation:substation}")
    Generator.capacity_mw = model.Property(f"{Generator} has {Float:capacity_mw}")
    Generator.marginal_cost = model.Property(f"{Generator} has {Float:marginal_cost}")
    Generator.is_renewable = model.Relationship(f"{Generator} is renewable")
    LoadReading = model.Concept("LoadReading", identify_by={"reading_id": Integer})
    LoadReading.substation = model.Property(f"{LoadReading} measures {Substation:substation}")
    LoadReading.reading_date = model.Property(f"{LoadReading} has {Date:reading_date}")
    LoadReading.load_mw = model.Property(f"{LoadReading} has {Float:load_mw}")
    LoadReading.temperature_f = model.Property(f"{LoadReading} has {Float:temperature_f}")
    LoadReading.is_peak_season = model.Relationship(f"{LoadReading} is in peak season")
    RenewableProfile = model.Concept("RenewableProfile", identify_by={"id": String})
    RenewableProfile.generator = model.Property(
    f"{RenewableProfile} is for {Generator:generator}"
    )
    RenewableProfile.period = model.Property(f"{RenewableProfile} has {Integer:period}")
    RenewableProfile.capacity_factor = model.Property(
    f"{RenewableProfile} has {Float:capacity_factor}"
    )
    SubstationUpgrade = model.Concept("SubstationUpgrade", identify_by={"id": String})
    SubstationUpgrade.substation = model.Property(
    f"{SubstationUpgrade} expands {Substation:substation}"
    )
    SubstationUpgrade.capacity_increase_mw = model.Property(
    f"{SubstationUpgrade} has {Float:capacity_increase_mw}"
    )
    SubstationUpgrade.cost_million = model.Property(
    f"{SubstationUpgrade} has {Float:cost_million}"
    )
    SubstationUpgrade.lead_time_months = model.Property(
    f"{SubstationUpgrade} has {Integer:lead_time_months}"
    )
    SubstationUpgrade.enables_low_carbon = model.Relationship(
    f"{SubstationUpgrade} enables low carbon service"
    )
    TransmissionLine = model.Concept("TransmissionLine", identify_by={"id": String})
    TransmissionLine.from_substation = model.Property(
    f"{TransmissionLine} originates at {Substation:from_substation}"
    )
    TransmissionLine.to_substation = model.Property(
    f"{TransmissionLine} terminates at {Substation:to_substation}"
    )
    TransmissionLine.capacity_mw = model.Property(f"{TransmissionLine} has {Float:capacity_mw}")
    TransmissionLine.maintenance_priority = model.Property(
    f"{TransmissionLine} has {String:maintenance_priority}"
    )
    TransmissionLine.is_active = model.Relationship(f"{TransmissionLine} is active")
    MaintenanceWindow = model.Concept("MaintenanceWindow", identify_by={"id": String})
    MaintenanceWindow.asset_type = model.Property(f"{MaintenanceWindow} has {String:asset_type}")
    MaintenanceWindow.start_period = model.Property(
    f"{MaintenanceWindow} has {Integer:start_period}"
    )
    MaintenanceWindow.end_period = model.Property(f"{MaintenanceWindow} has {Integer:end_period}")
    MaintenanceWindow.generator = model.Property(
    f"{MaintenanceWindow} affects {Generator:generator}"
    )
    MaintenanceWindow.transmission_line = model.Property(
    f"{MaintenanceWindow} affects {TransmissionLine:transmission_line}"
    )
    MaintenanceWindow.is_planned = model.Relationship(f"{MaintenanceWindow} is planned")

Deploy to a development schema with /rai-deployment

Section titled “Deploy to a development schema with /rai-deployment”

Your model is defined in Python, but it isn’t ready to query yet. Deploy it to create its outputs in Snowflake. The first refresh populates those outputs using your source data.

  1. Choose where to deploy.

    Choose a dedicated output schema for this tutorial, such as RAI_TUTORIAL.ENERGY_GRID_DEV. Don’t use the source schema (RAI_TUTORIAL.ENERGY_GRID) or a production schema. PyRel also uses a separate metadata schema to manage the deployment; by default, this would be RAI_TUTORIAL.ENERGY_GRID_DEV_META.

    You’ll also need a Snowflake role with permission to deploy and refresh the model. If you’re unsure which role to use, ask your administrator and share Set the deployment target. An administrator can also prepare schemas and tags for you.

  2. Ask the agent to deploy.

    Copy this prompt:

    /rai-deployment Deploy this model to a development schema using PyRel 1.38.0.
    Ask me for the output schema and deployment role. Keep my connection settings and source mappings. Configure model.path to load the complete model package.
    Create and activate a dev profile with a default refresh schedule and auto_deploy false. Explain the output schema, metadata schema, role, and schedule, then ask me to confirm before deploying through uv.
    Use the installed CLI's help for current options. Wait for the initial refresh to succeed before running any model queries.

    When prompted, provide your chosen schema and role—not credentials. Check the agent’s proposed settings before approving:

    • The output and metadata schemas are dedicated to this tutorial and separate from your source data.
    • The dev profile selects these settings for subsequent commands.
    • The refresh schedule controls how often outputs update.
    • auto_deploy: false means you’ll explicitly redeploy after changing the model.

    Keep your existing authentication method and secret references. In PyRel 1.38.0, the deployment command waits for the first refresh by default. Continue only when both deployment and that refresh succeed.

    If either fails, ask the agent to diagnose the reported error before continuing. See Troubleshoot a deployed model for common errors, or Monitor a deployed model if refresh status is unclear.

  3. Run the count query.

    Now ask the agent to run the query it prepared earlier:

    Run the data-center request count query in queries.py using the dev profile. Compare it with the number of requests in the Snowflake source tables that the model should include. Investigate any difference.

    The counts should match if the model includes every source request. If they don’t, ask the agent to check the source mappings and refresh status before proceeding. This checks the number of requests, not every property or relationship.

    Keep the dev profile active as you explore the model below.

Ask your agent a business question in plain language. The /rai-pyrel skill helps it write a PyRel query against the deployed model.

  1. Ask about substation capacity.

    Copy this prompt:

    /rai-pyrel Which substation has the highest percentage of its capacity in use? What customers would be affected if it went offline?
    Add the query to queries.py and run it using the dev profile against the deployed model. Keep model definitions unchanged. Explain any part the model cannot answer rather than inventing relationships.
  2. Check the answer and its limits.

    Review the query in queries.py and ask the agent to explain how it produced the answer. Check that it:

    • Identifies the substation with the highest capacity utilization and shows the calculation: current load ÷ maximum capacity × 100.
    • Reports affected customers only if the model contains relationships connecting them to the substation.
    • Explains any missing relationship instead of presenting an unsupported answer.

    If a relationship is missing, ask the agent whether the source tables contain the information needed to model it. Adding that relationship requires a model change and redeployment before querying again. If the query fails, ask the agent to check the error, active dev profile, and refresh status before retrying.

    See Query a model with PyRel to learn how the query works.

Plan the reasoning workflow with /rai-discovery

Section titled “Plan the reasoning workflow with /rai-discovery”

You’ve queried the model’s existing facts. Next, explore how reasoning could help choose which data-center requests to approve and which grid upgrades to fund.

  1. Ask for a plan and handoff prompts.

    Copy this prompt:

    /rai-discovery How can this model help choose which data-center requests to approve and which upgrades to fund within a budget?
    Explain how business rules, graph analysis, and prescriptive reasoning could work together. Identify missing data or relationships and the trade-offs of each stage.
    Give me one short, copyable handoff prompt per stage. Name the skill, files to change, required inputs, and model outputs the next stage will use. Keep model definitions in model/ and queries in separate root scripts.
    Each handoff must separate defining the model, redeploying with /rai-deployment using the existing dev profile, and querying only after a successful refresh. Include any extra setup needed for that stage. Plan only; don't change files or run optimization.
  2. Check the recommendation.

    Ask the agent to explain what each stage contributes:

    StagePurpose
    Business rulesCalculate available capacity and identify eligible requests.
    Graph analysisMeasure how substations connect to the rest of the network.
    Prescriptive reasoningChoose approvals and upgrades subject to capacity and budget constraints.

    Review the assumptions before accepting the plan. For example, eligibility thresholds are business choices, and graph centrality is a measure of network structure—not proof of electrical reliability or a substitute for power-flow analysis. Ask the agent why each proposed measure is useful for your decision.

  3. Check for missing information.

    If the agent identifies a gap, ask whether the information exists in Snowflake but isn’t mapped, or whether the source data itself is missing. A missing mapping requires a model change and redeployment; missing source data must be supplied before that part of the plan can work.

    The skill may label a recommendation READY. This means the model and data appear suitable for the proposed reasoning, not that deployment permissions or solver setup have been checked. If the plan assumes unavailable information, ask the agent to revise it before proceeding.

  4. Review the handoffs before using them.

    Each handoff prompt should name the skill to invoke, the files to edit, and the outputs it will add. Check that each stage reuses the previous stage’s outputs and keeps queries out of model imports. Deployment must use the existing dev profile and finish refreshing before result queries run.

    Use the following sections to review the generated handoffs. If a handoff omits a requirement, ask the agent to revise it before running it. Run one stage at a time so you can check its results before starting the next.

The following sections show the requirements and example prompts for this tutorial’s rules, graph, and prescriptive stages. Use your reviewed handoffs to carry out the plan; the examples help you check or refine them, rather than repeat work you’ve already completed.

Use the business-rules handoff prompt from your /rai-discovery output to add rules for available capacity, request eligibility, and upgrade comparisons.

  1. Review the rules handoff.

    Find it in your /rai-discovery output and check its formulas and thresholds. Treat these as tutorial assumptions, not operational grid-planning recommendations. Your handoff may look like this:

    /rai-pyrel Add the business rules from our agreed plan to model/rules.py and load them through model/__init__.py. Calculate available substation capacity, identify eligible requests, and compare upgrade efficiency. Preserve existing source mappings.
    Keep model imports free of queries and data printing. Prepare a summary in the root queries.py, guarded by if __name__ == "__main__". Show me the rules before deployment.
    After I approve, use /rai-deployment to redeploy with the existing dev settings. Wait for a successful refresh, then run the summary and explain the results, including missing values.
  2. Run the handoff and approve the changes.

    Send your reviewed handoff to the agent. Check that the generated rules match the plan before approving deployment. If deployment or refresh fails, resolve the error before querying; see Troubleshoot a deployed model.

  3. Review the summary.

    Check a few reported requests, capacity calculations, and upgrade comparisons against the agreed rules and source values. Eligibility is screening, not approval: a request may still need an upgrade. If a rule needs correcting, have the agent fix it, redeploy, and wait for refresh before querying again.

Reveal hidden structure with /rai-graph-analysis

Section titled “Reveal hidden structure with /rai-graph-analysis”

Use the graph handoff prompt from your /rai-discovery output to measure how substations connect to the rest of the grid. Graph reasoning adds scores that later rules and optimization can use. These scores describe network structure, not electrical reliability or power flow.

  1. Review the graph handoff.

    Check which substations and transmission lines it includes, how lines are weighted, and which scores it adds. Your handoff may look like this:

    /rai-graph-analysis In model/graph.py, define an undirected graph of substations connected by active transmission lines, weighted by line capacity. Bind eigenvector centrality to Substation.centrality_score and define Substation.network_weight as 1 + centrality_score. Load the module through model/__init__.py without running queries.
    Preserve the existing rules and source mappings. Prepare a centrality report in the root queries.py showing the top ten substations, their available capacity, and pending requests. Explain how isolated substations and missing scores are handled.
    Show me the model changes. After I approve, use /rai-deployment to redeploy with the existing dev settings. Wait for a successful refresh, then run the report and explain the scores.
  2. Run the handoff and approve the changes.

    Send your reviewed handoff to the agent. Check that graph scores are defined as model properties, not calculated only inside the report. Approve deployment to the existing dev target, and resolve any deployment or refresh errors before querying. See Troubleshoot a deployed model if needed.

  3. Review the graph report.

    Ask the agent to explain which substations rank highest and whether pending requests target them. Check that low scores and missing scores are distinguished. Before using the scores in optimization, review why favoring well-connected substations makes sense for the tutorial’s goal. After any model correction, redeploy and wait for refresh before querying again.

Find optimal actions with prescriptive reasoning

Section titled “Find optimal actions with prescriptive reasoning”

Use the prescriptive handoff prompt from your /rai-discovery output to choose requests and upgrades under capacity and budget constraints. Prescriptive reasoning finds decisions that best satisfy the objective you define. Its recommendations depend on your data, constraints, and assumptions.

  1. Review the prescriptive handoff.

    Check the eligible requests, budget levels, capacity limits, and objective with the agent. If graph scores influence the objective, ask how that changes the meaning of “best.” Your handoff may look like this:

    /rai-prescriptive-problem Use our agreed rules and graph outputs to define binary request-approval and upgrade-funding decisions in model/optimization.py.
    Compare budgets of $50M, $150M, and $300M using a Scenario concept. Give each scenario its own decisions, budget constraint, and per-substation capacity constraints. Maximize total centrality-weighted expected revenue across these independent scenarios.
    Configure one Problem with Problem.configure("highs"), not solve(). Publish scenario-specific approvals, funded upgrades, revenue, spend, and weighted objective values as model outputs. Load the module through model/__init__.py without running queries. Prepare a separate portfolio_decision.py report.
    Show me the formulation and check the deployment refresh credentials. After I approve, use /rai-deployment to redeploy with the existing dev settings and wait for a successful refresh. Then use /rai-prescriptive-results to query solver status and deployed results from the report, not to call solve().

    Each scenario represents a separate budget choice, not a share of one combined budget. For deployment, Problem.configure() declares the optimization; the refresh executes it. If the skill proposes problem.solve() or a Python loop that changes the budget and solves immediately, ask it to revise the handoff before running it.

  2. Run the handoff and approve the changes.

    Send your reviewed handoff to the agent. Check that each scenario’s approved load fits within available capacity plus funded upgrades, and that upgrade spending stays within that scenario’s budget. Approve deployment only after the formulation and refresh credentials are ready. If deployment or refresh fails, resolve the error before reading recommendations.

  3. Review the decisions and solver status.

    Have the agent run portfolio_decision.py using the dev profile. Check the solver status before interpreting approvals: a successful refresh alone doesn’t prove the solver found an optimal solution. If the problem is infeasible or the solve is incomplete, ask the agent to explain the status before recommending actions.

    Compare approved requests, funded upgrades, spending, and expected revenue across scenarios. Keep revenue separate from the graph-weighted objective; a higher weighted score doesn’t necessarily mean higher revenue. Ask what the extra budget buys and which assumptions could change the recommendation. After any model correction, redeploy and wait for refresh before rerunning the report.

When you’re finished exploring, remove the deployed model’s outputs and refresh tasks. Teardown leaves your local project, Snowflake source tables, and deployment schemas in place.

  1. Preview what will be removed.

    Copy this prompt:

    /rai-deployment Help me tear down this tutorial's deployment. Using the existing dev profile, run uv run rai models teardown without --force to preview the removal.
    Show me the target schemas and objects to be removed, then ask for confirmation. Only after I approve, run the same command with --force. Keep the source tables, schemas, local project, and configuration. If teardown is blocked, explain why; don't override teardown protections.
  2. Confirm the target and remove the deployment.

    Check that the preview targets only this tutorial’s development model. Approve removal only when the target and listed objects are correct. If teardown fails, ask the agent to resolve the reported error before retrying; see Tear down a model.

  3. Check the cleanup result.

    Ask the agent to confirm that the listed model outputs and refresh tasks were removed and that your source tables remain. The output and metadata schemas remain, as does raiconfig.yaml. Don’t run model queries to check cleanup: the deployment is gone. Deploying again with the same configuration recreates it in the same target schemas.

Nice work!

You used a coding agent and discovery handoffs to build a semantic model, deploy changes to Snowflake, and explore the results with business rules, graph analysis, and optimization.