Use Vector Search 2.0 with RAG

This page shows you how RAG Engine on Gemini Enterprise Agent Platform uses Gemini Enterprise Agent Platform Vector Search 2.0, which is a Google Cloud product that serves as a comprehensive storage and retrieval system to store and manage vector representations of your documents. The Gemini Enterprise Agent Platform Vector Search 2.0 is then used to retrieve relevant documents based on the document's semantic similarity to a given query.

RAG will manage the Vector Search 2.0 collections for you and you will have full access to the Vector Search 2.0 collections in your project.

Choosing a RAG backend

When using RAG Engine on Gemini Enterprise Agent Platform, you can choose between different backend storage options for your RAG corpora. The following table summarizes the key differences:

Backend Option Underlying Technology Management by RAG Engine Data Visibility in Project CMEK Support
RagManagedVertexVectorSearch Gemini Enterprise Agent Platform Vector Search 2.0 Fully managed by Google. Visible in your Google Cloud project No
VertexVectorSearch Gemini Enterprise Agent Platform Vector Search 1.0 User is expected to set up, manage, and clean up the Vector Search 1.0 instance. Visible in your Google Cloud project No
RagManagedDb Spanner Fully managed by Google. Not directly visible in your project Yes

Choose RagManagedVertexVectorSearch for the latest features, automated management, and transparency into your Vector Search 2.0 data. If CMEK support is a strict requirement, consider RagManagedDb, keeping in mind the lack of direct visibility into the backend database.

Enable Vector Search API

To use RAG Managed Gemini Enterprise Agent Platform Vector Search, you need to first enable the Vector Search API.

Create a RAG corpus with RagManagedVertexVectorSearch

This code samples demonstrates how to create a RAG corpus using RagManagedVertexVectorSearch.

Python

from vertexai.preview import rag
import vertexai

PROJECT_ID = YOUR_PROJECT_ID
LOCATION = YOUR_RAG_ENGINE_LOCATION
DISPLAY_NAME = YOUR_RAG_CORPUS_DISPLAY_NAME

# Initialize Agent Platform API once per session
vertexai.init(project=PROJECT_ID, location=LOCATION)

vector_db = rag.RagManagedVertexVectorSearch()
rag_corpus = rag.create_corpus(
    display_name=DISPLAY_NAME, backend_config=rag.RagVectorDbConfig(vector_db=vector_db))

REST

Replace the following variables:

  • PROJECT_ID: Your project ID.
  • LOCATION: The region to process the request.
  • CORPUS_DISPLAY_NAME: The display name of the RAG corpus.
PROJECT_ID=PROJECT_ID
LOCATION=LOCATION
CORPUS_DISPLAY_NAME=CORPUS_DISPLAY_NAME

curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://${LOCATION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_ID}/locations/${LOCATION}/ragCorpora \
-d '{
      "display_name" : '\""${CORPUS_DISPLAY_NAME}"\"',
      "vector_db_config": {
        "rag_managed_vertex_vector_search": {}
      }
    }'

Importing your data into RagManagedVertexVectorSearch

You can use either the ImportRagFiles API or the UploadRagFile API to import your data into the RagManagedVertexVectorSearch.

To upload your local file into your RAG corpus, see Upload a RAG file. To import data into your RAG corpus, see the following code sample that demonstrates how to import from Cloud Storage. To learn about the supported data sources, see Data sources supported for RAG.

Python

from vertexai.preview import rag
import vertexai

PROJECT_ID = YOUR_PROJECT_ID
LOCATION = YOUR_RAG_ENGINE_LOCATION
CORPUS_ID = YOUR_CORPUS_ID
PATHS = ["gs://my_bucket/my_files_dir"]

# Initialize Agent Platform API once per session
vertexai.init(project=PROJECT_ID, location=LOCATION)

corpus_name = f"projects/{PROJECT_ID}/locations/{LOCATION}/ragCorpora/{CORPUS_ID}"
# This is a non blocking call.
response = await rag.import_files_async(
    corpus_name=corpus_name,
    paths=PATHS,
)

# Wait for the import to complete.
await response.result()

REST

GCS_URI=GCS_URI

curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://${LOCATION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_ID}/locations/${LOCATION}/ragCorpora/${CORPUS_ID}/ragFiles:import \
-d '{
  "import_rag_files_config": {
    "gcs_source": {
      "uris": '\""${GCS_URI}"\"',
      },
  }
}'

Create an Index for your corpus

RAG Engine creates a Vector Search 2.0 Collection for each corpus, but it doesn't create an Index on that Collection. Until you create one, every retrieval is a brute-force KNN scan.

A brute-force scan is slower than an indexed search, and KNN runs only on Collections of up to 100,000 Data Objects. RAG Engine stores one chunk per Data Object, so once a corpus passes that limit, retrieval fails until an Index exists.

Don't index every corpus. An Index runs on its own serving infrastructure and is billed for as long as it exists, whether or not it is queried. To decide which corpora need one, see Decide which corpora to index.

Once an Index exists, retrieval uses it automatically. The corpus doesn't change and your data doesn't need to be imported again.

If you are indexing several corpora, script the loop: there is no bulk creation path. Concurrent index operations are quota-limited, so create the Indexes serially or in small batches. For more information, see Quotas.

Before you begin

Creating an Index requires the vectorsearch.indexes.create permission on the Collection, and counting its Data Objects requires vectorsearch.dataObjects.query. Both permissions are included in the Vector Search Admin (roles/vectorsearch.admin) role; Vector Search Index Writer (roles/vectorsearch.indexWriter) grants the first. The Vector Search API must also be enabled on the project.

Find your corpus IDs

In your project, each corpus has exactly one Collection, named vertex-rag-RAG_CORPUS_ID. List your corpora to get their IDs.

Replace PROJECT_ID with your project ID:

PROJECT_ID=PROJECT_ID
LOCATION=us-central1

curl -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://${LOCATION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_ID}/locations/${LOCATION}/ragCorpora?pageSize=100"

The numeric portion at the end of each corpus name is the RAG_CORPUS_ID. Each corpus also reports the full Collection resource name in vectorDbConfig.ragManagedVertexVectorSearch.collectionName.

The response lists at most 100 corpora, and at most 25 if you omit pageSize. If the response includes a nextPageToken, pass that value as pageToken to fetch the next page.

If you already know the RAG_CORPUS_ID you want to index, fetch that one corpus instead. The response carries the same collectionName:

curl -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://${LOCATION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_ID}/locations/${LOCATION}/ragCorpora/RAG_CORPUS_ID"

Decide which corpora to index

For each corpus, count the Data Objects in its Collection. Pass an empty filter to count the whole Collection:

COLLECTION_ID=vertex-rag-RAG_CORPUS_ID

curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
"https://vectorsearch.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/collections/${COLLECTION_ID}/dataObjects:aggregate" \
-d '{"aggregate": "COUNT", "filter": {}}'

The count comes back in aggregateResults:

{
  "aggregateResults": [
    {
      "count": 95217
    }
  ]
}

Create an Index for that corpus when either of the following is true:

  • The count is near or above 100,000. The limit counts Data Objects, not files, so a corpus holding a small number of large files can still be well over it.
  • Retrieval is too slow at your query rate. An indexed search is faster than a brute-force scan, and the gap widens as the Collection grows.

Otherwise leave the corpus unindexed and avoid the serving cost. You can add an Index later if it grows.

Review the Collection schema

RAG Engine creates each Collection with the following schema. Use these names when you specify the index field and filter fields.

Field Kind Description
text_embedding Dense vector The chunk embedding. This is the field you index. The number of dimensions matches the corpus embedding model.
corpus_id String The ID of the RAG corpus.
file_id String The ID of the RAG file the chunk came from.
chunk_id String The ID of the chunk.
chunk_data String The chunk text.
chunk_data_type String The type of the chunk data.
file_original_uri String The original URI of the source file.
rag_metadata String User-supplied metadata for the chunk.

Choose the Index settings

UpdateIndex changes only the display name, description, labels, and replica counts, so to change any of the settings in this section, you must delete the Index and create a new one.

Index field: Set it to text_embedding, the only vector field in the Collection schema.

Distance metric: Set it to cosine. RAG Engine always sends COSINE_DISTANCE on its retrieval requests, and Vector Search 2.0 rejects a retrieval request whose metric doesn't match the Index. The metric defaults to DOT_PRODUCT when you omit it, so an Index created without it breaks retrieval for that corpus. RAG Engine reports the failure as FAILED_PRECONDITION with the message Failed to process Rag Managed Vertex Vector Search response., which doesn't identify the metric as the cause. The spelling differs by surface: the REST body takes COSINE_DISTANCE, and the gcloud CLI flag takes cosine-distance.

Infrastructure: Leave the Index on the default performance-optimized infrastructure, because cosine distance isn't supported on storage-optimized infrastructure. An Index runs on dedicated serving infrastructure and is billed for as long as it exists. On v1, min_replica_count and max_replica_count both default to 2, so by default the Index is served by two replicas and doesn't autoscale. For more information, see Collection Indexes.

Filter fields: Set filter_fields to file_id only. It is the only field RAG Engine filters on at retrieval time, and it does so only when a retrieval request restricts the search to specific files. In particular, corpus_id holds the same value for every Data Object in a given Collection, so filtering on it provides no benefit.

Metadata filtering also needs no additional filter fields. When a retrieval request sets ragRetrievalConfig.filter.metadataFilter, RAG Engine resolves the filter against your file metadata first, converts the result into the set of matching RAG file IDs, and sends that set to Vector Search as the same file_id filter. Adding rag_metadata to filter_fields increases the Index size without changing retrieval.

Store fields: Leave store_fields unset. RAG Engine requests all data fields on every retrieval, which never qualifies for inline serving, so the chunk data is read from the backing database regardless. Fields listed in store_fields are pushed into the Index and count toward its size, so setting them adds cost without changing retrieval.

Create the Index

Python

Replace the following variables:

  • PROJECT_ID: Your project ID.
  • RAG_CORPUS_ID: The ID of the RAG corpus to index.
from google.cloud import vectorsearch_v1

client = vectorsearch_v1.VectorSearchServiceClient()

index = vectorsearch_v1.Index(
    index_field="text_embedding",
    distance_metric=vectorsearch_v1.DistanceMetric.COSINE_DISTANCE,
    filter_fields=["file_id"],
)
request = vectorsearch_v1.CreateIndexRequest(
    parent="projects/PROJECT_ID/locations/us-central1/collections/vertex-rag-RAG_CORPUS_ID",
    index_id="vertex-rag-RAG_CORPUS_ID-index",
    index=index,
)

operation = client.create_index(request=request)

# Wait for the result (note this may take up to several minutes).
operation.result()

REST

Replace the following variables:

  • PROJECT_ID: Your project ID.
  • RAG_CORPUS_ID: The ID of the RAG corpus to index.
PROJECT_ID=PROJECT_ID
RAG_CORPUS_ID=RAG_CORPUS_ID
LOCATION=us-central1
COLLECTION_ID=vertex-rag-${RAG_CORPUS_ID}
INDEX_ID=${COLLECTION_ID}-index

curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
"https://vectorsearch.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/collections/${COLLECTION_ID}/indexes?indexId=${INDEX_ID}" \
-d '{
      "index_field": "text_embedding",
      "distance_metric": "COSINE_DISTANCE",
      "filter_fields": ["file_id"]
    }'

gcloud

Replace the following variables:

  • PROJECT_ID: Your project ID.
  • RAG_CORPUS_ID: The ID of the RAG corpus to index.
gcloud vector-search collections indexes create vertex-rag-RAG_CORPUS_ID-index \
  --collection=vertex-rag-RAG_CORPUS_ID \
  --index-field=text_embedding \
  --distance-metric=cosine-distance \
  --filter-fields=file_id \
  --location=us-central1 \
  --project=PROJECT_ID

The command blocks until the operation finishes unless you pass --async.

Poll for completion

Creating an Index returns a long-running operation:

{
  "name": "projects/PROJECT_ID/locations/us-central1/operations/operation-1770302393524-64a14a54fa197-0dea326c-5a90efda",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.vectorsearch.v1.OperationMetadata",
    "createTime": "2026-02-05T14:39:53.558308609Z",
    "target": "projects/PROJECT_ID/locations/us-central1/collections/vertex-rag-RAG_CORPUS_ID/indexes/vertex-rag-RAG_CORPUS_ID-index",
    "verb": "create",
    "apiVersion": "v1"
  },
  "done": false
}

Poll the operation using the name from that response:

OPERATION_NAME=OPERATION_NAME

curl -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://vectorsearch.googleapis.com/v1/${OPERATION_NAME}"

When done is true, a response field means the Index built successfully, and an error field gives the reason it failed. Building an Index takes several minutes, and longer for a large corpus.

To list the operations in the project and location, run the following:

curl -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://vectorsearch.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/operations"

To inspect an existing Index, run the following:

gcloud vector-search collections indexes describe vertex-rag-RAG_CORPUS_ID-index \
  --collection=vertex-rag-RAG_CORPUS_ID \
  --location=us-central1 \
  --project=PROJECT_ID

To delete an Index, which you must do to change any of its permanent settings, run the following. Retrieval falls back to a brute-force KNN scan until the replacement Index is built, and fails entirely if the corpus exceeds 100,000 chunks.

gcloud vector-search collections indexes delete vertex-rag-RAG_CORPUS_ID-index \
  --collection=vertex-rag-RAG_CORPUS_ID \
  --location=us-central1 \
  --project=PROJECT_ID

What's next