mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: AI Agent with plugin (#1009)
* add sample to demonstrate plugins for ai agent * Update dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Agents/Agent_Step15_Plugins/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * remove lang version property * address review comments * rename functionInvocationServices to services * restore the solution file corrupted during the merge with lates main --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
38b6f68d70
commit
bd167ee476
@@ -48,8 +48,9 @@
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step12_AsFunctionTool/Agent_Step12_AsFunctionTool.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step13_Memory/Agent_Step13_Memory.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step14_Middleware/Agent_Step14_Middleware.csproj" />
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step15_Plugins/Agent_Step15_Plugins.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/AgentWithOpenAI/">
|
||||
<File Path="samples/GettingStarted/AgentWithOpenAI/README.md" />
|
||||
@@ -295,4 +296,4 @@
|
||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.UnitTests/Microsoft.Agents.AI.Hosting.UnitTests.csproj" />
|
||||
<Project Path="tests/Microsoft.Agents.AI.OpenAI.UnitTests/Microsoft.Agents.AI.OpenAI.UnitTests.csproj" />
|
||||
</Folder>
|
||||
</Solution>
|
||||
</Solution>
|
||||
+1
-2
@@ -3,8 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<NoWarn>VSTHRD200;CA1707</NoWarn>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<NoWarn>VSTHRD200;CA1707;CA1812</NoWarn>
|
||||
<RootNamespace>Agent_Step15_Plugins</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to use plugins with an AI agent. Plugin classes can
|
||||
// depend on other services that need to be injected. In this sample, the
|
||||
// AgentPlugin class uses the WeatherProvider and CurrentTimeProvider classes
|
||||
// to get weather and current time information. Both services are registered
|
||||
// in the service collection and injected into the plugin.
|
||||
// Plugin classes may have many methods, but only some are intended to be used
|
||||
// as AI functions. The AsAITools method of the plugin class shows how to specify
|
||||
// which methods should be exposed to the AI agent.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OpenAI;
|
||||
|
||||
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
|
||||
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
|
||||
|
||||
// Create a service collection to hold the agent plugin and its dependencies.
|
||||
ServiceCollection services = new();
|
||||
services.AddSingleton<WeatherProvider>();
|
||||
services.AddSingleton<CurrentTimeProvider>();
|
||||
services.AddSingleton<AgentPlugin>(); // The plugin depends on WeatherProvider and CurrentTimeProvider registered above.
|
||||
|
||||
IServiceProvider serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
const string AgentName = "Assistant";
|
||||
const string AgentInstructions = "You are a helpful assistant that helps people find information.";
|
||||
|
||||
AIAgent agent = new AzureOpenAIClient(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential())
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(
|
||||
instructions: AgentInstructions,
|
||||
name: AgentName,
|
||||
tools: [.. serviceProvider.GetRequiredService<AgentPlugin>().AsAITools()],
|
||||
services: serviceProvider); // Pass the service provider to the agent so it will be available to plugin functions to resolve dependencies.
|
||||
|
||||
Console.WriteLine(await agent.RunAsync("Tell me current time and weather in Seattle."));
|
||||
|
||||
/// <summary>
|
||||
/// The agent plugin that provides weather and current time information.
|
||||
/// </summary>
|
||||
/// <param name="weatherProvider">The weather provider to get weather information.</param>
|
||||
internal sealed class AgentPlugin(WeatherProvider weatherProvider)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the weather information for the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method demonstrates how to use the dependency that was injected into the plugin class.
|
||||
/// </remarks>
|
||||
/// <param name="location">The location to get the weather for.</param>
|
||||
/// <returns>The weather information for the specified location.</returns>
|
||||
public string GetWeather(string location)
|
||||
{
|
||||
return weatherProvider.GetWeather(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current date and time for the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method demonstrates how to resolve a dependency using the service provider passed to the method.
|
||||
/// </remarks>
|
||||
/// <param name="sp">The service provider to resolve the <see cref="CurrentTimeProvider"/>.</param>
|
||||
/// <param name="location">The location to get the current time for.</param>
|
||||
/// <returns>The current date and time as a <see cref="DateTimeOffset"/>.</returns>
|
||||
public DateTimeOffset GetCurrentTime(IServiceProvider sp, string location)
|
||||
{
|
||||
// Resolve the CurrentTimeProvider from the service provider
|
||||
var currentTimeProvider = sp.GetRequiredService<CurrentTimeProvider>();
|
||||
|
||||
return currentTimeProvider.GetCurrentTime(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the functions provided by this plugin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In real world scenarios, a class may have many methods and only a subset of them may be intended to be exposed as AI functions.
|
||||
/// This method demonstrates how to explicitly specify which methods should be exposed to the AI agent.
|
||||
/// </remarks>
|
||||
/// <returns>The functions provided by this plugin.</returns>
|
||||
public IEnumerable<AITool> AsAITools()
|
||||
{
|
||||
yield return AIFunctionFactory.Create(this.GetWeather);
|
||||
yield return AIFunctionFactory.Create(this.GetCurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The weather provider that returns weather information.
|
||||
/// </summary>
|
||||
internal sealed class WeatherProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the weather information for the specified location.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The weather information is hardcoded for demonstration purposes.
|
||||
/// In a real application, this could call a weather API to get actual weather data.
|
||||
/// </remarks>
|
||||
/// <param name="location">The location to get the weather for.</param>
|
||||
/// <returns>The weather information for the specified location.</returns>
|
||||
public string GetWeather(string location)
|
||||
{
|
||||
return $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current date and time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class returns the current date and time using the system's clock.
|
||||
/// </remarks>
|
||||
internal sealed class CurrentTimeProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current date and time.
|
||||
/// </summary>
|
||||
/// <param name="location">The location to get the current time for (not used in this implementation).</param>
|
||||
/// <returns>The current date and time as a <see cref="DateTimeOffset"/>.</returns>
|
||||
public DateTimeOffset GetCurrentTime(string location)
|
||||
{
|
||||
return DateTimeOffset.Now;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ Before you begin, ensure you have the following prerequisites:
|
||||
|[Exposing a simple agent as a function tool](./Agent_Step12_AsFunctionTool/)|This sample demonstrates how to expose an agent as a function tool|
|
||||
|[Using memory with an agent](./Agent_Step13_Memory/)|This sample demonstrates how to create a simple memory component and use it with an agent|
|
||||
|[Using middleware with an agent](./Agent_Step14_Middleware/)|This sample demonstrates how to use middleware with an agent|
|
||||
|[Using plugins with an agent](./Agent_Step15_Plugins/)|This sample demonstrates how to use plugins with an agent|
|
||||
|
||||
## Running the samples from the console
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ public static class OpenAIChatClientExtensions
|
||||
/// <param name="tools">Optional collection of AI tools that the agent can use during conversations.</param>
|
||||
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Chat Completion service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> is <see langword="null"/>.</exception>
|
||||
public static AIAgent CreateAIAgent(
|
||||
@@ -39,7 +40,8 @@ public static class OpenAIChatClientExtensions
|
||||
string? description = null,
|
||||
IList<AITool>? tools = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
ILoggerFactory? loggerFactory = null) =>
|
||||
ILoggerFactory? loggerFactory = null,
|
||||
IServiceProvider? services = null) =>
|
||||
client.CreateAIAgent(
|
||||
new ChatClientAgentOptions()
|
||||
{
|
||||
@@ -52,7 +54,8 @@ public static class OpenAIChatClientExtensions
|
||||
}
|
||||
},
|
||||
clientFactory,
|
||||
loggerFactory);
|
||||
loggerFactory,
|
||||
services);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="ChatClient"/> using the OpenAI Chat Completion API.
|
||||
@@ -61,13 +64,15 @@ public static class OpenAIChatClientExtensions
|
||||
/// <param name="options">Full set of options to configure the agent.</param>
|
||||
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Chat Completion service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
public static AIAgent CreateAIAgent(
|
||||
this ChatClient client,
|
||||
ChatClientAgentOptions options,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
ILoggerFactory? loggerFactory = null)
|
||||
ILoggerFactory? loggerFactory = null,
|
||||
IServiceProvider? services = null)
|
||||
{
|
||||
Throw.IfNull(client);
|
||||
Throw.IfNull(options);
|
||||
@@ -79,6 +84,6 @@ public static class OpenAIChatClientExtensions
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
return new ChatClientAgent(chatClient, options, loggerFactory);
|
||||
return new ChatClientAgent(chatClient, options, loggerFactory, services);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ public sealed class ChatClientAgent : AIAgent
|
||||
/// <param name="description">Optional description for the agent.</param>
|
||||
/// <param name="tools">Optional list of tools that the agent can use during invocation.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory to use for logging.</param>
|
||||
/// <param name="functionInvocationServices">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||
public ChatClientAgent(IChatClient chatClient, string? instructions = null, string? name = null, string? description = null, IList<AITool>? tools = null, ILoggerFactory? loggerFactory = null, IServiceProvider? functionInvocationServices = null)
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||
public ChatClientAgent(IChatClient chatClient, string? instructions = null, string? name = null, string? description = null, IList<AITool>? tools = null, ILoggerFactory? loggerFactory = null, IServiceProvider? services = null)
|
||||
: this(
|
||||
chatClient,
|
||||
new ChatClientAgentOptions
|
||||
@@ -50,7 +50,7 @@ public sealed class ChatClientAgent : AIAgent
|
||||
}
|
||||
},
|
||||
loggerFactory,
|
||||
functionInvocationServices)
|
||||
services)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ public sealed class ChatClientAgent : AIAgent
|
||||
/// <param name="chatClient">The chat client to use for invoking the agent.</param>
|
||||
/// <param name="options">Full set of options to configure the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory to use for logging.</param>
|
||||
/// <param name="functionInvocationServices">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||
public ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options, ILoggerFactory? loggerFactory = null, IServiceProvider? functionInvocationServices = null)
|
||||
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||
public ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options, ILoggerFactory? loggerFactory = null, IServiceProvider? services = null)
|
||||
{
|
||||
_ = Throw.IfNull(chatClient);
|
||||
|
||||
@@ -74,7 +74,7 @@ public sealed class ChatClientAgent : AIAgent
|
||||
this._chatClientType = chatClient.GetType();
|
||||
|
||||
// If the user has not opted out of using our default decorators, we wrap the chat client.
|
||||
this.ChatClient = options?.UseProvidedChatClientAsIs is true ? chatClient : chatClient.WithDefaultAgentMiddleware(options, functionInvocationServices);
|
||||
this.ChatClient = options?.UseProvidedChatClientAsIs is true ? chatClient : chatClient.WithDefaultAgentMiddleware(options, services);
|
||||
|
||||
this._logger = (loggerFactory ?? chatClient.GetService<ILoggerFactory>() ?? NullLoggerFactory.Instance).CreateLogger<ChatClientAgent>();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Microsoft.Extensions.AI;
|
||||
|
||||
internal static class ChatClientExtensions
|
||||
{
|
||||
internal static IChatClient WithDefaultAgentMiddleware(this IChatClient chatClient, ChatClientAgentOptions? options, IServiceProvider? functionInvocationServices = null)
|
||||
internal static IChatClient WithDefaultAgentMiddleware(this IChatClient chatClient, ChatClientAgentOptions? options, IServiceProvider? services = null)
|
||||
{
|
||||
var chatBuilder = chatClient.AsBuilder();
|
||||
|
||||
@@ -25,7 +25,7 @@ internal static class ChatClientExtensions
|
||||
});
|
||||
}
|
||||
|
||||
var agentChatClient = chatBuilder.Build(functionInvocationServices);
|
||||
var agentChatClient = chatBuilder.Build(services);
|
||||
|
||||
if (options?.ChatOptions?.Tools is { Count: > 0 })
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user