This pipeline ingests accounts-payable invoices from four disconnected ERP/AP systems β SAP, Oracle, Baan, and Workday β and conforms them into a single, always-fresh Silver view of what the business owes. Snowflake Dynamic Tables handle the incremental transformation from bronze landing tables through to a vendor-level rollup, with source-specific quirks (status vocabularies, a known Baan duplicate-extract issue, dropped system-specific columns) handled per a set of documented, finance-approved business rules rather than ad hoc judgment calls.
On top of that pipeline sits a Snowflake Cortex Agent grounded in a native Semantic View: it answers quantitative questions ("which vendors have the most overdue invoices, and how much") via text-to-SQL against governed business metrics. A 15-question evaluation harness β covering core questions, rephrasings, edge cases, and deliberately ambiguous questions the agent should push back on β runs against the live agent so answer quality is measured, not assumed.
This project implements a governed, agent-queryable accounts-payable data platform entirely inside Snowflake β no external orchestrator, no separate vector database, no copy of the data outside the warehouse.
It implements:
- 4-source bronze ingestion β SAP, Oracle, Baan, and Workday AP invoice tables, each keeping its native column shape and loaded with realistic synthetic sample data (50 invoices total, 3 currencies)
- Rule-driven Silver conformance via Dynamic Tables β a single union/normalize layer implementing 9 documented business rules (status normalization, no currency conversion, Baan duplicate handling, dropped system-specific columns,
TARGET_LAG = DOWNSTREAM), plus a vendor-level rollup - A native Semantic View β business-friendly facts, dimensions, and metrics (
total_spend,overdue_by_vendor, etc.) that ground natural-language questions in governed SQL instead of ad hoc joins - A Cortex Search service β semantic search over free-text invoice line descriptions (
ap_invoice_search), for fuzzy lookups that don't map cleanly to aWHEREclause - A Cortex Agent β Cortex Analyst (text-to-SQL over the semantic view) plus Cortex Search, answering both quantitative and free-text AP questions
- An evaluation harness β a 15-question golden set (core / rephrasings / edge cases / deliberately ambiguous / data-validation) scored against the live agent with pass/fail heuristics
| Layer | Tool | Purpose |
|---|---|---|
| Warehouse / Storage | Snowflake | Single platform for storage, transformation, and AI serving |
| Transformation | Dynamic Tables | Declarative, auto-refreshed incremental pipeline (no dbt/Airflow needed) |
| Semantic layer | Snowflake Semantic View | Governed facts/dimensions/metrics grounding the agent's text-to-SQL tool |
| Structured Q&A | Cortex Analyst | Text-to-SQL tool bound to the semantic view |
| Unstructured Q&A | Cortex Search | Semantic search over free-text invoice line descriptions |
| Orchestrating agent | Cortex Agents (REST API) | Wraps the Analyst and Search tools in one conversational interface |
| Agent client / eval | Python 3.11, requests, pyyaml |
Calls the Agents REST API and scores answers |
| Deployment | Bash + Snowflake CLI (snow sql) |
Applies all SQL in order against a target account |
AP teams routinely receive invoices through four or more disconnected ERP/AP systems, each with its own vendor ID scheme, status vocabulary, GL account format, and payment-terms notation. Answering "what do we currently owe, and to whom, and is any of it overdue" means manually reconciling across systems that don't share a common schema β and doing it consistently enough that finance trusts the answer.
Can accounts-payable invoice data from four disconnected ERP/AP systems be unified into a single governed view β one that a non-technical user can query in plain English and trust the answer to?
[SAP] [Oracle] [Baan] [Workday]
β β β β
βΌ βΌ βΌ βΌ
bronze.sap_ap_ bronze.oracle_ bronze.baan_ bronze.workday_
invoices ap_invoices ap_invoices ap_invoices
β β β β
βββββββββββββββββΌββββββββββββββββΌβββββββββββββββββ
βΌ
silver.dt_silver_ap_invoices (Dynamic Table β union + BR-001..BR-009)
β
βΌ
silver.dt_vendor_invoice_summary (Dynamic Table β vendor rollup)
β
ββββββββββββββ΄βββββββββββββ
βΌ βΌ
semantic.sv_ap_analytics agent.ap_invoice_search
(native Semantic View) (Cortex Search service)
β β
βΌ βΌ
Cortex Analyst (text-to-SQL) Cortex Search (semantic lookup)
β β
ββββββββββββββ¬ββββββββββββββ
βΌ
Cortex Agent β ap_invoice_agent
β
βΌ
eval/ harness (15-question golden set)
| Component | Role |
|---|---|
bronze.*_ap_invoices |
One landing table per source system (SAP, Oracle, Baan, Workday), native column shapes preserved |
dt_silver_ap_invoices |
Dynamic Table unioning all 4 sources into one invoice-header shape, per BR-001..BR-009 |
dt_vendor_invoice_summary |
Dynamic Table rolling invoices up to one row per vendor, incl. pending/overdue exposure |
sv_ap_analytics |
Semantic View exposing facts/dimensions/metrics to Cortex Analyst |
ap_invoice_search |
Cortex Search service over line_description, for fuzzy/semantic invoice lookup |
ap_invoice_agent |
Cortex Agent wrapping the Analyst and Search tools behind one conversational interface |
snowflake-cortex-ai/
β
βββ sql/
β βββ 00_setup/
β β βββ 00_database_schema_warehouse.sql β warehouse, bronze/silver/semantic schema creation + grants
β β βββ 01_github_git_integration.sql β optional: SECRET + API INTEGRATION + GIT REPOSITORY for Snowflake Workspaces
β βββ 01_bronze_sources/
β β βββ 01_sap_ap_invoices.sql β bronze SAP table + 15 sample invoices
β β βββ 02_oracle_ap_invoices.sql β bronze Oracle table + 15 sample invoices
β β βββ 03_baan_ap_invoices.sql β bronze Baan table + 10 sample invoices
β β βββ 04_workday_ap_invoices.sql β bronze Workday table + 10 sample invoices
β βββ 02_silver/
β β βββ 01_dt_silver_ap_invoices.sql β unions all 4 sources per BR-001..BR-009
β β βββ 02_dt_vendor_invoice_summary.sql β vendor-level rollup + pending/overdue calc
β βββ 03_semantic_view/
β βββ 01_sv_ap_analytics.sql β semantic view grounding Cortex Analyst
β βββ 02_cs_ap_invoice_search.sql β Cortex Search service grounding free-text lookup
β
βββ cortex_agent/
β βββ agent_spec.yaml β agent tool spec (Cortex Analyst + Cortex Search)
β βββ run_agent.py β REST client to run the agent interactively
β
βββ eval/
β βββ golden_dataset.jsonl β 15-question golden set (core/variation/edge/ambiguous/validation)
β βββ run_eval.py β runs the golden set through the agent + scores it
β βββ metrics.py β scoring heuristics per question type
β βββ results/ β timestamped eval run outputs (gitignored)
β
βββ docs/
β βββ business_requirements/ β source CSVs behind the Silver DT's design decisions
β β βββ README.md β BR-### β implementation cross-reference
β β βββ sample_business_requirements_source_onboarding.csv
β β βββ sample_business_requirements_column_mapping.csv
β β βββ sample_business_requirements_business_rules.csv
β βββ dynamic-tables-reference/ β Dynamic Tables best-practice reference material (plain docs)
β
βββ labs/ β Snowflake Northstar badge lab materials (separate from the pipeline above)
β βββ coco-foundations/ β CoCo Foundations lab assets + how-to-run README
β βββ from-zero-to-agents/ β From Zero to Agents lab assets + how-to-run README
β βββ northstar-data-engineering/ β Data Engineering with Snowflake lab assets + how-to-run README
β βββ declarative-dynamic-tables/ β Declarative Data Pipelines with Dynamic Tables lab assets + how-to-run README
β
βββ cortex_project/ β Snowflake CLI declarative project definition, auto-generated by Snowsight Agent Studio
β βββ cortex-project.yaml β project manifest (artifact β deployment target)
β βββ ap_invoice_agent.agent.yaml β CLI-deployable equivalent of cortex_agent/agent_spec.yaml
β
βββ config/
β βββ connection.example.toml β Snowflake connection template (copy β connection.toml)
β
βββ scripts/
β βββ deploy.sh β applies all sql/ files in order via the Snowflake CLI
β
βββ requirements.txt β Python deps for the agent client + eval harness
βββ .gitignore
git clone https://github.com/deepan-mehta-analytics/snowflake-cortex-ai.git
cd snowflake-cortex-aipython -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS/Linuxpip install -r requirements.txtcopy config\connection.example.toml config\connection.toml # Windows
cp config/connection.example.toml config/connection.toml # macOS/Linux
# then edit config/connection.toml with your account/userbash scripts/deploy.shThis applies, in order: warehouse/database/schema setup β the 4 bronze source tables (with sample data) β the 2 Silver Dynamic Tables β the semantic view β the Cortex Search service.
run_agent.py calls the Cortex Agents and SQL APIs directly with a Personal Access Token β it does not require registering a saved agent object first.
# PowerShell
$env:SNOWFLAKE_ACCOUNT = "<org>-<account>" # e.g. from your Snowsight URL: app.snowflake.com/<org>/<account>/...
$env:SNOWFLAKE_PAT = "<your PAT>" # generate via ALTER USER <you> ADD PROGRAMMATIC ACCESS TOKEN ...Note: PATs require PROGRAMMATIC_ACCESS_TOKEN to be listed in your account's authentication policy, and a network policy must be attached to the user (Snowflake enforces this for PATs even with an unrestricted 0.0.0.0/0 policy) β see ALTER AUTHENTICATION POLICY / ALTER USER ... SET NETWORK_POLICY if token creation is rejected.
python cortex_agent/run_agent.py "Which vendors have the most overdue invoices?"The cortex_analyst_text_to_sql tool returns governed SQL rather than executed results, so run_agent.py runs that SQL itself via the SQL API and prints the interpretation plus real rows.
python eval/run_eval.pyNo automated unit/integration test suite yet β see Roadmap. Correctness today is checked via eval/run_eval.py, which runs the 15-question golden set (eval/golden_dataset.jsonl) against the live agent and applies heuristic pass/fail checks per question (eval/metrics.py).
Deployed and evaluated end-to-end against a live Snowflake trial account (2026-07-16).
Pipeline row counts (bronze β silver, all verified via SHOW/SELECT after each step, not just a bare success message):
| Table | Rows |
|---|---|
bronze.sap_ap_invoices |
15 |
bronze.oracle_ap_invoices |
15 |
bronze.baan_ap_invoices |
10 |
bronze.workday_ap_invoices |
10 |
silver.dt_silver_ap_invoices |
50 (union of all 4 sources) |
Evaluation harness β 13/15 checks passed (eval/run_eval.py against the live agent):
| Category | Result |
|---|---|
| Core (6 questions) | 6/6 passed |
| Rephrasings/variation (4 questions) | 4/4 passed |
| Edge cases (2 questions) | 2/2 passed |
| Deliberately ambiguous (2 questions) | 0/2 passed |
| Data validation (1 question) | 1/1 passed |
The 2 ambiguous-category failures are heuristic-scoring gaps, not agent defects: one question ("Which vendors are problematic?") correctly triggered a refusal for a subjective/discriminatory framing, and the other ("Show me recent invoices") got a reasonable default interpretation β eval/metrics.py's keyword-based grounding check just doesn't recognize either phrasing style. See Known Limitations.
Sample query, run live via python cortex_agent/run_agent.py "Which vendors have the most overdue invoices?":
This is our interpretation of your question:
Which vendors have the most overdue invoices, ranked by total overdue amount in descending order?
VENDOR_NAME, VENDOR_TOTAL_OVERDUE_AMOUNT
Summit Energy Corp, 197600.00
National Insurance Brokers, 133500.00
Apex Staffing Solutions, 100000.00
...
- Payment-terms formats differ per source (
NET30vsN30vsNet 30) and are intentionally left unnormalized at Silver β an open decision per BR-005, deferred to a future Gold layer - GL account codes are not cross-mapped across sources (BR-006) β a unified chart of accounts is a Phase 2 concern, not implemented here
- The source data has no paid/unpaid flag β "overdue" is approximated as
due_date < CURRENT_DATE()β since the sample data is dated 2025, this approximation drifts further from reality the longer the demo sits unrefreshed eval/metrics.pychecks are heuristic (keyword/shape-based), not semantic β a good answer can fail a check and vice versa; the 2 failing "ambiguous" checks in the results above are scorer gaps, not agent defects- No CI pipeline runs
eval/run_eval.pyautomatically on change - Snowsight's Agent Studio UI and the
DATA_AGENT_RUNSQL function were both unreliable for testing the registered agent object during development (hung/incomplete tool configuration);cortex_agent/run_agent.py's direct REST client sidesteps this by embedding the full tool spec in each call rather than depending on the registered agent object, and is the supported path for this repo - The
cortex_analyst_text_to_sqltool returns governed SQL, not executed results βrun_agent.pyexecutes that SQL itself via the SQL API rather than relying on the orchestration model to do so ap_invoice_search(Cortex Search overline_description) is wired intoagent_spec.yaml, butrun_agent.py's result formatting has only been exercised againstcortex_analyst_text_to_sqlresponses β itscortex_searchresult handling is an untested generic JSON fallback, not a confirmed-working formatter, until it's actually run live
v0.2.0β BR-004 data-quality guardrail: flag invoices > $500K USD-equivalent via a Data Metric Functionv0.3.0β GitHub Actions workflow runningeval/run_eval.pyon every pushv1.0.0β Documented, reproducible end-to-end demo with CI-verified eval results
Synthetic AP invoice data (50 invoices across SAP, Oracle, Baan, and Workday; USD/EUR/GBP) plus the accompanying business-requirements CSVs and 15-question evaluation set are sourced from Snowflake's official Cortex Code Foundations workshop β a hands-on lab for Snowflake's Cortex Code (CoCo) AI coding agent. This repo implements the pipeline that workshop describes (Dynamic Tables β Semantic View β Cortex Agent β evaluation framework) as a standalone, version-controlled project. See docs/business_requirements/README.md for the rule-by-rule mapping from that workshop's requirements to this repo's SQL, and docs/dynamic-tables-reference/README.md for the Dynamic Tables best-practice reference the Silver layer's design follows.
Deepan Mehta
- Data Analytics β Data Engineering β AI/ML Engineering
- Focused on building end-to-end data and ML systems combining analytics, automation, and deployment
- Experience in ETL pipelines, predictive modelling, and analytical databases
π GitHub: deepan-mehta-analytics