Welcome to Part 3 of our Azure AI Foundry agent series! In Parts 1 and 2, we set up our project and built an intelligent agent in the playground. Now it’s time to write production code. In this tutorial, you’ll learn how to implement your agent using Python, Node.js, or C# with proper error handling, logging, and best practices.
By the end of this article, you’ll have production-ready code that you can deploy anywhere. Let’s transform your playground agent into professional software!
What We’re Building Today
In this tutorial, we’ll implement:
- Local Development Setup: Create a proper project structure
- Production Code: Write robust agent implementations in Python, Node.js, and C#
- Error Handling: Implement retry logic and graceful failures
- Configuration Management: Use environment variables and secure settings
- Logging: Add comprehensive logging for debugging
- Testing: Write tests to validate agent behavior
Step 1: Setting Up Local Development
Prerequisites
- Python 3.9+ OR Node.js 18+ OR .NET 8+
- Azure CLI installed
- VS Code recommended
- Git for version control
Project Structure
customer-support-agent/
├── .env.example
├── .gitignore
├── README.md
├── requirements.txt (Python)
├── package.json (Node.js)
├── Program.cs (C#)
└── src/
├── config.py/js/cs
├── agent.py/js/cs
└── main.py/js/cs
Step 2: Python Implementation
Setup Python Environment
# Create project
mkdir customer-support-agent
cd customer-support-agent
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install azure-ai-projects azure-identity python-dotenv
Configuration (config.py)
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
PROJECT_ENDPOINT = os.getenv("PROJECT_ENDPOINT")
MODEL_DEPLOYMENT_NAME = os.getenv("MODEL_DEPLOYMENT_NAME", "gpt-4o-mini")
MAX_RETRIES = 3
RETRY_DELAY = 2
AGENT_INSTRUCTIONS = """You are a helpful customer support agent.
Always search the knowledge base before answering.
Provide accurate information with citations.
Maintain a professional, friendly tone."""
@classmethod
def validate(cls):
if not cls.PROJECT_ENDPOINT:
raise ValueError("PROJECT_ENDPOINT is required")
Config.validate()
Agent Implementation (agent.py)
import time
import logging
from typing import Dict, Optional
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from config import Config
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class CustomerSupportAgent:
def __init__(self):
self.credential = DefaultAzureCredential()
self.client = AIProjectClient(
endpoint=Config.PROJECT_ENDPOINT,
credential=self.credential
)
self.agent_id = None
self.thread_id = None
logger.info("Agent initialized")
def create_agent(self) -> str:
try:
agent = self.client.agents.create_agent(
model=Config.MODEL_DEPLOYMENT_NAME,
name="customer-support-agent",
instructions=Config.AGENT_INSTRUCTIONS,
tools=[{"type": "file_search"}]
)
self.agent_id = agent.id
logger.info(f"Created agent: {self.agent_id}")
return self.agent_id
except Exception as e:
logger.error(f"Error creating agent: {e}")
raise
def create_thread(self) -> str:
try:
thread = self.client.agents.create_thread()
self.thread_id = thread.id
logger.info(f"Created thread: {self.thread_id}")
return self.thread_id
except Exception as e:
logger.error(f"Error creating thread: {e}")
raise
def send_message(self, message: str) -> Dict:
if not self.thread_id:
self.create_thread()
retries = 0
while retries < Config.MAX_RETRIES:
try:
# Create message
self.client.agents.create_message(
thread_id=self.thread_id,
role="user",
content=message
)
# Run agent
run = self.client.agents.create_and_process_run(
thread_id=self.thread_id,
assistant_id=self.agent_id
)
# Wait for completion
while run.status in ["queued", "in_progress"]:
time.sleep(1)
run = self.client.agents.get_run(
thread_id=self.thread_id,
run_id=run.id
)
if run.status == "failed":
raise Exception(f"Run failed: {run.last_error}")
# Get response
messages = self.client.agents.list_messages(
thread_id=self.thread_id
)
response = messages.data[0].content[0].text.value
logger.info("Message processed successfully")
return {
'response': response,
'status': 'success',
'thread_id': self.thread_id
}
except Exception as e:
retries += 1
logger.warning(f"Attempt {retries} failed: {e}")
if retries >= Config.MAX_RETRIES:
return {
'response': "Error processing request",
'status': 'error',
'error': str(e)
}
time.sleep(Config.RETRY_DELAY * retries)
Main Application (main.py)
from agent import CustomerSupportAgent
def main():
agent = CustomerSupportAgent()
agent.create_agent()
agent.create_thread()
print("Customer Support Agent Ready!")
print("Type 'quit' to exit\n")
while True:
user_input = input("You: ")
if user_input.lower() == 'quit':
break
result = agent.send_message(user_input)
if result['status'] == 'success':
print(f"\nAgent: {result['response']}\n")
else:
print(f"\nError: {result.get('error')}\n")
if __name__ == "__main__":
main()
Environment File (.env)
PROJECT_ENDPOINT=https://your-resource.services.ai.azure.com/api/projects/your-project
MODEL_DEPLOYMENT_NAME=gpt-4o-mini
Step 3: Node.js Implementation
Setup Node.js Project
# Initialize project
npm init -y
# Install dependencies
npm install @azure/ai-projects @azure/identity dotenv
Configuration (config.js)
require('dotenv').config();
class Config {
static PROJECT_ENDPOINT = process.env.PROJECT_ENDPOINT;
static MODEL_DEPLOYMENT_NAME = process.env.MODEL_DEPLOYMENT_NAME || 'gpt-4o-mini';
static MAX_RETRIES = 3;
static RETRY_DELAY = 2000;
static AGENT_INSTRUCTIONS = `You are a helpful customer support agent.
Always search the knowledge base before answering.
Provide accurate information with citations.
Maintain a professional, friendly tone.`;
static validate() {
if (!this.PROJECT_ENDPOINT) {
throw new Error('PROJECT_ENDPOINT is required');
}
}
}
Config.validate();
module.exports = Config;
Agent Implementation (agent.js)
const { AIProjectClient } = require('@azure/ai-projects');
const { DefaultAzureCredential } = require('@azure/identity');
const Config = require('./config');
class CustomerSupportAgent {
constructor() {
this.credential = new DefaultAzureCredential();
this.client = new AIProjectClient(
Config.PROJECT_ENDPOINT,
this.credential
);
this.agentId = null;
this.threadId = null;
}
async createAgent() {
try {
const agent = await this.client.agents.createAgent({
model: Config.MODEL_DEPLOYMENT_NAME,
name: 'customer-support-agent',
instructions: Config.AGENT_INSTRUCTIONS,
tools: [{ type: 'file_search' }]
});
this.agentId = agent.id;
console.log(`Created agent: ${this.agentId}`);
return this.agentId;
} catch (error) {
console.error('Error creating agent:', error);
throw error;
}
}
async createThread() {
try {
const thread = await this.client.agents.createThread();
this.threadId = thread.id;
console.log(`Created thread: ${this.threadId}`);
return this.threadId;
} catch (error) {
console.error('Error creating thread:', error);
throw error;
}
}
async sendMessage(message) {
if (!this.threadId) {
await this.createThread();
}
let retries = 0;
while (retries < Config.MAX_RETRIES) {
try {
// Create message
await this.client.agents.createMessage(
this.threadId,
{ role: 'user', content: message }
);
// Run agent
let run = await this.client.agents.createRun(
this.threadId,
{ assistantId: this.agentId }
);
// Wait for completion
while (['queued', 'in_progress'].includes(run.status)) {
await new Promise(resolve => setTimeout(resolve, 1000));
run = await this.client.agents.getRun(this.threadId, run.id);
}
if (run.status === 'failed') {
throw new Error(`Run failed: ${run.lastError}`);
}
// Get response
const messages = await this.client.agents.listMessages(this.threadId);
const response = messages.data[0].content[0].text.value;
return {
response,
status: 'success',
threadId: this.threadId
};
} catch (error) {
retries++;
console.warn(`Attempt ${retries} failed:`, error.message);
if (retries >= Config.MAX_RETRIES) {
return {
response: 'Error processing request',
status: 'error',
error: error.message
};
}
await new Promise(resolve =>
setTimeout(resolve, Config.RETRY_DELAY * retries)
);
}
}
}
}
module.exports = CustomerSupportAgent;
Main Application (main.js)
const readline = require('readline');
const CustomerSupportAgent = require('./agent');
async function main() {
const agent = new CustomerSupportAgent();
await agent.createAgent();
await agent.createThread();
console.log('Customer Support Agent Ready!');
console.log("Type 'quit' to exit\n");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const askQuestion = () => {
rl.question('You: ', async (input) => {
if (input.toLowerCase() === 'quit') {
rl.close();
return;
}
const result = await agent.sendMessage(input);
if (result.status === 'success') {
console.log(`\nAgent: ${result.response}\n`);
} else {
console.log(`\nError: ${result.error}\n`);
}
askQuestion();
});
};
askQuestion();
}
main().catch(console.error);
Step 4: C# Implementation
Setup C# Project
# Create project
dotnet new console -n CustomerSupportAgent
cd CustomerSupportAgent
# Add packages
dotnet add package Azure.AI.Projects --prerelease
dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
Configuration (appsettings.json)
{
"Azure": {
"ProjectEndpoint": "",
"ModelDeploymentName": "gpt-4o-mini"
},
"Agent": {
"MaxRetries": 3,
"RetryDelaySeconds": 2
}
}
Agent Implementation (Program.cs)
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
var projectEndpoint = configuration["Azure:ProjectEndpoint"];
var modelName = configuration["Azure:ModelDeploymentName"];
var credential = new DefaultAzureCredential();
var client = new AIProjectClient(new Uri(projectEndpoint), credential);
Console.WriteLine("Creating agent...");
var agent = await client.Agents.CreateAgentAsync(
model: modelName,
name: "customer-support-agent",
instructions: @"You are a helpful customer support agent.
Always search the knowledge base before answering.
Provide accurate information with citations.
Maintain a professional, friendly tone.",
tools: new[] { new FileSearchToolDefinition() }
);
Console.WriteLine($"Agent created: {agent.Value.Id}");
var thread = await client.Agents.CreateThreadAsync();
Console.WriteLine($"Thread created: {thread.Value.Id}");
Console.WriteLine("\nCustomer Support Agent Ready!");
Console.WriteLine("Type 'quit' to exit\n");
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
if (input?.ToLower() == "quit")
break;
try
{
await client.Agents.CreateMessageAsync(
thread.Value.Id,
MessageRole.User,
input
);
var run = await client.Agents.CreateRunAsync(
thread.Value.Id,
agent.Value.Id
);
while (run.Value.Status == RunStatus.Queued ||
run.Value.Status == RunStatus.InProgress)
{
await Task.Delay(1000);
run = await client.Agents.GetRunAsync(thread.Value.Id, run.Value.Id);
}
if (run.Value.Status == RunStatus.Failed)
{
Console.WriteLine($"\nError: {run.Value.LastError}\n");
continue;
}
var messages = await client.Agents.GetMessagesAsync(thread.Value.Id);
var response = messages.Value.Data[0].Content[0].Text.Value;
Console.WriteLine($"\nAgent: {response}\n");
}
catch (Exception ex)
{
Console.WriteLine($"\nError: {ex.Message}\n");
}
}
Step 5: Running Your Agent Locally
Python
# Authenticate with Azure
az login
# Run the agent
python src/main.py
Node.js
# Authenticate with Azure
az login
# Run the agent
node src/main.js
C#
# Authenticate with Azure
az login
# Run the agent
dotnet run
Step 6: Testing Your Agent
Test your agent with these queries:
You: What is your return policy?
Agent: [Response with citation from knowledge base]
You: How long does shipping take?
Agent: [Response with shipping information]
You: Can I change my subscription?
Agent: [Response about subscription changes]
Common Issues and Solutions
Authentication Errors:
# Ensure you're logged in to Azure
az login
# Check your account
az account show
Missing Environment Variables:
Verify your .env
file contains the correct PROJECT_ENDPOINT
from Azure AI Foundry portal.
Agent Not Finding Information:
Ensure you uploaded files to the vector store in Part 2 and the agent has access to the File Search tool.
Key Concepts Recap
- Configuration Management: Use environment variables for sensitive data
- Error Handling: Implement retry logic for network failures
- Logging: Add comprehensive logging for debugging
- DefaultAzureCredential: Use managed authentication instead of API keys
- Async/Await: Handle asynchronous operations properly
- Thread Management: Maintain conversation context across messages
What’s Next in Part 4?
In the final part of this series, we’ll cover deployment and advanced topics:
- Containerizing your application with Docker
- Deploying to Azure Container Apps
- Setting up CI/CD with GitHub Actions
- Implementing monitoring with Application Insights
- Security best practices and managed identities
- Cost optimization strategies
- Performance tuning and scaling
We’ll take your local agent and deploy it to production.
References
- Microsoft Learn – Azure AI Foundry SDK (https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/develop/sdk-overview)
- Microsoft Learn – Agent Service Quickstart (https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart)
- GitHub – Azure AI Projects Python SDK (https://learn.microsoft.com/en-us/python/api/overview/azure/ai-projects-readme)
- Microsoft Learn – Build AI Agents Workshop (https://microsoft.github.io/build-your-first-agent-with-azure-ai-agent-service-workshop/getting-started/)
This article is Part 3 of a 4-part series on building AI agents with Azure AI Foundry in 2025.