mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: [BREAKING] Change implementation to use AsFunctionTool extension method to create a function from an agent (#686)
* Change implementation to use AsFunctionTool extension method to create a function from an agent * Update dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentExtensionsTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address code review feedback and add a new sample * Address some code review feedback --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
2a75482777
commit
636e3acf37
@@ -49,6 +49,7 @@
|
||||
<Project Path="samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Agent_Step09_DependencyInjection.csproj" />
|
||||
<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" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/AgentWithOpenAI/">
|
||||
<File Path="samples/GettingStarted/AgentWithOpenAI/README.md" />
|
||||
|
||||
@@ -25,7 +25,7 @@ internal sealed class HostClientAgent
|
||||
// Connect to the remote agents via A2A
|
||||
var createAgentTasks = agentUrls.Select(agentUrl => this.CreateAgentAsync(agentUrl));
|
||||
var agents = await Task.WhenAll(createAgentTasks);
|
||||
var tools = agents.Select(agent => (AITool)AgentAIFunctionFactory.CreateFromAgent(agent)).ToList();
|
||||
var tools = agents.Select(agent => (AITool)agent.AsAIFunction()).ToList();
|
||||
|
||||
// Create the agent that uses the remote agents as tools
|
||||
this.Agent = new OpenAIClient(new ApiKeyCredential(apiKey))
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<UserSecretsId>3afc9b74-af74-4d8e-ae96-fa1c511d11ac</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Extensions.AI.Agents.OpenAI\Microsoft.Extensions.AI.Agents.OpenAI.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Extensions.AI.Agents\Microsoft.Extensions.AI.Agents.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
// This sample shows how to create and use a Azure OpenAI AI agent as a function tool.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.AI.Agents;
|
||||
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";
|
||||
|
||||
[Description("Get the weather for a given location.")]
|
||||
static string GetWeather([Description("The location to get the weather for.")] string location)
|
||||
=> $"The weather in {location} is cloudy with a high of 15°C.";
|
||||
|
||||
// Create the chat client and agent, and provide the function tool to the agent.
|
||||
AIAgent weatherAgent = new AzureOpenAIClient(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential())
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(
|
||||
instructions: "You answer questions about the weather.",
|
||||
name: "WeatherAgent",
|
||||
description: "An agent that answers questions about the weather.",
|
||||
tools: [AIFunctionFactory.Create(GetWeather)]);
|
||||
|
||||
// Create the main agent, and provide the weather agent as a function tool.
|
||||
AIAgent agent = new AzureOpenAIClient(
|
||||
new Uri(endpoint),
|
||||
new AzureCliCredential())
|
||||
.GetChatClient(deploymentName)
|
||||
.CreateAIAgent(instructions: "You are a helpful assistant who responds in French.", tools: [weatherAgent.AsAIFunction()]);
|
||||
|
||||
// Invoke the agent and output the text result.
|
||||
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));
|
||||
@@ -37,6 +37,7 @@ Before you begin, ensure you have the following prerequisites:
|
||||
|[Dependency injection with a simple agent](./Agent_Step09_DependencyInjection/)|This sample demonstrates how to add and resolve an agent with a dependency injection container|
|
||||
|[Exposing a simple agent as MCP tool](./Agent_Step10_AsMcpTool/)|This sample demonstrates how to expose an agent as an MCP tool|
|
||||
|[Using images with a simple agent](./Agent_Step11_UsingImages/)|This sample demonstrates how to use image multi-modality with an AI agent|
|
||||
|[Exposing a simple agent a function tool](./Agent_Step12_AsFunctionTool/)|This sample demonstrates how to expose an agent as a function tool|
|
||||
|
||||
## Running the samples from the console
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
using static Microsoft.Extensions.AI.Agents.OpenTelemetryConsts.GenAI;
|
||||
|
||||
namespace Microsoft.Extensions.AI.Agents;
|
||||
|
||||
/// <summary>
|
||||
/// Provides factory methods for creating implementations of <see cref="AIFunction"/> backed by an <see cref="AIAgent" />.
|
||||
/// </summary>
|
||||
public static class AgentAIFunctionFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="AIFunction"/> that will invoke the provided Agent.
|
||||
/// </summary>
|
||||
/// <param name="agent">The <see cref="Agent" /> to be represented via the created <see cref="AIFunction"/>.</param>
|
||||
/// <param name="options">Metadata to use to override defaults inferred from <paramref name="agent"/>.</param>
|
||||
/// <returns>The created <see cref="AIFunction"/> for invoking the <see cref="AIAgent"/>.</returns>
|
||||
public static AIFunction CreateFromAgent(
|
||||
AIAgent agent,
|
||||
AIFunctionFactoryOptions? options = null)
|
||||
{
|
||||
Throw.IfNull(agent);
|
||||
|
||||
async Task<string> RunAgentAsync(string query, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await agent.RunAsync(query, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
return response.Text;
|
||||
}
|
||||
|
||||
return AIFunctionFactory.Create(RunAgentAsync, options ?? new()
|
||||
{
|
||||
Name = agent.Name,
|
||||
Description = agent.Description,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Extensions.AI.Agents;
|
||||
|
||||
@@ -24,4 +28,31 @@ public static class AgentExtensions
|
||||
EnableSensitiveData = enableSensitiveData ?? false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="AIFunction"/> that will invoke the provided Agent.
|
||||
/// </summary>
|
||||
/// <param name="agent">The <see cref="AIAgent" /> to be represented via the created <see cref="AIFunction"/>.</param>
|
||||
/// <param name="options">Metadata to use to override defaults inferred from <paramref name="agent"/>.</param>
|
||||
/// <param name="thread">The <see cref="AgentThread"/> to use for the function.</param>
|
||||
/// <returns>The created <see cref="AIFunction"/> for invoking the <see cref="AIAgent"/>.</returns>
|
||||
public static AIFunction AsAIFunction(this AIAgent agent, AIFunctionFactoryOptions? options = null, AgentThread? thread = null)
|
||||
{
|
||||
Throw.IfNull(agent);
|
||||
|
||||
[Description("Invoke an agent to retrieve some information.")]
|
||||
async Task<string> InvokeAgentAsync(
|
||||
[Description("Input query to invoke the agent.")] string query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await agent.RunAsync(query, thread: thread, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
return response.Text;
|
||||
}
|
||||
|
||||
options ??= new();
|
||||
options.Name ??= agent.Name;
|
||||
options.Description ??= agent.Description;
|
||||
|
||||
return AIFunctionFactory.Create(InvokeAgentAsync, options);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -10,16 +10,16 @@ using Moq;
|
||||
namespace Microsoft.Extensions.AI.Agents.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="AgentAIFunctionFactory"/> class.
|
||||
/// Unit tests for the <see cref="AgentExtensions.AsAIFunction"/> method.
|
||||
/// </summary>
|
||||
public class AgentAIFunctionFactoryTests
|
||||
public class AgentExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void CreateFromAgent_WithNullAgent_ThrowsArgumentNullException()
|
||||
{
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentNullException>(() =>
|
||||
AgentAIFunctionFactory.CreateFromAgent(null!));
|
||||
AgentExtensions.AsAIFunction(null!));
|
||||
|
||||
Assert.Equal("agent", exception.ParamName);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class AgentAIFunctionFactoryTests
|
||||
mockAgent.Setup(a => a.Description).Returns("Test agent description");
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object);
|
||||
var result = mockAgent.Object.AsAIFunction();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -51,7 +51,7 @@ public class AgentAIFunctionFactoryTests
|
||||
mockAgent.Setup(a => a.Description).Returns("Test description");
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object);
|
||||
var result = mockAgent.Object.AsAIFunction();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -60,7 +60,7 @@ public class AgentAIFunctionFactoryTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFromAgent_WithAgentHavingNullDescription_UsesEmptyDescription()
|
||||
public void CreateFromAgent_WithAgentHavingNullDescription_UsesDefaultDescription()
|
||||
{
|
||||
// Arrange
|
||||
var mockAgent = new Mock<AIAgent>();
|
||||
@@ -68,12 +68,12 @@ public class AgentAIFunctionFactoryTests
|
||||
mockAgent.Setup(a => a.Description).Returns((string?)null);
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object);
|
||||
var result = mockAgent.Object.AsAIFunction();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("TestAgent", result.Name);
|
||||
Assert.Equal(string.Empty, result.Description);
|
||||
Assert.Equal("Invoke an agent to retrieve some information.", result.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -91,7 +91,7 @@ public class AgentAIFunctionFactoryTests
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object, customOptions);
|
||||
var result = mockAgent.Object.AsAIFunction(customOptions);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -108,7 +108,7 @@ public class AgentAIFunctionFactoryTests
|
||||
mockAgent.Setup(a => a.Description).Returns("Test agent description");
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object, null);
|
||||
var result = mockAgent.Object.AsAIFunction(null);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -123,7 +123,7 @@ public class AgentAIFunctionFactoryTests
|
||||
var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Test response"));
|
||||
var testAgent = new TestAgent("TestAgent", "Test description", expectedResponse);
|
||||
|
||||
var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent);
|
||||
var aiFunction = testAgent.AsAIFunction();
|
||||
|
||||
// Act
|
||||
var arguments = new AIFunctionArguments() { ["query"] = "Test query" };
|
||||
@@ -143,7 +143,7 @@ public class AgentAIFunctionFactoryTests
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
|
||||
var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent);
|
||||
var aiFunction = testAgent.AsAIFunction();
|
||||
|
||||
// Act
|
||||
var arguments = new AIFunctionArguments() { ["query"] = "Test query" };
|
||||
@@ -161,7 +161,7 @@ public class AgentAIFunctionFactoryTests
|
||||
var expectedException = new InvalidOperationException("Test exception");
|
||||
var testAgent = new TestAgent("TestAgent", "Test description", expectedException);
|
||||
|
||||
var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent);
|
||||
var aiFunction = testAgent.AsAIFunction();
|
||||
|
||||
// Act & Assert
|
||||
var arguments = new AIFunctionArguments() { ["query"] = "Test query" };
|
||||
@@ -180,7 +180,7 @@ public class AgentAIFunctionFactoryTests
|
||||
mockAgent.Setup(a => a.Description).Returns("Test description");
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object);
|
||||
var result = mockAgent.Object.AsAIFunction();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -204,7 +204,7 @@ public class AgentAIFunctionFactoryTests
|
||||
mockAgent.Setup(a => a.Description).Returns("Test description");
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object);
|
||||
var result = mockAgent.Object.AsAIFunction();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -221,7 +221,7 @@ public class AgentAIFunctionFactoryTests
|
||||
mockAgent.Setup(a => a.Description).Returns(string.Empty);
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object);
|
||||
var result = mockAgent.Object.AsAIFunction();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -244,7 +244,7 @@ public class AgentAIFunctionFactoryTests
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object, customOptions);
|
||||
var result = mockAgent.Object.AsAIFunction(customOptions);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
@@ -265,7 +265,7 @@ public class AgentAIFunctionFactoryTests
|
||||
};
|
||||
|
||||
var testAgent = new TestAgent("TestAgent", "Test description", expectedResponse);
|
||||
var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent);
|
||||
var aiFunction = testAgent.AsAIFunction();
|
||||
|
||||
// Act
|
||||
var arguments = new AIFunctionArguments() { ["query"] = "Test query" };
|
||||
Reference in New Issue
Block a user