Skip to content
Back to Blog
7 min read

Azure AI Search After Ignite 2023: What's GA and What's Still Preview

Ignite 2023 changed a lot about Azure Cognitive Search in one go: it got a new name, vector search and semantic ranking went GA, and a set of RAG-focused features arrived in preview. Two months on, the hard part is not knowing what exists. It’s knowing which pieces you can put in front of production traffic and which SDK or API version you need to reach each one. This is my stock-take of where things stand at the end of January 2024.

The rename is cosmetic, the API versions are not

On 15 November 2023 Azure Cognitive Search became Azure AI Search. Nothing changes for existing services: the endpoint is still https://<your-search-service>.search.windows.net, the SKUs are the same, and the Python package is still azure-search-documents. Update your diagrams and move on.

What matters is that the platform now has two API tracks you need to care about:

TrackREST API versionPython SDKWhat you get
GA2023-11-01azure-search-documents 11.4.0Vector search (HNSW and exhaustive KNN), vector prefiltering, hybrid queries, semantic ranker
Preview2023-10-01-Preview11.4.0 betas (up to 11.4.0b11)Everything above, plus integrated vectorization (vectorizers and the Azure OpenAI Embedding skill), index projections, Split skill overlap

Microsoft’s 2023 What’s new archive lists these under November 2023. The catch is in the 11.4.0 changelog: the GA SDK deliberately leaves out AzureOpenAIEmbeddingSkill, AzureOpenAIParameters and AzureOpenAIVectorizer, because those are preview-only. If you want integrated vectorization from Python today, you’re either pinned to a beta with older, pre-GA class names, or you call the REST API directly. I prefer the second option for the indexing side. Index and skillset definitions are infrastructure, they belong in source control as JSON anyway, and it keeps preview surface area out of the application code that runs at query time.

What’s GA and safe to build on

Vector search left preview with the 2023-11-01 API. Prefiltering and exhaustive KNN are GA too.

If you built against the 2023-07-01-Preview shape, expect to rewrite. vectorSearchConfiguration on a field became vectorSearchProfile, algorithms and profiles are now separate objects, and in Python vectors= became vector_queries= with VectorizedQuery. Those renames are mechanical but they will break every index definition and query you wrote last year. Do the migration now rather than carrying a preview API version into production.

Semantic ranker

Semantic ranking is also GA. It re-scores the top 50 results from your initial query with a language model built for ranking and can return extractive captions and answers. The semantic ranking overview covers the mechanics, and I go deeper on configuration in the semantic ranker deep dive.

My position: for RAG, hybrid retrieval plus semantic ranker should be the default, not an optimisation you add later. Microsoft’s published relevance benchmarks show hybrid retrieval with semantic reranking beating pure vector retrieval, and that matches what I’d expect from first principles. Embeddings are good at paraphrase and poor at exact identifiers. BM25 is the opposite. Reciprocal Rank Fusion merges the two lists (how RRF scoring works), and the semantic ranker then puts the genuinely best chunks at the top, which is what your prompt’s limited context window actually needs.

Here is a hybrid plus semantic query against the GA API with the 11.4.0 SDK. The query embedding comes from your own Azure OpenAI call, which keeps the query path entirely on GA components:

import os

from azure.core.credentials import AzureKeyCredential
from azure.search.documents import SearchClient
from azure.search.documents.models import VectorizedQuery
from openai import AzureOpenAI

openai_client = AzureOpenAI(
    azure_endpoint="https://<your-openai-resource>.openai.azure.com",
    api_key=os.environ["AZURE_OPENAI_API_KEY"],
    api_version="2023-05-15",
)

search_client = SearchClient(
    endpoint="https://<your-search-service>.search.windows.net",
    index_name="docs-chunks",
    credential=AzureKeyCredential(os.environ["AZURE_SEARCH_QUERY_KEY"]),
)


def retrieve(question: str, top: int = 5) -> list[dict]:
    embedding = openai_client.embeddings.create(
        model="<your-embedding-deployment>",
        input=question,
    ).data[0].embedding

    results = search_client.search(
        search_text=question,
        vector_queries=[
            VectorizedQuery(
                vector=embedding,
                k_nearest_neighbors=50,
                fields="chunk_vector",
            )
        ],
        query_type="semantic",
        semantic_configuration_name="default",
        query_caption="extractive",
        select=["chunk_id", "title", "chunk"],
        top=top,
    )

    return [
        {
            "chunk_id": r["chunk_id"],
            "title": r["title"],
            "chunk": r["chunk"],
            "reranker_score": r.get("@search.reranker_score") or 0.0,
        }
        for r in results
    ]


if __name__ == "__main__":
    for hit in retrieve("How do I rotate the storage account keys?"):
        print(f"{hit['reranker_score']:.2f}  {hit['title']}")

Two details are deliberate. k_nearest_neighbors=50 gives RRF and the semantic ranker a full candidate pool even though only five chunks go to the model. And I return @search.reranker_score because it’s on a fixed scale (0 to 4), which makes it usable as a relevance cut-off. The RRF @search.score is not comparable across queries, so don’t threshold on it. A missing reranker score (which the code above turns into 0.0) means semantic ranking didn’t run for that request, for example because the free plan’s monthly quota ran out and the service fell back to the RRF results.

When not to turn on the semantic ranker: high-volume, low-value queries such as autocomplete or catalogue filtering, where the extra latency and the per-request billing on the Standard plan buy you nothing. Check the semantic ranker pricing for your region before you make it the default for every query.

What’s preview, and how much I’d lean on it

Integrated vectorization

Integrated vectorization is the biggest change for RAG builders. A skillset can now chunk documents with the Split skill, call your Azure OpenAI embedding deployment through the Azure OpenAI Embedding skill, and write the vectors into the index. A vectorizer on the index means queries can send text and let the service embed it. The integrated vectorization concept page has the full picture, and my follow-up post walks through a build.

The index side, using the preview REST API, looks like this:

{
  "name": "docs-chunks",
  "fields": [
    { "name": "chunk_id", "type": "Edm.String", "key": true, "searchable": true, "analyzer": "keyword" },
    { "name": "parent_id", "type": "Edm.String", "filterable": true },
    { "name": "title", "type": "Edm.String", "searchable": true },
    { "name": "chunk", "type": "Edm.String", "searchable": true },
    {
      "name": "chunk_vector",
      "type": "Collection(Edm.Single)",
      "searchable": true,
      "dimensions": 1536,
      "vectorSearchProfile": "aoai-profile"
    }
  ],
  "vectorSearch": {
    "algorithms": [
      { "name": "hnsw-default", "kind": "hnsw", "hnswParameters": { "metric": "cosine" } }
    ],
    "profiles": [
      { "name": "aoai-profile", "algorithm": "hnsw-default", "vectorizer": "aoai-vectorizer" }
    ],
    "vectorizers": [
      {
        "name": "aoai-vectorizer",
        "kind": "azureOpenAI",
        "azureOpenAIParameters": {
          "resourceUri": "https://<your-openai-resource>.openai.azure.com",
          "deploymentId": "<your-embedding-deployment>",
          "apiKey": "<your-openai-key>"
        }
      }
    ]
  },
  "semantic": {
    "configurations": [
      {
        "name": "default",
        "prioritizedFields": {
          "titleField": { "fieldName": "title" },
          "prioritizedContentFields": [ { "fieldName": "chunk" } ]
        }
      }
    ]
  }
}

Send it with PUT https://<your-search-service>.search.windows.net/indexes/docs-chunks?api-version=2023-10-01-Preview. The 1536 dimensions match text-embedding-ada-002, which is the embedding model most teams are on right now.

Would I put this in production? For an internal knowledge assistant where an indexing failure means stale answers for a few hours, yes, with the API version pinned and a rebuild script ready. For anything customer-facing with an SLA, I’d keep chunking and embedding in my own pipeline (an Azure Function or a Durable Functions fan-out) and push documents to the GA API. Preview features have no SLA and the property names can still move, as everyone who built on 2023-07-01-Preview has just learned.

Index projections

The Split skill produces many chunks per document, but historically the index stored one search document per source document, which is useless for retrieval. Index projections fix that by mapping each chunk to its own search document with a pointer back to the parent. The index projections overview explains the rules, and the index projections post covers the edge cases.

Projections live on the skillset, not the indexer:

{
  "name": "docs-chunking",
  "skills": [
    {
      "@odata.type": "#Microsoft.Skills.Text.SplitSkill",
      "name": "split",
      "context": "/document",
      "textSplitMode": "pages",
      "maximumPageLength": 2000,
      "pageOverlapLength": 500,
      "inputs": [ { "name": "text", "source": "/document/content" } ],
      "outputs": [ { "name": "textItems", "targetName": "pages" } ]
    },
    {
      "@odata.type": "#Microsoft.Skills.Text.AzureOpenAIEmbeddingSkill",
      "name": "embed",
      "context": "/document/pages/*",
      "resourceUri": "https://<your-openai-resource>.openai.azure.com",
      "deploymentId": "<your-embedding-deployment>",
      "apiKey": "<your-openai-key>",
      "inputs": [ { "name": "text", "source": "/document/pages/*" } ],
      "outputs": [ { "name": "embedding", "targetName": "vector" } ]
    }
  ],
  "indexProjections": {
    "selectors": [
      {
        "targetIndexName": "docs-chunks",
        "parentKeyFieldName": "parent_id",
        "sourceContext": "/document/pages/*",
        "mappings": [
          { "name": "chunk", "source": "/document/pages/*" },
          { "name": "chunk_vector", "source": "/document/pages/*/vector" },
          { "name": "title", "source": "/document/metadata_storage_name" }
        ]
      }
    ],
    "parameters": { "projectionMode": "skipIndexingParentDocuments" }
  }
}

maximumPageLength is measured in characters, so 2,000 characters with a 500-character overlap is roughly 400 to 500 tokens per chunk. That’s a reasonable starting point for ada-002, not a universal answer. Tune it against real questions. skipIndexingParentDocuments stops the unchunked parent from landing in the index alongside its chunks, which you almost always want for RAG.

The portal’s Import and vectorize data wizard (also preview) generates all of this for you. Use it to get a working baseline, then export the JSON and own it in source control.

Index size: the constraint nobody mentions

The limit that bites RAG projects first is not document count or storage. It’s vector index size. HNSW graphs have to sit in memory, so the service enforces a separate vector quota per partition, and indexing fails once you hit it. For services created from July 2023 onwards, the published vector index size limits are:

TierVector quota per partition
Basic1 GB
S13 GB
S212 GB
S336 GB
L112 GB
L236 GB

Services created before July 2023 have lower limits (S1 is 1 GB), and there’s no in-place upgrade, so check your service’s creation date before you plan capacity on an old one.

Do the arithmetic early. A 1536-dimension ada-002 vector stored as 32-bit floats is about 6 KB before HNSW overhead, so a single S1 partition holds a few hundred thousand chunks at most. Chunk overlap multiplies that count. Neither API version offers vector compression or quantization today, so your levers are the tier, the partition count, how many chunks you create, and what you choose to index at all. Exhaustive KNN fields don’t count against the quota, which is a fair trade for small, filtered corpora but not for anything that needs low latency at scale. Smaller chunks of the right content beat bigger indexes of everything. I cover the operational side in index management.

Where I’d land in January 2024

  • Query path: GA only. 2023-11-01, SDK 11.4.0, hybrid plus semantic ranker, with your own query-time embedding call.
  • Indexing path: integrated vectorization and index projections on 2023-10-01-Preview if the workload can tolerate preview; a custom chunk-and-embed pipeline pushing to the GA API if it can’t.
  • Migrations: anything still on 2023-07-01-Preview should move now. The renames are tedious, not hard.

That split, GA at query time and preview only where a failure is recoverable, gets you most of the value of the Ignite releases without staking production on API shapes that haven’t settled. For how this fits into a full RAG design, my earlier RAG pattern post still holds; only the names and the plumbing have improved.

Michael John Peña

Michael John Peña

Data & AI Director and Microsoft MVP based in Sydney. Writing about Microsoft Fabric, Azure, data engineering, and AI.