Embeddings + Vector Search on Azure
Azure AI Search is the managed search service our team uses as the vector database backend for retrieval-augmented generation (RAG) applications. This guide walks you through the core concepts, resource setup, and your first end-to-end RAG query β from document ingestion to LLM-powered answers.
If you understand basic LangChain concepts and need to set up vector search on Azure, this is the place to start.
What Is Azure AI Searchβ
Azure AI Search (formerly Azure Cognitive Search) is a fully managed cloud search service that combines traditional keyword search with vector (embedding-based) search, semantic ranking, and rich data enrichment pipelines. It runs on Azure infrastructure and integrates natively with Azure OpenAI, Azure Machine Learning, and managed identity.
Why We Chose It Over Alternativesβ
We evaluated Pinecone, Weaviate, Qdrant, and PostgreSQL with pgvector before settling on Azure AI Search. Here is why:
| Criterion | Azure AI Search | Pinecone / Weaviate | PostgreSQL + pgvector |
|---|---|---|---|
| Unified keyword + vector | Native hybrid search | Vector-only (keyword requires add-on) | Requires custom application logic |
| Semantic ranking | Built-in, tier-dependent | Not available | Not available |
| Azure ecosystem | Native managed identity, VNet integration | External service, extra networking | Requires self-management on Azure |
| Data enrichment | Skillsets for OCR, translation, entity extraction | Manual pipeline needed | Manual pipeline needed |
| Operational overhead | Fully managed | Managed but separate cloud | Self-managed or Azure Database for PostgreSQL |
| Cost model | Predictable per-hour tier pricing | Per-operation + storage | Server sizing + storage |
The deciding factor was hybrid search with semantic ranking β the ability to combine keyword and vector signals in a single query with re-ranking, all managed by Azure. For RAG workloads, this consistently produces higher-quality results than pure vector search.
Key Capabilitiesβ
- Vector search β similarity search against high-dimensional embeddings using cosine distance, dot product, or Euclidean distance.
- Hybrid search β combines keyword (full-text) and vector search in a single query, with configurable weighting.
- Semantic ranking β re-ranks hybrid results using a semantic ranker model trained for relevance.
- Filtering β OData filter expressions on non-searchable fields for metadata-based narrowing.
- Scoring profiles β custom weighting and boosting rules for different field types.
- Indexers β automated data ingestion from Azure Blob Storage, SQL Database, Cosmos DB, and more.
Core Conceptsβ
Before writing code, understand the building blocks that every Azure AI Search interaction is built on.
Indexes and Fieldsβ
An index is the fundamental data structure in Azure AI Search. It defines the schema β what fields exist, their types, and how they are searchable. Each field has a role:
| Field Attribute | Purpose | Example |
|---|---|---|
@searchable | Full-text keyword search | Document content, titles |
@searchable + vector profile | Vector (embedding) search | Embedded document chunks |
@filterable | OData filtering | Document source, category, date |
@facetable | Faceted navigation / grouping | Tags, authors |
@retrievable | Returned in search results | All fields you want in responses |
@sortable | Ordering results | Timestamps, scores |
A typical RAG index has three kinds of fields:
- A key field β unique identifier (string, required).
- A vector field β stores embeddings, referenced by a vector search profile.
- Searchable text fields β for keyword search (document content, titles).
- Metadata fields β filterable, retrievable fields for document metadata.
Embeddings and Embedding Modelsβ
An embedding is a dense vector representation of text β a list of floating-point numbers that capture semantic meaning. Similar texts produce similar vectors, enabling similarity search.
Azure OpenAI provides two families of embedding models:
| Model | Dimensions | Max Input (tokens) | Best For |
|---|---|---|---|
text-embedding-3-small | 512 (default) or 1536 | 8,191 | General-purpose, cost-effective |
text-embedding-3-large | 3072 (default) or 1536, 256 | 8,191 | Highest quality, complex semantics |
text-embedding-ada-002 | 1,536 (fixed) | 8,191 | Legacy compatibility |
Use text-embedding-3-small with 512 dimensions for most RAG workloads. The quality-to-cost ratio is excellent, and the smaller dimensionality means faster indexing, lower storage, and quicker search. Only move to text-embedding-3-large if you measure a meaningful recall gap.
Vector vs. Keyword vs. Hybrid Searchβ
Understanding when to use each search mode is critical:
- Vector search β finds semantically similar content. Strong for paraphrased queries and conceptual matching. Weak at exact term matching and boolean logic.
- Keyword search β finds exact or stemmed term matches. Strong for proper nouns, product codes, and boolean queries. Blind to semantic similarity.
- Hybrid search β combines both signals in a single query with reciprocal rank fusion. Best overall quality for RAG because it covers both semantic and lexical matching.
Semantic Rankingβ
Semantic ranking is an Azure AI Search feature that re-ranks hybrid search results using a deep learning model fine-tuned for query-document relevance. It understands query intent and document context beyond what vector similarity or keyword matching alone can capture.
Semantic ranking requires the Basic tier or higher and must be explicitly enabled on the index.
Setting Up Azure AI Searchβ
Creating an Azure AI Search Resourceβ
You can create an Azure AI Search service from the Azure Portal or via the CLI.
- Azure Portal
- Azure CLI
-
Navigate to the Azure Portal and select Create a resource.
-
Search for Azure AI Search and select it.
-
Fill in the creation form:
- Subscription β your team's subscription.
- Resource group β use the shared AI engineering resource group.
- Name β a unique service name (e.g.,
ai-eng-search-prod). - Region β match your Azure OpenAI region (e.g.,
swedencentral). - Pricing tier β select based on feature requirements (see below).
-
Review and create.
infoService names must be globally unique across Azure. If your desired name is taken, append a suffix like
-euor-de.
az search service create \
--name ai-eng-search-prod \
--resource-group rg-ai-engineering \
--location swedencentral \
--sku basic \
--partition-count 1 \
--replica-count 1
Verify the service was created:
az search service show \
--name ai-eng-search-prod \
--resource-group rg-ai-engineering \
--query "{name: name, status: provisioningState, sku: sku.name}"
Pricing Tiers and Feature Availabilityβ
Choose your tier based on the features you need:
| Feature | Free | Basic | Standard | Standard 2 | Standard 3 |
|---|---|---|---|---|---|
| Indexes | 1 | 100 | 100 | 100 | 100 |
| Documents | 10K | 1M | 10M | 10M | 100M |
| Vector search | No | Yes | Yes | Yes | Yes |
| Hybrid search | No | Yes | Yes | Yes | Yes |
| Semantic ranking | No | Yes | Yes | Yes | Yes |
| Cognitive skills | No | Limited | Yes | Yes | Yes |
| Managed identity | Yes | Yes | Yes | Yes | Yes |
| Private endpoint | No | Yes | Yes | Yes | Yes |
Start with the Basic tier for development. It supports vector search, hybrid search, and semantic ranking. Upgrade to Standard when you need cognitive skillsets or higher document limits.
Setting Up Authenticationβ
Azure AI Search supports two authentication methods:
- API Keys
- Managed Identity
Every search service has an admin key (full access) and one or more query keys (read-only). Use query keys in application code:
# List admin keys
az search admin-key show \
--service-name ai-eng-search-prod \
--resource-group rg-ai-engineering
# Create a query key with limited scope
az search query-key create \
--service-name ai-eng-search-prod \
--resource-group rg-ai-engineering \
--name rag-query-key
Set the keys as environment variables:
export AZURE_SEARCH_API_KEY="your-query-key-here"
export AZURE_SEARCH_ENDPOINT="https://ai-eng-search-prod.search.windows.net"
When running on Azure (App Service, Container Apps, VMs), use managed identity to avoid storing keys:
# Assign the built-in Search Index Data Contributor role
az role assignment create \
--assignee "$(az rest -m GET -u 'https://management.azure.com/subscriptions/{subscription-id}/providers/Microsoft.ManagedIdentity/identities/{identity-id}?api-version=2023-01-31' --query principalId -o tsv)" \
--role "Search Index Data Contributor" \
--scope "/subscriptions/{subscription-id}/resourceGroups/rg-ai-engineering/providers/Microsoft.Search/searchServices/ai-eng-search-prod"
Then connect using Azure AD authentication:
from azure.identity import DefaultAzureCredential
from azure.search.documents import SearchClient
credential = DefaultAzureCredential()
search_client = SearchClient(
endpoint="https://ai-eng-search-prod.search.windows.net",
index_name="rag-documents",
credential=credential,
)
Creating Your First Index with Vector Fieldsβ
An index with vector search capability requires a vector search configuration that defines the algorithmic configuration (HNSW or exhaustive k-NN) and the vector field that references it.
- Azure Portal
- Python SDK
- In the Azure Portal, open your Azure AI Search service.
- Navigate to Indexes and select + Add index.
- Define fields:
id(Key, Retrievable, sortable)content(Searchable, Retrievable)embedding(Vector, 1536 dimensions, referencing a vector profile)source(Filterable, Facetable, Retrievable)
- Under Vector search, add a profile using
hnswalgorithm andcosinedistance. - Save the index.
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents.indexes.models import (
SearchIndex,
SearchField,
SearchFieldDataType,
SimpleVectorIndexer,
HnswAlgorithmConfiguration,
VectorSearch,
VectorSearchProfile,
VectorSearchAlgorithmKind,
VectorSearchAlgorithmMetric,
)
from azure.search.documents import SearchClient
from azure.core.credentials import AzureKeyCredential
# --- Configuration ---
ENDPOINT = "https://ai-eng-search-prod.search.windows.net"
API_KEY = os.environ["AZURE_SEARCH_API_KEY"]
INDEX_NAME = "rag-documents"
# --- Create the index ---
index_client = SearchIndexClient(endpoint=ENDPOINT, credential=AzureKeyCredential(API_KEY))
index = SearchIndex(
name=INDEX_NAME,
fields=[
SearchField(
name="id",
type=SearchFieldDataType.String,
key=True,
sortable=True,
retrievable=True,
),
SearchField(
name="content",
type=SearchFieldDataType.String,
searchable=True,
retrievable=True,
),
SearchField(
name="embedding",
type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
searchable=True,
vector_search_dimensions=1536,
vector_search_profile_name="default-profile",
),
SearchField(
name="source",
type=SearchFieldDataType.String,
filterable=True,
facetable=True,
retrievable=True,
),
],
vector_search=VectorSearch(
profiles=[
VectorSearchProfile(
name="default-profile",
algorithm_configuration_name="default-algorithm",
)
],
algorithms=[
HnswAlgorithmConfiguration(
name="default-algorithm",
kind=VectorSearchAlgorithmKind.HNSW,
parameters={
"m": 4,
"efConstruction": 400,
"efSearch": 500,
"metric": VectorSearchAlgorithmMetric.COSINE,
},
)
],
),
)
result = index_client.create_or_update_index(index)
print(f"Index '{result.name}' created successfully.")
Embedding Modelsβ
Available Models in Azure OpenAIβ
Embedding models must be deployed to your Azure OpenAI resource before use. The deployment name is what you reference in code, not the model name itself.
- Azure Portal
- Azure CLI
- Open your Azure OpenAI resource in the portal.
- Go to Deployments β Create new deployment.
- Select the model (
text-embedding-3-small,text-embedding-3-large, ortext-embedding-ada-002). - Give it a deployment name (e.g.,
embeddings-small). - Deploy.
az cognitiveservices account deployment create \
--name your-aoai-resource \
--resource-group rg-ai-engineering \
--deployment-name embeddings-small \
--model-name text-embedding-3-small \
--model-version "1" \
--model-format OpenAI \
--scale-settings-scale-type "Standard"
Dimension Sizes and Model Selectionβ
- text-embedding-3-small
- text-embedding-3-large
512 dimensions (recommended default):
from openai import AzureOpenAI
client = AzureOpenAI(
api_version="2024-10-01-preview",
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_key=os.environ["AZURE_OPENAI_API_KEY"],
)
response = client.embeddings.create(
model="text-embedding-3-small",
input="Azure AI Search supports hybrid vector and keyword search.",
dimensions=512,
)
embedding = response.data[0].embedding
print(f"Dimensionality: {len(embedding)}") # 512
3072 dimensions (highest quality):
response = client.embeddings.create(
model="text-embedding-3-large",
input="Azure AI Search supports hybrid vector and keyword search.",
dimensions=3072,
)
embedding = response.data[0].embedding
print(f"Dimensionality: {len(embedding)}") # 3072
Cost Comparisonβ
Approximate pricing per 1 million tokens (Azure OpenAI, pay-as-you-go):
| Model | Price (per 1M tokens) | Dimensions (default) | Quality Tier |
|---|---|---|---|
text-embedding-3-small | $0.020 | 512 (configurable) | Good |
text-embedding-3-large | $0.130 | 3072 (configurable) | Best |
text-embedding-ada-002 | $0.100 | 1536 (fixed) | Good |
text-embedding-3-small at 512 dimensions gives you the best cost-to-quality ratio for RAG. At roughly 6.5x cheaper than the large model, it handles the embedding workload for ingestion pipelines and query embedding without meaningful recall degradation on typical document corpora.
Best Practices for Embedding Generationβ
- Chunk before embedding. Split documents into 256β1024 token chunks with 50β100 token overlap. Embedding whole documents wastes tokens and dilutes retrieval precision.
- Use the same model for ingestion and query. Mixing embedding models produces incompatible vector spaces.
- Set consistent dimensions. If you use
text-embedding-3-smallwith 512 dimensions for ingestion, use 512 for queries too. - Batch embeddings. The API accepts up to 16,384 input items per call. Batch to reduce round trips and cost.
- Add prefix instructions for task-specific embeddings. Prepend a task description to each text (e.g.,
"passage: "for document chunks,"query: "for search queries) to improve retrieval alignment.
# Batch embedding example
texts = [
"Azure AI Search supports hybrid search combining vector and keyword signals.",
"The semantic ranker re-orders results based on query intent.",
"Vector search uses cosine similarity to find semantically related content.",
]
response = client.embeddings.create(
model="text-embedding-3-small",
input=["passage: " + t for t in texts],
dimensions=512,
)
embeddings = [item.embedding for item in response.data]
print(f"Generated {len(embeddings)} embeddings of dimension {len(embeddings[0])}")
Your First RAG Queryβ
This section walks through a complete RAG pipeline: install dependencies, create an index, ingest documents, and run queries using vector search, keyword search, and hybrid search.
Step 1 β Install Dependenciesβ
# Create the project
uv init rag-demo
cd rag-demo
# Install LangChain integrations
uv add langchain-azure-ai-search langchain-openai
# Sync the environment
uv sync
Step 2 β Set Environment Variablesβ
# Azure AI Search
export AZURE_SEARCH_ENDPOINT="https://ai-eng-search-prod.search.windows.net"
export AZURE_SEARCH_API_KEY="your-query-key"
# Azure OpenAI
export AZURE_OPENAI_API_KEY="your-aoai-key"
export AZURE_OPENAI_ENDPOINT="https://your-aoai.openai.azure.com"
export AZURE_OPENAI_API_VERSION="2024-10-01-preview"
Step 3 β Create Index and Ingest Documentsβ
We use LangChain's AzureAISearch vector store to create the index and load documents in one step:
import os
from langchain_openai import AzureOpenAIEmbeddings
from langchain_azure_ai_search import AzureAISearch
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.docstore.document import Document
# --- Embedding model ---
embeddings = AzureOpenAIEmbeddings(
model="text-embedding-3-small",
azure_deployment="embeddings-small",
)
# --- Sample documents ---
raw_documents = [
Document(
page_content="Azure AI Search is a fully managed cloud search service "
"that supports vector search, keyword search, and hybrid search.",
metadata={"source": "azure-docs", "topic": "overview"},
),
Document(
page_content="Hybrid search combines the precision of keyword matching "
"with the semantic understanding of vector search using "
"reciprocal rank fusion.",
metadata={"source": "azure-docs", "topic": "hybrid-search"},
),
Document(
page_content="The semantic ranker uses a deep learning model to re-rank "
"search results based on query-document relevance beyond "
"simple similarity scores.",
metadata={"source": "azure-docs", "topic": "semantic-ranking"},
),
Document(
page_content="HNSW (Hierarchical Navigable Small World) is the default "
"vector search algorithm in Azure AI Search, offering "
"fast approximate nearest neighbor search.",
metadata={"source": "azure-docs", "topic": "vector-algorithms"},
),
]
# --- Split documents into chunks ---
splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50,
)
documents = splitter.split_documents(raw_documents)
# --- Create vector store (creates index if it does not exist) ---
vector_store = AzureAISearch(
embedding=embeddings,
index_name="rag-demo-index",
azure_search_endpoint=os.environ["AZURE_SEARCH_ENDPOINT"],
azure_search_key=os.environ["AZURE_SEARCH_API_KEY"],
)
# --- Ingest documents ---
vector_store.add_documents(documents)
print(f"Ingested {len(documents)} document chunks.")
Step 4 β Build a Simple RAG Chainβ
Now connect the vector store to an LLM for question answering:
from langchain_openai import AzureChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# --- LLM ---
llm = AzureChatOpenAI(
model="gpt-4o",
azure_deployment="gpt-4o",
)
# --- Context retrieval ---
def retrieve_context(query: str) -> str:
"""Retrieve relevant document chunks from the vector store."""
results = vector_store.similarity_search(query, k=3)
return "\n\n".join(doc.page_content for doc in results)
# --- RAG prompt ---
rag_prompt = ChatPromptTemplate.from_messages([
(
"system",
"You are a helpful assistant. Answer the user's question using "
"only the provided context. If the context does not contain "
"enough information, say so honestly.",
),
("human", "Context:\n{context}\n\nQuestion: {question}"),
])
# --- RAG chain ---
rag_chain = (
RunnablePassthrough.assign(context=retrieve_context)
| rag_prompt
| llm
| StrOutputParser()
)
# --- Run a query ---
answer = rag_chain.invoke({"question": "What is hybrid search and how does it work?"})
print(answer)
Step 5 β Try Different Search Modesβ
Azure AI Search supports multiple search strategies. Here is how to use each one:
- Vector Search
- Keyword Search
- Hybrid Search
- Hybrid + Semantic Ranking
Pure semantic similarity search:
# Vector search β finds semantically similar content
results = vector_store.similarity_search(
"How do I combine keyword and vector search?",
k=3,
)
for doc in results:
print(f"[{doc.metadata.get('topic')}] {doc.page_content[:100]}...")
Full-text keyword matching:
# Keyword search β finds exact or stemmed term matches
results = vector_store.similarity_search_by_vector(
embeddings.embed_query("HNSW algorithm Azure"),
k=3,
)
# Or use the underlying search client for pure keyword queries
from azure.search.documents import SearchClient
from azure.core.credentials import AzureKeyCredential
search_client = SearchClient(
endpoint=os.environ["AZURE_SEARCH_ENDPOINT"],
index_name="rag-demo-index",
credential=AzureKeyCredential(os.environ["AZURE_SEARCH_API_KEY"]),
)
keyword_results = search_client.search(
search_text="HNSW algorithm",
top=3,
)
for result in keyword_results:
print(f"[{result['source']}] {result['content'][:100]}...")
Combined keyword + vector with reciprocal rank fusion:
from azure.search.documents import SearchClient
from azure.core.credentials import AzureKeyCredential
search_client = SearchClient(
endpoint=os.environ["AZURE_SEARCH_ENDPOINT"],
index_name="rag-demo-index",
credential=AzureKeyCredential(os.environ["AZURE_SEARCH_API_KEY"]),
)
# Hybrid search: keyword + vector combined
query = "How does Azure AI Search handle vector similarity?"
query_vector = embeddings.embed_query(query)
hybrid_results = search_client.search(
search_text=query,
vector_queries=[{
"kind": "vector",
"fields": "embedding",
"k_nearest_neighbors": 50,
"fields_weights": {"embedding": 1.0},
}],
top=5,
)
for result in hybrid_results:
print(f"Score: {result['@search.score']:.3f} | {result['content'][:80]}...")
The highest-quality option β hybrid search with semantic re-ranking:
from azure.search.documents import SearchClient
from azure.search.documents.models import VectorQuery, QueryType
from azure.core.credentials import AzureKeyCredential
search_client = SearchClient(
endpoint=os.environ["AZURE_SEARCH_ENDPOINT"],
index_name="rag-demo-index",
credential=AzureKeyCredential(os.environ["AZURE_SEARCH_API_KEY"]),
)
query = "Explain how hybrid search combines different search signals"
query_vector = embeddings.embed_query(query)
semantic_results = search_client.search(
search_text=query,
vector_queries=[
VectorQuery(
kind="vector",
fields=["embedding"],
k_nearest_neighbors=50,
)
],
query_type=QueryType.SEMANTIC,
semantic_configuration_name="default",
query_caption="extractive",
query_answer="extractive",
top=5,
)
for result in semantic_results:
score = result["@search.reranker_score"] or result["@search.score"]
print(f"Rerank: {score:.3f} | {result['content'][:80]}...")
Semantic ranking requires a semantic configuration on the index. When using LangChain's AzureAISearch, this is created automatically if you pass semantic_config_name during initialization. For manual index creation, add a SemanticSearch configuration block.
Connecting with LangChainβ
The langchain-azure-ai-search package provides a first-class integration between LangChain and Azure AI Search.
The AzureAISearch Vector Store Classβ
AzureAISearch implements LangChain's VectorStore interface, giving you a consistent API regardless of your underlying vector database:
from langchain_azure_ai_search import AzureAISearch
from langchain_openai import AzureOpenAIEmbeddings
embeddings = AzureOpenAIEmbeddings(
model="text-embedding-3-small",
azure_deployment="embeddings-small",
)
vector_store = AzureAISearch(
embedding=embeddings,
index_name="my-rag-index",
azure_search_endpoint=os.environ["AZURE_SEARCH_ENDPOINT"],
azure_search_key=os.environ["AZURE_SEARCH_API_KEY"],
)
Key initialization parameters:
| Parameter | Description |
|---|---|
embedding | LangChain embedding model instance |
index_name | Name of the Azure AI Search index |
azure_search_endpoint | Search service URL |
azure_search_key | API key (or use managed identity via azure_credential) |
fields | Custom field mapping for content, metadata, and embeddings |
vector_search_configuration | Override default HNSW settings |
Similarity Search vs. MMR Searchβ
LangChain provides two retrieval strategies:
query = "vector search algorithms"
# Similarity search β pure cosine similarity
similar_docs = vector_store.similarity_search(query, k=5)
# MMR (Maximal Marginal Relevance) β balances relevance with diversity
diverse_docs = vector_store.max_marginal_relevance_search(
query,
k=5,
lambda_mult=0.5, # 0 = max diversity, 1 = max similarity
)
- Similarity search returns the top-k most similar vectors. Simple and fast, but can return redundant results.
- MMR search trades some relevance for diversity by penalizing vectors that are too similar to already-selected results. Use
lambda_multto tune the balance.
Metadata Filteringβ
Narrow search results by applying filters on metadata fields:
# Filter to documents from a specific source
filtered_docs = vector_store.similarity_search(
"how does hybrid search work?",
k=3,
filter="source eq 'azure-docs'",
)
# Filter by multiple conditions
filtered_docs = vector_store.similarity_search(
"vector search configuration",
k=3,
filter="source eq 'azure-docs' and topic eq 'vector-algorithms'",
)
# Filter by date range
filtered_docs = vector_store.similarity_search(
"recent updates",
k=3,
filter="created_at ge 2025-01-01T00:00:00Z",
)
Filters use OData syntax. Supported operators include eq, ne, gt, ge, lt, le, and, or, and not.
Asynchronous Operationsβ
For high-throughput ingestion or concurrent queries, use async methods:
import asyncio
async def ingest_and_query():
# Async document ingestion
await vector_store.aadd_documents(documents)
# Async similarity search
results = await vector_store.asimilarity_search(
"semantic ranking in Azure AI Search",
k=3,
)
for doc in results:
print(doc.page_content[:100])
asyncio.run(ingest_and_query())
Using as a Retriever in LCEL Chainsβ
The cleanest integration pattern wraps the vector store as a retriever and plugs it into an LCEL chain:
from langchain_core.runnables import RunnablePassthrough
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# Create a retriever from the vector store
retriever = vector_store.as_retriever(
search_type="similarity", # or "mmr"
search_kwargs={"k": 5, "filter": "source eq 'internal-wiki'"},
)
# Format retrieved documents into context
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# RAG prompt
prompt = ChatPromptTemplate.from_messages([
("system", "Answer based on the following context:\n{context}"),
("human", "{question}"),
])
# Full RAG chain
chain = (
{"question": lambda x: x["question"]}
| RunnablePassthrough.assign(context=retriever | format_docs)
| prompt
| llm
| StrOutputParser()
)
# Invoke
answer = chain.invoke({"question": "What are the differences between HNSW and exhaustive k-NN?"})
print(answer)
Next Stepsβ
You now have a working Azure AI Search index, ingested documents, and a RAG chain that answers questions. Here is where to go next:
- Best Practices β production patterns for indexing strategies, chunking, query optimization, monitoring, and cost management.
- Azure AI Search Documentation β official Microsoft documentation for advanced features.
- LangChain AzureAISearch Reference β full API reference for the LangChain integration.
- Hybrid Search Deep Dive β understanding reciprocal rank fusion and semantic ranking in detail.
Before moving to production, read our Best Practices guide. It covers chunking strategies, embedding model selection trade-offs, and monitoring patterns specific to our Azure setup.