diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 403c61f54f5..0ba3890a2d6 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -402,7 +402,8 @@ - + + diff --git a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj b/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/A2AAgent_AsFunctionTool.csproj similarity index 77% rename from dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj rename to dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/A2AAgent_AsFunctionTool.csproj index d91b20e34bc..b23848c138d 100644 --- a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj +++ b/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/A2AAgent_AsFunctionTool.csproj @@ -10,14 +10,12 @@ - - - + diff --git a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/Program.cs b/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/Program.cs new file mode 100644 index 00000000000..9216c6c19c7 --- /dev/null +++ b/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/Program.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to expose an A2A agent as a function tool that another agent can call. + +using A2A; +using Azure.AI.Projects; +using Azure.Identity; +using Microsoft.Agents.AI; + +var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set."); +var model = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini"; +var a2aAgentHost = Environment.GetEnvironmentVariable("A2A_AGENT_HOST") ?? throw new InvalidOperationException("A2A_AGENT_HOST is not set."); + +// Initialize an A2ACardResolver to get an A2A agent card. +A2ACardResolver agentCardResolver = new(new Uri(a2aAgentHost)); + +// Get the agent card +AgentCard agentCard = await agentCardResolver.GetAgentCardAsync(); + +// Create an instance of the AIAgent for an existing A2A agent specified by the agent card. +AIAgent a2aAgent = agentCard.AsAIAgent(); + +// Create the main agent, and provide the A2A agent as a function tool. +// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. +// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid +// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. +AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential()) + .AsAIAgent( + model: model, + instructions: "You are a helpful assistant. Use the available tools to answer the user's questions.", + tools: [a2aAgent.AsAIFunction()] + ); + +// Invoke the agent and output the text result. +Console.WriteLine(await agent.RunAsync("What can you help me with?")); diff --git a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/README.md b/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/README.md new file mode 100644 index 00000000000..be055754606 --- /dev/null +++ b/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTool/README.md @@ -0,0 +1,30 @@ +# A2A Agent as a Function Tool + +This sample demonstrates how to expose an A2A agent as a single function tool that another AI agent can call. + +The sample: + +- Discovers a remote A2A agent from its agent card +- Converts the agent card to an `AIAgent` +- Converts that agent to one function tool with `AsAIFunction()` +- Registers the function tool with a main AI agent + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Access to the A2A agent host service +- A Microsoft Foundry project with a deployed model + +**Note**: These samples need to be run against a valid A2A server. If no A2A server is available, they can be run against the echo-agent that can be spun up locally by following the guidelines at: https://github.com/a2aproject/a2a-dotnet/blob/main/samples/AgentServer/README.md + +Set the following environment variables: + +Authenticate with Azure CLI by running `az login`, then set: + +```powershell +$env:A2A_AGENT_HOST="https://your-a2a-agent-host" # Replace with your A2A agent host endpoint +$env:FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project" # Replace with your Foundry project endpoint +$env:FOUNDRY_MODEL="gpt-5.4-mini" # Optional, defaults to gpt-5.4-mini +``` diff --git a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/README.md b/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/README.md deleted file mode 100644 index 33b5f692f2e..00000000000 --- a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# A2A Agent as Function Tools - -This sample demonstrates how to represent an A2A agent as a set of function tools, where each function tool corresponds to a skill of the A2A agent, -and register these function tools with another AI agent so it can leverage the A2A agent's skills. - -# Prerequisites - -Before you begin, ensure you have the following prerequisites: - -- .NET 10 SDK or later -- Access to the A2A agent host service - -**Note**: These samples need to be run against a valid A2A server. If no A2A server is available, they can be run against the echo-agent that can be -spun up locally by following the guidelines at: https://github.com/a2aproject/a2a-dotnet/blob/main/samples/AgentServer/README.md - -Set the following environment variables: - -```powershell -$env:A2A_AGENT_HOST="https://your-a2a-agent-host" # Replace with your A2A agent host endpoint -$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI resource endpoint -$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-5.4-mini" # Optional, defaults to gpt-5.4-mini -``` \ No newline at end of file diff --git a/dotnet/samples/02-agents/A2A/A2AAgent_Skills/A2AAgent_Skills.csproj b/dotnet/samples/02-agents/A2A/A2AAgent_Skills/A2AAgent_Skills.csproj new file mode 100644 index 00000000000..b23848c138d --- /dev/null +++ b/dotnet/samples/02-agents/A2A/A2AAgent_Skills/A2AAgent_Skills.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + + + + + + + diff --git a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/Program.cs b/dotnet/samples/02-agents/A2A/A2AAgent_Skills/Program.cs similarity index 77% rename from dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/Program.cs rename to dotnet/samples/02-agents/A2A/A2AAgent_Skills/Program.cs index d0b71ef7855..cf46ab51805 100644 --- a/dotnet/samples/02-agents/A2A/A2AAgent_AsFunctionTools/Program.cs +++ b/dotnet/samples/02-agents/A2A/A2AAgent_Skills/Program.cs @@ -1,39 +1,30 @@ // Copyright (c) Microsoft. All rights reserved. -// This sample shows how to represent an A2A agent as a set of function tools, where each function tool -// corresponds to a skill of the A2A agent, and register these function tools with another AI agent so -// it can leverage the A2A agent's skills. +// This sample shows how to expose each skill advertised by an A2A agent as a separate function tool. using System.Text.RegularExpressions; using A2A; -using Azure.AI.OpenAI; +using Azure.AI.Projects; using Azure.Identity; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; -using OpenAI.Chat; -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-5.4-mini"; +var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set."); +var model = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini"; var a2aAgentHost = Environment.GetEnvironmentVariable("A2A_AGENT_HOST") ?? throw new InvalidOperationException("A2A_AGENT_HOST is not set."); -// Initialize an A2ACardResolver to get an A2A agent card. +// Resolve the remote A2A agent and its advertised skills. A2ACardResolver agentCardResolver = new(new Uri(a2aAgentHost)); - -// Get the agent card AgentCard agentCard = await agentCardResolver.GetAgentCardAsync(); - -// Create an instance of the AIAgent for an existing A2A agent specified by the agent card. AIAgent a2aAgent = agentCard.AsAIAgent(); -// Create the main agent, and provide the a2a agent skills as a function tools. +// Create the main agent, and provide each A2A skill as a separate function tool. // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. // In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid // latency issues, unintended credential probing, and potential security risks from fallback mechanisms. -AIAgent agent = new AzureOpenAIClient( - new Uri(endpoint), - new DefaultAzureCredential()) - .GetChatClient(deploymentName) +AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential()) .AsAIAgent( + model: model, instructions: "You are a helpful assistant that helps people with travel planning.", tools: [.. CreateFunctionTools(a2aAgent, agentCard)] ); diff --git a/dotnet/samples/02-agents/A2A/A2AAgent_Skills/README.md b/dotnet/samples/02-agents/A2A/A2AAgent_Skills/README.md new file mode 100644 index 00000000000..06238052f04 --- /dev/null +++ b/dotnet/samples/02-agents/A2A/A2AAgent_Skills/README.md @@ -0,0 +1,34 @@ +# A2A Agent Skills + +This sample demonstrates how to expose each skill advertised by an A2A agent as a separate function tool for another AI agent. + +Unlike exposing the whole A2A agent as one tool, this approach gives the model a distinct tool for each skill. The sample uses the skill metadata from the A2A agent card to name and describe those tools. + +All generated tools invoke the same A2A agent. Their names and descriptions help the main agent choose an advertised capability; they do not invoke separate skill-specific endpoints. + +The sample: + +- Discovers a remote A2A agent and its skills from the agent card +- Creates one function tool for each advertised skill +- Adds the skill's description, tags, examples, and supported modes to the function description +- Registers all generated function tools with a main AI agent + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Access to the A2A agent host service +- A Microsoft Foundry project with a deployed model + +**Note**: These samples need to be run against a valid A2A server. If no A2A server is available, they can be run against the echo-agent that can be spun up locally by following the guidelines at: https://github.com/a2aproject/a2a-dotnet/blob/main/samples/AgentServer/README.md + +Set the following environment variables: + +Authenticate with Azure CLI by running `az login`, then set: + +```powershell +$env:A2A_AGENT_HOST="https://your-a2a-agent-host" # Replace with your A2A agent host endpoint +$env:FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/api/projects/your-project" # Replace with your Foundry project endpoint +$env:FOUNDRY_MODEL="gpt-5.4-mini" # Optional, defaults to gpt-5.4-mini +``` diff --git a/dotnet/samples/02-agents/A2A/README.md b/dotnet/samples/02-agents/A2A/README.md index 28f2c0a9102..8f6cecc7e43 100644 --- a/dotnet/samples/02-agents/A2A/README.md +++ b/dotnet/samples/02-agents/A2A/README.md @@ -13,7 +13,8 @@ See the README.md for each sample for the prerequisites for that sample. |Sample|Description| |---|---| -|[A2A Agent As Function Tools](./A2AAgent_AsFunctionTools/)|This sample demonstrates how to represent an A2A agent as a set of function tools, where each function tool corresponds to a skill of the A2A agent, and register these function tools with another AI agent so it can leverage the A2A agent's skills.| +|[A2A Agent as a Function Tool](./A2AAgent_AsFunctionTool/)|This sample demonstrates how to expose an A2A agent as a single function tool that another AI agent can call.| +|[A2A Agent Skills](./A2AAgent_Skills/)|This sample demonstrates how to expose each skill advertised by an A2A agent as a separate function tool.| |[A2A Agent Polling For Task Completion](./A2AAgent_PollingForTaskCompletion/)|This sample demonstrates how to poll for long-running task completion using continuation tokens with an A2A agent.| |[A2A Agent Stream Reconnection](./A2AAgent_StreamReconnection/)|This sample demonstrates how to reconnect to an A2A agent's streaming response using continuation tokens, allowing recovery from stream interruptions.| |[A2A Agent Protocol Selection](./A2AAgent_ProtocolSelection/)|This sample demonstrates how to select the A2A protocol binding (HTTP+JSON vs JSON-RPC) when creating an AIAgent from an A2A agent card using A2AClientOptions.| @@ -23,7 +24,7 @@ See the README.md for each sample for the prerequisites for that sample. To run the samples, navigate to the desired sample directory, e.g. ```powershell -cd A2AAgent_AsFunctionTools +cd A2AAgent_AsFunctionTool ``` Set the required environment variables as documented in the sample readme.