Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Force the following filetypes to have unix eols, so Windows does not break them
* text eol=lf

# Keep windows files with windows line endings
*.winfile eol=crlf

# Declare files that will always have CRLF line endings on checkout.
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf

# Common files config
*.pdf binary
*.gif binary
*.tif binary
*.ico binary
*.jpg binary
*.jpeg binary
*.png binary
*.vsdx binary
36 changes: 15 additions & 21 deletions 001-customer-support-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,15 @@ This sample is a customer support agent application for the fictional **Contoso

Below is a high-level architecture diagram of the application:

<p align="center">
<img src="images/application.png">
</p>
![Application](./images/application.png)

At the completion of this walkthrough, the application and Radius configuration will look like:

<p align="center">
<img src="images/radius-config.png">
</p>
![Config](./images/radius-config.png)

## 📁 Repository structure

```
```text
├── knowledge-base/ # Contoso policy PDFs for RAG
├── scripts/setup-azure.sh # One-command Azure prerequisite setup
├── src/
Expand Down Expand Up @@ -63,7 +59,7 @@ Before you begin, you need:
>
> - WSL (recommended)
> - Git Bash
> - Azure Cloud Shell
> - [Azure Cloud Shell](https://shell.azure.com/)
>
> `jq` is required for parsing JSON output in Bash. Install it depending on the bash environment. PowerShell users can follow along with minor syntax adjustments (examples provided where needed).

Expand Down Expand Up @@ -91,10 +87,7 @@ Run the setup script to create an Azure resource group, AKS cluster, service pri
**PowerShell**

```powershell

(Get-Content .\scripts\setup-azure.sh -Raw) -replace "`r`n","`n" | Set-Content .\scripts\setup-azure.sh -Encoding UTF8

bash ./scripts/setup-azure.sh --location westus3 --resource-group customer-support-agent --cluster-name customer-support-agent-aks
./scripts/setup-azure.ps1 -Location westus3 -ResourceGroupName customer-support-agent -ClusterName customer-support-agent-aks
```

> [!NOTE]
Expand Down Expand Up @@ -139,7 +132,7 @@ kubectl get pods -n radius-system

You should see all Radius pods running:

```
```text
NAME READY STATUS RESTARTS AGE
applications-rp 1/1 Running 0 1m
bicep-de 1/1 Running 0 1m
Expand Down Expand Up @@ -184,7 +177,7 @@ Verify the credential is registered:
rad credential list
```

```
```text
PROVIDER REGISTERED
azure true
```
Expand Down Expand Up @@ -215,7 +208,7 @@ You can verify the types were created:
rad resource-type list
```

```
```text
TYPE NAMESPACE APIVERSION
Applications.Core/applications Applications.Core ["2023-10-01-preview"]
...
Expand All @@ -236,6 +229,7 @@ rad bicep publish-extension -f radius/types/agent.yaml --target radius/extension
rad bicep publish-extension -f radius/types/postgreSqlDatabases.yaml --target radius/extensions/radiusdata.tgz
rad bicep publish-extension -f radius/types/blobStorages.yaml --target radius/extensions/radiusstorage.tgz
```

</details>

### Step 6: Create the Radius Environment
Expand Down Expand Up @@ -305,7 +299,7 @@ Now lets look at the recipes added to the environment. A Recipe defines *how* to
rad recipe list
```

```
```text
RECIPE TYPE TEMPLATE KIND TEMPLATE
default Radius.AI/agents bicep ghcr.io/radius-project/lab/recipes/agent:1.0
default Radius.Data/postgreSqlDatabases bicep ghcr.io/radius-project/lab/recipes/postgres:1.0
Expand Down Expand Up @@ -451,7 +445,7 @@ The deployment will take approximately 15-20 minutes to complete. In the meantim

When the deployment is complete, you should see output similar to:

```
```text
Deployment In Progress...

Completed contoso-support-agent Applications.Core/applications
Expand Down Expand Up @@ -482,23 +476,23 @@ Here are some things to try that demonstrate the agentic behavior:

**Simple order lookup** (single tool call):

```
```text
What's the status of ORD-10001?
```

The agent will call `lookup_order` and respond with the order details from the database.

**Policy question** (knowledge base search):

```
```text
What's your return policy for electronics?
```

The agent will call `search_knowledge_base` and respond using information from the uploaded PDF documents.

**Multi-step tool chaining** (multiple tool calls in sequence):

```
```text
I want to return the headphones from order ORD-10001. Can you help?
```

Expand All @@ -511,7 +505,7 @@ The agent will:

**Escalation** (knowing when to hand off):

```
```text
This is unacceptable, I've been waiting 3 weeks and nobody can help me. I want to speak to a manager.
```

Expand Down
141 changes: 141 additions & 0 deletions 001-customer-support-agent/scripts/setup-azure.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#Requires -Modules Az.Accounts, Az.Resources, Az.Aks

<#
.SYNOPSIS
Setup Azure prerequisites for the Customer Support Agent.

.DESCRIPTION
Creates: resource group, AKS cluster (if it doesn't exist),
service principal, and registers required resource providers.
Saves service principal credentials to .azure-sp.env for use by Radius.

.PARAMETER Location
Azure region for resources. Default: westus3

.PARAMETER ResourceGroupName
Name of the resource group. Default: customer-support-agent

.PARAMETER ClusterName
Name of the AKS cluster. Default: customer-support-agent-aks

.EXAMPLE
./scripts/setup-azure.ps1
./scripts/setup-azure.ps1 -Location eastus2
./scripts/setup-azure.ps1 -ResourceGroupName my-rg -ClusterName my-aks
#>

[CmdletBinding()]
param(
[string]$ResourceGroupName = 'customer-support-agent',
[string]$Location = 'westus3',
[string]$ClusterName = 'customer-support-agent-aks'
)

$ErrorActionPreference = 'Stop'

$SpName = 'radius-sp'

# ── 0. Ensure Azure PowerShell is logged in ──────────────────
Write-Host '==> Checking Azure PowerShell login...'
$context = Get-AzContext
if (-not $context) {
Write-Host ' Not logged in. Opening browser for login...'
Connect-AzAccount
$context = Get-AzContext
}

$SubscriptionId = $context.Subscription.Id
Write-Host "Subscription: $SubscriptionId"
Write-Host "Resource Group: $ResourceGroupName"
Write-Host "Location: $Location"
Write-Host "AKS Cluster: $ClusterName"
Write-Host ''

# ── 1. Register resource providers ────────────────────────────
Write-Host '==> Registering resource providers...'
$providers = @(
'Microsoft.Storage'
'Microsoft.DBforPostgreSQL'
'Microsoft.ContainerInstance'
'Microsoft.OperationalInsights'
'Microsoft.Search'
'Microsoft.CognitiveServices'
)
foreach ($provider in $providers) {
Register-AzResourceProvider -ProviderNamespace $provider | Out-Null
Write-Host " ✓ $provider"
}

# ── 2. Create resource group ─────────────────────────────────
Write-Host ''
Write-Host "==> Creating resource group '$ResourceGroupName' in '$Location'..."
New-AzResourceGroup -Name $ResourceGroupName -Location $Location -Force | Out-Null

# ── 3. Create or connect to AKS cluster ──────────────────────
Write-Host ''
$existingCluster = Get-AzAksCluster -ResourceGroupName $ResourceGroupName -Name $ClusterName -ErrorAction SilentlyContinue
if ($existingCluster) {
Write-Host "==> AKS cluster '$ClusterName' already exists, getting credentials..."
Import-AzAksCredential `
-ResourceGroupName $ResourceGroupName `
-Name $ClusterName `
-Force
}
else {
Write-Host "==> Creating AKS cluster '$ClusterName' (this takes a few minutes)..."
$sshKey = Join-Path -Path $HOME -ChildPath '.ssh/id_rsa.pub'
$aksParams = @{
ResourceGroupName = $ResourceGroupName
Name = $ClusterName
Location = $Location
NodeCount = 1
NodeVmSize = 'Standard_D2s_v3'
}
if (Test-Path $sshKey) {
$aksParams['SshKeyValue'] = $sshKey
}
else {
$aksParams['GenerateSshKey'] = $true
}
New-AzAksCluster @aksParams

Write-Host '==> Getting AKS credentials...'
Import-AzAksCredential `
-ResourceGroupName $ResourceGroupName `
-Name $ClusterName `
-Force
}

# ── 4. Create service principal ──────────────────────────────
Write-Host ''
Write-Host "==> Creating service principal '$SpName' with Owner role..."
$scope = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName"

# Create the AD application and service principal with a password credential
$sp = New-AzADServicePrincipal `
-DisplayName $SpName `
-Role 'Owner' `
-Scope $scope

$ClientId = $sp.AppId
$ClientSecret = $sp.PasswordCredentials.SecretText
$TenantId = $context.Tenant.Id

# ── 5. Save credentials to file ──────────────────────────────
$envFile = '.azure-sp.env'
@"
AZURE_CLIENT_ID=$ClientId
AZURE_CLIENT_SECRET=$ClientSecret
AZURE_TENANT_ID=$TenantId
AZURE_SUBSCRIPTION_ID=$SubscriptionId
AZURE_RESOURCE_GROUP=$ResourceGroupName
"@ | Set-Content -Path $envFile -NoNewline

Write-Host ''
Write-Host "==> Service principal credentials saved to $envFile"

Write-Host ''
Write-Host '================================================'
Write-Host ' Azure setup complete!'
Write-Host '================================================'
Write-Host ''
Loading