Skip to content

Commit c8da301

Browse files
Merge pull request #583 from HeidiSteen/heidist-rag
[azure search] updated rag tutorial for rbac
2 parents 84adfbd + 9f42c19 commit c8da301

File tree

4 files changed

+89
-50
lines changed

4 files changed

+89
-50
lines changed

articles/search/tutorial-rag-build-solution-index-schema.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ author: HeidiSteen
88
ms.author: heidist
99
ms.service: cognitive-search
1010
ms.topic: tutorial
11-
ms.date: 09/12/2024
11+
ms.date: 10/01/2024
1212

1313
---
1414

@@ -111,8 +111,25 @@ A minimal index for LLM is designed to store chunks of content. It typically inc
111111
The schema also includes a `locations` field for storing generated content that's created by the [indexing pipeline](tutorial-rag-build-solution-pipeline.md).
112112

113113
```python
114+
from azure.identity import DefaultAzureCredential
115+
from azure.identity import get_bearer_token_provider
116+
from azure.search.documents.indexes import SearchIndexClient
117+
from azure.search.documents.indexes.models import (
118+
SearchField,
119+
SearchFieldDataType,
120+
VectorSearch,
121+
HnswAlgorithmConfiguration,
122+
VectorSearchProfile,
123+
AzureOpenAIVectorizer,
124+
AzureOpenAIVectorizerParameters,
125+
SearchIndex
126+
)
127+
128+
credential = DefaultAzureCredential()
129+
130+
# Create a search index
114131
index_name = "py-rag-tutorial-idx"
115-
index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=AZURE_SEARCH_CREDENTIAL)
132+
index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
116133
fields = [
117134
SearchField(name="parent_id", type=SearchFieldDataType.String),
118135
SearchField(name="title", type=SearchFieldDataType.String),
@@ -131,20 +148,20 @@ A minimal index for LLM is designed to store chunks of content. It typically inc
131148
VectorSearchProfile(
132149
name="myHnswProfile",
133150
algorithm_configuration_name="myHnsw",
134-
vectorizer="myOpenAI",
151+
vectorizer_name="myOpenAI",
135152
)
136153
],
137154
vectorizers=[
138155
AzureOpenAIVectorizer(
139-
name="myOpenAI",
156+
vectorizer_name="myOpenAI",
140157
kind="azureOpenAI",
141-
azure_open_ai_parameters=AzureOpenAIParameters(
142-
resource_uri=AZURE_OPENAI_ACCOUNT,
143-
deployment_id="text-embedding-ada-002",
158+
parameters=AzureOpenAIVectorizerParameters(
159+
resource_url=AZURE_OPENAI_ACCOUNT,
160+
deployment_name="text-embedding-ada-002",
144161
model_name="text-embedding-ada-002"
145162
),
146163
),
147-
],
164+
],
148165
)
149166

150167
# Create the search index

articles/search/tutorial-rag-build-solution-models.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ms.author: heidist
99
ms.service: cognitive-search
1010
ms.topic: tutorial
1111
ms.custom: references_regions
12-
ms.date: 09/30/2024
12+
ms.date: 10/01/2024
1313

1414
---
1515

@@ -32,7 +32,7 @@ If you don't have an Azure subscription, create a [free account](https://azure.m
3232

3333
- The Azure portal, used to deploy models and configure role assignments in the Azure cloud.
3434

35-
- An **Owner** role on your Azure subscription, necessary for creating role assignments. You use at least three Azure resources in this tutorial. The connections are authenticated using Microsoft Entra ID, which requires the ability to create roles. Role assignments for connecting to models are documented in this article.
35+
- An **Owner** or **User Access Administrator** role on your Azure subscription, necessary for creating role assignments. You use at least three Azure resources in this tutorial. The connections are authenticated using Microsoft Entra ID, which requires the ability to create roles. Role assignments for connecting to models are documented in this article. If you can't create roles, you can use [API keys](search-security-api-keys.md) instead.
3636

3737
- A model provider, such as [Azure OpenAI](/azure/ai-services/openai/how-to/create-resource), Azure AI Vision via an [Azure AI multi-service account](/azure/ai-services/multi-service-resource), or [Azure AI Studio](https://ai.azure.com/).
3838

articles/search/tutorial-rag-build-solution-pipeline.md

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ author: HeidiSteen
88
ms.author: heidist
99
ms.service: cognitive-search
1010
ms.topic: tutorial
11-
ms.date: 09/23/2024
11+
ms.date: 10/01/2024
1212

1313
---
1414

@@ -19,7 +19,7 @@ Learn how to build an automated indexing pipeline for a RAG solution on Azure AI
1919
In this tutorial, you:
2020

2121
> [!div class="checklist"]
22-
> - Provide the index schema from the previous tutorial
22+
> - Provide the index schema from the previous tutorial
2323
> - Create a data source connection
2424
> - Create an indexer
2525
> - Create a skillset that chunks, vectorizes, and recognizes entities
@@ -53,8 +53,25 @@ Open or create a Jupyter notebook (`.ipynb`) in Visual Studio Code to contain th
5353
Let's start with the index schema from the [previous tutorial](tutorial-rag-build-solution-index-schema.md). It's organized around vectorized and nonvectorized chunks. It includes a `locations` field that stores AI-generated content created by the skillset.
5454

5555
```python
56+
from azure.identity import DefaultAzureCredential
57+
from azure.identity import get_bearer_token_provider
58+
from azure.search.documents.indexes import SearchIndexClient
59+
from azure.search.documents.indexes.models import (
60+
SearchField,
61+
SearchFieldDataType,
62+
VectorSearch,
63+
HnswAlgorithmConfiguration,
64+
VectorSearchProfile,
65+
AzureOpenAIVectorizer,
66+
AzureOpenAIVectorizerParameters,
67+
SearchIndex
68+
)
69+
70+
credential = DefaultAzureCredential()
71+
72+
# Create a search index
5673
index_name = "py-rag-tutorial-idx"
57-
index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=AZURE_SEARCH_CREDENTIAL)
74+
index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
5875
fields = [
5976
SearchField(name="parent_id", type=SearchFieldDataType.String),
6077
SearchField(name="title", type=SearchFieldDataType.String),
@@ -63,7 +80,7 @@ fields = [
6380
SearchField(name="chunk", type=SearchFieldDataType.String, sortable=False, filterable=False, facetable=False),
6481
SearchField(name="text_vector", type=SearchFieldDataType.Collection(SearchFieldDataType.Single), vector_search_dimensions=1536, vector_search_profile_name="myHnswProfile")
6582
]
66-
83+
6784
# Configure the vector search configuration
6885
vector_search = VectorSearch(
6986
algorithms=[
@@ -73,23 +90,23 @@ vector_search = VectorSearch(
7390
VectorSearchProfile(
7491
name="myHnswProfile",
7592
algorithm_configuration_name="myHnsw",
76-
vectorizer="myOpenAI",
93+
vectorizer_name="myOpenAI",
7794
)
7895
],
7996
vectorizers=[
8097
AzureOpenAIVectorizer(
81-
name="myOpenAI",
98+
vectorizer_name="myOpenAI",
8299
kind="azureOpenAI",
83-
azure_open_ai_parameters=AzureOpenAIParameters(
84-
resource_uri=AZURE_OPENAI_ACCOUNT,
85-
deployment_id="text-embedding-ada-002",
100+
parameters=AzureOpenAIVectorizerParameters(
101+
resource_url=AZURE_OPENAI_ACCOUNT,
102+
deployment_name="text-embedding-ada-002",
86103
model_name="text-embedding-ada-002"
87104
),
88105
),
89-
],
106+
],
90107
)
91-
92-
# Create the search index on Azure AI Search
108+
109+
# Create the search index
93110
index = SearchIndex(name=index_name, fields=fields, vector_search=vector_search)
94111
result = index_client.create_or_update_index(index)
95112
print(f"{result.name} created")
@@ -101,11 +118,11 @@ In this step, set up the sample data and a connection to Azure Blob Storage. The
101118

102119
The original ebook is large, over 100 pages and 35 MB in size. We broke it up into smaller PDFs, one per page of text, to stay under the [API payload limit](search-limits-quotas-capacity.md#api-request-limits) of 16 MB per API call and also the [AI enrichment data limits](search-limits-quotas-capacity.md#data-limits-ai-enrichment). For simplicity, we omit image vectorization for this exercise.
103120

104-
1. Sign in to the Azure portal and find your Azure Storage account.
121+
1. Sign in to the [Azure portal](https://portal.azure.com) and find your Azure Storage account.
105122

106123
1. Create a container and upload the PDFs from [earth_book_2019_text_pages](https://github.com/Azure-Samples/azure-search-sample-data/tree/main/nasa-e-book/earth_book_2019_text_pages).
107124

108-
1. Make sure Azure AI Search has **Storage Blob Data Reader** permissions on the resource.
125+
1. Make sure Azure AI Search has [**Storage Blob Data Reader** permissions](/azure/role-based-access-control/role-assignments-portal) on the resource.
109126

110127
1. Next, in Visual Studio Code, define an indexer data source that provides connection information during indexing.
111128

@@ -117,8 +134,8 @@ The original ebook is large, over 100 pages and 35 MB in size. We broke it up in
117134
)
118135

119136
# Create a data source
120-
indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=AZURE_SEARCH_CREDENTIAL)
121-
container = SearchIndexerDataContainer(name="nasa-ebook-pdfs-all")
137+
indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
138+
container = SearchIndexerDataContainer(name="nasa-ebooks-pdfs-all")
122139
data_source_connection = SearchIndexerDataSourceConnection(
123140
name="py-rag-tutorial-ds",
124141
type="azureblob",
@@ -130,11 +147,15 @@ The original ebook is large, over 100 pages and 35 MB in size. We broke it up in
130147
print(f"Data source '{data_source.name}' created or updated")
131148
```
132149

150+
If you set up a managed identity for Azure AI Search for the connection, the connection string includes a `ResourceId=` suffix. It should look similar to the following example: `"ResourceId=/subscriptions/FAKE-SUBCRIPTION=ID/resourceGroups/FAKE-RESOURCE-GROUP/providers/Microsoft.Storage/storageAccounts/FAKE-ACCOUNT;"`
151+
133152
## Create a skillset
134153

135154
Skills are the basis for integrated data chunking and vectorization. At a minimum, you want a Text Split skill to chunk your content, and an embedding skill that create vector representations of your chunked content.
136155

137-
In this skillset, an extra skill is used to create structured data in the index. The Entity Recognition skill is used to identify locations, which can range from proper names to generic references, such as "ocean" or "mountain". Having structured data gives you more options for creating interesting queries and boosting relevance.
156+
In this skillset, an extra skill is used to create structured data in the index. The [Entity Recognition skill](cognitive-search-skill-entity-recognition-v3.md) is used to identify locations, which can range from proper names to generic references, such as "ocean" or "mountain". Having structured data gives you more options for creating interesting queries and boosting relevance.
157+
158+
The AZURE_AI_MULTISERVICE_KEY is needed even if you're using role-based access control. Azure AI Search uses the key for billing purposes and it's required unless your workloads stay under the free limit.
138159

139160
```python
140161
from azure.search.documents.indexes.models import (
@@ -143,7 +164,7 @@ from azure.search.documents.indexes.models import (
143164
OutputFieldMappingEntry,
144165
AzureOpenAIEmbeddingSkill,
145166
EntityRecognitionSkill,
146-
SearchIndexerIndexProjections,
167+
SearchIndexerIndexProjection,
147168
SearchIndexerIndexProjectionSelector,
148169
SearchIndexerIndexProjectionsParameters,
149170
IndexProjectionMode,
@@ -171,8 +192,8 @@ split_skill = SplitSkill(
171192
embedding_skill = AzureOpenAIEmbeddingSkill(
172193
description="Skill to generate embeddings via Azure OpenAI",
173194
context="/document/pages/*",
174-
resource_uri=AZURE_OPENAI_ACCOUNT,
175-
deployment_id="text-embedding-ada-002",
195+
resource_url=AZURE_OPENAI_ACCOUNT,
196+
deployment_name="text-embedding-ada-002",
176197
model_name="text-embedding-ada-002",
177198
dimensions=1536,
178199
inputs=[
@@ -196,7 +217,7 @@ entity_skill = EntityRecognitionSkill(
196217
]
197218
)
198219

199-
index_projections = SearchIndexerIndexProjections(
220+
index_projections = SearchIndexerIndexProjection(
200221
selectors=[
201222
SearchIndexerIndexProjectionSelector(
202223
target_index_name=index_name,
@@ -205,7 +226,7 @@ index_projections = SearchIndexerIndexProjections(
205226
mappings=[
206227
InputFieldMappingEntry(name="chunk", source="/document/pages/*"),
207228
InputFieldMappingEntry(name="text_vector", source="/document/pages/*/text_vector"),
208-
InputFieldMappingEntry(name="locations", source="/document/pages/*/locations"),
229+
InputFieldMappingEntry(name="locations", source="/document/pages/*/locations"),
209230
InputFieldMappingEntry(name="title", source="/document/metadata_storage_name"),
210231
],
211232
),
@@ -223,13 +244,13 @@ skillset = SearchIndexerSkillset(
223244
name=skillset_name,
224245
description="Skillset to chunk documents and generating embeddings",
225246
skills=skills,
226-
index_projections=index_projections,
247+
index_projection=index_projections,
227248
cognitive_services_account=cognitive_services_account
228249
)
229250

230-
client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=AZURE_SEARCH_CREDENTIAL)
251+
client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
231252
client.create_or_update_skillset(skillset)
232-
print(f"{skillset.name} created")
253+
print(f"{skillset.name} created")
233254
```
234255

235256
## Create and run the indexer
@@ -261,24 +282,26 @@ indexer = SearchIndexer(
261282
)
262283

263284
# Create and run the indexer
264-
indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=AZURE_SEARCH_CREDENTIAL)
285+
indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
265286
indexer_result = indexer_client.create_or_update_indexer(indexer)
266287

267-
print(f' {indexer_name} is created and running. Give the indexer a few minutes before running a query.')
288+
print(f' {indexer_name} is created and running. Give the indexer a few minutes before running a query.')
268289
```
269290

270291
## Run a query to check results
271292

272293
Send a query to confirm your index is operational. This request converts the text string "`where are the nasa headquarters located?`" into a vector for a vector search. Results consist of the fields in the select statement, some of which are printed as output.
273294

295+
There's no chat or generative AI at this point. The results are verbatim content from your search index.
296+
274297
```python
275298
from azure.search.documents import SearchClient
276299
from azure.search.documents.models import VectorizableTextQuery
277300

278-
# Hybrid Search
279-
query = "where are the nasa headquarters located?"
301+
# Vector Search using text-to-vector conversion of the querystring
302+
query = "where are NASA's headquarters located?"
280303

281-
search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, credential=AZURE_SEARCH_CREDENTIAL, index_name=index_name)
304+
search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential, index_name=index_name)
282305
vector_query = VectorizableTextQuery(text=query, k_nearest_neighbors=1, fields="text_vector", exhaustive=True)
283306

284307
results = search_client.search(
@@ -292,7 +315,7 @@ for result in results:
292315
print(f"Score: {result['@search.score']}")
293316
print(f"Title: {result['title']}")
294317
print(f"Locations: {result['locations']}")
295-
print(f"Content: {result['chunk']}")
318+
print(f"Content: {result['chunk']}")
296319
```
297320

298321
This query returns a single match (`top=1`) consisting of the one chunk determined by the search engine to be the most relevant. Results from the query should look similar to the following example:

articles/search/tutorial-rag-build-solution-query.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ author: HeidiSteen
88
ms.author: heidist
99
ms.service: cognitive-search
1010
ms.topic: tutorial
11-
ms.date: 09/12/2024
11+
ms.date: 10/01/2024
1212

1313
---
1414

@@ -46,10 +46,10 @@ You're setting up two clients, so you need permissions on both resources. We use
4646

4747
```python
4848
# Set endpoints and API keys for Azure services
49-
AZURE_SEARCH_SERVICE: str = "PUT YOUR SEARCH SERVICE URL HERE"
50-
AZURE_SEARCH_KEY: str = "PUT YOUR SEARCH SERVICE ADMIN KEY HERE"
51-
AZURE_OPENAI_ACCOUNT: str = "PUT YOUR AZURE OPENAI ACCOUNT URL HERE"
52-
AZURE_OPENAI_KEY: str = "PUT YOUR AZURE OPENAI KEY HERE"
49+
AZURE_SEARCH_SERVICE: str = "PUT YOUR SEARCH SERVICE ENDPOINT HERE"
50+
# AZURE_SEARCH_KEY: str = "DELETE IF USING ROLES, OTHERWISE PUT YOUR SEARCH SERVICE ADMIN KEY HERE"
51+
AZURE_OPENAI_ACCOUNT: str = "PUR YOUR AZURE OPENAI ENDPOINT HERE"
52+
# AZURE_OPENAI_KEY: str = "DELETE IF USING ROLES, OTHERWISE PPUT YOUR AZURE OPENAI KEY HERE"
5353
```
5454

5555
## Example script for prompt and query
@@ -59,22 +59,21 @@ Here's the Python script that instantiates the clients, defines the prompt, and
5959
```python
6060
# Import libraries
6161
from azure.search.documents import SearchClient
62-
from azure.core.credentials import AzureKeyCredential
6362
from openai import AzureOpenAI
6463

65-
# Set up clients and specify the chat model
64+
token_provider = get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default")
6665
openai_client = AzureOpenAI(
6766
api_version="2024-06-01",
6867
azure_endpoint=AZURE_OPENAI_ACCOUNT,
69-
api_key=AZURE_OPENAI_KEY
68+
azure_ad_token_provider=token_provider
7069
)
7170

7271
deployment_name = "gpt-35-turbo"
7372

7473
search_client = SearchClient(
7574
endpoint=AZURE_SEARCH_SERVICE,
7675
index_name=index_name,
77-
credential=AZURE_SEARCH_CREDENTIAL
76+
credential=credential
7877
)
7978

8079
# Provide instructions to the model

0 commit comments

Comments
 (0)