What you will be able to do
- Put ai_query inside a Lakeflow pipeline that ingests, classifies, validates and enriches data
- Restrict LLM classification to fixed labels with a responseFormat enum, and validate the result before the next step
- Choose between ai_classify and ai_query for a classification task
- Apply production practices: failOnError, submitting the full dataset at once, batch-optimized models and query-profile monitoring
1.Where inference sits in a batch pipeline
For large-scale batch inference, you can put AI Functions straight into production pipelines, either task-specific functions or ai_query. You can write them in SQL or Python and deploy them as Lakeflow pipelines, as scheduled Databricks workflows, or as Structured Streaming jobs. Inference is one step in a longer flow, with ingestion before it and post-processing after it.
The Databricks reference pipeline works on news articles. A streaming table, news_raw, ingests CSV files from a Unity Catalog volume. A materialized view, news_categorized, then calls ai_query on each article to extract a title and a category.
@dp.materialized_view(
comment="Extract category and title from news articles using LLM inference."
)
def news_categorized():
# Limit the number of rows to 2 as in the SQL version
df_raw = spark.read.table("news_raw").limit(2)
# Inject the JSON schema variable into the ai_query call using an f-string.
return df_raw.withColumn(
"meta_data",
expr(
f"ai_query('system.ai.meta-llama-3-3-70b-instruct', "
f"concat('Extract the category of the following news article: ', inputs), "
f"responseFormat => '{news_extraction_schema}')"
)
)In Python, ai_query is a SQL expression wrapped in expr() inside withColumn. Nothing needs installing: the same SQL function runs from a DataFrame.
Checkpoint 1 of 5· Put it in order
Put the stages of the reference news pipeline in order
- 1.Validate the model's title and category in news_validated
- 2.Ingest raw articles from a volume into the news_raw streaming table
- 3.Classify each article with ai_query into news_categorized
- 4.Generate summaries with a second ai_query in news_summarized
The pipeline follows the documented sequence of ingestion, preprocessing, inference and post-processing. Classification runs first, validation checks its output, and a second enrichment step builds on the rows that pass.
“end-to-end workflows that include ingestion, preprocessing, inference, and post-processing”Source: docs.databricks.com
2.Classify with a schema, validate, then enrich
The news_extraction_schema variable is a json_schema responseFormat with two properties. title is a free string. category is a string limited to an enum of six labels. That enum turns open-ended generation into classification: the model has to pick one of your labels. Because responseFormat returns a JSON string, later stages read individual fields with get_json_object(meta_data, '$.category').
Model output is still data you didn't write, so the next table checks it before anything relies on it. news_validated declares expectations on the parsed fields. The title must have at least three words and the category must be one of the six labels. In the Python version the category rule uses @dp.expect_or_fail. How expectation actions behave is covered in the data quality lesson. What matters here is the pattern: check the inference output before the next step uses it.
CREATE OR REFRESH MATERIALIZED VIEW news_validated (
CONSTRAINT valid_title EXPECT (size(split(get_json_object(meta_data, '$.title'), ' ')) >= 3),
CONSTRAINT valid_category EXPECT (get_json_object(meta_data, '$.category') IN ('Politics', 'Sports', 'Technology', 'Health', 'Entertainment', 'Business'))
)
COMMENT "Validated news articles ensuring the title has at least 3 words and the category is valid."
AS
SELECT *
FROM news_categorized;The last stage, news_summarized, reads category and title out of meta_data and calls ai_query again with a plain prompt, "Summarize the following political news article in 2-3 sentences". One pipeline can chain several inference steps, and each adds columns to rows that passed the step before.
When there is no schema to enforce, a few-shot prompt is another way to classify. The scheduled-workflow example tells the model it is an opinion-mining service, lists the allowed classifications (Quality, Service, Design, Safety, Efficiency, Usability, Price) and includes one worked DOCUMENT/RESULT pair. It then appends each REVIEW_TEXT.
Checkpoint 2 of 5· Exam question
A team deployed a custom fine-tuned classification model to a Databricks model-serving endpoint named `ticket_priority_classifier`, whose logged model signature expects two input features, `ticket_text` and `queue_name`. They want to call it from `ai_query` inside a Lakeflow pipeline to enrich an incoming streaming table. Which of the following are true about calling this endpoint correctly with ai_query?(Select 3)
Correct answers: A, B, C — The `request` argument must be a STRUCT such as `named_struct('ticket_text', ticket_text, 'queue_name', queue_name)`, with field names matching the endpoint's logged input features.; The calling user or service principal must hold `CAN QUERY` permission on the `ticket_priority_classifier` endpoint, or the call is rejected at the permissions layer.; On a sufficiently recent Databricks Runtime, ai_query can infer the `returnType` from the endpoint's logged model schema, making explicit returnType optional rather than mandatory.
- A. Custom endpoints backed by a model with named input features expect a STRUCT request whose field names line up with those features, exactly as named_struct produces here.
- B. ai_query enforces standard model-serving permissions, so the caller needs CAN QUERY on the target endpoint before the SQL call is allowed to reach it.
- C. On newer runtimes ai_query reads the endpoint's logged model schema to infer the return type automatically, which is why returnType became optional rather than required in every case.
- D. Flattening every request into a STRING is the foundation and external-model pattern; custom endpoints with named features specifically require the STRUCT form so field names map correctly.
- E. ai_query is precisely the mechanism for reaching model-serving endpoints, including custom ones, from SQL and DataFrame code without going through the REST API directly.
- F. ai_query takes the bare endpoint name registered in the same workspace, not a fully qualified URL; prefixing it with a workspace address is not valid syntax.
Sources2
3.When ai_classify is the better tool
If the job is just "put this text in one of these buckets," Databricks' advice to start with a task-specific function points to ai_classify. You supply labels, either as a JSON array of names or as a JSON object mapping each name to a description. Descriptions improve accuracy. You need at least 2 labels and no more than 500. The labels argument can be any expression that evaluates to a STRING, including a Delta table column, so the taxonomy can live in a table. The content can be raw text or a VARIANT produced by an upstream AI function such as ai_parse_document.
| Need | ai_classify | ai_query |
|---|---|---|
| Labels | JSON array or label-to-description object, 2–500 labels | Your own prompt, or a responseFormat enum |
| Model choice | Managed by Databricks | Any supported endpoint, including custom ML models |
| Extra output | confidence_score and rationale (version 2.1 options) | Whatever your schema or prompt asks for |
| Multiple labels per row | options multilabel set to "true" | Must be designed into the prompt or schema |
Databricks recommends version 2.1. It adds enableConfidenceScores and enableRationales, and it changes the output shape. Each returned classification becomes an object with a value key instead of a plain label string. That change applies to every 2.1 call, even with the new options turned off, so moving from 2.0 to 2.1 breaks downstream code that reads the label directly. Pin the version with options => map('version', '2.1').
Checkpoint 3 of 5· Check yourself
A pipeline moves from ai_classify version 2.0 to 2.1 without enabling any new options. Downstream code that reads the label string directly starts failing. Why?
The 2.1 output shape changes for every call, so 2.0 and 2.1 output is not interchangeable. Downstream code has to read the label from the value key.
“Update your queries and downstream code to read the label from the value key instead of reading it directly.”Source: docs.databricks.com
Sources3
4.Running inference at production scale
By default one bad row can sink a large batch. failOnError is an optional boolean that defaults to true, and it needs Databricks Runtime 15.3 or above. With the default, ai_query returns the parsed response and a failed row fails the query. Set it to false and ai_query returns a STRUCT holding the response and an error message. A row that succeeds has a null errorMessage. A row that fails because of a model endpoint error has a null response. Errors of any other kind still fail the whole query.
Checkpoint 4 of 5· Fill the gap
Which argument keeps one failing row from stopping this summarization query?
SELECT text, ai_query(
"system.ai.meta-llama-3-3-70b-instruct",
"Summarize the given text comprehensively, covering key points and main ideas concisely while retaining relevant details and examples. Ensure clarity and accuracy without unnecessary repetition or omissions: " || text,
? => false
) AS summary
FROM uc_catalog.schema.table;failOnError => false makes ai_query return a STRUCT of response and errorMessage, so endpoint failures show up per row instead of failing the query.
Source: docs.databricks.comDatabricks lists further practices for large workloads. Use Databricks-hosted system.ai model services rather than provisioned throughput endpoints, because they scale automatically. Pick a model that is optimized for batch inference, since other models can cut throughput. Submit the whole dataset in one query: AI Functions already handles parallelization, retries and scaling, and splitting the data into small batches by hand can lower throughput. To see progress, open the query profile and click AI Query. It shows how many inferences completed and how many failed.
Checkpoint 5 of 5· Check yourself
An engineer loops over 10,000-row slices of a 50-million-row table and calls ai_query once per slice, "to avoid overloading the model". What does the documentation recommend instead?
AI Functions handles parallelization, retries and scaling automatically, and splitting the data by hand can reduce throughput.
“Manually splitting data into small batches can reduce throughput.”Source: docs.databricks.com
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.With failOnError => false, ai_query never fails the query.Why is that wrong?
Only model endpoint errors become per-row error messages. Errors of any other kind still fail the whole query.
Covered in Running inference at production scale
2.Splitting a large table into small manual batches makes ai_query faster and safer.Why is that wrong?
AI Functions handles parallelization, retries and scaling itself. Databricks says to submit the full dataset in one query.
Covered in Running inference at production scale
3.Upgrading ai_classify from version 2.0 to 2.1 is safe as long as you don't turn on confidence scores or rationales.Why is that wrong?
The 2.1 output shape, an object with a value key for each label, applies to every 2.1 call. The upgrade is not output-compatible.
Covered in When ai_classify is the better tool
Practise it for real
Enrich a sample table with ai_query, then make the query robust and tune it the way a production pipeline would
1.On a non-Classic SQL warehouse or Databricks Runtime 15.4 LTS+, run the documented query that calls ai_query with 'system.ai.meta-llama-3-3-70b-instruct' and a prompt joined to pickup_zip, against samples.nyctaxi.trips with LIMIT 10.
Why: It confirms that a Databricks-hosted model works with no endpoint setup.
You should see: Ten trip rows, each with an extra column holding the model's answer about the state for that ZIP code.
2.Add failOnError => false as a third argument to the same call.
Why: Production batch jobs should keep the rows that succeed when some inferences fail.
You should see: The new column is now a STRUCT with a response field and an errorMessage field, and errorMessage is null for successful rows.
3.Replace failOnError with modelParameters => named_struct('max_tokens', 100, 'temperature', 0.7).
Why: It shows that model settings are constants passed through modelParameters, not per-row values.
You should see: The query runs and the answers may vary more, since temperature is now above the 0.0 default.
4.Open the query profile from the results window and click AI Query.
Why: This is where you monitor batch inference progress.
You should see: Counts of completed and failed inferences, plus the total time the request took.
Stuck? Get a nudge
If ai_query is not recognized, check that you are not on a Classic SQL warehouse.
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“you can integrate task-specific AI Functions, or the general-purpose function ai_query into your production workflows, like Lakeflow pipelines, Databricks workflows and Structured Streaming.”
↩︎ Where inference sits in a batch pipeline“Click AI Query to see metrics for that particular query including the number of completed and failed inferences”
↩︎ Running inference at production scale - 2.
“Pipelines can be authored in SQL or Python and deployed as:”
↩︎ Where inference sits in a batch pipeline“Validated news articles ensuring the title has at least 3 words and the category is valid.”
↩︎ Classify with a schema, validate, then enrich“output an array of json results that extracts key user opinions, a classification, and a Positive, Negative, Neutral, or Mixed sentiment”
↩︎ Classify with a schema, validate, then enrich“end-to-end workflows that include ingestion, preprocessing, inference, and post-processing”
↩︎ Checkpoint“"enum": ["Politics", "Sports", "Technology", "Health", "Entertainment", "Business"]”
↩︎ Prediction - 3.
“labels must contain at least 2 labels, and no more than 500 labels.”
↩︎ When ai_classify is the better tool“To pin a version explicitly, pass options => map('version', '2.1').”
↩︎ When ai_classify is the better tool“This output shape change applies to all version 2.1 calls, even when the new options are disabled.”
↩︎ Exam trap 3“Update your queries and downstream code to read the label from the value key instead of reading it directly.”
↩︎ Checkpoint - 4.
“A boolean literal that defaults to true.”
↩︎ Running inference at production scale“If failOnError => false, the function returns a STRUCT object that contains the parsed response and the error message string.”
↩︎ Running inference at production scale“If the inference of the row fails due to other errors, the whole query fails.”
↩︎ Exam trap 1
Also cited
“Submit your full dataset in a single query.”
↩︎ Exam trap 2“Manually splitting data into small batches can reduce throughput.”
↩︎ Checkpoint