There's no built-in Genie feature that enforces a post-join HAVING threshold centrally, so you're right to think about this architecturally. The most reliable pattern in AppKit is to own the query execution layer yourself rather than letting Genie serve results directly.
Here's the approach: instead of embedding the Genie UI component directly, call the Genie REST API from your Databricks App's Python backend. When Genie responds to a user's question, the message contains a query attachment with the generated SQL text. Before showing anything to the user, you intercept that SQL, wrap it in a CTE, and re-execute it with your HAVING constraint enforced:
from databricks.sdk import WorkspaceClient
w = WorkspaceClient()
# Send the user's question to Genie
resp = w.genie.create_message_and_wait(
space_id=SPACE_ID,
conversation_id=conv.conversation_id,
content=user_question,
)
# Pull the generated SQL from the first query attachment
generated_sql = None
for attachment in (resp.attachments or []):
if attachment.query and attachment.query.query:
generated_sql = attachment.query.query
break
# Wrap with your privacy constraint before execution
if generated_sql:
safe_sql = f"""
WITH _genie AS (
{generated_sql}
)
SELECT *
FROM _genie
WHERE (
SELECT COUNT(DISTINCT customer_id)
FROM _genie
) >= 5
"""
# Execute safe_sql against your warehouse
The exact wrapping depends on your schema. If Genie's output is already grouped (e.g. counts by product/region), you'd want to filter rows where the underlying customer count in each group meets the threshold. If it returns individual rows, a scalar subquery like above works for the whole result. You may need to adapt based on what cohort dimension Genie is grouping by.
As a second layer, use SQL Instructions in your Genie Agent configuration ("Always include HAVING COUNT(DISTINCT customer_id) >= 5 when aggregating or grouping demographic data") to nudge the LLM toward generating safe queries in the first place. This doesn't replace the code-level enforcement but reduces the cases where wrapping gets complicated.
The key difference between the two: SQL instructions are best-effort guidance to the model. The CTE wrapping at the app layer is your actual enforcement boundary.