Skip to content

Commit a64bfd4

Browse files
authored
Merge pull request #41 from nick863/nirovins/add_index
Add possibility to exploit index search instead of file
2 parents 74bb552 + a5452f0 commit a64bfd4

15 files changed

+2061
-146
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,20 @@ git clone https://github.com/Azure-Samples/get-started-with-ai-agents.git
5050
```
5151
At this point you could make changes to the code if required. However, no changes are needed to deploy and test the app as shown in the next step.
5252

53-
#### Configure your Agent (optional)
53+
#### How to configure Agent model and version
5454
<!-- TODO where do we want this? probably after downloading the code -->
55-
If you want to personalize your agent, you can change the default configuration for your agent. Additional details on changing your agent can be found in [customizing model deployments](docs/deploy_customization.md#customizing-model-deployments). For more information on the Azure OpenAI models and non-Microsoft models that can be used in your deployment, view the [list of models supported by Azure AI Agent Service](https://learn.microsoft.com/azure/ai-services/agents/concepts/model-region-support).
55+
By default, the template uses model `gpt-4o-mini`, version `2024-07-18` for text generation and `text-embedding-3-small` version `1` for embeddings. If you want to personalize your agent, you can change the default configuration for your agent. Additional details on changing your agent can be found in [customizing model deployments](docs/deploy_customization.md#customizing-model-deployments). For more information on the Azure OpenAI models and non-Microsoft models that can be used in your deployment, view the [list of models supported by Azure AI Agent Service](https://learn.microsoft.com/azure/ai-services/agents/concepts/model-region-support).
5656

5757
To specify the model (e.g. gpt-4o-mini, gpt-4o) that is deployed for the agent when `azd up` is called, set the following environment variables:
5858
```shell
5959
azd env set AZURE_AI_AGENT_MODEL_NAME <MODEL_NAME>
6060
azd env set AZURE_AI_AGENT_MODEL_VERSION <MODEL_VERSION>
6161
```
6262

63+
#### How to configure Agent knowledge retrieval
64+
By default, the template deploys OpenAI's [file search](https://learn.microsoft.com/azure/ai-services/agents/how-to/tools/file-search?tabs=python&pivots=overview) for agent's knowledge retrieval. An agent also can perform search using the search index, deployed in Azure AI Search resource. The semantic index search represents so-called hybrid search i.e. it uses LLM to search for the relevant context in the provided index as well as embedding similarity search. This index is built from the `embeddings.csv` file, containing the embeddings vectors, followed by the contexts.
65+
To use index search, please set the local environment variable `USE_AZURE_AI_SEARCH_SERVICE` to `true` during the `azd up` command. In this case the Azure AI Search resource will be deployed and used.
66+
6367
#### Logging
6468
If you want to enable logging to a file, uncomment the following line in Dockerfile located in the src directory:
6569

azure.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,6 @@ pipeline:
5656
- AZURE_AI_EMBED_MODEL_NAME
5757
- AZURE_AI_EMBED_MODEL_FORMAT
5858
- AZURE_AI_EMBED_MODEL_VERSION
59+
- AZURE_AI_EMBED_DIMENSIONS
60+
- AZURE_AI_SEARCH_INDEX_NAME
5961
- AZURE_EXISTING_AIPROJECT_CONNECTION_STRING

infra/api.bicep

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ param serviceName string = 'api'
99
param exists bool
1010
param projectConnectionString string
1111
param agentDeploymentName string
12+
param searchConnectionName string
13+
param embeddingDeploymentName string
14+
param aiSearchIndexName string
15+
param embeddingDeploymentDimensions string
16+
param searchServiceEndpoint string
1217
param agentName string
1318
param agentID string
1419

@@ -38,10 +43,30 @@ var env = [
3843
name: 'AZURE_AI_AGENT_DEPLOYMENT_NAME'
3944
value: agentDeploymentName
4045
}
46+
{
47+
name: 'AZURE_AI_EMBED_DEPLOYMENT_NAME'
48+
value: embeddingDeploymentName
49+
}
50+
{
51+
name: 'AZURE_AI_SEARCH_INDEX_NAME'
52+
value: aiSearchIndexName
53+
}
54+
{
55+
name: 'AZURE_AI_EMBED_DIMENSIONS'
56+
value: embeddingDeploymentDimensions
57+
}
4158
{
4259
name: 'RUNNING_IN_PRODUCTION'
4360
value: 'true'
4461
}
62+
{
63+
name: 'AZURE_AI_SEARCH_CONNECTION_NAME'
64+
value: searchConnectionName
65+
}
66+
{
67+
name: 'AZURE_AI_SEARCH_ENDPOINT'
68+
value: searchServiceEndpoint
69+
}
4570
]
4671

4772
module app 'core/host/container-app-upsert.bicep' = {

infra/core/ai/hub-dependencies.bicep

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ module searchService '../search/search-services.bicep' =
142142
location: location
143143
tags: tags
144144
name: searchServiceName
145+
semanticSearch: 'free'
146+
authOptions: { aadOrApiKey: { aadAuthFailureMode: 'http401WithBearerChallenge'}}
145147
}
146148
}
147149

infra/main.bicep

Lines changed: 137 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ param keyVaultName string = ''
6262
param searchServiceName string = ''
6363
@description('The Azure Search connection name. If ommited will use a default value')
6464
param searchConnectionName string = ''
65+
@description('The search index name')
66+
param aiSearchIndexName string = ''
6567
@description('The Azure Storage Account resource name. If ommited will be generated')
6668
param storageAccountName string = ''
6769
@description('The log analytics workspace name. If ommited will be generated')
@@ -103,7 +105,9 @@ param embedModelFormat string
103105
@description('Name of the embedding model to deploy')
104106
param embedModelName string
105107
@description('Name of the embedding model deployment')
106-
param embedDeploymentName string
108+
param embeddingDeploymentName string
109+
@description('Embedding model dimensionality')
110+
param embeddingDeploymentDimensions string
107111

108112
@description('Version of the embedding model to deploy')
109113
// See version availability in this table:
@@ -121,16 +125,20 @@ param embedDeploymentCapacity int
121125

122126
param useContainerRegistry bool = true
123127
param useApplicationInsights bool = true
124-
param useSearch bool = false
128+
@description('Do we want to use the Azure AI Search')
129+
param useSearchService bool = false
130+
131+
@description('Random seed to be used during generation of new resources suffixes.')
132+
param seed string = newGuid()
125133

126134
var abbrs = loadJsonContent('./abbreviations.json')
127-
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
135+
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location, seed))
128136
var projectName = !empty(aiProjectName) ? aiProjectName : 'ai-project-${resourceToken}'
129137
var tags = { 'azd-env-name': environmentName }
130138

131139
var agentID = !empty(aiAgentID) ? aiAgentID : ''
132140

133-
var aiDeployments = [
141+
var aiChatModel = [
134142
{
135143
name: agentDeploymentName
136144
model: {
@@ -143,8 +151,10 @@ var aiDeployments = [
143151
capacity: agentDeploymentCapacity
144152
}
145153
}
154+
]
155+
var aiEmbeddingModel = [
146156
{
147-
name: embedDeploymentName
157+
name: embeddingDeploymentName
148158
model: {
149159
format: embedModelFormat
150160
name: embedModelName
@@ -157,6 +167,10 @@ var aiDeployments = [
157167
}
158168
]
159169

170+
var aiDeployments = concat(
171+
aiChatModel,
172+
useSearchService ? aiEmbeddingModel : [])
173+
160174
//for container and app api
161175
param apiAppExists bool = false
162176

@@ -173,6 +187,10 @@ var logAnalyticsWorkspaceResolvedName = !useApplicationInsights
173187
? logAnalyticsWorkspaceName
174188
: '${abbrs.operationalInsightsWorkspaces}${resourceToken}'
175189

190+
var resolvedSearchServiceName = !useSearchService
191+
? ''
192+
: !empty(searchServiceName) ? searchServiceName : '${abbrs.searchSearchServices}${resourceToken}'
193+
176194
var containerRegistryResolvedName = !useContainerRegistry
177195
? ''
178196
: !empty(containerRegistryName) ? containerRegistryName : '${abbrs.containerRegistryRegistries}${resourceToken}'
@@ -197,15 +215,17 @@ module ai 'core/host/ai-environment.bicep' = if (empty(aiExistingProjectConnecti
197215
? ''
198216
: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}'
199217
containerRegistryName: containerRegistryResolvedName
200-
searchServiceName: !useSearch
201-
? ''
202-
: !empty(searchServiceName) ? searchServiceName : '${abbrs.searchSearchServices}${resourceToken}'
203-
searchConnectionName: !useSearch
218+
searchServiceName: resolvedSearchServiceName
219+
searchConnectionName: !useSearchService
204220
? ''
205221
: !empty(searchConnectionName) ? searchConnectionName : 'search-service-connection'
206222
}
207223
}
208224

225+
var searchServiceEndpoint = !useSearchService
226+
? ''
227+
: ai.outputs.searchServiceEndpoint
228+
209229
// If bringing an existing AI project, set up the log analytics workspace here
210230
module logAnalytics 'core/monitor/loganalytics.bicep' = if (!empty(aiExistingProjectConnectionString)) {
211231
name: 'logAnalytics'
@@ -222,6 +242,75 @@ var projectConnectionString = empty(hostName)
222242
? aiExistingProjectConnectionString
223243
: '${hostName};${subscription().subscriptionId};${rg.name};${projectName}'
224244

245+
var resolvedApplicationInsightsName = !useApplicationInsights || !empty(aiExistingProjectConnectionString)
246+
? ''
247+
: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}'
248+
249+
module monitoringMetricsContribuitorRoleAzureAIDeveloperRG 'core/security/appinsights-access.bicep' = if (!empty(resolvedApplicationInsightsName)) {
250+
name: 'monitoringmetricscontributor-role-azureai-developer-rg'
251+
scope: rg
252+
params: {
253+
appInsightsName: resolvedApplicationInsightsName
254+
principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
255+
}
256+
}
257+
258+
resource existingProjectRG 'Microsoft.Resources/resourceGroups@2021-04-01' existing = if (!empty(aiExistingProjectConnectionString)) {
259+
name: split(aiExistingProjectConnectionString, ';')[2]
260+
}
261+
262+
module userRoleAzureAIDeveloperBackendExistingProjectRG 'core/security/role.bicep' = if (!empty(aiExistingProjectConnectionString)) {
263+
name: 'backend-role-azureai-developer-existing-project-rg'
264+
scope: existingProjectRG
265+
params: {
266+
principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
267+
roleDefinitionId: '64702f94-c441-49e6-a78b-ef80e0188fee'
268+
}
269+
}
270+
271+
//Container apps host and api
272+
// Container apps host (including container registry)
273+
module containerApps 'core/host/container-apps.bicep' = {
274+
name: 'container-apps'
275+
scope: rg
276+
params: {
277+
name: 'app'
278+
location: location
279+
tags: tags
280+
containerAppsEnvironmentName: 'containerapps-env-${resourceToken}'
281+
containerRegistryName: empty(aiExistingProjectConnectionString)
282+
? ai.outputs.containerRegistryName
283+
: containerRegistryResolvedName
284+
logAnalyticsWorkspaceName: empty(aiExistingProjectConnectionString)
285+
? ai.outputs.logAnalyticsWorkspaceName
286+
: logAnalytics.outputs.name
287+
}
288+
}
289+
290+
// API app
291+
module api 'api.bicep' = {
292+
name: 'api'
293+
scope: rg
294+
params: {
295+
name: 'ca-api-${resourceToken}'
296+
location: location
297+
tags: tags
298+
identityName: '${abbrs.managedIdentityUserAssignedIdentities}api-${resourceToken}'
299+
containerAppsEnvironmentName: containerApps.outputs.environmentName
300+
containerRegistryName: containerApps.outputs.registryName
301+
projectConnectionString: projectConnectionString
302+
agentDeploymentName: agentDeploymentName
303+
searchConnectionName: searchConnectionName
304+
aiSearchIndexName: aiSearchIndexName
305+
searchServiceEndpoint: searchServiceEndpoint
306+
embeddingDeploymentName: embeddingDeploymentName
307+
embeddingDeploymentDimensions: embeddingDeploymentDimensions
308+
agentName: agentName
309+
agentID: agentID
310+
exists: apiAppExists
311+
}
312+
}
313+
225314
module userAcrRolePush 'core/security/role.bicep' = if (!empty(principalId)) {
226315
name: 'user-role-acr-push'
227316
scope: rg
@@ -267,76 +356,66 @@ module userRoleAzureAIDeveloper 'core/security/role.bicep' = if (!empty(principa
267356
}
268357
}
269358

270-
module backendRoleAzureAIDeveloperRG 'core/security/role.bicep' = {
271-
name: 'backend-role-azureai-developer-rg'
359+
module backendRoleSearchIndexDataContributorRG 'core/security/role.bicep' = if (useSearchService) {
360+
name: 'backend-role-azure-index-data-contributor-rg'
272361
scope: rg
273362
params: {
274363
principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
275-
roleDefinitionId: '64702f94-c441-49e6-a78b-ef80e0188fee'
364+
roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7'
276365
}
277366
}
278367

279-
var resolvedApplicationInsightsName = !useApplicationInsights || !empty(aiExistingProjectConnectionString)
280-
? ''
281-
: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}'
368+
module backendRoleSearchIndexDataReaderRG 'core/security/role.bicep' = if (useSearchService) {
369+
name: 'backend-role-azure-index-data-reader-rg'
370+
scope: rg
371+
params: {
372+
principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
373+
roleDefinitionId: '1407120a-92aa-4202-b7e9-c0e197c71c8f'
374+
}
375+
}
282376

283-
module monitoringMetricsContribuitorRoleAzureAIDeveloperRG 'core/security/appinsights-access.bicep' = if (!empty(resolvedApplicationInsightsName)) {
284-
name: 'monitoringmetricscontributor-role-azureai-developer-rg'
377+
module backendRoleSearchServiceContributorRG 'core/security/role.bicep' = if (useSearchService) {
378+
name: 'backend-role-azure-search-service-contributor-rg'
285379
scope: rg
286380
params: {
287-
appInsightsName: resolvedApplicationInsightsName
288381
principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
382+
roleDefinitionId: '7ca78c08-252a-4471-8644-bb5ff32d4ba0'
289383
}
290384
}
291385

292-
resource existingProjectRG 'Microsoft.Resources/resourceGroups@2021-04-01' existing = if (!empty(aiExistingProjectConnectionString)) {
293-
name: split(aiExistingProjectConnectionString, ';')[2]
386+
module userRoleSearchIndexDataContributorRG 'core/security/role.bicep' = if (useSearchService && !empty(principalId)) {
387+
name: 'user-role-azure-index-data-contributor-rg'
388+
scope: rg
389+
params: {
390+
principalId: principalId
391+
roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7'
392+
}
294393
}
295394

296-
module userRoleAzureAIDeveloperBackendExistingProjectRG 'core/security/role.bicep' = if (!empty(aiExistingProjectConnectionString)) {
297-
name: 'backend-role-azureai-developer-existing-project-rg'
298-
scope: existingProjectRG
395+
module userRoleSearchIndexDataReaderRG 'core/security/role.bicep' = if (useSearchService && !empty(principalId)) {
396+
name: 'user-role-azure-index-data-reader-rg'
397+
scope: rg
299398
params: {
300-
principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
301-
roleDefinitionId: '64702f94-c441-49e6-a78b-ef80e0188fee'
399+
principalId: principalId
400+
roleDefinitionId: '1407120a-92aa-4202-b7e9-c0e197c71c8f'
302401
}
303402
}
304403

305-
//Container apps host and api
306-
// Container apps host (including container registry)
307-
module containerApps 'core/host/container-apps.bicep' = {
308-
name: 'container-apps'
404+
module userRoleSearchServiceContributorRG 'core/security/role.bicep' = if (useSearchService && !empty(principalId)) {
405+
name: 'user-role-azure-search-service-contributor-rg'
309406
scope: rg
310407
params: {
311-
name: 'app'
312-
location: location
313-
tags: tags
314-
containerAppsEnvironmentName: 'containerapps-env-${resourceToken}'
315-
containerRegistryName: empty(aiExistingProjectConnectionString)
316-
? ai.outputs.containerRegistryName
317-
: containerRegistryResolvedName
318-
logAnalyticsWorkspaceName: empty(aiExistingProjectConnectionString)
319-
? ai.outputs.logAnalyticsWorkspaceName
320-
: logAnalytics.outputs.name
408+
principalId: principalId
409+
roleDefinitionId: '7ca78c08-252a-4471-8644-bb5ff32d4ba0'
321410
}
322411
}
323412

324-
// API app
325-
module api 'api.bicep' = {
326-
name: 'api'
413+
module backendRoleAzureAIDeveloperRG 'core/security/role.bicep' = {
414+
name: 'backend-role-azureai-developer-rg'
327415
scope: rg
328416
params: {
329-
name: 'ca-api-${resourceToken}'
330-
location: location
331-
tags: tags
332-
identityName: '${abbrs.managedIdentityUserAssignedIdentities}api-${resourceToken}'
333-
containerAppsEnvironmentName: containerApps.outputs.environmentName
334-
containerRegistryName: containerApps.outputs.registryName
335-
projectConnectionString: projectConnectionString
336-
agentDeploymentName: agentDeploymentName
337-
agentName: agentName
338-
agentID: agentID
339-
exists: apiAppExists
417+
principalId: api.outputs.SERVICE_API_IDENTITY_PRINCIPAL_ID
418+
roleDefinitionId: '64702f94-c441-49e6-a78b-ef80e0188fee'
340419
}
341420
}
342421

@@ -346,6 +425,11 @@ output AZURE_RESOURCE_GROUP string = rg.name
346425
output AZURE_TENANT_ID string = tenant().tenantId
347426
output AZURE_AIPROJECT_CONNECTION_STRING string = projectConnectionString
348427
output AZURE_AI_AGENT_DEPLOYMENT_NAME string = agentDeploymentName
428+
output AZURE_AI_SEARCH_CONNECTION_NAME string = searchConnectionName
429+
output AZURE_AI_EMBED_DEPLOYMENT_NAME string = embeddingDeploymentName
430+
output AZURE_AI_SEARCH_INDEX_NAME string = aiSearchIndexName
431+
output AZURE_AI_SEARCH_ENDPOINT string = searchServiceEndpoint
432+
output AZURE_AI_EMBED_DIMENSIONS string = embeddingDeploymentDimensions
349433
output AZURE_AI_AGENT_NAME string = agentName
350434
output AZURE_AI_AGENT_ID string = agentID
351435

0 commit comments

Comments
 (0)