Business Professionals
Power BI | Power Pivot | Power Query | DAX
Cloud Flows | RPA | AI Builder | Copilot
60+ Formulas | Data Stories | Advanced Reporting & Modeling
VB Programming | Report Automation |
MS-Office Automation
Techno-Business Professionals
Power BI | Power Query | Advanced DAX | SQL - Query &
Programming
Microsoft Fabric | Power BI | Power Query | Advanced DAX |
SQL - Query & Programming
Power BI | Power Apps | Power Automate | Copilot Studio | Power Pages | Dataverse
Microsoft Power Apps | Microsoft Power Automate
Power BI | Adv. DAX | SQL (Query & Programming) |
VBA | Python | Web Scrapping | API Integration
Power BI | Power Apps | Power Automate |
SQL (Query & Programming)
Power BI | Adv. DAX | Power Apps | Power Automate |
SQL (Query & Programming) | VBA | Python | Web Scrapping | API Integration
Power Apps | Power Automate | SQL | VBA | Python |
Web Scraping | RPA | API Integration
Technology Professionals
Power BI | DAX | SQL | ETL with SSIS | SSAS | VBA | Python
Power BI | SQL | Azure Data Lake | Synapse Analytics |
Data Factory | Databricks | Power Apps | Power Automate |
Azure Analysis Services
Microsoft Fabric | Power BI | SQL | Lakehouse |
Data Factory (Pipelines) | Dataflows Gen2 | KQL | Delta Tables | Power Apps | Power Automate
Power BI | Power Apps | Power Automate | SQL | VBA | Python | API Integration
Power BI | Advanced DAX | Databricks | SQL | Lakehouse Architecture
Business Professionals
Power BI | Power Pivot | Power Query | DAX
Cloud Flows | RPA | AI Builder | Copilot
60+ Formulas | Data Stories | Advanced Reporting & Modeling
VB Programming | Report Automation |
MS-Office Automation
Techno-Business Professionals
Power BI | Power Query | Advanced DAX | SQL - Query &
Programming
Microsoft Fabric | Power BI | Power Query | Advanced DAX |
SQL - Query & Programming
Power BI | Power Apps | Power Automate | Copilot Studio | Power Pages | Dataverse
Microsoft Power Apps | Microsoft Power Automate
Power BI | Adv. DAX | SQL (Query & Programming) |
VBA | Web Scrapping | API Integration
Power BI | Power Apps | Power Automate |
SQL (Query & Programming)
Power BI | Adv. DAX | Power Apps | Power Automate |
SQL (Query & Programming) | VBA | Web Scrapping | API Integration
Power Apps | Power Automate | SQL | VBA |
Web Scraping | RPA | API Integration
Technology Professionals
Power BI | DAX | SQL | ETL with SSIS | SSAS | VBA
Power BI | SQL | Azure Data Lake | Synapse Analytics |
Data Factory | Azure Analysis Services
Microsoft Fabric | Power BI | SQL | Lakehouse |
Data Factory (Pipelines) | Dataflows Gen2 | KQL | Delta Tables
Power BI | Power Apps | Power Automate | SQL | VBA | API Integration
Power BI | Advanced DAX | Databricks | SQL | Lakehouse Architecture
You’ve wired up an Azure OpenAI-powered Copilot for your organisation, pointed it at “all company documents”, and the first user test asks a simple question… that the bot answers with an outdated policy from three years ago. The model is fine; your data engineering for AI is not.
This article walks through how to design a RAG data pipeline on Azure that keeps Copilot-style agents accurate and grounded, focusing on clean source data, chunking, and vector search – using one realistic scenario and concrete patterns you can reuse.
Imagine a data team asked to build an internal Copilot for:
The stack is roughly:
Symptoms from the first pilot:
We’ll tighten this pipeline in three dimensions:
RAG quality depends more on what you index than on the model you call.
For our policy Copilot, the team first defines a single authoritative source per domain:
HR-Policies (modern pages + PDFs)IT-GovernanceRules:
Use Azure Data Factory or Synapse pipelines to land content into Azure Data Lake Storage Gen2 in a structured layout, e.g.:
/raw
/sharepoint
/hr_policies
/it_policies
/devops
/adr
/curated
/policies
/hr
/it
/adr
Key practices:
For text extraction from Office and PDF into the lake, many teams use Azure Functions or Logic Apps calling Microsoft Graph and an extraction library. The exact implementation differs, but the important point: the curated zone holds clean, text-ready content.
Long documents are a poor fit for RAG if you index them as single blobs. You want semantic chunks that:
For Azure AI Search and Azure OpenAI, chunking is generally done before indexing vectors.
For our scenario, a practical chunking strategy:
H1, H2), bullet lists, and paragraph breaksThis is not a hard rule; you’ll tune it based on your documents and model. But it’s a good starting point for GPT-4 class models.
A common pattern is a Python Azure Function or containerized job that reads text from the curated zone, chunks it, and writes a chunked representation back to the lake or directly into Azure AI Search.
A simplified example of a chunking function in Python:
import re
from typing import List
def chunk_text(text: str, max_chars: int = 1500, overlap_chars: int = 200) -> List[str]:
# Normalize whitespace
normalized = re.sub(r"\s+", " ", text).strip()
chunks = []
start = 0
length = len(normalized)
while start < length:
end = min(start + max_chars, length)
# Try to break at a sentence boundary
boundary = normalized.rfind(". ", start, end)
if boundary == -1 or boundary <= start + 100: # fallback if no good boundary
boundary = end
else:
boundary += 1 # include the period
chunk = normalized[start:boundary].strip()
if chunk:
chunks.append(chunk)
if boundary >= length:
break
# Move start forward with overlap
start = max(boundary - overlap_chars, 0)
return chunks
This function:
max_charsYou would call this per document, then attach metadata like document_id, section_heading, version, and source_url to each chunk.
Azure AI Search (previously Azure Cognitive Search) supports vector search with:
For our policy Copilot, a typical index schema might include:
id (key)content (full chunk text, searchable)contentVector (vector field for embeddings)title (section or document title)policyType (HR, IT, ADR, etc.)effectiveDate (for filtering out outdated policies)isCurrentVersion (boolean)sourceUrlYou define this index via REST, SDK, or the Azure portal. The critical part: mark the vector field correctly and ensure dimensions match the embedding model you use.
Embeddings are created by calling Azure OpenAI with an embeddings model (for example, text-embedding-3-large or another current model). The response returns a vector you store in the index.
A minimal Python example using the Azure OpenAI client libraries (pattern only):
from azure.core.credentials import AzureKeyCredential
from azure.search.documents import SearchClient
from openai import AzureOpenAI
# Azure AI Search setup
search_service_endpoint = "https://<your-search-service>.search.windows.net"
index_name = "policy-index"
search_api_key = "<search-api-key>" # store securely in Key Vault in real use
search_client = SearchClient(
endpoint=search_service_endpoint,
index_name=index_name,
credential=AzureKeyCredential(search_api_key)
)
# Azure OpenAI setup
client = AzureOpenAI(
api_key="<openai-api-key>", # also should be in Key Vault
api_version="2024-02-15-preview",
azure_endpoint="https://<your-openai-resource>.openai.azure.com"
)
embedding_model = "text-embedding-3-large" # example; use a current model
def embed_text(text: str) -> list:
response = client.embeddings.create(
model=embedding_model,
input=text
)
return response.data[0].embedding
def index_chunk(chunk_id: str, content: str, metadata: dict):
vector = embed_text(content)
doc = {
"id": chunk_id,
"content": content,
"contentVector": vector,
**metadata,
}
search_client.upload_documents(documents=[doc])
This snippet illustrates:
contentVector fieldIn production, you’d handle batching, retries, and secrets via Azure Key Vault and managed identities.
For RAG, you typically:
Azure AI Search supports vector search via the vector parameter and filter expressions.
Example pattern for querying (Python):
def search_chunks(query: str, top_k: int = 5):
query_vector = embed_text(query)
results = search_client.search(
search_text="", # empty when using pure vector search
vector={
"value": query_vector,
"fields": "contentVector",
"k": top_k,
},
filter="isCurrentVersion eq true"
)
chunks = []
for result in results:
chunks.append({
"content": result["content"],
"sourceUrl": result["sourceUrl"],
"title": result.get("title"),
"effectiveDate": result.get("effectiveDate"),
})
return chunks
This pattern:
contentVector fieldYou can switch to hybrid search by providing search_text as well, allowing keyword scoring to complement vector similarity.
Once you have clean chunks coming back from Azure AI Search, the next step is to construct the prompt for Azure OpenAI.
The orchestration layer (Function App, Web API, or Logic App) typically:
search_chunks to get top N chunksExample prompt pattern (conceptual):
def build_prompt(question: str, chunks: list) -> dict:
context_blocks = []
for i, c in enumerate(chunks, start=1):
block = f"Source {i} (title: {c['title']}):\n{c['content']}\n"
context_blocks.append(block)
context_text = "\n".join(context_blocks)
system_message = (
"You are an assistant answering questions about internal HR and IT policies. "
"Use ONLY the provided sources. If the answer is not in the sources, say you "
"cannot find an applicable policy. Always mention which sources you used."
)
user_message = (
f"Question: {question}\n\n"
f"Sources:\n{context_text}"
)
return {
"messages": [
{"role": "system", "content": system_message},
{"role": "user", "content": user_message},
]
}
You then send this to Azure OpenAI’s chat completion endpoint with a GPT-4 class model.
The key data engineering responsibilities here:
The scenario team’s initial problems (obsolete answers, missing context, slow queries) usually trace back to three operational issues.
If policies change weekly, your RAG pipeline must:
Common patterns:
Data engineering for AI is also about access control:
For our policy Copilot, the team limits the index to published policies only, excluding drafts and internal notes.
Latency issues often come from:
top_k) increasing prompt sizePractical steps:
top_k around 3–8 and adjust based on answer qualityBack to our scenario.
Before:
User question: “What is the current remote work policy for employees?”
Result: Copilot surfaces a 3-year-old policy PDF from the file share, missing the latest update.
After implementing the pipeline above:
effectiveDate and isCurrentVersionSame question now returns:
The model hasn’t changed; the data engineering around it has.
If your Copilot agents are confidently wrong, start by treating RAG data pipeline Azure work as first-class data engineering, not an afterthought: define authoritative sources, chunk documents with context, and build a vector index in Azure AI Search that only exposes clean, current data. Once that foundation is solid, prompt tuning and model selection become optimisations instead of firefighting.
This article reflects how data engineering teams are formalising RAG-oriented pipelines on Azure so Copilot-style agents can reliably use internal content, especially in environments with mixed SharePoint, SQL, and legacy file sources.
Professionals who want to apply these patterns to their own data can explore Excelgoodies' Data Engineering & BI Azure (On Cloud) programme - taught live by instructors, with certification awarded once a real project is running at work.
Insights compiled through ongoing industry research and discussions within the Excelgoodies Analytics Community.
Azure
New
Next Batches Now Live
Power BI
SQL
Power Apps
Power Automate
Microsoft Fabrics
Azure Data Engineering