Probamos Cortex Analyst con Datos Financieros Reales. Esto fue lo que Pasó.
Corrimos Snowflake Cortex Analyst contra un gran conjunto de datos de ingresos validados construidos sobre 51 modelos dbt. La capa semántica funcionó — con caveats importantes que vale conocer antes de construir.

Punto Clave
Nadie escribe sobre construir Cortex Analyst con datos financieros de producción. Teníamos 90 modelos dbt (en ese punto del proyecto), 6 líneas de producto, 11 multiplicadores de precios regionales y un gran conjunto de datos de ingresos validados. Construimos una semantic view encima de una tabla de hechos de capa mart, la conectamos a una app Streamlit-in-Snowflake, y validamos los resultados contra nuestros dashboards de Sigma. Las queries de agregación coincidieron. Las de calendario personalizado no. La definición de la semantic view resultó ser más exigente de lo que sugiere la documentación — y más valiosa de lo que esperábamos.
Nadie escribe sobre construir Cortex Analyst con datos de producción. Los tutoriales usan datasets de juguete. Los demos muestran esquemas limpios. La documentación asume que empiezas desde cero.
Nosotros no empezábamos desde cero. Teníamos 90 modelos dbt (en ese punto del proyecto), seis líneas de producto, 11 multiplicadores de precios regionales y un gran conjunto de datos de ingresos validados. Queríamos saber si un stakeholder de finanzas podía preguntar "¿cuál es el ingreso neto por producto en Q3?" en inglés conversacional y obtener una respuesta correcta — sin escribir SQL, sin un analista de datos en el loop, y sin un esquema de demo diseñado para comportarse bien.
Esto es lo que descubrimos.
Respuesta rápida: Cortex Analyst sobre datos financieros dbt reales funciona, con caveats importantes. Las definiciones de semantic views requieren más precisión de lo que sugiere la documentación. Los resultados de queries se validaron contra Sigma — coincidieron. La mayor sorpresa no fue la capa de IA; fue cuánto trabajo exige la capa semántica en sí misma. Tómalo en cuenta desde el inicio.
Por qué vale la pena intentar analytics conversacional sobre datos financieros
La empresa con la que trabajábamos — una compañía de seguridad en la nube — ya tenía un entorno Sigma funcional cuando empezamos este experimento. Los dashboards que tardaban 60 segundos en cargar ahora corrían en menos de 3. Finanzas podía actualizar precios sin abrir un ticket de Jira. La migración estaba hecha.
Pero seguía habiendo una clase de preguntas que requerían o un analista de datos, o alguien con la paciencia suficiente para encontrar el workbook de Sigma correcto y aplicar los filtros adecuados. "¿Cuánto de nuestro ingreso de Q3 vino de cuentas en segmento POC?" "¿Cuál es nuestra tasa de descuento efectiva por categoría de producto este mes?" Preguntas razonables. Minutos de respuesta si sabes dónde buscar. Invisibles si no.
El pitch de Cortex Analyst para este escenario: ya tienes los modelos dbt. Construyes una semantic view encima — diciéndole a Snowflake qué significan los hechos, dimensiones y métricas — y Cortex traduce queries en lenguaje natural a SQL contra esa semantic view. El SQL corre en Snowflake. Los resultados regresan a la UI que hayas construido.
La parte que nadie escribe: construir una semantic view sobre un esquema financiero real no es como construirla sobre orders y customers.
La semantic view, construida sobre un mart dbt real
El modelo fuente era fct_product__plan_monthly_revenue_by_product_v2 — una tabla de capa mart con filas al grain de cuenta × plan × producto × mes. Era el output de una arquitectura dbt de 51 modelos que había sido validada al 0.002% de precisión contra facturas históricas.
Envolvimos un macro DDL de Snowflake alrededor de la definición de la semantic view. La estructura completa:
{% macro create_sv_product__plan_monthly_revenue() %}
{% set sql %}
CREATE OR REPLACE SEMANTIC VIEW {{ db }}.{{ schema }}.sv_product__plan_monthly_revenue
TABLES (
revenue AS {{ db }}.{{ schema }}.fct_product__plan_monthly_revenue_by_product_v2
PRIMARY KEY (account_id, plan_id, period_month, product_id)
)
FACTS (
revenue.revenue_gross AS revenue_gross,
revenue.revenue_net AS revenue_net,
revenue.usage_count AS usage_count
)
DIMENSIONS (
revenue.account_id AS account_id,
revenue.period_month AS period_month,
revenue.plan_name AS plan_name,
revenue.account_type AS account_type,
revenue.account_segment AS account_segment,
revenue.is_poc AS is_poc,
revenue.product_name AS product_name,
revenue.product_category AS product_category,
revenue.team_name AS team_name
)
METRICS (
revenue.total_gross_revenue AS SUM(revenue.revenue_gross),
revenue.total_net_revenue AS SUM(revenue.revenue_net),
revenue.total_ela_discount AS SUM(revenue.revenue_gross) - SUM(revenue.revenue_net),
revenue.account_count AS COUNT(DISTINCT revenue.account_id),
revenue.effective_discount_rate AS
(SUM(revenue.revenue_gross) - SUM(revenue.revenue_net))
/ NULLIF(SUM(revenue.revenue_gross), 0) * 100
)
COMMENT = 'Ingresos mensuales a nivel de plan por producto.'
{% endset %}
{% do run_query(sql) %}
{% endmacro %}
El macro corre vía dbt run-operation. Es idempotente — CREATE OR REPLACE hace que volver a ejecutarlo en cambios de esquema sea seguro. Lo llamamos desde un post-hook sobre el mart fuente para que la semantic view se mantuviera actualizada conforme el modelo evolucionaba.
La app de Streamlit
Corrimos la interfaz de Cortex Analyst como una app Streamlit-in-Snowflake — sin infraestructura externa, sin API keys que manejar fuera de Snowflake, acceso nativo a la semantic view.
La estructura de la app fue directa: un input de texto, la llamada a la API de Cortex Analyst usando el patrón de inferencia snowflake.cortex.complete, y renderizado automático de gráficas basado en lo que regresaba.
import streamlit as st
import pandas as pd
from snowflake.snowpark.context import get_active_session
session = get_active_session()
def query_cortex_analyst(question: str, semantic_view: str) -> dict:
"""Envía pregunta en lenguaje natural a Cortex Analyst, regresa SQL + resultados."""
response = session.sql("""
SELECT SNOWFLAKE.CORTEX.ANALYST(
?,
OBJECT_CONSTRUCT('semantic_view', ?)
) AS response
""", params=[question, semantic_view]).collect()
return response[0]["RESPONSE"]
def render_chart(df: pd.DataFrame):
"""Auto-detecta tipo de gráfica: temporal → línea, categórico → barras."""
date_cols = [c for c in df.columns if any(t in c.lower() for t in ["month", "date", "period"])]
revenue_cols = [c for c in df.columns if any(t in c.lower() for t in ["revenue", "amount", "total"])]
if date_cols and revenue_cols:
st.line_chart(df.set_index(date_cols[0])[revenue_cols])
elif revenue_cols:
cat_col = [c for c in df.columns if c not in revenue_cols][0] if len(df.columns) > 1 else None
if cat_col:
st.bar_chart(df.set_index(cat_col)[revenue_cols])
La lógica de auto-gráfica cubría los patrones de query financiera más comunes sin configuración. Las queries temporales (ingresos por mes) obtuvieron gráficas de línea. Los desgloses categóricos (ingresos por producto, por segmento) obtuvieron gráficas de barras. Todo lo demás obtuvo una tabla.
Qué preguntas realmente funcionaron
Cortex Analyst manejó bien las preguntas de agregación y agrupación — el tipo de queries que se traducen naturalmente en SUM ... GROUP BY:
"What is total gross revenue by month for the current year?"
"Show net revenue by product category"
"Which account segments have the highest revenue?"
"Compare effective discount rate by plan name"
"How many unique accounts are active this quarter?"
El SQL generado era legible. Un ejemplo representativo para "net revenue by product for 2025":
SELECT
product_name,
SUM(revenue_net) AS total_net_revenue
FROM sv_product__plan_monthly_revenue
WHERE YEAR(period_month) = 2025
GROUP BY product_name
ORDER BY total_net_revenue DESC
Validamos estos resultados contra Sigma. Coincidieron. Esa es la parte que realmente importaba — no si la app lucía impresionante, sino si podía producir números en los que un equipo de finanzas confiaría.
Los gotchas que nadie menciona
Las queries de calendario personalizado fueron poco confiables. Nuestros modelos dbt usan un calendario retail 4-4-5 — trimestres que no coinciden con los trimestres ISO. La semantic view no tiene mecanismo para comunicar esto. Cuando un usuario preguntaba "¿cuál es el ingreso de Q3?", Cortex Analyst usaba el Q3 del calendario (julio–septiembre), no el Q3 de la empresa. El SQL era sintácticamente correcto. El número estaba mal. Agregamos una nota a la UI de Streamlit: "Esta herramienta usa meses del calendario. Para números por trimestre de la empresa, usa Sigma." No es elegante, pero es honesto.
Las combinaciones complejas de filtros degradaban la calidad. Las preguntas simples de group-by funcionaban consistentemente. Las preguntas multi-condición como "ingresos netos de cuentas POC en la categoría de producto Detection excluyendo cuentas con descuentos personalizados" producían SQL que a veces era correcto y a veces sutilmente incorrecto — generalmente un filtro faltante o un orden de agregación incorrecto. No podíamos predecir cuándo fallaría sin correr el output contra una respuesta conocida. Para queries exploratorias, aceptable. Para una cifra que Finanzas va a firmar, no.
Las métricas derivadas necesitan definición explícita. La tasa de descuento efectiva — (gross - net) / gross * 100 — funcionó porque la definimos explícitamente en el bloque METRICS. Cualquier ratio o cálculo derivado que no predefinimos produjo SQL incorrecto o hizo que Cortex Analyst rechazara la query con una respuesta de "no puedo responder eso". La lección: cada métrica que un usuario podría querer consultar necesita existir en la semantic view. No puedes confiar en que Cortex infiera ratios a partir de hechos crudos.
La semantic view es un snapshot. Refleja el esquema del mart al momento de creación. Cuando agregamos una nueva columna de dimensión al modelo dbt subyacente, la semantic view no se actualizó automáticamente. CREATE OR REPLACE lo arregló, pero eso requiere saber que el cambio ocurrió. En un entorno de equipo sin el patrón de post-hook que usamos, las semantic views pueden quedar silenciosamente desactualizadas respecto al modelo de datos.
Snowflake Cortex evoluciona rápido. El comportamiento que observamos corresponde a nuestro proyecto (noviembre 2025–marzo 2026). Cortex Analyst estaba todavía en disponibilidad relativamente temprana. Las formas de respuesta de la API, las expresiones de métricas soportadas y la calidad de las queries cambian con cada release de Snowflake. Trata los detalles aquí como "lo que observamos en su momento" — valida contra la documentación actual antes de construir sobre ellos.
Lo que realmente requiere la definición de la semantic view
La parte más subestimada de este trabajo fue escribir la semantic view en sí.
El bloque METRICS es donde más importa la precisión. El DDL de semantic views de Snowflake es estricto con las expresiones de métricas — no puedes usar funciones SQL arbitrarias, y la sintaxis de referencia (revenue.metric_name) tiene reglas específicas que difieren de los alias estándar de SQL. Iteramos en effective_discount_rate tres veces antes de que el DDL lo aceptara sin error:
-- Primer intento — rechazado (la división no está directamente soportada como métrica de nivel superior)
revenue.effective_discount_rate AS
(revenue_gross - revenue_net) / revenue_gross * 100
-- Segundo intento — rechazado (referencias de columna sin alias de tabla)
revenue.effective_discount_rate AS
(SUM(revenue_gross) - SUM(revenue_net)) / SUM(revenue_gross) * 100
-- Tercer intento — aceptado
revenue.effective_discount_rate AS
(SUM(revenue.revenue_gross) - SUM(revenue.revenue_net))
/ NULLIF(SUM(revenue.revenue_gross), 0) * 100
El NULLIF importa: sin él, las cuentas con ingreso bruto cero producen un error de división por cero que aparece como un fallo confuso de Cortex Analyst en lugar de un error claro de SQL.
El campo COMMENT no es cosmético. Cortex Analyst usa los comentarios a nivel de vista y de columna para mejorar la generación de queries. Un comentario a nivel de vista de 'Ingresos mensuales por producto.' produjo resultados notablemente mejores que ningún comentario para queries ambiguas. Agregamos comentarios a cada dimensión con significado de negocio no obvio:
DIMENSIONS (
revenue.account_segment AS account_segment
COMMENT 'Segmento de cliente: Internal, External POC, External Upside, Unknown',
revenue.is_poc AS is_poc
COMMENT 'Flag booleano — true si esta es una cuenta de prueba de concepto',
...
)
Es el mismo principio que la buena documentación de modelos dbt — la capa de IA solo es tan buena como los metadatos que le das.
El fin de semana en que todo se armó
Construir el prototipo de Streamlit fue un proyecto de fin de semana — el tipo que empiezas el sábado por la tarde pensando que le vas a dedicar dos horas y terminas atascado hasta la medianoche. Los datos financieros que consultábamos habían pasado por meses de validación. Verlos regresar de una pregunta en lenguaje natural fue una experiencia distinta.
"No sé cómo sentirme si los datos me dan alegría durante el fin de semana" — mensaje real de Slack, domingo real.
Esa es la versión honesta de cómo se sintió Cortex Analyst sobre datos en los que confiábamos. Los caveats son reales. El problema del calendario fiscal es real. El límite en filtros complejos es real. Pero también lo es ver a un stakeholder de finanzas escribir "¿qué producto tuvo el mayor ingreso neto el mes pasado?" y obtener el número correcto sin abrir un editor de SQL.
El trabajo de analytics de autoservicio que habíamos hecho con las tablas de input de Sigma — descrito en detalle en el post sobre cómo potenciar a los equipos de Finanzas — ya estaba cambiando quién podía responder preguntas en la organización. La capa semántica fue un paso más en la misma dirección: no reemplazar a los analistas, sino reducir la fila de preguntas que requerían uno.
Lecciones para uso en producción
Construye la semantic view sobre un mart validado, no sobre tablas crudas. El problema de calidad de datos es separado del problema de NL-to-SQL. Teníamos confianza en nuestras respuestas porque el modelo fuente había sido validado al 0.002% de precisión. Sin esa base, no puedes distinguir un fallo de Cortex Analyst de un problema de calidad de datos.
Predefine cada métrica que los usuarios van a querer consultar. No asumas que Cortex va a inferir cálculos derivados. Margen bruto, tasas de descuento, cambios de ARR — cualquier cosa que sea un ratio, porcentaje o cálculo de múltiples columnas necesita una definición explícita en METRICS. Si no está en la semantic view, falla o produce SQL incorrecto silenciosamente.
Establece expectativas de alcance desde el principio. Cortex Analyst no es un reemplazo de una herramienta de BI para queries complejas. Es rápido, flexible y sorprendentemente preciso en preguntas de agregación. Es poco confiable en queries que requieren lógica de múltiples pasos o definiciones de calendario específicas del dominio que no están en el esquema. Sigma se quedó en el stack. La capa semántica agregó una capacidad; no reemplazó una existente.
Usa el patrón de post-hook. Ejecutar create_semantic_view() como post-hook sobre el mart fuente mantiene la semantic view actualizada sin un proceso separado. Sin eso, la evolución del esquema rompe silenciosamente la capa semántica hasta que alguien nota respuestas incorrectas.
Temas
Arturo Cárdenas
Fundador y Chief Data Analytics & AI Officer
Arturo es un consultor senior en analítica e IA que ayuda a empresas medianas y grandes a eliminar el caos de datos para desbloquear claridad, velocidad y ROI medible.


