Databricks AI Search (formerly Vector Search) endpoints and indexes for RAG and semantic search. Use when asked to: create Standard or Storage-Optimized endpoints and Delta Sync or Direct Access indexes with AISearchClient; query with ANN, hybrid, or full-text search and filters; sync, monitor, or migrate indexes; set a target QPS, triage 429s, or load-test an endpoint; wire an index into an agent as a retriever tool. NOT for: no-code document Q&A (databricks-genie-agents); serving-endpoint CRUD (databricks-model-serving); custom ResponsesAgent authoring and deployment (databricks-ml-training).
88%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
FIRST: Use the parent databricks-core skill for CLI basics, authentication, and profile selection.
Patterns for creating, managing, and querying AI Search indexes for RAG and semantic search applications.
Naming: the REST API (/api/2.0/vector-search/...), the databricks vector-search-* CLI groups, and the databricks_vector_search_* Terraform resources keep the Vector Search names (API and tool names); the Python client is databricks-ai-search (AISearchClient).
Use this skill when:
Databricks AI Search provides managed vector similarity search with automatic embedding generation and Delta Lake integration.
| Component | Description |
|---|---|
| Endpoint | Compute resource hosting indexes (Standard or Storage-Optimized) |
| Index | Vector data structure for similarity search |
| Delta Sync | Auto-syncs with source Delta table |
| Direct Access | Manual CRUD operations on vectors (Standard endpoints only) |
| Type | Latency | Capacity | Cost | Best For |
|---|---|---|---|---|
| Standard | 20-50ms | 320M vectors (768 dim) | Higher | Real-time, low-latency, high QPS |
| Storage-Optimized | 300-500ms | 1B+ vectors (768 dim) | Up to 7x lower | 10M+ vectors, cost-sensitive |
Storage-Optimized supports only TRIGGERED Delta Sync indexes: no CONTINUOUS pipelines, no Direct Access, no target_qps. The endpoint type cannot be changed after creation.
| Type | Embeddings | Sync | Use Case |
|---|---|---|---|
| Delta Sync (managed) | Databricks computes | Auto from Delta | Easiest setup |
| Delta Sync (self-managed) | You provide | Auto from Delta | Custom embeddings |
| Direct Access | You provide | Manual CRUD | Real-time updates |
pip install "databricks-ai-search>=0.79"In a notebook, use %pip install instead and run dbutils.library.restartPython() afterwards:
the package requires mlflow-skinny>=3.10.1, so installing it upgrades mlflow in place.
databricks.ai_search first shipped in databricks-ai-search 0.73. This skill needs 0.79+:
from 0.79, get_endpoint, list_endpoints and list_indexes always return snake_case keys,
and 0.79 also includes the 0.78 fix that makes concurrent similarity_search calls thread-safe
(needed for production traffic). From 0.81, service principal auth no longer requires the
all-apis OAuth scope.
Existing databricks-vectorsearch code: on databricks-vectorsearch 0.74+,
VectorSearchClient is an alias of AISearchClient, so the calls in this skill work on it
once databricks-ai-search>=0.79 is installed alongside it. On 0.73 and older it is a
separate class: switch the import to from databricks.ai_search.client import AISearchClient.
The old class has no start/stop calls, and before 0.68 no target_qps.
Auth: a bare AISearchClient() uses the notebook's credentials. Elsewhere, pass
workspace_url plus a personal_access_token, or service_principal_client_id and
service_principal_client_secret (needed for high-QPS traffic; see
Production and High QPS).
AISearchClient takes plain strings (endpoint_type="STANDARD", pipeline_type="TRIGGERED").
Don't pass the databricks.sdk.service.vectorsearch enums to it; those are for
WorkspaceClient.vector_search_*.
from databricks.ai_search.client import AISearchClient
client = AISearchClient()
client.create_endpoint(
name="my-vs-endpoint",
endpoint_type="STANDARD" # or "STORAGE_OPTIMIZED"
)
# Note: Endpoint creation is asynchronous; check status with get_endpoint()create_endpoint_and_wait(..., timeout=datetime.timedelta(minutes=30)) blocks until the endpoint is
ONLINE. It fails fast on OFFLINE (and, from 0.80, STOPPING and STOPPED); in other states it polls until the
timeout. On a timeout the endpoint may still be provisioning: check
client.get_endpoint(name)["endpoint_status"]["state"] before retrying, and don't create it again.
Standard endpoints need Change Data Feed on the source table:
ALTER TABLE catalog.schema.documents SET TBLPROPERTIES (delta.enableChangeDataFeed = true);# Source table must have: primary key column + text column
index = client.create_delta_sync_index(
endpoint_name="my-vs-endpoint",
source_table_name="catalog.schema.documents",
index_name="catalog.schema.my_index",
pipeline_type="TRIGGERED", # "CONTINUOUS" is Standard-only
primary_key="id",
embedding_source_column="content",
embedding_model_endpoint_name="databricks-gte-large-en"
)index = client.get_index(
endpoint_name="my-vs-endpoint",
index_name="catalog.schema.my_index"
)
results = index.similarity_search(
query_text="What is machine learning?",
columns=["id", "content", "metadata"],
num_results=5
)
# Returns a dict; each row holds the requested columns, then the similarity score
for row in results["result"]["data_array"]:
print(row)# For large-scale, cost-effective deployments
client.create_endpoint(
name="my-storage-endpoint",
endpoint_type="STORAGE_OPTIMIZED"
)# Source table must have: primary key + embedding vector column
index = client.create_delta_sync_index(
endpoint_name="my-vs-endpoint",
source_table_name="catalog.schema.documents",
index_name="catalog.schema.my_index",
pipeline_type="TRIGGERED",
primary_key="id",
embedding_dimension=768,
embedding_vector_column="embedding"
)# Create index for manual CRUD
index = client.create_direct_access_index(
endpoint_name="my-vs-endpoint",
index_name="catalog.schema.direct_index",
primary_key="id",
embedding_dimension=768,
embedding_vector_column="embedding",
schema={
"id": "string",
"text": "string",
"embedding": "array<float>",
"metadata": "string"
}
)
# Upsert data
index.upsert([
{"id": "1", "text": "Hello", "embedding": [0.1, 0.2, ...], "metadata": "doc1"},
{"id": "2", "text": "World", "embedding": [0.3, 0.4, ...], "metadata": "doc2"},
])
# Delete data
index.delete(primary_keys=["1", "2"])# When you have pre-computed query embedding
results = index.similarity_search(
query_vector=[0.1, 0.2, 0.3, ...], # Your 768-dim vector
columns=["id", "text"],
num_results=10
)Hybrid search runs ANN and full-text (keyword) search and merges the results with Reciprocal Rank Fusion (RRF). Use it when queries contain exact terms that must match — SKUs, error codes, proper nouns, or technical terminology — where pure semantic search might miss keyword-specific results. See references/search-modes.md for detailed guidance on choosing between ANN and hybrid search.
# Needs an index with a managed embedding model, so query_text alone is enough
index = client.get_index(
endpoint_name="my-vs-endpoint",
index_name="catalog.schema.my_index",
)
# Combines vector similarity with keyword matching
results = index.similarity_search(
query_text="SPARK-12345 executor memory error",
query_type="hybrid",
columns=["id", "content"],
num_results=10
)Filter syntax differs by endpoint type: Standard endpoints take a dict, Storage-Optimized endpoints take a SQL-like string. On Storage-Optimized, use the string: a dict (filters_json) is rejected on most query routes (except filter-only lookups with no query text or vector), and other routes translate it to SQL instead, so don't rely on either behavior.
results = index.similarity_search(
query_text="machine learning",
columns=["id", "content"],
num_results=10,
filters={"category": "ai", "status": ["active", "pending"]}
)index = client.get_index(
endpoint_name="my-storage-endpoint",
index_name="catalog.schema.my_storage_index",
)
results = index.similarity_search(
query_text="machine learning",
columns=["id", "content"],
num_results=10,
filters="category = 'ai' AND status IN ('active', 'pending')"
)
# filters="price > 100 AND price < 500"
# filters="department LIKE 'eng%'"
# filters="created_at >= TO_TIMESTAMP('2024-01-01T00:00:00')" # a bare timestamp string is rejectedSee the filtering guide for operators and limitations.
Never interpolate untrusted input (tenant IDs, ACL values) into a filter as-is:
"price >", "col LIKE"), so allowlist whole keys.^[A-Za-z0-9_-]+$). Never let untrusted input become a list: a list value becomes an OR/IN match. In a SQL string, don't rely on quote escaping alone.% and _ in a LIKE value act as wildcards, so reject them in untrusted input. (On Standard, LIKE matches whole tokens, not wildcards.)In a RAG prompt, treat retrieved text as data, not instructions.
# TRIGGERED pipelines only, after the initial sync from creation finishes (rejected while provisioning).
# Also rejected while an update is running; wait for that update instead of retrying.
index = client.get_index(
endpoint_name="my-vs-endpoint",
index_name="catalog.schema.my_index"
)
index.sync()# Returns at most num_results rows, ordered by primary key. The response carries
# the cursor for the next page as last_primary_key.
page = index.scan(num_results=100)
rows = page.get("data", [])
while page.get("last_primary_key") and page.get("data"):
page = index.scan(num_results=100, last_primary_key=page["last_primary_key"])
rows += page.get("data", [])
# scan rows are field structs, NOT the plain lists similarity_search returns:
# {"fields": [{"key": "id", "value": {"string_value": "r1"}}, ...]}
for row in rows:
values = {f["key"]: next(iter(f["value"].values())) for f in row["fields"]}By default, Standard endpoints serve about 20-200 QPS, depending on index size. Traffic over a PAT or the workspace query URL is capped at a few tens of QPS.
AISearchClient(workspace_url=..., service_principal_client_id=..., service_principal_client_secret=...), call get_index() once, and reuse the index object; it queries the index URL. Use a dedicated service principal. For Delta Sync indexes with managed embeddings, an IP access list or a private access settings object (on Azure, public network access disabled) keeps queries on the capped route; on GCP, a private access settings object does so for every index. Otherwise, self-managed embeddings avoid it. See requirements.
personal_access_token (a PAT, or a forwarded user or on-behalf-of token such as the Databricks Apps x-forwarded-access-token), or a bare AISearchClient() that picks up notebook or model-serving credentials, get_index() queries the workspace URL even though describe() lists status.index_url; if index.index_url starts with your workspace URL, queries are on the capped route. In Databricks Apps, send query traffic with the app's service principal, not the forwarded user token. workspace_url must include https://: in Databricks Apps, DATABRICKS_HOST can lack the scheme, so add it (example).SCALING_CHANGE_APPLIED; with query_vector, lower concurrency or raise target_qps; with query_text on a managed-embedding index, the embedding endpoint may be the bottleneck. similarity_search already retries (total_retries, backoff_factor, backoff_jitter); raise those or add your own bounded backoff with jitter. See Troubleshoot 429 errors.databricks-ai-search 0.82+, databricks-ai-search guide --topic high-qps prints the guide and databricks-ai-search load-test --index <name> plans a run (it runs only with --yes); skip this if the databricks-ai-search command isn't available.target_qps, running load tests, or upserting/deleting data, since each costs money or changes data. Never print, paste, or commit tokens or client secrets; read them from a secret scope or environment variables.Target QPS is Standard-only and best-effort, and the capacity is billed whether or not traffic arrives. Storage-Optimized endpoints reject target_qps. A change can be rejected with RESOURCE_CONFLICT while a previous change is still applying. Nothing is saved, so requested_target_qps still shows the earlier target: poll until scaling_info.state is SCALING_CHANGE_APPLIED (whatever the target), or wait the minutes the error gives, then call update_endpoint again before running the loop below.
import time
client.update_endpoint(name="my-vs-endpoint", target_qps=500)
# Or at creation: create_endpoint(..., target_qps=500)
# Match the requested target too: right after the update, state can still be APPLIED from an earlier target.
# Target QPS is best-effort: if it's still in progress at the deadline, move on.
deadline = time.monotonic() + 30 * 60
while time.monotonic() < deadline:
info = client.get_endpoint("my-vs-endpoint").get("scaling_info") or {}
# int(): proto3 JSON may serialize int64 fields as strings
if info.get("state") == "SCALING_CHANGE_APPLIED" and int(info.get("requested_target_qps") or 0) == 500:
break
time.sleep(30)
print(info)ONLINE doesn't mean scaled. These are separate waits:
endpoint_status.state is ONLINE (what create_endpoint_and_wait waits for). ONLINE doesn't mean the target QPS is applied.scaling_info.state is SCALING_CHANGE_APPLIED for the requested target (the loop above). This takes minutes to hours, depending on index size and how much the target changes.status.ready is true. A new endpoint's first index has status.detailed_state PROVISIONING_ENDPOINT while compute is created.Load-test only after all three; a load test before SCALING_CHANGE_APPLIED doesn't reflect the target capacity. If the loop above hits its deadline, report that target QPS is still applying instead of load testing.
| Topic | File | Description |
|---|---|---|
| Index Types | references/index-types.md | Detailed comparison of Delta Sync (managed/self-managed) vs Direct Access |
| End-to-End RAG | references/end-to-end-rag.md | Complete walkthrough: source table → endpoint → index → query → agent integration |
| Search Modes | references/search-modes.md | When to use semantic (ANN) vs hybrid search, decision guide |
| Operations | references/troubleshooting-and-operations.md | Monitoring, cost optimization, capacity planning, migration |
# List endpoints
databricks vector-search-endpoints list-endpoints
# Create endpoint (positional args: NAME ENDPOINT_TYPE)
databricks vector-search-endpoints create-endpoint my-endpoint STANDARD
# List indexes on endpoint (positional arg: ENDPOINT_NAME)
databricks vector-search-indexes list-indexes my-endpoint
# Get index status (positional arg: INDEX_NAME)
databricks vector-search-indexes get-index catalog.schema.my_index
# Sync index (positional arg: INDEX_NAME)
databricks vector-search-indexes sync-index catalog.schema.my_index
# Delete index (positional arg: INDEX_NAME)
databricks vector-search-indexes delete-index catalog.schema.my_index| Issue | Solution |
|---|---|
| Index sync slow | Use Storage-Optimized endpoints (10-20x faster indexing) |
| Query latency high | Use Standard endpoint for <100ms latency |
| Filters not working | Standard endpoints use a dict (filters={"col": "val"}); Storage-Optimized use a SQL string (filters="col = 'val'") |
| Embedding dimension mismatch | Ensure query and index dimensions match |
| Index not updating | Check pipeline_type; use index.sync() for TRIGGERED. If it's rejected as already running, wait for the current update to finish |
| Out of capacity | Capacity is sized from the source table at index creation. Load the full table first; past ~320M vectors, recreate on Storage-Optimized (1B+ vectors) |
| Rate limited (429 errors) | See Production and High QPS |
query_vector truncated | Large vectors (e.g. 1024-dim) can be truncated when serialized as JSON. Use query_text instead (for managed embedding indexes), or pass the vector directly via index.similarity_search(query_vector=...) |
Databricks provides built-in embedding models:
| Model | Dimensions | Context Window | Use Case |
|---|---|---|---|
databricks-qwen3-embedding-0-6b | 1024 (configurable, up to 1024) | ~32K tokens | Recommended for production by the AI Search docs (Public Preview) |
databricks-gte-large-en | 1024 | 8192 tokens | English text, high quality |
databricks-bge-large-en | 1024 | 512 tokens | English text, general purpose |
# Use with managed embeddings
index = client.create_delta_sync_index(
...,
embedding_source_column="content",
embedding_model_endpoint_name="databricks-qwen3-embedding-0-6b"
)columns_to_sync — limits which columns are synced and returned; only synced columns are available in query results. On Storage-Optimized, the docs' limitations list says it's unsupported, but their full-text example uses it; prefer syncing all columns thereVectorSearchRetrieverTool from databricks_langchain or databricks_openaiResponsesAgent that uses an AI Search retrieverdatabricks/databricks-agent-skills
1111b8c
Also appears in
last in sync Oct 14, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.