Aspire and Agent Framework: Building Distributed Agentic Systems

Learn how Aspire orchestrates distributed multi-agent systems. Deep dive into the AlpineAI demo architecture with Microsoft Agent Framework.

0

Why Agentic Apps Demand Distributed Systems Infrastructure

When you start building with Microsoft Agent Framework, the first agents feel simple. A single agent, a local LLM call, maybe a database query. But the moment you need multiple specialized agents talking to each other, voice interfaces, real-time telemetry, and everything running across containers in Azure, you realize you are not just building an AI application anymore. You are building a distributed system. And distributed systems need proper tooling.

The AlpineAI ski resort demo makes this crystal clear. It is not a single agent solving one problem. It is a choreography of specialist agents, each with its own responsibility, each deployed as a separate service, all coordinated through a unified infrastructure layer. That layer is Aspire.

Understanding the AlpineAI Architecture

Let me walk through how AlpineAI is structured, because the architecture follows directly from the business logic.

AlpineAI is a ski resort concierge system. Guests interact through a voice interface. Behind that interface is a hosted advisor agent that understands natural language requests like “What are the conditions today?” or “Which slopes are open?” The advisor does not do everything itself. It delegates to specialist agents: one provides weather intelligence, another manages lift status and traffic, a third evaluates safety conditions, and a fourth offers personalized coaching. Each specialist is a Microsoft Agent Framework agent, implemented as its own service. The advisor composes these specialists as tools.

There is also a Foundry prompt agent that handles general skiing research backed by web search. Voice connections flow through Azure AI Voice Live via WebSocket. All the live resort telemetry is generated by a data service. The frontend is a separate React application. And every service, every data source, every external connection is declared once in the Aspire AppHost.

The AppHost Model: One Place to See Everything

This is where Aspire shifts your thinking. Instead of scattering service definitions across deployment manifests, environment configs, and container orchestration files, you declare the entire application topology in one place: the AppHost.

In the AlpineAI demo, the AppHost conceptually looks like this:

var builder = DistributedApplication.CreateBuilder(args);

// Data generator for live telemetry
var dataGenerator = builder.AddProject("data-generator");

// Specialist agents (each is a service)
var weatherAgent = builder.AddProject("weather-agent")
    .WithReference(dataGenerator);

var liftAgent = builder.AddProject("lift-agent")
    .WithReference(dataGenerator);

var safetyAgent = builder.AddProject("safety-agent")
    .WithReference(dataGenerator);

var coachAgent = builder.AddProject("coach-agent")
    .WithReference(dataGenerator);

// Foundry prompt agent for web-backed research
var skiResearcher = builder.AddProject("ski-researcher");

// Hosted advisor (composes the specialists)
var advisor = builder.AddProject("advisor-agent")
    .WithReference(weatherAgent)
    .WithReference(liftAgent)
    .WithReference(safetyAgent)
    .WithReference(coachAgent)
    .WithReference(skiResearcher);

// Voice interface
var voiceService = builder.AddProject("voice-service")
    .WithReference(advisor);

// Frontend
var frontend = builder.AddNgxApp("frontend")
    .WithReference(advisor);

await builder.Build().RunAsync();

This is not just deployment configuration. This is your application model. Every service knows its dependencies because you declared them here. The Aspire dashboard reads this same model and shows you exactly what is running, what is connected to what, and how data flows.

Specialist Agents as Composable Tools

The power of this architecture is in the composition pattern. Each specialist agent is a full Microsoft Agent Framework agent with its own tools, its own state management, and its own logic. But from the advisor’s perspective, each specialist is just a tool.

Here is the conceptual pattern:

// In the advisor agent
public class AdvisorAgent
{
    private readonly HttpClient _httpClient;
    
    public AdvisorAgent(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    
    public async Task<AgentState> ProcessRequest(string userRequest)
    {
        var agent = new Agent()
            .AddTool(new Tool
            {
                Name = "GetWeather",
                Handler = async (args) => await CallWeatherAgent(args)
            })
            .AddTool(new Tool
            {
                Name = "CheckLiftStatus",
                Handler = async (args) => await CallLiftAgent(args)
            })
            .AddTool(new Tool
            {
                Name = "EvaluateSafety",
                Handler = async (args) => await CallSafetyAgent(args)
            })
            .AddTool(new Tool
            {
                Name = "GetCoachingAdvice",
                Handler = async (args) => await CallCoachAgent(args)
            });
        
        return await agent.RunAsync(userRequest);
    }
    
    private async Task<string> CallWeatherAgent(Dictionary<string, object> args)
    {
        var response = await _httpClient.PostAsJsonAsync(
            "http://weather-agent/api/conditions", 
            args
        );
        return await response.Content.ReadAsStringAsync();
    }
    
    // Similar for lift, safety, and coach agents
}

Each service boundary is a network call. Aspire gives you service discovery for free. The advisor agent does not need to know the IP address of the weather agent. It just references it by name in the AppHost, and Aspire injects the correct endpoint.

Voice Integration and WebSocket Connections

The voice component adds another layer of complexity that Aspire handles well. Azure AI Voice Live connects via WebSocket. The voice service in AlpineAI is essentially a WebSocket gateway that bridges incoming voice calls to the advisor agent and streams responses back.

Because the voice service is declared in the AppHost just like any other service, it gets the same service discovery, the same environment variable injection, and the same observability as the rest of the system. No special configuration needed. The WebSocket connection itself is just HTTP under the hood, so Aspire’s reference model works transparently.

Unified Observability: OpenTelemetry Across the Entire System

Here is where Aspire proves its value in production. A distributed system with multiple agents, multiple services, voice connections, and live data generates a lot of telemetry. Without a unified approach, you end up with logs scattered across Azure Monitor, Application Insights, and local files.

Aspire uses OpenTelemetry to collect traces from every service. When a user request comes in through the voice interface, hits the advisor agent, which calls the weather agent, which queries the data generator, the entire chain is traced as a single logical operation. The Aspire dashboard shows you the trace tree, the latency of each step, and any errors that occurred.

Each service in AlpineAI is configured to emit OpenTelemetry traces. The pattern is straightforward:

builder.AddServiceDefaults();
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddAspNetCoreInstrumentation();
        tracing.AddHttpClientInstrumentation();
        tracing.AddSqlClientInstrumentation();
    });

The Aspire dashboard aggregates all these traces and makes them queryable. You can filter by service, by operation, by status code, by latency. When something goes wrong, you can see exactly which agent failed and why.

Scaling and Resilience Patterns

As your agentic system grows, you need to think about resilience. What happens if the weather agent is temporarily unavailable? What if a call to the LLM times out? Aspire does not solve these problems by itself, but it makes them visible and manageable.

The pattern is to use standard .NET resilience libraries like Polly alongside Aspire. When the advisor agent calls a specialist agent, it wraps the HTTP call in a retry policy and a circuit breaker:

var httpClient = new HttpClientBuilder()
    .AddStandardResilienceHandler()
    .Build();

var response = await httpClient.PostAsJsonAsync(
    "http://weather-agent/api/conditions",
    request
);

The AddStandardResilienceHandler method adds retry logic, exponential backoff, and circuit breaking automatically. Because these calls are traced through OpenTelemetry, you can see in the Aspire dashboard which calls are being retried and how often the circuit breaker is triggered.

Local Development and Production Parity

One of the most practical benefits of Aspire is that your local development environment mirrors production. In development, you run the AppHost locally, and Aspire spins up all the services. The same code that runs locally runs in Azure Container Apps in production.

This eliminates the “it works on my machine” problem. If an agent works locally, it will work in production, assuming the Azure resources are configured correctly.

The AlpineAI demo showcases this. Developers can clone the repo, run the AppHost, and immediately see the entire ski resort system running locally with full tracing and service discovery. Then the same AppHost configuration deploys to Azure with minimal changes.

Practical Patterns for Your Own Agentic Systems

If you are building agentic systems in .NET, here are the patterns from AlpineAI that transfer directly to your work.

Treat each agent as a service from the start. Even if you begin with a monolith, structure your code so agents are logically separate. This makes it easier to extract them into separate services later when they need to scale independently or be deployed on different schedules.

Declare your entire application in the AppHost. This becomes your source of truth for what the system looks like. New team members can read it and understand the architecture in minutes. The AppHost is not just a launch script; it captures the operational truth of the system.

Instrument everything with OpenTelemetry from day one. The cost is minimal, and the benefit when debugging production issues is enormous. You will thank yourself when a customer reports an issue and you can trace the exact request path through all your agents.

Use service references in the AppHost to handle service discovery. Do not hardcode endpoints or rely on environment variables. Let Aspire inject them. This keeps your code clean and your configuration centralized.

Plan for resilience early. Agentic systems often have unpredictable latencies due to LLM calls and external dependencies. Use retry policies and circuit breakers to handle transient failures gracefully. Aspire’s observability makes it easy to tune these policies based on real data.

The Bigger Picture: Agentic Architecture as Distributed Systems

The AlpineAI demo crystallizes an important shift in how we think about AI applications. They are not monolithic inference engines. They are distributed systems where multiple specialized agents coordinate to solve complex problems. Each agent is a service. Services need infrastructure. Aspire provides that infrastructure with a unified model, built-in service discovery, and comprehensive observability.

As agentic AI becomes more central to production applications, the infrastructure layer becomes as important as the AI layer. Aspire gives you that infrastructure layer without the overhead of learning Kubernetes manifests or complex deployment orchestration.

The lesson from AlpineAI is simple but powerful: treat your agents as services, declare them in Aspire, and let the platform handle the rest. Your future self debugging a production issue will appreciate the clarity and observability you get in return.

What is Microsoft Agent Framework and how does it fit into Aspire?

Microsoft Agent Framework is a library for building AI agents in .NET. Agents are services that can use tools, call LLMs, and maintain state. In Aspire, each agent becomes a separate service declared in the AppHost. Aspire handles the orchestration, service discovery, and observability of those agents.

Can I use Aspire with agents that are not built with Microsoft Agent Framework?

Yes. Aspire works with any .NET service or any containerized service. You can mix Microsoft Agent Framework agents, Foundry prompt agents, Python services, Go services, and anything else. Aspire treats them all as services and provides the same service discovery and observability.

Do I need to use Azure Container Apps to deploy Aspire applications?

No. Aspire can deploy to any container orchestration platform: Kubernetes, Azure Container Apps, Docker Compose, or even traditional VMs. The AppHost is platform-agnostic. You write your application once and deploy it to the target platform of your choice.

How does OpenTelemetry tracing work across multiple agent services?

Each service emits traces to a common OpenTelemetry collector. The Aspire dashboard aggregates these traces and correlates them using trace IDs. When a request flows through multiple agents, the same trace ID follows it through the entire chain, giving you end-to-end visibility.

Is Aspire suitable for production workloads, or is it mainly for development?

Aspire is designed for both development and production. The local Aspire dashboard is primarily for development, but the infrastructure patterns and service definitions apply to production deployments. Many teams use the same AppHost for local development and production deployment.

Leave a Reply

Your email address will not be published. Required fields are marked *