AI Agent Architecture in C# with Semantic Kernel: A Practical Guide
The rise of autonomous AI agents is transforming enterprise software engineering. However, when companies try to move their Proofs of Concept (PoCs) in Python to mission-critical .NET systems, they face a wall of instability: lack of determinism, state loss, uncontrolled hallucinations, and extreme coupling to vendor APIs (OpenAI, Anthropic, etc.).
Writing a wrapper around an LLM is trivial. Orchestrating an agent ecosystem that makes business decisions, executes tools, and accesses production databases without compromising the system architecture is a pure engineering challenge.
In this article, we explore how to orchestrate agentic architectures in .NET / C# using Semantic Kernel under the principles of Clean Architecture.
Challenges of Agents in Production
Large language models (LLMs) are inherently probabilistic. Enterprise systems require determinism. This friction creates three fundamental challenges:
- State Management (Statefulness): An agent must remember the context of a complex operation that may last minutes or hours. If the server restarts, does the agent lose memory?
- Orchestration and “Tool Calling”: When an agent needs to query a customer database or trigger an email, it must do so through controlled interfaces (sandbox), never by injecting SQL or making direct HTTP calls without validation.
- Failure Handling (Resilience): AI providers impose rate limits and suffer from unpredictable latencies.
The Proposed Architecture: Semantic Kernel + Clean Architecture
Microsoft Semantic Kernel is the ideal SDK for .NET because it treats AI as just another component of the infrastructure, not the center of the universe.
In a Clean Architecture context, AI resides strictly in the Infrastructure layer. The 'Brain' of your application (the Domain and Application layers) dictates the rules, validates agent proposals, and maintains absolute control.
Practical Example: Metadata Extraction Agent
We will implement an agent that analyzes an unstructured document, extracts key metadata, and persists it, using dependency injection and state isolation.
1. Plugin Definition (Constrained Tool Calling)
In Semantic Kernel, the tools the agent can use are defined as Plugins. These plugins are native C# classes decorated with attributes.
// Capa: Application (Use Cases / Plugins)
using Microsoft.SemanticKernel;
using System.ComponentModel;
public class DatabasePlugin
{
private readonly IMetadataRepository _repository;
public DatabasePlugin(IMetadataRepository repository)
{
_repository = repository; // Inyectado de forma segura
}
[KernelFunction, Description("Guarda los metadatos extraídos de un modelo 3D en la base de datos.")]
public async Task<string> SaveMetadataAsync(
[Description("ID único del modelo")] string modelId,
[Description("Número de polígonos detectados")] int polygons,
[Description("Formato del archivo")] string format)
{
// 🛡️ El dominio C# valida los datos antes de hacer nada
if (polygons < 0) return "Error: Los polígonos no pueden ser negativos.";
await _repository.SaveAsync(modelId, polygons, format);
return "Metadata guardada con éxito en SQL Server.";
}
}
2. Agent Orchestration with Redis and Azure OpenAI
We build the agent by instantiating the Kernel and connecting it to our providers.
// Capa: Infrastructure (Agent Orchestrator)
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
public class MetadataAgentService : IMetadataAgentService
{
private readonly Kernel _kernel;
private readonly IChatCompletionService _chatService;
public MetadataAgentService(Kernel kernel, DatabasePlugin dbPlugin)
{
_kernel = kernel;
// Inyectamos el plugin C# de forma nativa
_kernel.Plugins.AddFromObject(dbPlugin, "DatabaseTools");
_chatService = _kernel.GetRequiredService<IChatCompletionService>();
}
public async Task ProcessDocumentAsync(string userPrompt)
{
var history = new ChatHistory("Eres un agente experto en extracción de datos. Extrae polígonos y formatos. Usa la herramienta DatabaseTools para guardar tus hallazgos.");
history.AddUserMessage(userPrompt);
// Habilitamos que el agente decida llamar automáticamente a nuestras funciones C#
var executionSettings = new OpenAIPromptExecutionSettings
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
// El SDK orquesta el bucle de "Razonamiento -> Llamada a Herramienta C# -> Respuesta"
var result = await _chatService.GetChatMessageContentAsync(
history,
executionSettings,
_kernel
);
}
}
3. Integration with Services (Persistence and Decoupling)
To achieve true scalability, your agent cannot live isolated in RAM.
- Redis for ChatHistory: We use Redis to persist the
ChatHistory. If the Docker container restarts, the agent recovers its exact context and continues the operation. - Azure OpenAI + Polly: We wrap the
IChatCompletionServicewith Exponential Backoff (Polly). If Azure returns an HTTP 429 error (Too Many Requests), the backend intelligently retries without failing the user's request. - RabbitMQ for Worker Agents: Requests to the agent are queued. The web API only returns a
202 Accepted, while a Worker Service processes the AI in the background.
Results and Metrics in Production
When applying this architecture to real clients, the results are conclusive:
- Reduction in Timeouts (504): From 8% to 0.01% by isolating AI processing in Workers.
- Data Reliability: By confining Tool Calling to C# classes with strong typing, SQL injection (SQLi) errors caused by malformed LLM JSONs were eliminated.
- Flexibility (Vendor-Agnostic): Switching from
AzureOpenAIto a localOllamamodel took 2 lines of code inProgram.cs, without touching the business logic or the plugins.
Conclusion
Generative AI does not replace software engineering; it makes it more necessary than ever. An agent is as strong as the architectural rails it operates on. Semantic Kernel, coupled with Clean Architecture principles in .NET, provides those rails.
🎁 Secure the scalability of your AI Agents
Download my 3D and AI Architecture Checklist to discover how to audit your infrastructure and ensure your system is fault-tolerant, scalable, and free of vendor lock-in.
Is your system already in production and in need of stabilization? Schedule a free 15-minute technical session with me.
