.NET: Add a CreateAIAgent extension method to IChatClient (#1223)

* Add a CreateAIAgent extension method to IChatClient

* Fix unit tests

* Add tests with null chat clients to ensure appropriate failure

* Switch to similar constructor

* Add ChatClientBuilder BuildAgent extensions

* Address code review comments.
This commit is contained in:
westey
2025-10-07 09:47:33 +00:00
committed by GitHub
parent 74905af778
commit 3dc14dac79
7 changed files with 452 additions and 20 deletions
@@ -4,6 +4,7 @@
// WARNING: ONNX doesn't support function calling, so any function tools passed to the agent will be ignored.
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.ML.OnnxRuntimeGenAI;
// E.g. C:\repos\Phi-4-mini-instruct-onnx\cpu_and_mobile\cpu-int4-rtn-block-32-acc-level-4
@@ -14,7 +15,7 @@ const string JokerInstructions = "You are good at telling jokes.";
// Get a chat client for ONNX and use it to construct an AIAgent.
using OnnxRuntimeGenAIChatClient chatClient = new(modelPath);
AIAgent agent = new ChatClientAgent(chatClient, JokerInstructions, JokerName);
AIAgent agent = chatClient.CreateAIAgent(JokerInstructions, JokerName);
// Invoke the agent and output the text result.
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
@@ -3,6 +3,7 @@
// This sample shows how to create and use a simple AI agent with Ollama as the backend.
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OllamaSharp;
var endpoint = Environment.GetEnvironmentVariable("OLLAMA_ENDPOINT") ?? throw new InvalidOperationException("OLLAMA_ENDPOINT is not set.");
@@ -12,8 +13,8 @@ const string JokerName = "Joker";
const string JokerInstructions = "You are good at telling jokes.";
// Get a chat client for Ollama and use it to construct an AIAgent.
using OllamaApiClient chatClient = new(new Uri(endpoint), modelName);
AIAgent agent = new ChatClientAgent(chatClient, JokerInstructions, JokerName);
AIAgent agent = new OllamaApiClient(new Uri(endpoint), modelName)
.CreateAIAgent(JokerInstructions, JokerName);
// Invoke the agent and output the text result.
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
@@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.ChatClient;
using Microsoft.Extensions.AI;
// Get Azure AI Foundry configuration from environment variables
@@ -28,25 +29,21 @@ static string GetWeather([Description("The location to get the weather for.")] s
static string GetDateTime()
=> DateTimeOffset.Now.ToString();
// Adding middleware to the chat client level
var chatClient = azureOpenAIClient.AsIChatClient()
// Adding middleware to the chat client level and building an agent on top of it
var originalAgent = azureOpenAIClient.AsIChatClient()
.AsBuilder()
.Use(getResponseFunc: ChatClientMiddleware, getStreamingResponseFunc: null)
.Build();
// For flexibility we create the agent without any middleware.
var originalAgent = new ChatClientAgent(chatClient, new ChatClientAgentOptions(
.Use(getResponseFunc: ChatClientMiddleware, getStreamingResponseFunc: null)
.BuildAIAgent(
instructions: "You are an AI assistant that helps people find information.",
// Agent level tools
tools: [AIFunctionFactory.Create(GetDateTime, name: nameof(GetDateTime))]));
tools: [AIFunctionFactory.Create(GetDateTime, name: nameof(GetDateTime))]);
// Adding middleware to the agent level
var middlewareEnabledAgent = originalAgent
.AsBuilder()
.Use(FunctionCallMiddleware)
.Use(FunctionCallOverrideWeather)
.Use(PIIMiddleware, null)
.Use(GuardrailMiddleware, null)
.Use(FunctionCallMiddleware)
.Use(FunctionCallOverrideWeather)
.Use(PIIMiddleware, null)
.Use(GuardrailMiddleware, null)
.Build();
var thread = middlewareEnabledAgent.GetNewThread();
@@ -83,15 +80,15 @@ var optionsWithApproval = new ChatClientAgentRunOptions(new()
{
ChatClientFactory = (chatClient) => chatClient
.AsBuilder()
.Use(PerRequestChatClientMiddleware, null) // Using the non-streaming for handling streaming as well
.Use(PerRequestChatClientMiddleware, null) // Using the non-streaming for handling streaming as well
.Build()
};
// var response = middlewareAgent // Using per-request middleware pipeline in addition to existing agent-level middleware
var response = await originalAgent // Using per-request middleware pipeline without existing agent-level middleware
.AsBuilder()
.Use(PerRequestFunctionCallingMiddleware)
.Use(ConsolePromptingApprovalMiddleware, null)
.Use(PerRequestFunctionCallingMiddleware)
.Use(ConsolePromptingApprovalMiddleware, null)
.Build()
.RunAsync("What's the current time and the weather in Seattle?", thread, optionsWithApproval);