From 7aed2eecaa1b3525a4fd524b4eb9f74a0958ce5b Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 13:58:37 -0500 Subject: [PATCH 01/16] add feedback capabilities --- README.md | 59 ++++++++++++++++++- .../models/cortex_agent_test.sql | 43 +++++++++++++- .../cortex_agent_test_has_feedback_proc.sql | 10 ++++ macros/materializations/cortex_agent.sql | 27 +++++++-- .../create_feedback_procedure.sql | 21 +++++++ .../cortex_agent/create_feedback_table.sql | 13 ++++ 6 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 integration_tests/tests/cortex_agent_test_has_feedback_proc.sql create mode 100644 macros/relations/cortex_agent/create_feedback_procedure.sql create mode 100644 macros/relations/cortex_agent/create_feedback_table.sql diff --git a/README.md b/README.md index 7df28ae..43b9b30 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ SHOW AGENTS IN SCHEMA my_db.my_schema; | `comment` | string | No | Agent description visible in Snowflake | | `profile` | string (JSON) | No | `{"display_name": "...", "avatar": "...", "color": "..."}` | | `agent_grants` | list | No | Role names to grant `USAGE` on the agent, e.g. `['my_role']` | +| `feedback_table` | string | No | Fully-qualified table for user feedback, e.g. `'MY_DB.MY_SCHEMA.AGENT_FEEDBACK'`. See [Feedback Tool](#feedback-tool). | ## How It Works @@ -95,13 +96,69 @@ Every `dbt run` issues `CREATE OR REPLACE AGENT`, so re-runs are safe and fully ## Supported Tools in Specification -As of the current Snowflake documentation: +Built-in tool types (add to the `tools:` section of your spec body): - `cortex_analyst_text_to_sql` — text-to-SQL via a Cortex Analyst semantic model - `cortex_search` — semantic search over unstructured content +- `generic` — any Snowflake stored procedure or UDF (see [Feedback Tool](#feedback-tool) for an example) Refer to the [Snowflake CREATE AGENT docs](https://docs.snowflake.com/en/sql-reference/sql/create-agent) for the full and up-to-date YAML specification reference. +## Feedback Tool + +Set `feedback_table` in your model config to automatically provision: + +1. A **feedback table** (created once, never replaced) with columns: `feedback_id`, `agent_name`, `session_id`, `rating`, `comment`, `conversation_history`, `created_at` +2. A **stored procedure** named `{AGENT_NAME}_SUBMIT_FEEDBACK` in the same database/schema as the agent + +Then add the tool entry to your spec body so the agent can call it: + +```sql +{{ config( + materialized='cortex_agent', + feedback_table='MY_DB.MY_SCHEMA.AGENT_FEEDBACK' +) }} + +tools: + - tool_spec: + type: generic + name: SUBMIT_FEEDBACK + description: 'Records user feedback. Call when the user rates or comments on a response. Always pass the last 10 conversation messages.' + input_schema: + type: object + properties: + session_id: + type: string + description: 'Current conversation session identifier.' + rating: + type: string + enum: [thumbs_up, thumbs_down] + comment: + type: string + description: 'Optional free-text feedback.' + conversation_history: + type: array + description: 'Last 10 messages from the conversation, in order.' + items: + type: object + properties: + role: { type: string } + content: { type: string } + required: [session_id, rating, conversation_history] + +tool_resources: + SUBMIT_FEEDBACK: + execution_environment: + query_timeout: 300 + type: warehouse + warehouse: '' + identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier }}_SUBMIT_FEEDBACK' + name: '{{ this.identifier }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARIANT)' + type: procedure +``` + +The procedure is recreated on every `dbt run`, so changes to the feedback table schema are picked up automatically. The feedback table uses `CREATE TABLE IF NOT EXISTS`, so existing data is never dropped. + ## License Apache 2.0 diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index c9d1049..c1ef556 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -5,7 +5,8 @@ comment='Full integration test agent — exercises every config and spec option', profile='{"display_name": "Full Test Agent", "avatar": "robot", "color": "blue"}', tags=['integration'], - agent_grants=['dbt_demo_role'] + agent_grants=['dbt_demo_role'], + feedback_table=target.database ~ '.' ~ target.schema ~ '_INTEGRATION_TESTS.CORTEX_AGENT_FEEDBACK' ) }} @@ -52,6 +53,38 @@ tools: required: - query + - tool_spec: + type: generic + name: SUBMIT_FEEDBACK + description: 'Records user feedback about agent responses. Call when the user expresses satisfaction or dissatisfaction, or explicitly asks to rate or submit feedback. Always include the last 10 conversation messages.' + input_schema: + type: object + properties: + session_id: + type: string + description: 'Current conversation session identifier.' + rating: + type: string + enum: [thumbs_up, thumbs_down] + description: 'The user rating.' + comment: + type: string + description: 'Optional free-text feedback from the user.' + conversation_history: + type: array + description: 'Last 10 messages from the conversation, in order.' + items: + type: object + properties: + role: { type: string } + content: { type: string } + required: + - session_id + - rating + - conversation_history + +skills: [] + tool_resources: analyst_tool: semantic_view: '{{ ref("test_semantic_view") }}' @@ -60,3 +93,11 @@ tool_resources: max_results: 10 title_column: title id_column: id + SUBMIT_FEEDBACK: + execution_environment: + query_timeout: 300 + type: warehouse + warehouse: '' + identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier }}_SUBMIT_FEEDBACK' + name: '{{ this.identifier }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARIANT)' + type: procedure diff --git a/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql b/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql new file mode 100644 index 0000000..523fd0d --- /dev/null +++ b/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql @@ -0,0 +1,10 @@ +-- Fails if the SUBMIT_FEEDBACK stored procedure was not created by the materialization. +-- Returns 0 rows on success (standard dbt test contract). + +select 'CORTEX_AGENT_TEST_SUBMIT_FEEDBACK procedure not found' as error +where not exists ( + select 1 + from {{ target.database }}.information_schema.procedures + where procedure_schema = '{{ target.schema }}_INTEGRATION_TESTS' + and procedure_name = 'CORTEX_AGENT_TEST_SUBMIT_FEEDBACK' +) diff --git a/macros/materializations/cortex_agent.sql b/macros/materializations/cortex_agent.sql index 2e97849..caa2890 100644 --- a/macros/materializations/cortex_agent.sql +++ b/macros/materializations/cortex_agent.sql @@ -3,15 +3,20 @@ -- The materialization wraps it in CREATE OR REPLACE AGENT ... FROM SPECIFICATION $$ ... $$. -- -- Config options: --- comment (string, optional) : agent-level comment visible in Snowflake --- profile (string, optional) : JSON object with display_name, avatar, and color --- agent_grants (list, optional) : list of role names to grant USAGE on the agent +-- comment (string, optional) : agent-level comment visible in Snowflake +-- profile (string, optional) : JSON object with display_name, avatar, and color +-- agent_grants (list, optional) : list of role names to grant USAGE on the agent +-- feedback_table (string, optional) : fully-qualified table name for user feedback, +-- e.g. 'MY_DB.MY_SCHEMA.AGENT_FEEDBACK'. +-- Creates the table (if absent) and a stored procedure +-- named {agent}_SUBMIT_FEEDBACK on every dbt run. {% materialization cortex_agent, adapter='snowflake' %} - {%- set comment = config.get('comment', default=none) -%} - {%- set profile = config.get('profile', default=none) -%} - {%- set agent_grants = config.get('agent_grants', default=[]) -%} + {%- set comment = config.get('comment', default=none) -%} + {%- set profile = config.get('profile', default=none) -%} + {%- set agent_grants = config.get('agent_grants', default=[]) -%} + {%- set feedback_table = config.get('feedback_table', default=none) -%} {%- set target_relation = api.Relation.create( identifier=this.identifier, @@ -22,6 +27,16 @@ {{ run_hooks(pre_hooks) }} + {%- if feedback_table is not none %} + {% call statement('feedback_table') %} + {{ dbt_cortex_agent.snowflake__create_feedback_table(feedback_table) }} + {% endcall %} + + {% call statement('feedback_procedure') %} + {{ dbt_cortex_agent.snowflake__create_feedback_procedure(target_relation, feedback_table) }} + {% endcall %} + {%- endif %} + {% call statement('main') %} {{ dbt_cortex_agent.snowflake__create_cortex_agent(target_relation, sql, comment, profile) }} {% endcall %} diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql new file mode 100644 index 0000000..520bd1f --- /dev/null +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -0,0 +1,21 @@ +{% macro snowflake__create_feedback_procedure(relation, feedback_table) %} + + create or replace procedure + {{ relation.database }}.{{ relation.schema }}.{{ relation.identifier }}_SUBMIT_FEEDBACK( + SESSION_ID varchar, + RATING varchar, + COMMENT varchar, + CONVERSATION_HISTORY variant + ) + returns varchar + language sql + as + $$ + insert into {{ feedback_table }} + (agent_name, session_id, rating, comment, conversation_history, created_at) + values + ('{{ relation.identifier }}', :SESSION_ID, :RATING, :COMMENT, :CONVERSATION_HISTORY, current_timestamp()); + return 'Feedback submitted'; + $$ + +{% endmacro %} diff --git a/macros/relations/cortex_agent/create_feedback_table.sql b/macros/relations/cortex_agent/create_feedback_table.sql new file mode 100644 index 0000000..a34bec0 --- /dev/null +++ b/macros/relations/cortex_agent/create_feedback_table.sql @@ -0,0 +1,13 @@ +{% macro snowflake__create_feedback_table(feedback_table) %} + + create table if not exists {{ feedback_table }} ( + feedback_id varchar default uuid_string(), + agent_name varchar not null, + session_id varchar, + rating varchar, + comment varchar, + conversation_history variant, + created_at timestamp_ntz default current_timestamp() + ) + +{% endmacro %} From 36105f5342ab4b2d81d58222ac661662642705c9 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 14:26:06 -0500 Subject: [PATCH 02/16] fix procedure syntax --- .gitignore | 3 ++- macros/relations/cortex_agent/create_feedback_procedure.sql | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 2cb680e..2d35c02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode/ target/ -logs/ \ No newline at end of file +logs/ +.venv/ \ No newline at end of file diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index 520bd1f..8655201 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -11,11 +11,13 @@ language sql as $$ + begin insert into {{ feedback_table }} (agent_name, session_id, rating, comment, conversation_history, created_at) values - ('{{ relation.identifier }}', :SESSION_ID, :RATING, :COMMENT, :CONVERSATION_HISTORY, current_timestamp()); + ('{{ relation.identifier }}', SESSION_ID, RATING, COMMENT, CONVERSATION_HISTORY, current_timestamp()); return 'Feedback submitted'; + end; $$ {% endmacro %} From 5a9da7348534ce130f13beb2a773240f351b43c3 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 14:54:48 -0500 Subject: [PATCH 03/16] add real search service --- integration_tests/dbt_project.yml | 7 +++---- integration_tests/models/base_table.sql | 17 +++++++++++------ integration_tests/models/cortex_agent_test.sql | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/integration_tests/dbt_project.yml b/integration_tests/dbt_project.yml index 7e84879..5356837 100644 --- a/integration_tests/dbt_project.yml +++ b/integration_tests/dbt_project.yml @@ -17,8 +17,7 @@ models: dbt_cortex_agent_integration_tests: +schema: INTEGRATION_TESTS -# Override test_cortex_search_service to point at a real Cortex Search Service. -# Snowflake Cortex Search is not a dbt object so it cannot be ref()'d. -# Passed via: dbt run --vars '{"test_cortex_search_service": "DB.SCHEMA.MY_SERVICE"}' vars: - test_cortex_search_service: "DB.SCHEMA.SEARCH_SERVICE" + # Points at the Cortex Search Service created by base_table's post_hook. + # The schema suffix matches dbt's custom-schema behaviour (target.schema ~ '_INTEGRATION_TESTS'). + test_cortex_search_service: "{{ target.database }}.{{ target.schema }}_INTEGRATION_TESTS.TEST_SEARCH_SERVICE" diff --git a/integration_tests/models/base_table.sql b/integration_tests/models/base_table.sql index 6e8aee2..278ff96 100644 --- a/integration_tests/models/base_table.sql +++ b/integration_tests/models/base_table.sql @@ -1,7 +1,12 @@ -{{- config(materialized='table') -}} +{{- config( + materialized='table', + post_hook="create or replace cortex search service {{ this.database }}.{{ this.schema }}.TEST_SEARCH_SERVICE on description attributes id warehouse = {{ target.warehouse }} target_lag = '7 days' as (select id, description from {{ this }})" +) -}} --- Minimal base table used by test_semantic_view in the integration test suite. -select - 1 as id, - 'test row' as description, - 100.00 as amount +-- Minimal base table used by test_semantic_view and TEST_SEARCH_SERVICE in the integration test suite. +select * from values + (1, 'Revenue figures show strong growth in the enterprise segment last quarter.', 100.00), + (2, 'Chargeback rates increased by 2% among small business clients in Q3.', 200.00), + (3, 'Top client by revenue is Acme Corp with 1.2M in annual recurring revenue.', 300.00), + (4, 'Integration test row for search indexing — safe to ignore in production.', 0.00) + as t(id, description, amount) diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index c1ef556..ecf6fca 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -89,9 +89,9 @@ tool_resources: analyst_tool: semantic_view: '{{ ref("test_semantic_view") }}' search_tool: - name: '{{ var("test_cortex_search_service", "DB.SCHEMA.SEARCH_SERVICE") }}' + name: '{{ var("test_cortex_search_service") }}' max_results: 10 - title_column: title + title_column: description id_column: id SUBMIT_FEEDBACK: execution_environment: From dce9db595365a4bbd25e1b0b4e5fd01f8918961e Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 15:04:00 -0500 Subject: [PATCH 04/16] test fix of quoting --- README.md | 2 +- integration_tests/models/cortex_agent_test.sql | 4 ++-- integration_tests/tests/cortex_agent_test_spec_content.sql | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 43b9b30..f354119 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Create a model file whose **body is the raw Snowflake agent YAML specification** ) }} models: - orchestration: claude-4-sonnet + orchestration: auto orchestration: budget: seconds: 30 diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index ecf6fca..3089931 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -11,7 +11,7 @@ }} models: - orchestration: claude-4-sonnet + orchestration: auto orchestration: budget: @@ -87,7 +87,7 @@ skills: [] tool_resources: analyst_tool: - semantic_view: '{{ ref("test_semantic_view") }}' + semantic_view: '{{ ref(test_semantic_view) }}' search_tool: name: '{{ var("test_cortex_search_service") }}' max_results: 10 diff --git a/integration_tests/tests/cortex_agent_test_spec_content.sql b/integration_tests/tests/cortex_agent_test_spec_content.sql index 5b0677e..0d88686 100644 --- a/integration_tests/tests/cortex_agent_test_spec_content.sql +++ b/integration_tests/tests/cortex_agent_test_spec_content.sql @@ -13,7 +13,7 @@ with ddl as ( ), assertions as ( - select 'orchestration model missing' as error from ddl where not contains(content, 'claude-4-sonnet') + select 'orchestration model missing' as error from ddl where not contains(content, 'auto') union all select 'token budget missing' as error from ddl where not contains(content, '32000') union all From 42a5642f8d9f528ebea1e5fbd1d22e3985a5f8a1 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 15:06:06 -0500 Subject: [PATCH 05/16] test quoting --- integration_tests/models/cortex_agent_test.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index 3089931..63814b1 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -87,7 +87,7 @@ skills: [] tool_resources: analyst_tool: - semantic_view: '{{ ref(test_semantic_view) }}' + semantic_view: '{{ ref('test_semantic_view') }}' search_tool: name: '{{ var("test_cortex_search_service") }}' max_results: 10 From 4d06b44f6b51cd9595b076f4cde9d6794fd5cc10 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 15:10:13 -0500 Subject: [PATCH 06/16] Fix quoting --- integration_tests/models/base_table.sql | 1 + integration_tests/models/test_semantic_view.sql | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integration_tests/models/base_table.sql b/integration_tests/models/base_table.sql index 278ff96..0bc7bc5 100644 --- a/integration_tests/models/base_table.sql +++ b/integration_tests/models/base_table.sql @@ -1,5 +1,6 @@ {{- config( materialized='table', + alias='BASE_TABLE', post_hook="create or replace cortex search service {{ this.database }}.{{ this.schema }}.TEST_SEARCH_SERVICE on description attributes id warehouse = {{ target.warehouse }} target_lag = '7 days' as (select id, description from {{ this }})" ) -}} diff --git a/integration_tests/models/test_semantic_view.sql b/integration_tests/models/test_semantic_view.sql index fb36923..da33313 100644 --- a/integration_tests/models/test_semantic_view.sql +++ b/integration_tests/models/test_semantic_view.sql @@ -1,4 +1,4 @@ -{{ config(materialized='semantic_view') }} +{{ config(materialized='semantic_view', alias='TEST_SEMANTIC_VIEW') }} -- Minimal semantic view used as a tool_resource in cortex_agent_test. -- Requires the dbt_semantic_view package. From 703d5715ba98dbd9afa4477b5a7e93428416c0ed Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 15:15:46 -0500 Subject: [PATCH 07/16] fix feedback procedure --- README.md | 15 +++++---------- integration_tests/models/cortex_agent_test.sql | 15 +++++---------- .../cortex_agent/create_feedback_procedure.sql | 4 ++-- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f354119..8d0bae0 100644 --- a/README.md +++ b/README.md @@ -132,18 +132,13 @@ tools: description: 'Current conversation session identifier.' rating: type: string - enum: [thumbs_up, thumbs_down] + enum: [good, bad] comment: type: string description: 'Optional free-text feedback.' conversation_history: - type: array - description: 'Last 10 messages from the conversation, in order.' - items: - type: object - properties: - role: { type: string } - content: { type: string } + type: string + description: 'Last 10 messages from the conversation as a JSON string, e.g. [{"role":"user","content":"..."}].' required: [session_id, rating, conversation_history] tool_resources: @@ -152,8 +147,8 @@ tool_resources: query_timeout: 300 type: warehouse warehouse: '' - identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier }}_SUBMIT_FEEDBACK' - name: '{{ this.identifier }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARIANT)' + identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier | upper }}_SUBMIT_FEEDBACK' + name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARCHAR)' type: procedure ``` diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index 63814b1..c92aab6 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -65,19 +65,14 @@ tools: description: 'Current conversation session identifier.' rating: type: string - enum: [thumbs_up, thumbs_down] + enum: [good, bad] description: 'The user rating.' comment: type: string description: 'Optional free-text feedback from the user.' conversation_history: - type: array - description: 'Last 10 messages from the conversation, in order.' - items: - type: object - properties: - role: { type: string } - content: { type: string } + type: string + description: 'Last 10 messages from the conversation as a JSON string, e.g. [{"role":"user","content":"..."}].' required: - session_id - rating @@ -98,6 +93,6 @@ tool_resources: query_timeout: 300 type: warehouse warehouse: '' - identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier }}_SUBMIT_FEEDBACK' - name: '{{ this.identifier }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARIANT)' + identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier | upper }}_SUBMIT_FEEDBACK' + name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARCHAR)' type: procedure diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index 8655201..3b702cf 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -5,7 +5,7 @@ SESSION_ID varchar, RATING varchar, COMMENT varchar, - CONVERSATION_HISTORY variant + CONVERSATION_HISTORY varchar ) returns varchar language sql @@ -15,7 +15,7 @@ insert into {{ feedback_table }} (agent_name, session_id, rating, comment, conversation_history, created_at) values - ('{{ relation.identifier }}', SESSION_ID, RATING, COMMENT, CONVERSATION_HISTORY, current_timestamp()); + ('{{ relation.identifier }}', SESSION_ID, RATING, COMMENT, parse_json(CONVERSATION_HISTORY), current_timestamp()); return 'Feedback submitted'; end; $$ From 5af58c9c609b344b86e29979697e141192e11498 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 15:26:51 -0500 Subject: [PATCH 08/16] rename column --- README.md | 2 +- integration_tests/models/cortex_agent_test.sql | 2 +- macros/relations/cortex_agent/create_feedback_procedure.sql | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8d0bae0..3e8dabb 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ tools: rating: type: string enum: [good, bad] - comment: + user_comment: type: string description: 'Optional free-text feedback.' conversation_history: diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index c92aab6..62f009f 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -67,7 +67,7 @@ tools: type: string enum: [good, bad] description: 'The user rating.' - comment: + user_comment: type: string description: 'Optional free-text feedback from the user.' conversation_history: diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index 3b702cf..72a022d 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -4,7 +4,7 @@ {{ relation.database }}.{{ relation.schema }}.{{ relation.identifier }}_SUBMIT_FEEDBACK( SESSION_ID varchar, RATING varchar, - COMMENT varchar, + USER_COMMENT varchar, CONVERSATION_HISTORY varchar ) returns varchar @@ -15,7 +15,7 @@ insert into {{ feedback_table }} (agent_name, session_id, rating, comment, conversation_history, created_at) values - ('{{ relation.identifier }}', SESSION_ID, RATING, COMMENT, parse_json(CONVERSATION_HISTORY), current_timestamp()); + ('{{ relation.identifier }}', SESSION_ID, RATING, USER_COMMENT, parse_json(CONVERSATION_HISTORY), current_timestamp()); return 'Feedback submitted'; end; $$ From 9f897fa2fb005fd01dc5cc1c8047cb122d47b13e Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 15:40:50 -0500 Subject: [PATCH 09/16] change feedback proc --- README.md | 6 +++--- integration_tests/models/cortex_agent_test.sql | 7 +++---- .../cortex_agent/create_feedback_procedure.sql | 14 +++++++------- .../cortex_agent/create_feedback_table.sql | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3e8dabb..d30c7bb 100644 --- a/README.md +++ b/README.md @@ -131,8 +131,8 @@ tools: type: string description: 'Current conversation session identifier.' rating: - type: string - enum: [good, bad] + type: number + description: 'The user rating from 1 (worst) to 5 (best).' user_comment: type: string description: 'Optional free-text feedback.' @@ -148,7 +148,7 @@ tool_resources: type: warehouse warehouse: '' identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier | upper }}_SUBMIT_FEEDBACK' - name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARCHAR)' + name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, NUMBER, VARCHAR, VARCHAR)' type: procedure ``` diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index 62f009f..382c358 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -64,9 +64,8 @@ tools: type: string description: 'Current conversation session identifier.' rating: - type: string - enum: [good, bad] - description: 'The user rating.' + type: number + description: 'The user rating from 1 (worst) to 5 (best).' user_comment: type: string description: 'Optional free-text feedback from the user.' @@ -94,5 +93,5 @@ tool_resources: type: warehouse warehouse: '' identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier | upper }}_SUBMIT_FEEDBACK' - name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, VARCHAR, VARCHAR)' + name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, NUMBER, VARCHAR, VARCHAR)' type: procedure diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index 72a022d..5435ee9 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -1,23 +1,23 @@ {% macro snowflake__create_feedback_procedure(relation, feedback_table) %} create or replace procedure - {{ relation.database }}.{{ relation.schema }}.{{ relation.identifier }}_SUBMIT_FEEDBACK( + {{ relation.database }}.{{ relation.schema }}.{{ relation.identifier | upper }}_SUBMIT_FEEDBACK( SESSION_ID varchar, - RATING varchar, + RATING number, USER_COMMENT varchar, CONVERSATION_HISTORY varchar ) returns varchar language sql + execute as owner as - $$ begin + let parsed_history variant := parse_json(:CONVERSATION_HISTORY); insert into {{ feedback_table }} (agent_name, session_id, rating, comment, conversation_history, created_at) - values - ('{{ relation.identifier }}', SESSION_ID, RATING, USER_COMMENT, parse_json(CONVERSATION_HISTORY), current_timestamp()); + select + '{{ relation.identifier | upper }}', :SESSION_ID, :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); return 'Feedback submitted'; - end; - $$ + end {% endmacro %} diff --git a/macros/relations/cortex_agent/create_feedback_table.sql b/macros/relations/cortex_agent/create_feedback_table.sql index a34bec0..720a3d3 100644 --- a/macros/relations/cortex_agent/create_feedback_table.sql +++ b/macros/relations/cortex_agent/create_feedback_table.sql @@ -4,7 +4,7 @@ feedback_id varchar default uuid_string(), agent_name varchar not null, session_id varchar, - rating varchar, + rating number, comment varchar, conversation_history variant, created_at timestamp_ntz default current_timestamp() From 2a18f9efe7e0fb17e3d0743f10174e06fa3b0882 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 15:59:00 -0500 Subject: [PATCH 10/16] print failure. --- .github/workflows/integration_tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index ee0faf6..a7d3b29 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -47,6 +47,12 @@ jobs: SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.SNOWFLAKE_TEST_WAREHOUSE }} SNOWFLAKE_TEST_SCHEMA: ${{ secrets.SNOWFLAKE_TEST_SCHEMA }} + - name: Print compiled SQL on failure + if: failure() + run: | + echo "=== Compiled cortex_agent_test.sql ===" + cat integration_tests/target/compiled/dbt_cortex_agent_integration_tests/models/cortex_agent_test.sql || echo "(file not found)" + - name: Run dbt tests working-directory: integration_tests run: dbt test From c8acd8883b361c2cc7fc95eeff02d2bfec7b10de Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 16:04:29 -0500 Subject: [PATCH 11/16] fix syntax error --- .github/workflows/integration_tests.yml | 4 +++- macros/relations/cortex_agent/create_feedback_procedure.sql | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index a7d3b29..88152a5 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -47,11 +47,13 @@ jobs: SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.SNOWFLAKE_TEST_WAREHOUSE }} SNOWFLAKE_TEST_SCHEMA: ${{ secrets.SNOWFLAKE_TEST_SCHEMA }} - - name: Print compiled SQL on failure + - name: Print compiled and run SQL on failure if: failure() run: | echo "=== Compiled cortex_agent_test.sql ===" cat integration_tests/target/compiled/dbt_cortex_agent_integration_tests/models/cortex_agent_test.sql || echo "(file not found)" + echo "=== Run cortex_agent_test.sql ===" + cat integration_tests/target/run/dbt_cortex_agent_integration_tests/models/cortex_agent_test.sql || echo "(file not found)" - name: Run dbt tests working-directory: integration_tests diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index 5435ee9..f03abb4 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -11,6 +11,7 @@ language sql execute as owner as + $$ begin let parsed_history variant := parse_json(:CONVERSATION_HISTORY); insert into {{ feedback_table }} @@ -18,6 +19,7 @@ select '{{ relation.identifier | upper }}', :SESSION_ID, :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); return 'Feedback submitted'; - end + end; + $$ {% endmacro %} From e635d5952e2ec8c05bd15683d233bd5643bc56a0 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Wed, 27 May 2026 16:24:04 -0500 Subject: [PATCH 12/16] ensure each agent gets it's own table --- README.md | 16 +++++++++++----- integration_tests/models/cortex_agent_test.sql | 3 +-- macros/materializations/cortex_agent.sql | 8 ++++++-- .../cortex_agent/create_feedback_procedure.sql | 4 ++-- .../cortex_agent/create_feedback_table.sql | 2 +- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d30c7bb..9879c95 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ SHOW AGENTS IN SCHEMA my_db.my_schema; | `comment` | string | No | Agent description visible in Snowflake | | `profile` | string (JSON) | No | `{"display_name": "...", "avatar": "...", "color": "..."}` | | `agent_grants` | list | No | Role names to grant `USAGE` on the agent, e.g. `['my_role']` | -| `feedback_table` | string | No | Fully-qualified table for user feedback, e.g. `'MY_DB.MY_SCHEMA.AGENT_FEEDBACK'`. See [Feedback Tool](#feedback-tool). | +| `feedback_table` | string | No | Fully-qualified table for user feedback. Defaults to `{DB}.{SCHEMA}.{AGENT_NAME}_FEEDBACK`. See [Feedback Tool](#feedback-tool). | ## How It Works @@ -106,18 +106,24 @@ Refer to the [Snowflake CREATE AGENT docs](https://docs.snowflake.com/en/sql-ref ## Feedback Tool -Set `feedback_table` in your model config to automatically provision: +Every agent automatically gets its own feedback table and stored procedure — no config required. On each `dbt run` the materialization provisions: -1. A **feedback table** (created once, never replaced) with columns: `feedback_id`, `agent_name`, `session_id`, `rating`, `comment`, `conversation_history`, `created_at` +1. A **feedback table** named `{AGENT_NAME}_FEEDBACK` in the same database and schema as the agent, with columns: `feedback_id`, `session_id`, `rating`, `comment`, `conversation_history`, `created_at` 2. A **stored procedure** named `{AGENT_NAME}_SUBMIT_FEEDBACK` in the same database/schema as the agent -Then add the tool entry to your spec body so the agent can call it: +To use a different table name, set `feedback_table` explicitly in your config: ```sql {{ config( materialized='cortex_agent', - feedback_table='MY_DB.MY_SCHEMA.AGENT_FEEDBACK' + feedback_table='MY_DB.MY_SCHEMA.SHARED_FEEDBACK' ) }} +``` + +Add the tool entry to your spec body so the agent can call it: + +```sql +{{ config(materialized='cortex_agent') }} tools: - tool_spec: diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index 382c358..1fd6e97 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -5,8 +5,7 @@ comment='Full integration test agent — exercises every config and spec option', profile='{"display_name": "Full Test Agent", "avatar": "robot", "color": "blue"}', tags=['integration'], - agent_grants=['dbt_demo_role'], - feedback_table=target.database ~ '.' ~ target.schema ~ '_INTEGRATION_TESTS.CORTEX_AGENT_FEEDBACK' + agent_grants=['dbt_demo_role'] ) }} diff --git a/macros/materializations/cortex_agent.sql b/macros/materializations/cortex_agent.sql index caa2890..bca5e6f 100644 --- a/macros/materializations/cortex_agent.sql +++ b/macros/materializations/cortex_agent.sql @@ -6,8 +6,8 @@ -- comment (string, optional) : agent-level comment visible in Snowflake -- profile (string, optional) : JSON object with display_name, avatar, and color -- agent_grants (list, optional) : list of role names to grant USAGE on the agent --- feedback_table (string, optional) : fully-qualified table name for user feedback, --- e.g. 'MY_DB.MY_SCHEMA.AGENT_FEEDBACK'. +-- feedback_table (string, optional) : fully-qualified table name for user feedback. +-- Defaults to {DB}.{SCHEMA}.{AGENT}_FEEDBACK. -- Creates the table (if absent) and a stored procedure -- named {agent}_SUBMIT_FEEDBACK on every dbt run. @@ -25,6 +25,10 @@ type='view' ) -%} + {%- if feedback_table is none -%} + {%- set feedback_table = target_relation.database ~ '.' ~ target_relation.schema ~ '.' ~ (target_relation.identifier | upper) ~ '_FEEDBACK' -%} + {%- endif -%} + {{ run_hooks(pre_hooks) }} {%- if feedback_table is not none %} diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index f03abb4..df52da6 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -15,9 +15,9 @@ begin let parsed_history variant := parse_json(:CONVERSATION_HISTORY); insert into {{ feedback_table }} - (agent_name, session_id, rating, comment, conversation_history, created_at) + (session_id, rating, comment, conversation_history, created_at) select - '{{ relation.identifier | upper }}', :SESSION_ID, :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); + :SESSION_ID, :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); return 'Feedback submitted'; end; $$ diff --git a/macros/relations/cortex_agent/create_feedback_table.sql b/macros/relations/cortex_agent/create_feedback_table.sql index 720a3d3..0d377fd 100644 --- a/macros/relations/cortex_agent/create_feedback_table.sql +++ b/macros/relations/cortex_agent/create_feedback_table.sql @@ -2,7 +2,7 @@ create table if not exists {{ feedback_table }} ( feedback_id varchar default uuid_string(), - agent_name varchar not null, + agent_name varchar, session_id varchar, rating number, comment varchar, From 9aac43271abdecd0419cb43e2e145121e932e96b Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Thu, 28 May 2026 08:50:20 -0500 Subject: [PATCH 13/16] Update feedback table to be optional and store user --- README.md | 12 +++++++++++- macros/materializations/cortex_agent.sql | 15 +++++++++------ .../cortex_agent/create_feedback_procedure.sql | 6 +++--- .../cortex_agent/create_feedback_table.sql | 1 + 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9879c95..c0f896f 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,8 @@ SHOW AGENTS IN SCHEMA my_db.my_schema; | `comment` | string | No | Agent description visible in Snowflake | | `profile` | string (JSON) | No | `{"display_name": "...", "avatar": "...", "color": "..."}` | | `agent_grants` | list | No | Role names to grant `USAGE` on the agent, e.g. `['my_role']` | -| `feedback_table` | string | No | Fully-qualified table for user feedback. Defaults to `{DB}.{SCHEMA}.{AGENT_NAME}_FEEDBACK`. See [Feedback Tool](#feedback-tool). | +| `create_feedback_table` | bool | No | Whether to create the feedback table and procedure. Defaults to `true`. Set to `false` to skip. See [Feedback Tool](#feedback-tool). | +| `feedback_table` | string | No | Fully-qualified table for user feedback. Defaults to `{DB}.{SCHEMA}.{AGENT_NAME}_FEEDBACK`. Ignored when `create_feedback_table` is `false`. See [Feedback Tool](#feedback-tool). | ## How It Works @@ -111,6 +112,15 @@ Every agent automatically gets its own feedback table and stored procedure — n 1. A **feedback table** named `{AGENT_NAME}_FEEDBACK` in the same database and schema as the agent, with columns: `feedback_id`, `session_id`, `rating`, `comment`, `conversation_history`, `created_at` 2. A **stored procedure** named `{AGENT_NAME}_SUBMIT_FEEDBACK` in the same database/schema as the agent +To disable feedback provisioning entirely, set `create_feedback_table: false`: + +```sql +{{ config( + materialized='cortex_agent', + create_feedback_table=false +) }} +``` + To use a different table name, set `feedback_table` explicitly in your config: ```sql diff --git a/macros/materializations/cortex_agent.sql b/macros/materializations/cortex_agent.sql index bca5e6f..191ce10 100644 --- a/macros/materializations/cortex_agent.sql +++ b/macros/materializations/cortex_agent.sql @@ -6,6 +6,8 @@ -- comment (string, optional) : agent-level comment visible in Snowflake -- profile (string, optional) : JSON object with display_name, avatar, and color -- agent_grants (list, optional) : list of role names to grant USAGE on the agent +-- create_feedback_table (bool, optional) : whether to create the feedback table and procedure. +-- Defaults to true. Set to false to skip. -- feedback_table (string, optional) : fully-qualified table name for user feedback. -- Defaults to {DB}.{SCHEMA}.{AGENT}_FEEDBACK. -- Creates the table (if absent) and a stored procedure @@ -13,10 +15,11 @@ {% materialization cortex_agent, adapter='snowflake' %} - {%- set comment = config.get('comment', default=none) -%} - {%- set profile = config.get('profile', default=none) -%} - {%- set agent_grants = config.get('agent_grants', default=[]) -%} - {%- set feedback_table = config.get('feedback_table', default=none) -%} + {%- set comment = config.get('comment', default=none) -%} + {%- set profile = config.get('profile', default=none) -%} + {%- set agent_grants = config.get('agent_grants', default=[]) -%} + {%- set create_feedback_table = config.get('create_feedback_table', default=true) -%} + {%- set feedback_table = config.get('feedback_table', default=none) -%} {%- set target_relation = api.Relation.create( identifier=this.identifier, @@ -25,13 +28,13 @@ type='view' ) -%} - {%- if feedback_table is none -%} + {%- if create_feedback_table and feedback_table is none -%} {%- set feedback_table = target_relation.database ~ '.' ~ target_relation.schema ~ '.' ~ (target_relation.identifier | upper) ~ '_FEEDBACK' -%} {%- endif -%} {{ run_hooks(pre_hooks) }} - {%- if feedback_table is not none %} + {%- if create_feedback_table and feedback_table is not none %} {% call statement('feedback_table') %} {{ dbt_cortex_agent.snowflake__create_feedback_table(feedback_table) }} {% endcall %} diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index df52da6..ccaa91a 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -9,15 +9,15 @@ ) returns varchar language sql - execute as owner + execute as caller as $$ begin let parsed_history variant := parse_json(:CONVERSATION_HISTORY); insert into {{ feedback_table }} - (session_id, rating, comment, conversation_history, created_at) + (session_id, user_name, rating, comment, conversation_history, created_at) select - :SESSION_ID, :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); + :SESSION_ID, current_user(), :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); return 'Feedback submitted'; end; $$ diff --git a/macros/relations/cortex_agent/create_feedback_table.sql b/macros/relations/cortex_agent/create_feedback_table.sql index 0d377fd..24cfd26 100644 --- a/macros/relations/cortex_agent/create_feedback_table.sql +++ b/macros/relations/cortex_agent/create_feedback_table.sql @@ -4,6 +4,7 @@ feedback_id varchar default uuid_string(), agent_name varchar, session_id varchar, + user_name varchar, rating number, comment varchar, conversation_history variant, From c182cf234e9c1cb092e2c14af0f218aa1735478d Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Thu, 28 May 2026 08:55:59 -0500 Subject: [PATCH 14/16] all feedback goes through one table and procedure --- README.md | 21 +++++++++++-------- .../models/cortex_agent_test.sql | 12 +++++++---- .../cortex_agent_test_has_feedback_proc.sql | 6 +++--- macros/materializations/cortex_agent.sql | 4 ++-- .../create_feedback_procedure.sql | 7 ++++--- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c0f896f..afb90d6 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ SHOW AGENTS IN SCHEMA my_db.my_schema; | `profile` | string (JSON) | No | `{"display_name": "...", "avatar": "...", "color": "..."}` | | `agent_grants` | list | No | Role names to grant `USAGE` on the agent, e.g. `['my_role']` | | `create_feedback_table` | bool | No | Whether to create the feedback table and procedure. Defaults to `true`. Set to `false` to skip. See [Feedback Tool](#feedback-tool). | -| `feedback_table` | string | No | Fully-qualified table for user feedback. Defaults to `{DB}.{SCHEMA}.{AGENT_NAME}_FEEDBACK`. Ignored when `create_feedback_table` is `false`. See [Feedback Tool](#feedback-tool). | +| `feedback_table` | string | No | Fully-qualified table for user feedback. Defaults to `{DB}.{SCHEMA}.AGENT_FEEDBACK` (shared across all agents in the schema). Ignored when `create_feedback_table` is `false`. See [Feedback Tool](#feedback-tool). | ## How It Works @@ -107,10 +107,10 @@ Refer to the [Snowflake CREATE AGENT docs](https://docs.snowflake.com/en/sql-ref ## Feedback Tool -Every agent automatically gets its own feedback table and stored procedure — no config required. On each `dbt run` the materialization provisions: +All agents in the same schema share a single feedback table and a single stored procedure — no config required. On each `dbt run` the materialization provisions (idempotently): -1. A **feedback table** named `{AGENT_NAME}_FEEDBACK` in the same database and schema as the agent, with columns: `feedback_id`, `session_id`, `rating`, `comment`, `conversation_history`, `created_at` -2. A **stored procedure** named `{AGENT_NAME}_SUBMIT_FEEDBACK` in the same database/schema as the agent +1. A **feedback table** named `AGENT_FEEDBACK` in the same database and schema as the agent, with columns: `feedback_id`, `agent_name`, `session_id`, `user_name`, `rating`, `comment`, `conversation_history`, `created_at` +2. A **stored procedure** named `AGENT_SUBMIT_FEEDBACK` in the same database/schema as the agent To disable feedback provisioning entirely, set `create_feedback_table: false`: @@ -138,11 +138,14 @@ Add the tool entry to your spec body so the agent can call it: tools: - tool_spec: type: generic - name: SUBMIT_FEEDBACK + name: AGENT_SUBMIT_FEEDBACK description: 'Records user feedback. Call when the user rates or comments on a response. Always pass the last 10 conversation messages.' input_schema: type: object properties: + agent_name: + type: string + description: 'Name of this agent. Always pass "{{ this.identifier | upper }}".' session_id: type: string description: 'Current conversation session identifier.' @@ -155,16 +158,16 @@ tools: conversation_history: type: string description: 'Last 10 messages from the conversation as a JSON string, e.g. [{"role":"user","content":"..."}].' - required: [session_id, rating, conversation_history] + required: [agent_name, session_id, rating, conversation_history] tool_resources: - SUBMIT_FEEDBACK: + AGENT_SUBMIT_FEEDBACK: execution_environment: query_timeout: 300 type: warehouse warehouse: '' - identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier | upper }}_SUBMIT_FEEDBACK' - name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, NUMBER, VARCHAR, VARCHAR)' + identifier: '{{ this.database }}.{{ this.schema }}.AGENT_SUBMIT_FEEDBACK' + name: 'AGENT_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, NUMBER, VARCHAR, VARCHAR)' type: procedure ``` diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index 1fd6e97..686a678 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -54,11 +54,14 @@ tools: - tool_spec: type: generic - name: SUBMIT_FEEDBACK + name: AGENT_SUBMIT_FEEDBACK description: 'Records user feedback about agent responses. Call when the user expresses satisfaction or dissatisfaction, or explicitly asks to rate or submit feedback. Always include the last 10 conversation messages.' input_schema: type: object properties: + agent_name: + type: string + description: 'Name of this agent. Always pass "{{ this.identifier | upper }}".' session_id: type: string description: 'Current conversation session identifier.' @@ -72,6 +75,7 @@ tools: type: string description: 'Last 10 messages from the conversation as a JSON string, e.g. [{"role":"user","content":"..."}].' required: + - agent_name - session_id - rating - conversation_history @@ -86,11 +90,11 @@ tool_resources: max_results: 10 title_column: description id_column: id - SUBMIT_FEEDBACK: + AGENT_SUBMIT_FEEDBACK: execution_environment: query_timeout: 300 type: warehouse warehouse: '' - identifier: '{{ this.database }}.{{ this.schema }}.{{ this.identifier | upper }}_SUBMIT_FEEDBACK' - name: '{{ this.identifier | upper }}_SUBMIT_FEEDBACK(VARCHAR, NUMBER, VARCHAR, VARCHAR)' + identifier: '{{ this.database }}.{{ this.schema }}.AGENT_SUBMIT_FEEDBACK' + name: 'AGENT_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, NUMBER, VARCHAR, VARCHAR)' type: procedure diff --git a/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql b/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql index 523fd0d..09e43e1 100644 --- a/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql +++ b/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql @@ -1,10 +1,10 @@ --- Fails if the SUBMIT_FEEDBACK stored procedure was not created by the materialization. +-- Fails if the shared AGENT_SUBMIT_FEEDBACK stored procedure was not created by the materialization. -- Returns 0 rows on success (standard dbt test contract). -select 'CORTEX_AGENT_TEST_SUBMIT_FEEDBACK procedure not found' as error +select 'AGENT_SUBMIT_FEEDBACK procedure not found' as error where not exists ( select 1 from {{ target.database }}.information_schema.procedures where procedure_schema = '{{ target.schema }}_INTEGRATION_TESTS' - and procedure_name = 'CORTEX_AGENT_TEST_SUBMIT_FEEDBACK' + and procedure_name = 'AGENT_SUBMIT_FEEDBACK' ) diff --git a/macros/materializations/cortex_agent.sql b/macros/materializations/cortex_agent.sql index 191ce10..dae9887 100644 --- a/macros/materializations/cortex_agent.sql +++ b/macros/materializations/cortex_agent.sql @@ -11,7 +11,7 @@ -- feedback_table (string, optional) : fully-qualified table name for user feedback. -- Defaults to {DB}.{SCHEMA}.{AGENT}_FEEDBACK. -- Creates the table (if absent) and a stored procedure --- named {agent}_SUBMIT_FEEDBACK on every dbt run. +-- named {agent}_AGENT_SUBMIT_FEEDBACK on every dbt run. {% materialization cortex_agent, adapter='snowflake' %} @@ -29,7 +29,7 @@ ) -%} {%- if create_feedback_table and feedback_table is none -%} - {%- set feedback_table = target_relation.database ~ '.' ~ target_relation.schema ~ '.' ~ (target_relation.identifier | upper) ~ '_FEEDBACK' -%} + {%- set feedback_table = target_relation.database ~ '.' ~ target_relation.schema ~ '.AGENT_FEEDBACK' -%} {%- endif -%} {{ run_hooks(pre_hooks) }} diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index ccaa91a..9ad06e2 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -1,7 +1,8 @@ {% macro snowflake__create_feedback_procedure(relation, feedback_table) %} create or replace procedure - {{ relation.database }}.{{ relation.schema }}.{{ relation.identifier | upper }}_SUBMIT_FEEDBACK( + {{ relation.database }}.{{ relation.schema }}.AGENT_SUBMIT_FEEDBACK( + AGENT_NAME varchar, SESSION_ID varchar, RATING number, USER_COMMENT varchar, @@ -15,9 +16,9 @@ begin let parsed_history variant := parse_json(:CONVERSATION_HISTORY); insert into {{ feedback_table }} - (session_id, user_name, rating, comment, conversation_history, created_at) + (agent_name, session_id, user_name, rating, comment, conversation_history, created_at) select - :SESSION_ID, current_user(), :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); + :AGENT_NAME, :SESSION_ID, current_user(), :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); return 'Feedback submitted'; end; $$ From 92b4c1fbef4e13e2d1cfba4bf5851ed8a668aba2 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Thu, 28 May 2026 09:07:10 -0500 Subject: [PATCH 15/16] remove session id. db schema override. --- README.md | 52 +++++++++++++------ .../models/cortex_agent_test.sql | 6 +-- macros/materializations/cortex_agent.sql | 28 ++++++++-- .../create_feedback_procedure.sql | 9 ++-- .../cortex_agent/create_feedback_table.sql | 1 - 5 files changed, 64 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index afb90d6..c6380ba 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,8 @@ SHOW AGENTS IN SCHEMA my_db.my_schema; | `profile` | string (JSON) | No | `{"display_name": "...", "avatar": "...", "color": "..."}` | | `agent_grants` | list | No | Role names to grant `USAGE` on the agent, e.g. `['my_role']` | | `create_feedback_table` | bool | No | Whether to create the feedback table and procedure. Defaults to `true`. Set to `false` to skip. See [Feedback Tool](#feedback-tool). | -| `feedback_table` | string | No | Fully-qualified table for user feedback. Defaults to `{DB}.{SCHEMA}.AGENT_FEEDBACK` (shared across all agents in the schema). Ignored when `create_feedback_table` is `false`. See [Feedback Tool](#feedback-tool). | +| `feedback_schema` | string | No | Schema for the feedback table and `AGENT_SUBMIT_FEEDBACK` procedure. Accepts `'SCHEMA'` or `'DB.SCHEMA'`. Defaults to the agent's own database and schema. See [Feedback Tool](#feedback-tool). | +| `feedback_table` | string | No | Fully-qualified table name override for user feedback. Defaults to `{feedback_schema}.AGENT_FEEDBACK`. Ignored when `create_feedback_table` is `false`. See [Feedback Tool](#feedback-tool). | ## How It Works @@ -107,33 +108,55 @@ Refer to the [Snowflake CREATE AGENT docs](https://docs.snowflake.com/en/sql-ref ## Feedback Tool -All agents in the same schema share a single feedback table and a single stored procedure — no config required. On each `dbt run` the materialization provisions (idempotently): +All agents share a single feedback table and a single `AGENT_SUBMIT_FEEDBACK` procedure — no config required. On each `dbt run` the materialization provisions (idempotently): -1. A **feedback table** named `AGENT_FEEDBACK` in the same database and schema as the agent, with columns: `feedback_id`, `agent_name`, `session_id`, `user_name`, `rating`, `comment`, `conversation_history`, `created_at` -2. A **stored procedure** named `AGENT_SUBMIT_FEEDBACK` in the same database/schema as the agent +1. A **feedback table** named `AGENT_FEEDBACK` in the agent's database and schema, with columns: `feedback_id`, `agent_name`, `user_name`, `rating`, `comment`, `conversation_history`, `created_at` +2. A **stored procedure** named `AGENT_SUBMIT_FEEDBACK` in the same location -To disable feedback provisioning entirely, set `create_feedback_table: false`: +By default both land in the agent's own schema. Use `feedback_schema` to place them in a shared schema instead: ```sql {{ config( materialized='cortex_agent', - create_feedback_table=false + feedback_schema='SHARED_SCHEMA' +) }} +``` + +Or with an explicit database: + +```sql +{{ config( + materialized='cortex_agent', + feedback_schema='MY_DB.SHARED_SCHEMA' +) }} +``` + +To override the table name independently of the schema, use `feedback_table`: + +```sql +{{ config( + materialized='cortex_agent', + feedback_schema='SHARED_SCHEMA', + feedback_table='MY_DB.SHARED_SCHEMA.AGENT_FEEDBACK' ) }} ``` -To use a different table name, set `feedback_table` explicitly in your config: +To disable feedback provisioning entirely, set `create_feedback_table: false`: ```sql {{ config( materialized='cortex_agent', - feedback_table='MY_DB.MY_SCHEMA.SHARED_FEEDBACK' + create_feedback_table=false ) }} ``` -Add the tool entry to your spec body so the agent can call it: +Add the tool entry to your spec body so the agent can call it. Update the `identifier` to match your `feedback_schema` if you set one: ```sql -{{ config(materialized='cortex_agent') }} +{{ config( + materialized='cortex_agent', + feedback_schema='SHARED_SCHEMA' +) }} tools: - tool_spec: @@ -146,9 +169,6 @@ tools: agent_name: type: string description: 'Name of this agent. Always pass "{{ this.identifier | upper }}".' - session_id: - type: string - description: 'Current conversation session identifier.' rating: type: number description: 'The user rating from 1 (worst) to 5 (best).' @@ -158,7 +178,7 @@ tools: conversation_history: type: string description: 'Last 10 messages from the conversation as a JSON string, e.g. [{"role":"user","content":"..."}].' - required: [agent_name, session_id, rating, conversation_history] + required: [agent_name, rating, conversation_history] tool_resources: AGENT_SUBMIT_FEEDBACK: @@ -166,8 +186,8 @@ tool_resources: query_timeout: 300 type: warehouse warehouse: '' - identifier: '{{ this.database }}.{{ this.schema }}.AGENT_SUBMIT_FEEDBACK' - name: 'AGENT_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, NUMBER, VARCHAR, VARCHAR)' + identifier: '{{ this.database }}.SHARED_SCHEMA.AGENT_SUBMIT_FEEDBACK' + name: 'AGENT_SUBMIT_FEEDBACK(VARCHAR, NUMBER, VARCHAR, VARCHAR)' type: procedure ``` diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index 686a678..d653dc1 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -62,9 +62,6 @@ tools: agent_name: type: string description: 'Name of this agent. Always pass "{{ this.identifier | upper }}".' - session_id: - type: string - description: 'Current conversation session identifier.' rating: type: number description: 'The user rating from 1 (worst) to 5 (best).' @@ -76,7 +73,6 @@ tools: description: 'Last 10 messages from the conversation as a JSON string, e.g. [{"role":"user","content":"..."}].' required: - agent_name - - session_id - rating - conversation_history @@ -96,5 +92,5 @@ tool_resources: type: warehouse warehouse: '' identifier: '{{ this.database }}.{{ this.schema }}.AGENT_SUBMIT_FEEDBACK' - name: 'AGENT_SUBMIT_FEEDBACK(VARCHAR, VARCHAR, NUMBER, VARCHAR, VARCHAR)' + name: 'AGENT_SUBMIT_FEEDBACK(VARCHAR, NUMBER, VARCHAR, VARCHAR)' type: procedure diff --git a/macros/materializations/cortex_agent.sql b/macros/materializations/cortex_agent.sql index dae9887..38a9e13 100644 --- a/macros/materializations/cortex_agent.sql +++ b/macros/materializations/cortex_agent.sql @@ -8,10 +8,13 @@ -- agent_grants (list, optional) : list of role names to grant USAGE on the agent -- create_feedback_table (bool, optional) : whether to create the feedback table and procedure. -- Defaults to true. Set to false to skip. --- feedback_table (string, optional) : fully-qualified table name for user feedback. --- Defaults to {DB}.{SCHEMA}.{AGENT}_FEEDBACK. +-- feedback_schema (string, optional) : schema for the feedback table and AGENT_SUBMIT_FEEDBACK +-- procedure. Accepts 'SCHEMA' or 'DB.SCHEMA'. Defaults to +-- the agent's own database and schema. +-- feedback_table (string, optional) : fully-qualified override for the feedback table name. +-- Defaults to {feedback_schema}.AGENT_FEEDBACK. -- Creates the table (if absent) and a stored procedure --- named {agent}_AGENT_SUBMIT_FEEDBACK on every dbt run. +-- named AGENT_SUBMIT_FEEDBACK on every dbt run. {% materialization cortex_agent, adapter='snowflake' %} @@ -19,6 +22,7 @@ {%- set profile = config.get('profile', default=none) -%} {%- set agent_grants = config.get('agent_grants', default=[]) -%} {%- set create_feedback_table = config.get('create_feedback_table', default=true) -%} + {%- set feedback_schema_config = config.get('feedback_schema', default=none) -%} {%- set feedback_table = config.get('feedback_table', default=none) -%} {%- set target_relation = api.Relation.create( @@ -28,8 +32,22 @@ type='view' ) -%} + {%- if feedback_schema_config is not none -%} + {%- set _parts = feedback_schema_config.split('.') -%} + {%- if _parts | length == 2 -%} + {%- set feedback_db = _parts[0] -%} + {%- set feedback_schema = _parts[1] -%} + {%- else -%} + {%- set feedback_db = target_relation.database -%} + {%- set feedback_schema = feedback_schema_config -%} + {%- endif -%} + {%- else -%} + {%- set feedback_db = target_relation.database -%} + {%- set feedback_schema = target_relation.schema -%} + {%- endif -%} + {%- if create_feedback_table and feedback_table is none -%} - {%- set feedback_table = target_relation.database ~ '.' ~ target_relation.schema ~ '.AGENT_FEEDBACK' -%} + {%- set feedback_table = feedback_db ~ '.' ~ feedback_schema ~ '.AGENT_FEEDBACK' -%} {%- endif -%} {{ run_hooks(pre_hooks) }} @@ -40,7 +58,7 @@ {% endcall %} {% call statement('feedback_procedure') %} - {{ dbt_cortex_agent.snowflake__create_feedback_procedure(target_relation, feedback_table) }} + {{ dbt_cortex_agent.snowflake__create_feedback_procedure(feedback_db, feedback_schema, feedback_table) }} {% endcall %} {%- endif %} diff --git a/macros/relations/cortex_agent/create_feedback_procedure.sql b/macros/relations/cortex_agent/create_feedback_procedure.sql index 9ad06e2..a105b04 100644 --- a/macros/relations/cortex_agent/create_feedback_procedure.sql +++ b/macros/relations/cortex_agent/create_feedback_procedure.sql @@ -1,9 +1,8 @@ -{% macro snowflake__create_feedback_procedure(relation, feedback_table) %} +{% macro snowflake__create_feedback_procedure(feedback_db, feedback_schema, feedback_table) %} create or replace procedure - {{ relation.database }}.{{ relation.schema }}.AGENT_SUBMIT_FEEDBACK( + {{ feedback_db }}.{{ feedback_schema }}.AGENT_SUBMIT_FEEDBACK( AGENT_NAME varchar, - SESSION_ID varchar, RATING number, USER_COMMENT varchar, CONVERSATION_HISTORY varchar @@ -16,9 +15,9 @@ begin let parsed_history variant := parse_json(:CONVERSATION_HISTORY); insert into {{ feedback_table }} - (agent_name, session_id, user_name, rating, comment, conversation_history, created_at) + (agent_name, user_name, rating, comment, conversation_history, created_at) select - :AGENT_NAME, :SESSION_ID, current_user(), :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); + :AGENT_NAME, current_user(), :RATING, :USER_COMMENT, :parsed_history, current_timestamp(); return 'Feedback submitted'; end; $$ diff --git a/macros/relations/cortex_agent/create_feedback_table.sql b/macros/relations/cortex_agent/create_feedback_table.sql index 24cfd26..6867fe7 100644 --- a/macros/relations/cortex_agent/create_feedback_table.sql +++ b/macros/relations/cortex_agent/create_feedback_table.sql @@ -3,7 +3,6 @@ create table if not exists {{ feedback_table }} ( feedback_id varchar default uuid_string(), agent_name varchar, - session_id varchar, user_name varchar, rating number, comment varchar, From e7f7a9ef363c9ae22a55d76bbdf33493786ebc29 Mon Sep 17 00:00:00 2001 From: Paul Narup Date: Thu, 28 May 2026 09:14:00 -0500 Subject: [PATCH 16/16] update feedback instructions --- README.md | 2 +- integration_tests/models/cortex_agent_test.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c6380ba..4709496 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ tools: - tool_spec: type: generic name: AGENT_SUBMIT_FEEDBACK - description: 'Records user feedback. Call when the user rates or comments on a response. Always pass the last 10 conversation messages.' + description: 'Records user feedback. Call when the user says "feedback" or explicitly rates or comments on a response. Always pass the last 10 conversation messages. Example: user says "feedback: 1. Output wrong, expected $100" → call with rating=1, user_comment="Output wrong, expected $100". Example: user says "feedback: 5. Completely Correct" → call with rating=5, user_comment="Completely Correct".' input_schema: type: object properties: diff --git a/integration_tests/models/cortex_agent_test.sql b/integration_tests/models/cortex_agent_test.sql index d653dc1..df9114a 100644 --- a/integration_tests/models/cortex_agent_test.sql +++ b/integration_tests/models/cortex_agent_test.sql @@ -55,7 +55,7 @@ tools: - tool_spec: type: generic name: AGENT_SUBMIT_FEEDBACK - description: 'Records user feedback about agent responses. Call when the user expresses satisfaction or dissatisfaction, or explicitly asks to rate or submit feedback. Always include the last 10 conversation messages.' + description: 'Records user feedback about agent responses. Call when the user says "feedback" or explicitly rates or comments on a response. Always include the last 10 conversation messages. Example: user says "feedback: 1. Output wrong, expected $100" → call with rating=1, user_comment="Output wrong, expected $100". Example: user says "feedback: 5. Completely Correct" → call with rating=5, user_comment="Completely Correct".' input_schema: type: object properties: