Skip to content

Commit b81395f

Browse files
committed
RAG quickstart
1 parent a92257a commit b81395f

File tree

2 files changed

+259
-0
lines changed

2 files changed

+259
-0
lines changed
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# RAG Quickstart for Azure AI Search\n",
8+
"\n",
9+
"This quickstart provides a query for RAG scenarios. It demonstrates a approach for grounding chat queries with data in a search index on Azure AI Search.\n",
10+
"\n",
11+
"We took a few shortcuts to keep the exercise basic and focused on query definitions:\n",
12+
"\n",
13+
"- We use the hotels-sample-index, which can be created in minutes and runs on any search service tier. This index is created by a wizard using built-in sample data.\n",
14+
"- We omit vectors so that we can skip chunking and embedding.\n",
15+
"\n",
16+
"Once you understand the fundamentals of integrating queries from Azure AI Search to an LLM, you can build on that experience by adding vector fields and vector and hybrid queries. We recommend the [phi-chat Python code example](https://github.com/Azure/azure-search-vector-samples/blob/main/demo-python/code/phi-chat/phi-chat.ipynb) for that step.\n",
17+
"\n",
18+
"This quickstart is documented in [Quickstart: Generative search (RAG) with grounding data from Azure AI Search](https://learn.microsoft.com/azure/search/search-get-started-rag). If you need more guidance than the readme provides, please refer to the article.\n"
19+
]
20+
},
21+
{
22+
"cell_type": "markdown",
23+
"metadata": {},
24+
"source": [
25+
"## Prerequisites\n",
26+
"\n",
27+
"- Azure AI Search, any tier\n",
28+
"\n",
29+
"- Azure OpenAI, in the same region as Azure AI Search\n",
30+
"\n",
31+
"- Deployment GPT-3.5-Turbo, GPT-4, or equivalent LLM\n",
32+
"\n",
33+
"### Configuration requirements\n",
34+
"\n",
35+
"Use the Azure portal for these steps.\n",
36+
"\n",
37+
"1. Configure Azure OpenAI to use a system-assigned managed identity.\n",
38+
"\n",
39+
"1. Configure Azure AI Search to use a system-assigned managed identity.\n",
40+
"\n",
41+
"1. Create the hotels-sample-index using the Import data wizard.\n",
42+
"\n",
43+
"1. Add a semantic configuration named \"semantic-config\" and make it the default configuration:\n",
44+
" \n",
45+
" ```json\n",
46+
" \"semantic\": {\n",
47+
" \"defaultConfiguration\": \"semantic-config\",\n",
48+
" \"configurations\": [\n",
49+
" {\n",
50+
" \"name\": \"semantic-config\",\n",
51+
" \"prioritizedFields\": {\n",
52+
" \"titleField\": { \"fieldName\": \"HotelName\" },\n",
53+
" \"prioritizedContentFields\": [ { \"fieldName\": \"Description\" } ],\n",
54+
" \"prioritizedKeywordsFields\": [\n",
55+
" { \"fieldName\": \"Category\" },\n",
56+
" { \"fieldName\": \"Tags\" }\n",
57+
" ]}\n",
58+
" }]},\n",
59+
" ```\n",
60+
"\n",
61+
"1. On Azure AI Search, create a role assignment for the Azure OpenAI managed identity. Required roles: Search Index Data Reader, Search Service Contributor.\n",
62+
"\n",
63+
"1. On Azure OpenAI, create a role assigmnet for the user running the code: Required role: Cognitive Services OpenAI User."
64+
]
65+
},
66+
{
67+
"cell_type": "code",
68+
"execution_count": 1,
69+
"metadata": {},
70+
"outputs": [],
71+
"source": [
72+
"# Package install for quickstart\n",
73+
"! pip install azure-search-documents==11.6.0b4 --quiet\n",
74+
"! pip install azure-identity==1.16.0 --quiet\n",
75+
"! pip install openai --quiet"
76+
]
77+
},
78+
{
79+
"cell_type": "code",
80+
"execution_count": 2,
81+
"metadata": {},
82+
"outputs": [],
83+
"source": [
84+
"# Set endpoints and deployment model\n",
85+
"AZURE_SEARCH_SERVICE: str = \"PUT YOUR SEARCH SERVICE ENDPOINT HERE\"\n",
86+
"AZURE_OPENAI_ACCOUNT: str = \"PUT YOUR AZURE OPENAI ENDPOINT HERE\"\n",
87+
"AZURE_DEPLOYMENT_MODEL: str = \"gpt-35-turbo\""
88+
]
89+
},
90+
{
91+
"cell_type": "code",
92+
"execution_count": 3,
93+
"metadata": {},
94+
"outputs": [],
95+
"source": [
96+
"# Set query parameters for grounding the conversation on your search index\n",
97+
"k=50\n",
98+
"search_type=\"text\"\n",
99+
"use_semantic_reranker=True\n",
100+
"sources_to_include=5"
101+
]
102+
},
103+
{
104+
"cell_type": "code",
105+
"execution_count": 11,
106+
"metadata": {},
107+
"outputs": [],
108+
"source": [
109+
"# Set up the query for generating responses\n",
110+
"from azure.core.credentials_async import AsyncTokenCredential\n",
111+
"from azure.identity.aio import get_bearer_token_provider\n",
112+
"from azure.search.documents.aio import SearchClient\n",
113+
"from azure.search.documents.models import VectorizableTextQuery, HybridSearch\n",
114+
"from openai import AsyncAzureOpenAI\n",
115+
"from enum import Enum\n",
116+
"from typing import List, Optional\n",
117+
"\n",
118+
"\n",
119+
"def create_openai_client(credential: AsyncTokenCredential) -> AsyncAzureOpenAI:\n",
120+
" token_provider = get_bearer_token_provider(credential, \"https://cognitiveservices.azure.com/.default\")\n",
121+
" return AsyncAzureOpenAI(\n",
122+
" api_version=\"2024-04-01-preview\",\n",
123+
" azure_endpoint=AZURE_OPENAI_ACCOUNT,\n",
124+
" azure_ad_token_provider=token_provider\n",
125+
" )\n",
126+
"\n",
127+
"def create_search_client(credential: AsyncTokenCredential) -> SearchClient:\n",
128+
" return SearchClient(\n",
129+
" endpoint=AZURE_SEARCH_SERVICE,\n",
130+
" index_name=\"hotels-sample-index\",\n",
131+
" credential=credential\n",
132+
" )\n",
133+
"\n",
134+
"# This quickstart is only using text at the moment\n",
135+
"class SearchType(Enum):\n",
136+
" TEXT = \"text\"\n",
137+
" VECTOR = \"vector\"\n",
138+
" HYBRID = \"hybrid\"\n",
139+
"\n",
140+
"# This function retrieves the sselected fields from the search index\n",
141+
"async def get_sources(search_client: SearchClient, query: str, search_type: SearchType, use_semantic_reranker: bool = True, sources_to_include: int = 5, k: int = 50) -> List[str]:\n",
142+
" search_type == SearchType.TEXT,\n",
143+
" response = await search_client.search(\n",
144+
" search_text=query,\n",
145+
" query_type=\"semantic\" if use_semantic_reranker else \"simple\",\n",
146+
" top=sources_to_include,\n",
147+
" select=\"Description,HotelName,Tags\"\n",
148+
" )\n",
149+
"\n",
150+
" return [ document async for document in response ]\n",
151+
"\n",
152+
"GROUNDED_PROMPT=\"\"\"\n",
153+
"You are a friendly assistant that recommends hotels based on activities and amenities.\n",
154+
"Answer the query using only the sources provided below in a friendly and concise bulleted manner.\n",
155+
"Answer ONLY with the facts listed in the list of sources below.\n",
156+
"If there isn't enough information below, say you don't know.\n",
157+
"Do not generate answers that don't use the sources below.\n",
158+
"Query: {query}\n",
159+
"Sources:\\n{sources}\n",
160+
"\"\"\"\n",
161+
"class ChatThread:\n",
162+
" def __init__(self):\n",
163+
" self.messages = []\n",
164+
" self.search_results = []\n",
165+
" \n",
166+
" def append_message(self, role: str, message: str):\n",
167+
" self.messages.append({\n",
168+
" \"role\": role,\n",
169+
" \"content\": message\n",
170+
" })\n",
171+
"\n",
172+
" async def append_grounded_message(self, search_client: SearchClient, query: str, search_type: SearchType, use_semantic_reranker: bool = True, sources_to_include: int = 5, k: int = 50):\n",
173+
" sources = await get_sources(search_client, query, search_type, use_semantic_reranker, sources_to_include, k)\n",
174+
" sources_formatted = \"\\n\".join([f'{document[\"HotelName\"]}:{document[\"Description\"]}:{document[\"Tags\"]}' for document in sources])\n",
175+
" self.append_message(role=\"user\", message=GROUNDED_PROMPT.format(query=query, sources=sources_formatted))\n",
176+
" self.search_results.append(\n",
177+
" {\n",
178+
" \"message_index\": len(self.messages) - 1,\n",
179+
" \"query\": query,\n",
180+
" \"sources\": sources\n",
181+
" }\n",
182+
" )\n",
183+
"\n",
184+
" async def get_openai_response(self, openai_client: AsyncAzureOpenAI, model: str):\n",
185+
" response = await openai_client.chat.completions.create(\n",
186+
" messages=self.messages,\n",
187+
" model=model\n",
188+
" )\n",
189+
" self.append_message(role=\"assistant\", message=response.choices[0].message.content)\n",
190+
"\n",
191+
" def get_last_message(self) -> Optional[object]:\n",
192+
" return self.messages[-1] if len(self.messages) > 0 else None\n",
193+
"\n",
194+
" def get_last_message_sources(self) -> Optional[List[object]]:\n",
195+
" return self.search_results[-1][\"sources\"] if len(self.search_results) > 0 else None"
196+
]
197+
},
198+
{
199+
"cell_type": "code",
200+
"execution_count": 12,
201+
"metadata": {},
202+
"outputs": [
203+
{
204+
"name": "stdout",
205+
"output_type": "stream",
206+
"text": [
207+
"Based on your request, here are a few hotel recommendations with beach access and good views:\n",
208+
"\n",
209+
"1. Ocean Air Motel - oceanfront hotel with a private balcony and indoor and outdoor pools\n",
210+
"2. Marquis Plaza & Suites - offers a view, free Wi-Fi, and a pool\n",
211+
"3. Pull'r Inn Motel - offers a view, a pool, and free Wi-Fi\n",
212+
"\n",
213+
"I hope this helps! Let me know if you need any further assistance.\n"
214+
]
215+
}
216+
],
217+
"source": [
218+
"import azure.identity.aio\n",
219+
"\n",
220+
"chat_thread = ChatThread()\n",
221+
"chat_deployment = AZURE_DEPLOYMENT_MODEL\n",
222+
"\n",
223+
"async with azure.identity.aio.DefaultAzureCredential() as credential, create_search_client(credential) as search_client, create_openai_client(credential) as openai_client:\n",
224+
" await chat_thread.append_grounded_message(\n",
225+
" search_client=search_client,\n",
226+
" query=\"Can you recommend a few hotels near the ocean with beach access and good views\",\n",
227+
" search_type=SearchType(search_type),\n",
228+
" use_semantic_reranker=use_semantic_reranker,\n",
229+
" sources_to_include=sources_to_include,\n",
230+
" k=k)\n",
231+
" await chat_thread.get_openai_response(openai_client=openai_client, model=chat_deployment)\n",
232+
"\n",
233+
"print(chat_thread.get_last_message()[\"content\"])"
234+
]
235+
}
236+
],
237+
"metadata": {
238+
"kernelspec": {
239+
"display_name": ".venv",
240+
"language": "python",
241+
"name": "python3"
242+
},
243+
"language_info": {
244+
"codemirror_mode": {
245+
"name": "ipython",
246+
"version": 3
247+
},
248+
"file_extension": ".py",
249+
"mimetype": "text/x-python",
250+
"name": "python",
251+
"nbconvert_exporter": "python",
252+
"pygments_lexer": "ipython3",
253+
"version": "3.11.3"
254+
}
255+
},
256+
"nbformat": 4,
257+
"nbformat_minor": 2
258+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This repository contains Python code samples used in Azure AI Search documentati
66
|--------|-------------|
77
| quickstart | "Day One" introduction to the fundamental tasks of working with a search index: create, load, and query. This sample is a notebook .ipynb file. The index is modeled on a subset of the Hotels dataset, widely used in Azure AI Search samples, but reduced here for readability and comprehension. |
88
| quickstart-semantic-search | Extends the quickstart through modifications that invoke semantic search. This notebook adds a semantic configuration to the index and semantic query options that formulate the query and response. |
9+
| quickstart-rag | "Day One" introduction to LLM integration with a chat model such as GPT-3.5-turbo or equivalent. |
910
| search-website-functions-v4 | Shows how to create, load, and query a search index in Python using the Azure.Search.Documents library in the Azure SDK for Python. It also includes application code and sample data so that you can see search integration in the context of a full app. The data is from [https://github.com/zygmuntz/goodbooks-10k](https://github.com/zygmuntz/goodbooks-10k). The app is an Azure Static Web app, using the React library for user interaction, and Azure Function to handle the query requests and responses in the application layer. |
1011

1112
## Archived samples

0 commit comments

Comments
 (0)