KI-Agent in .NET: Microsoft Agent Framework trifft Clean Architecture
Ein Tutorial zeigt, wie sich mit dem Microsoft Agent Framework und Clean Architecture ein KI-Agent in .NET bauen laesst. Grosses Sprachmodell und klassische Softwarearchitektur werden dabei strukturiert kombiniert.

Kuenstliche Intelligenz veraendert die Art, wie wir moderne Anwendungen entwickeln. Traditionell folgt eine Anwendung vordefinierten Geschaeftsregeln, um Anfragen zu verarbeiten, Daten abzurufen und Antworten zurueckzugeben. Mit KI-gestuetzten Anwendungen koennen wir einen Schritt weitergehen, indem wir einem Large Language Model (LLM) erlauben, die Absicht des Nutzers zu verstehen und durch kontrollierte Anwendungswerkzeuge geeignete Aktionen auszuwaehlen.
Ein Beispiel: Stellen Sie sich einen Kunden eines Online-Shops vor, der fragt:
"Koennen Sie mir sagen, wo meine Bestellung ist und wann sie ankommt?"
Eine traditionelle Anwendung wuerde vermutlich verlangen, dass der Kunde zu einer Bestellverfolgungsseite navigiert, eine Bestellnummer eingibt und das Ergebnis selbst interpretiert.
Ein KI-gestuetzter Kundenservice-Agent kann die Frage verstehen, die benoetigten Informationen identifizieren, eine C#-Methode aufrufen, um den Bestellstatus abzurufen, und das Ergebnis in natuerlicher Sprache erklaeren.
In diesem Artikel untersuchen wir, wie sich ein solcher KI-Agent in.NET mit dem Microsoft Agent Framework, Clean Architecture, Ollama und einer ASP.NET Core Web API entwerfen laesst.
Wir werden auch besprechen, warum es wichtig ist, die KI-Integration von der Geschaeftslogik zu trennen und wie sich diese Architektur auf andere Anwendungen uebertragen laesst.
Ein KI-Agent ist ein KI-gestuetztes System, das eine Anfrage interpretieren, entscheiden kann, welche verfuegbare Aktion weiterhilft, erlaubte Werkzeuge aufrufen und deren Ergebnisse nutzen kann, um eine Aufgabe abzuschliessen.
Eine Standard-LLM-Integration folgt ueblicherweise diesem Ablauf:
Ein Agent kann einem interaktiveren Ablauf folgen:
"Wie ist der Lieferstatus der Bestellung 1052?"
Das KI-Modell kann entscheiden, dass es Bestellinformationen benoetigt, und ein Werkzeug namens GetOrderStatus auswaehlen.
Die Anwendung fuehrt die entsprechende C#-Methode aus, ruft die freigegebenen Bestellinformationen ab und gibt das Ergebnis an den Agenten zurueck. Der Agent generiert daraufhin eine nutzerfreundliche Antwort.
Der wichtige Unterschied besteht darin, dass das LLM nicht automatisch Zugriff auf Ihre Datenbank erhaelt. Es kann ausschliesslich die Funktionen nutzen, die Ihre Anwendung bereitstellt und erlaubt.
Das Microsoft Agent Framework stellt Bausteine fuer die Entwicklung von KI-Agenten und Workflows in.NET und anderen unterstuetzten Umgebungen bereit.
Statt jeden Teil einer Agenteninteraktion manuell zu implementieren, koennen Entwickler die Faehigkeiten des Frameworks nutzen, um Agenten zu konfigurieren, Modelle anzubinden, Funktionen als Werkzeuge bereitzustellen und Interaktionen zu koordinieren.
Eine typische Integration umfasst drei Komponenten:
Bei einer Kundenserviceanwendung verbindet sich der Chat-Client mit einem Modell, der Agent erhaelt Anweisungen zu seiner Rolle, und die Werkzeuge stellen den Zugriff auf genehmigte Geschaeftsoperationen bereit.
Beispielsweise darf der Agent Folgendes tun:
Er sollte keinen uneingeschraenkten Zugriff auf jede Datenbanktabelle oder Geschaeftsoperation erhalten.
Wichtig: Die Framework-APIs und Paketnamen koennen sich zwischen Versionen aendern. Die Implementierung sollte der Dokumentation der jeweils verwendeten Version des Microsoft Agent Framework folgen.
Clean Architecture gliedert eine Anwendung in Schichten mit klar definierten Zuständigkeiten. Ihr vorrangiges Ziel ist es, die Kerngeschaeftsregeln unabhaengig von Infrastruktur und externen Frameworks zu halten.
Das wird besonders nuetzlich, wenn man KI integriert, da KI-Anbieter, Modell-APIs und Agent-Frameworks sich im Laufe der Zeit aendern koennen.
Man koennte beispielsweise mit einem lokalen Modell beginnen, das ueber Ollama laeuft. Spaeter entscheidet man sich moeglicherweise fuer einen gehostetenModellanbieter.
Ist die Anwendung richtig entworfen, erfordert der Wechsel der KI-Integration kein Neuschreiben der Geschaeftslogik fuer die Bestellverwaltung.
Eine typische Loesung laesst sich in vier Projekte gliedern.
Die Domainschicht enthaelt die zentralen Geschaeftskonzepte und Regeln.
Bei einer E-Commerce-Anwendung kann sie Folgendes umfassen:
namespace MyApp.Domain.Entities; public class Order { public int Id { get; private set; } public string Status { get; private set; } public Order ( int id, string status ) { Id = id; Status = status; } } Vollbildmodus oeffnen Vollbildmodus verlassen Dies ist ein vereinfachtes Beispiel einer Bestell-Entitaet.
Die Domainschicht sollte nicht vom Microsoft Agent Framework, Ollama-SDKs, HTTP-Clients oder datenbankspezifischen Implementierungen abhaengen.
Denn die Geschaeftsregeln sollen auch dann nutzbar bleiben, wenn die Anwendung ihren KI-Anbieter oder ihre Persistenztechnologie wechselt.
Die Anwendungsschicht enthaelt Anwendungsfaelle und koordiniert Geschaeftsoperationen.
Beispielsweise ist das Abrufen des Status einer Bestellung ein Anwendungsfall. Er sollte nicht wissen muessen, wie ein KI-Agent konfiguriert ist.
using MyApp.Domain.Entities; namespace MyApp.Application.Abstractions; public interface IOrderRepository { Task < Order?> GetByIdAsync ( int orderId, CancellationToken cancellationToken ); } Vollbildmodus oeffnen Vollbildmodus verlassen Als Naechstes erstellt man einen Anwendungsdienst:
using MyApp.Application.Abstractions; namespace MyApp.Application.Orders; public sealed class GetOrderStatusService { private readonly IOrderRepository _repository; public GetOrderStatusService ( IOrderRepository repository ) { _repository = repository; } public async Task < string?> ExecuteAsync ( int orderId, CancellationToken cancellationToken ) { var order = await _repository. GetByIdAsync ( orderId, cancellationToken ); return order?. Status; } } Vollbildmodus oeffnen Vollbildmodus verlassen Dieser Dienst koordiniert den Anwendungsfall, ohne zu wissen, ob die Anfrage von einem Web-Controller, einem Hintergrundprozess oder einem KI-Agenten stammt.
In einer Produktionsanwendung wuerde man zusaetzlich die Bestell-ID validieren und die entsprechenden Autorisierungsregeln fuer Kunden durchsetzen.
Die Infrastrukturschicht enthaelt Implementierungen, die von externen Technologien abhaengen.
Das Infrastruktur-Repository implementiert die Abstraktion der Anwendungsschicht:
using MyApp.Application.Abstractions; using MyApp.Domain.Entities; namespace MyApp.Infrastructure.Persistence; public sealed class OrderRepository: IOrderRepository { public Task < Order?> GetByIdAsync ( int orderId, CancellationToken cancellationToken ) { // Query the actual database here. throw new NotImplementedException (); } } Vollbildmodus oeffnen Vollbildmodus verlassen Die Implementierung ist absichtlich unvollstaendig, da die tatsaechliche Datenbanktechnologie und das Schema bestimmen, wie die Abfrage formuliert werden sollte.
Auch die Konfiguration des KI-Agenten gehoert in diese Schicht, wodurch frameworkspezifische Details ausserhalb der Domainschicht bleiben.
Die API-Schicht stellt Clients HTTP-Endpunkte zur Verfuegung.
Sie nimmt Anfragen entgegen, prueft deren Anforderungen auf Transportebene, ruft die passende Anwendungskomponente auf und liefert HTTP-Antworten zurueck.
public sealed record CustomerQuestion ( string Message ); Enter fullscreen mode Exit fullscreen mode Ein ASP.NET-Core-Controller kann einen Endpunkt bereitstellen, der die Frage an eine Abstraktion auf Anwendungsebene, etwa einen Agenten, weiterleitet.
[ ApiController ] [ Route ( "api/support" )] public class SupportController: ControllerBase { private readonly ICustomerSupportAgent _agent; public SupportController ( ICustomerSupportAgent agent ) { _agent = agent; } [ HttpPost ( "ask" )] public async Task < IActionResult > Ask ( [ FromBody ] CustomerQuestion request, CancellationToken cancellationToken ) { var response = await _agent. AskAsync ( request. Message, cancellationToken ); return Ok ( new { response }); } } Enter fullscreen mode Exit fullscreen mode Hier stellt ICustomerSupportAgent eine Abstraktion dar, die der Anwendung gehoert. Sie muss als Teil der Solution definiert und implementiert werden.
Dieses Beispiel veranschaulicht die Architekturgrenze und nicht ein vollstaendiges, direkt ausfuehrbares Projekt.
Der Controller muss nicht wissen, welcher LLM-Provider verwendet wird oder wie Tools registriert sind.
Kombinieren wir die Schichten zu einem einzigen Ablauf.
Die Anfrage durchlaeuft die folgenden Schritte:
Wenn die Datenbank beispielsweise meldet, dass eine Bestellung versendet wurde, koennte die endgueltige Antwort lauten:
"Ihre Bestellung wurde versendet und befindet sich derzeit im Versand."
Diese Antwort basiert auf abgerufenen Anwendungsdaten und nicht darauf, dass das Modell den Status der Bestellung erraet.
Eines der wichtigsten Konzepte in der Agentenentwicklung ist Tool Calling, haeufig auch Function Calling genannt.
Ein Tool ist eine Funktion, die ein KI-Modell die Anwendung ausfuehren lassen kann.
Die Funktion selbst laeuft innerhalb Ihrer Anwendung. Das Modell legt in der Regel fest, welches Tool angefordert wird und welche Argumente uebergeben werden; Ihr Framework und Ihre Anwendung fuehren den eigentlichen Code aus.
public async Task < string > GetOrderStatusAsync ( int orderId, CancellationToken cancellationToken ) { var status = await _orderStatusService. ExecuteAsync ( orderId, cancellationToken ); return status?? "Order not found."; } Enter fullscreen mode Exit fullscreen mode Diese Methode koennte als Grundlage fuer ein Agenten-Tool dienen.
Wenn man jedoch eine gewoehnliche C#-Methode definiert, wird sie dadurch nicht automatisch fuer das LLM verfuegbar. Die Methode muss ueber den vom Framework unterstuetzten Tool-Mechanismus registriert oder bereitgestellt werden.
Das Tool sollte zudem einen klaren Namen, eine aussagekraeftige Beschreibung, klar definierte Parameter und einen kontrollierten Rueckgabewert besitzen.
Gute Beschreibungen helfen dem Modell zu verstehen, wann ein Tool geeignet ist. Sie ersetzen weder Autorisierung noch Eingabevalidierung.
Bei der Registrierung eines Tools hilft beschreibende Metadaten dem Modell, den Zweck zu verstehen.
"Ruft den aktuellen Status einer Bestellung anhand ihrer ID ab. Verwenden Sie dieses Tool, wenn ein Kunde nach dem Bestellfortschritt fragt."
Die zweite Beschreibung gibt eine klarere Orientierung zur vorgesehenen Verwendung des Tools.
Je nach Framework-Version koennen C#-Methoden ueber Function Factories, Methodenannotationen oder andere unterstuetzte Registrierungsmechanismen bereitgestellt werden.
Konzeptionell sieht der Registrierungsprozess so aus:
// Illustrativer Pseudocode, kein vollstaendiger Framework-API-Aufruf. var tools = new [] { CreateTool ( method: GetOrderStatusAsync, description: "Retrieve the current status of an order." ) }; // Registriert die Tools beim konfigurierten Agenten. Enter fullscreen mode Exit fullscreen mode Die genaue Implementierung haengt von der Version des Microsoft Agent Framework und der jeweiligen Tool-Registrierungs-API ab.
Das Prinzip bleibt gleich: Statt dem Modell die Ausfuehrung beliebigen Codes zu erlauben, stellt man eine kleine Menge klar definierter Operationen bereit.
Ollama ermoeglicht Entwicklern, unterstuetzte Sprachmodelle lokal auszufuehren.
Das kann waehrend der Entwicklung nuetzlich sein, da man das Verhalten von Agenten testen kann, ohne jede Anfrage notwendigerweise an einen gehosteten Modell-Provider zu senden.
Die Anwendungskonfiguration koennte Einstellungen wie die folgenden enthalten:
{ "AI": { "Provider": "Ollama", "Model": "your-local-model", "Endpoint": "http://localhost:11434" } } Enter fullscreen mode Exit fullscreen mode Der Modellname ist ein Platzhalter. Verwenden Sie ein Modell, das tatsaechlich installiert und mit der gewaehlten Integration kompatibel ist.
In einer realen Anwendung sollten umgebungsspezifische Einstellungen angemessen verwaltet werden. Zugangsdaten, falls erforderlich, sollten in Umgebungsvariablen oder einem Secrets Manager gespeichert und nicht in die Quellcodeverwaltung eingecheckt werden.
Beachten Sie ausserdem, dass die lokale Ausfuehrung Anforderungen an die Hardware stellt. Modellgroesse, verfuegbarer Arbeitsspeicher, Inferenzgeschwindigkeit und Unterstuetzung fuer Tool Calling beeinflussen das Erlebnis alle spuerbar.
Das Erstellen eines Agenten kann mehrere Konfigurationsschritte erfordern:
Werden diese Schritte in Controllern oder Anwendungsdiensten wiederholt, wird der Code schwer wartbar.
Eine Agenten-Factory zentralisiert den Erstellungsprozess.
public interface ICustomerSupportAgentFactory { ICustomerSupportAgent Create (); } Enter fullscreen mode Exit fullscreen Mode Das Infrastrukturprojekt kann diese Factory unter Verwendung des gewaehlten KI-Frameworks implementieren.
Die API- oder Anwendungsschicht haengt von einer Abstraktion ab, die der Anwendung gehoert, statt direkt frameworkspezifische Agenten zu erzeugen.
Nicht jede Anwendung muss fuer jede Anfrage eine neue Agenteninstanz erstellen. Abhaengig von den Lifecycle- und Thread-Safety-Anforderungen des Frameworks kann ein konfigurierter Agent wiederverwendet werden. Konversationsspezifischer Zustand sollte separat und sicher behandelt werden.
ASP.NET Core bietet eine integrierte Dependency Injection.
Statt Repositories, Anwendungsdienste oder KI-Clients manuell in Controllern zu erzeugen, registriert man sie im Service Container.
builder. Services. AddScoped < GetOrderStatusService >(); builder. Services. AddScoped < IOrderRepository, OrderRepository >(); Enter fullscreen mode Exit fullscreen mode Die Agentenabstraktion und ihre Factory wuerden entsprechend ihrer Implementierungen und Lifecycle-Anforderungen ebenfalls registriert.
Ein Beispiel für eine Methode zur Infrastruktur-Erweiterung könnte so aussehen:
public static class DependencyInjection { public static IServiceCollection AddAiInfrastructure ( this IServiceCollection services, IConfiguration configuration ) { // Registrierung des AI-Clients, der Tools, der Agent-Fabrik // und der Agent-Abstraktion erfolgt hier. return services; } } Die Methode ist ein strukturelles Beispiel; die tatsächlichen Registrierungen hängen von den APIs des gewählten Frameworks ab.
In der Datei Program.cs kann das API-Projekt dann Folgendes aufrufen:
builder. Services. AddAiInfrastructure ( builder. Configuration ); Auf diese Weise bleibt die Startdatei der Anwendung von Modellkonfigurationen und Details zur Tool-Registrierung frei.
Dies ist eines der wichtigsten Prinzipien der Clean Architecture in einer KI-Anwendung.
Angenommen, Ihre Domain-Entität verweist direkt auf eine Klasse aus einem KI-Framework.
Vermeiden Sie diesen architektonischen Ansatz. public class Order { // Domain-Eigenschaften sollten nicht von // klassenspezifischen KI-Frameworks abhängen. } Das Problem ist nicht, dass eine Domain-Entität keine Geschäftslogik enthalten darf. Das Problem besteht darin, eine unnötige Abhängigkeit von einer externen KI-Technologie einzuführen.
Wenn Sie später das KI-Framework austauschen, sollte Ihr Domänenmodell nicht allein deshalb Änderungen erfordern, weil sich eine Infrastrukturabhängigkeit geändert hat.
Die Infrastruktur hängt von Abstraktionen der Anwendungsschicht ab, nicht umgekehrt.
Das erleichtert das Unit-Testing von Geschäftslogik, ohne ein LLM zu starten, eine echte Datenbank anzubinden oder Netzwerkanfragen durchzuführen.
Ein KI-Agent bringt zusätzliche Sicherheitsaspekte mit sich, da er anhand natürlichsprachlicher Eingaben aus den Fähigkeiten der Anwendung auswählen kann.
Das Modell sollte niemals als Autorisierungsmechanismus behandelt werden.
Auch wenn das Modell das Tool zum Abruf des Bestellstatus korrekt identifiziert, muss die Anwendung überprüfen, ob der aktuelle Kunde auf diese Bestellung zugreifen darf.
Authentifizierung: Identifizieren Sie den aktuellen Benutzer über Ihren üblichen Authentifizierungsmechanismus.
Autorisierung: Prüfen Sie Zugriffsrechte innerhalb des Anwendungsdienstes oder einer anderen vertrauenswürdigen Durchsetzungsschicht.
Eingabevalidierung: Validieren Sie Bestell-IDs und andere Argumente, die an Tools übergeben werden.
Geringste Rechte: Legen Sie nur die Operationen offen, die der Agent benötigt.
Schutz sensibler Daten: Geben Sie nur die Felder zurück, die zur Beantwortung der Frage erforderlich sind.
Kontrollierte Seiteneffekte: Verlangen Sie eine angemessene Autorisierung und Bestätigung, bevor Aktionen wie das Stornieren von Bestellungen, das Ausstellen von Rückerstattungen oder das Ändern von Kontoinformationen durchgeführt werden.
Protokollierung und Monitoring: Erfassen Sie nützliche betriebliche Details, ohne unnötigerweise Passwörter, Tokens, persönliche Informationen oder sensible Prompts zu protokollieren.
Eine Tool-Beschreibung kann dem Modell mitteilen, nicht auf die Bestellung eines anderen Kunden zuzugreifen, aber die Anwendung muss diese Einschränkung im Code durchsetzen.
Tests sollten sowohl die gewöhnliche.NET-Anwendung als auch die KI-Integration abdecken.
Testen Sie Anwendungsdienste unabhängig voneinander.
Prüfen Sie beispielsweise, ob GetOrderStatusService den korrekten Status zurückgibt, wenn ein Repository eine Bestellung bereitstellt, und dass fehlende Bestellungen angemessen behandelt werden.
Verwenden Sie ein Mock- oder Fake-Repository, um keine echte Datenbank zu benötigen.
Überprüfen Sie, ob das Infrastruktur-Repository korrekt mit der Datenbank kommuniziert und ob die Dependency Injection wie erwartet konfiguriert ist.
Testen Sie die Validierung von Tool-Argumenten, die Autorisierung, erfolgreiche Ergebnisse, die Fehlerbehandlung und unerwartete Eingaben.
Bewerten Sie, ob das Modell für repräsentative Benutzerfragen das richtige Tool auswählt und Antworten erzeugt, die mit den Tool-Ergebnissen übereinstimmen.
Da Modellausgaben variieren können, sollten Sie sich nicht ausschließlich auf exakte Zeichenfolgenvergleiche verlassen. Testen Sie wichtige Verhaltenseigenschaften und Sicherheitsgrenzen.
Testen Sie den gesamten Anforderungspfad, vom HTTP-Endpunkt über die Tool-Ausführung bis zur finalen Antwort.
Diese Kombination hilft dabei, Probleme in Geschäftslogik, Infrastrukturkonfiguration, Tool-Auswahl und Modellverhalten zu identifizieren.
Dasselbe Design lässt sich auch in Anwendungen über den E-Commerce hinaus verwenden.
Eine Blogging-Plattform könnte beispielsweise kontrollierte Tools für das Abrufen von Artikeln, das Auffinden verwandter Inhalte oder das Ab autorisierter Engagement-Statistiken bereitstellen.
Eine Anwendung zur Entwicklerunterstützung könnte Dokumentationen und Codebeispiele abrufen.
Eine interne Geschäftsanwendung könnte Mitarbeitern helfen, Informationen aus autorisierten Unternehmenssystemen zu finden.
Das zentrale Designprinzip besteht darin, den KI-Agenten als Orchestrierungskomponente zu betrachten und ihn nicht zum Eigentümer von Geschäftsregeln oder Datenzugriffen zu machen.
Das Modell interpretiert Anfragen, die Anwendung setzt Regeln durch, und die Infrastruktur verbindet sich mit externen Systemen.
Der Aufbau eines KI-Agenten in.NET umfasst mehr als die Anbindung eines Sprachmodells an einen API-Endpunkt. Eine wartbare Lösung erfordert klare architektonische Grenzen, kontrollierte Tools, Dependency Injection, Anbieterkonfiguration, Sicherheitskontrollen und angemessene Tests.
Das Microsoft Agent Framework kann bei der Orchestrierung von Agenten und Tools helfen. Ollama kann eine Option für die lokale Modellausführung bieten. ASP.NET Core stellt die Anwendung über HTTP bereit, während Clean Architecture die Domänen- und Anwendungslogik unabhängig von KI-spezifischen Implementierungsdetails hält.
Wenn diese Komponenten gemeinsam entworfen werden, können Entwickler KI-Funktionen hinzufügen, ohne ihre zentrale Geschäftslogik eng an einen bestimmten Modellanbieter oder ein bestimmtes Framework zu koppeln.
Die zentrale Erkenntnis: Lassen Sie das KI-Modell entscheiden, welches der zulässigen Tools helfen darf, aber Ihre.NET-Anwendung bleibt verantwortlich für Geschäftsregeln, Autorisierung, Datenintegrität und Ausführung.
Hinweis: Die Codebeispiele in diesem Artikel veranschaulichen die Architektur und die Zuständigkeiten der einzelnen Ebenen. Die Agentenregistrierung und die Ollama-Integration sollten mit den APIs der jeweils von Ihnen eingesetzten Microsoft Agent Framework Version abgeschlossen werden.
Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.
Original (Englisch) anzeigen
Artificial Intelligence is changing how we build modern applications. Traditionally, an application follows predefined business rules to process requests, retrieve data, and return responses. With AI-powered applications, we can go a step further by allowing a Large Language Model (LLM) to understand user intent and choose appropriate actions through controlled application tools.
For example, imagine an e-commerce customer asking:
"Can you tell me where my order is and when it will arrive?"
A traditional application might require the customer to navigate to an order-tracking page, enter an order number, and interpret the result.
An AI-powered customer support agent can understand the question, identify the required information, invoke a C# method to retrieve the order status, and explain the result in natural language.
In this article, we'll explore how to design such an AI agent in.NET using the Microsoft Agent Framework, Clean Architecture, Ollama, and ASP.NET Core Web API.
We'll also discuss why separating AI integration from business logic is important and how this architecture can be extended to other applications.
An AI agent is an AI-powered system that can interpret a request, decide which available action may help, invoke permitted tools, and use their results to complete a task.
A standard LLM integration commonly follows this flow:
An agent can follow a more interactive workflow:
"What's the delivery status of order 1052?"
The AI model may decide that it needs order information and select a tool named GetOrderStatus.
The application executes the corresponding C# method, retrieves the authorized order information, and returns the result to the agent. The agent then generates a user-friendly answer.
The important distinction is that the LLM does not automatically gain access to your database. It can use only the capabilities that your application exposes and permits.
The Microsoft Agent Framework provides building blocks for developing AI agents and workflows in.NET and other supported environments.
Instead of manually implementing every part of an agent interaction, developers can use framework capabilities to configure agents, connect models, expose functions as tools, and coordinate interactions.
A typical integration involves three components:
For a customer support application, the chat client connects to a model, the agent receives instructions about its role, and the tools provide access to approved business operations.
For example, the agent may be allowed to:
It should not receive unrestricted access to every database table or business operation.
Important: Framework APIs and package names can change between versions. The implementation should follow the documentation for the specific Microsoft Agent Framework version being used.
Clean Architecture organizes an application into layers with clearly defined responsibilities. Its primary goal is to keep core business rules independent of infrastructure and external frameworks.
This becomes particularly useful when integrating AI because AI providers, model APIs, and agent frameworks can change over time.
For example, you might begin with a local model running through Ollama. Later, you may decide to use a hosted model provider.
If the application is designed properly, changing the AI integration should not require rewriting your order-management business logic.
A typical solution can be organized into four projects.
The Domain Layer contains the core business concepts and rules.
For an e-commerce application, it may include:
namespace MyApp.Domain.Entities; public class Order { public int Id { get; private set; } public string Status { get; private set; } public Order ( int id, string status ) { Id = id; Status = status; } } Enter fullscreen mode Exit fullscreen mode This is a simplified example of an order entity.
The Domain Layer should not depend on the Microsoft Agent Framework, Ollama SDKs, HTTP clients, or database-specific implementations.
Because business rules should remain usable even if the application changes its AI provider or persistence technology.
The Application Layer contains use cases and coordinates business operations.
For example, retrieving an order's status is an application use case. It should not need to know how an AI agent is configured.
using MyApp.Domain.Entities; namespace MyApp.Application.Abstractions; public interface IOrderRepository { Task < Order?> GetByIdAsync ( int orderId, CancellationToken cancellationToken ); } Enter fullscreen mode Exit fullscreen mode Next, create an application service:
using MyApp.Application.Abstractions; namespace MyApp.Application.Orders; public sealed class GetOrderStatusService { private readonly IOrderRepository _repository; public GetOrderStatusService ( IOrderRepository repository ) { _repository = repository; } public async Task < string?> ExecuteAsync ( int orderId, CancellationToken cancellationToken ) { var order = await _repository. GetByIdAsync ( orderId, cancellationToken ); return order?. Status; } } Enter fullscreen mode Exit fullscreen mode This service coordinates the use case without knowing whether the request originated from a web controller, a background process, or an AI agent.
In a production application, you would also validate the order ID and enforce the appropriate customer authorization rules.
The Infrastructure Layer contains implementations that depend on external technologies.
The infrastructure repository implements the application abstraction:
using MyApp.Application.Abstractions; using MyApp.Domain.Entities; namespace MyApp.Infrastructure.Persistence; public sealed class OrderRepository: IOrderRepository { public Task < Order?> GetByIdAsync ( int orderId, CancellationToken cancellationToken ) { // Query the actual database here. throw new NotImplementedException (); } } Enter fullscreen mode Exit fullscreen mode The implementation is intentionally incomplete because the actual database technology and schema determine how the query should be written.
The AI agent configuration also belongs in this layer, keeping framework-specific details outside the Domain Layer.
The API Layer exposes HTTP endpoints to clients.
It receives requests, validates their transport-level requirements, invokes the appropriate application component, and returns HTTP responses.
public sealed record CustomerQuestion ( string Message ); Enter fullscreen mode Exit fullscreen mode An ASP.NET Core controller can expose an endpoint that delegates the question to an application-level agent abstraction.
[ ApiController ] [ Route ( "api/support" )] public class SupportController: ControllerBase { private readonly ICustomerSupportAgent _agent; public SupportController ( ICustomerSupportAgent agent ) { _agent = agent; } [ HttpPost ( "ask" )] public async Task < IActionResult > Ask ( [ FromBody ] CustomerQuestion request, CancellationToken cancellationToken ) { var response = await _agent. AskAsync ( request. Message, cancellationToken ); return Ok ( new { response }); } } Enter fullscreen mode Exit fullscreen mode Here, ICustomerSupportAgent represents an application-owned abstraction. It must be defined and implemented as part of the solution.
This example illustrates the architectural boundary rather than a complete, directly runnable project.
The controller does not need to know which LLM provider is being used or how tools are registered.
Let's combine the layers into a single workflow.
The request travels through the following steps:
For example, if the database reports that an order has shipped, the final answer might be:
"Your order has been shipped and is currently in transit."
This answer is based on retrieved application data rather than the model guessing the order's status.
One of the most important concepts in agent development is tool calling, often called function calling.
A tool is a function that an AI model can request the application to execute.
The function itself runs within your application. The model generally determines which tool to request and what arguments to provide; your framework and application execute the actual code.
public async Task < string > GetOrderStatusAsync ( int orderId, CancellationToken cancellationToken ) { var status = await _orderStatusService. ExecuteAsync ( orderId, cancellationToken ); return status?? "Order not found."; } Enter fullscreen mode Exit fullscreen mode This method could serve as the basis for an agent tool.
However, defining a normal C# method does not automatically make it available to the LLM. The method must be registered or exposed using the framework's supported tool mechanism.
The tool should also have a clear name, a meaningful description, well-defined parameters, and a controlled return value.
Good descriptions help the model understand when a tool is appropriate. They do not replace authorization or input validation.
When registering a tool, descriptive metadata helps the model understand its purpose.
"Retrieves the current status of an order using its ID. Use this tool when a customer asks about order progress."
The second description provides clearer guidance about the tool's intended use.
Depending on the framework version, C# methods can be exposed using function factories, method annotations, or other supported registration mechanisms.
Conceptually, the registration process looks like this:
// Illustrative pseudocode, not a complete framework API call. var tools = new [] { CreateTool ( method: GetOrderStatusAsync, description: "Retrieve the current status of an order." ) }; // Register the tools with the configured agent. Enter fullscreen mode Exit fullscreen mode The exact implementation depends on the Microsoft Agent Framework version and the specific tool-registration API.
The principle remains the same: expose a small set of well-defined operations rather than allowing the model to execute arbitrary code.
Ollama allows developers to run supported language models locally.
This can be useful during development because you can experiment with agent behavior without necessarily sending every prompt to a hosted model provider.
The application configuration might contain settings such as:
{ "AI": { "Provider": "Ollama", "Model": "your-local-model", "Endpoint": "http://localhost:11434" } } Enter fullscreen mode Exit fullscreen mode The model name is a placeholder. Use a model that is actually installed and compatible with the selected integration.
In a real application, environment-specific settings should be managed appropriately. Credentials, if required, should be stored in environment variables or a secrets manager rather than committed to source control.
Also remember that local execution has hardware requirements. Model size, available memory, inference speed, and tool-calling support all affect the experience.
Creating an agent may require several configuration steps:
Repeating these steps in controllers or application services makes the code difficult to maintain.
An agent factory centralizes the creation process.
public interface ICustomerSupportAgentFactory { ICustomerSupportAgent Create (); } Enter fullscreen mode Exit fullscreen mode The infrastructure project can implement this factory using the chosen AI framework.
The API or application layer depends on an application-owned abstraction instead of directly constructing framework-specific agents.
Not every application needs to create a new agent instance for every request. Depending on the framework's lifecycle and thread-safety requirements, a configured agent may be reused. Conversation-specific state should be handled separately and safely.
ASP.NET Core provides built-in dependency injection.
Instead of creating repositories, application services, or AI clients manually inside controllers, you register them with the service container.
builder. Services. AddScoped < GetOrderStatusService >(); builder. Services. AddScoped < IOrderRepository, OrderRepository >(); Enter fullscreen mode Exit fullscreen mode The agent abstraction and its factory would also be registered according to their implementations and lifecycle requirements.
For example, an infrastructure extension method might look like this:
public static class DependencyInjection { public static IServiceCollection AddAiInfrastructure ( this IServiceCollection services, IConfiguration configuration ) { // Register the AI client, tools, agent factory, // and agent abstraction here. return services; } } Enter fullscreen mode Exit fullscreen mode The method is a structural example; the actual registrations depend on the chosen framework APIs.
In Program.cs, the API project can then call:
builder. Services. AddAiInfrastructure ( builder. Configuration ); Enter fullscreen mode Exit fullscreen mode This keeps the application startup file from becoming crowded with model configuration and tool-registration details.
This is one of the most important Clean Architecture principles in an AI application.
Suppose your domain entity directly references a class from an AI framework.
// Avoid this architectural approach. public class Order { // Domain properties should not depend on // AI framework-specific classes. } Enter fullscreen mode Exit fullscreen mode The problem is not that a domain entity cannot contain business logic. The problem is introducing an unnecessary dependency on an external AI technology.
If you later replace the AI framework, your domain model should not require changes simply because an infrastructure dependency changed.
Infrastructure depends on application abstractions, not the other way around.
This makes it easier to unit-test business logic without starting an LLM, connecting to a real database, or making network requests.
An AI agent introduces additional security considerations because it can select from application capabilities based on natural-language input.
The model should never be treated as an authorization mechanism.
Even if the model correctly identifies the order-status tool, the application must verify that the current customer is allowed to access that order.
Authentication: Identify the current user through your normal authentication mechanism.
Authorization: Check access permissions inside the application service or another trusted enforcement layer.
Input validation: Validate order IDs and other arguments supplied to tools.
Least privilege: Expose only the operations required by the agent.
Sensitive-data protection: Return only the fields needed to answer the question.
Controlled side effects: Require appropriate authorization and confirmation before performing actions such as cancelling orders, issuing refunds, or changing account information.
Logging and monitoring: Record useful operational details without unnecessarily logging passwords, tokens, personal information, or sensitive prompts.
A tool description may tell the model not to access another customer's order, but the application must enforce that restriction in code.
Testing should cover the ordinary.NET application as well as the AI integration.
Test application services independently.
For example, verify that GetOrderStatusService returns the correct status when a repository provides an order and handles missing orders appropriately.
Use a mock or fake repository to avoid requiring a real database.
Verify that the infrastructure repository communicates correctly with the database and that dependency injection is configured as expected.
Test tool argument validation, authorization, successful results, error handling, and unexpected inputs.
Evaluate whether the model selects the correct tool for representative user questions and produces responses consistent with the tool results.
Because model outputs may vary, avoid relying exclusively on exact-string comparisons. Test important behavioral properties and security boundaries.
Test the entire request path, from the HTTP endpoint through tool execution to the final response.
This combination helps identify problems in business logic, infrastructure configuration, tool selection, and model behavior.
The same design can be used in applications beyond e-commerce.
For example, a blogging platform could expose controlled tools for retrieving articles, finding related content, or retrieving authorized engagement statistics.
A developer-support application could retrieve documentation and code examples.
An internal business application could help employees find information from authorized company systems.
The key design principle is to keep the AI agent as an orchestration component rather than making it the owner of business rules or data access.
The model interprets requests, the application enforces rules, and the infrastructure connects to external systems.
Building an AI agent in.NET involves more than connecting a language model to an API endpoint. A maintainable solution needs clear architectural boundaries, controlled tools, dependency injection, provider configuration, security controls, and appropriate testing.
The Microsoft Agent Framework can help with agent and tool orchestration. Ollama can provide a local model execution option. ASP.NET Core exposes the application over HTTP, while Clean Architecture keeps the domain and application logic independent of AI-specific implementation details.
When these components are designed together, developers can add AI capabilities without tightly coupling their core business logic to a particular model provider or framework.
The key takeaway: Let the AI model decide which permitted tool may help, but let your.NET application remain responsible for business rules, authorization, data integrity, and execution.
Note: The code examples in this article illustrate the architecture and responsibilities of each layer. The agent registration and Ollama integration should be completed using the APIs available in your selected Microsoft Agent Framework version.