Merge branch 'feature-foundry-agents' into feature-declarative-agents-dotnet

This commit is contained in:
Mark Wallace
2025-11-12 12:10:27 +00:00
committed by GitHub
95 changed files with 2829 additions and 858 deletions
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.Agents.Persistent" />
<PackageReference Include="Azure.Identity" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI.Persistent\Microsoft.Agents.AI.AzureAI.Persistent.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample shows how to create an Azure AI Foundry Agent with the Deep Research Tool.
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Agents.AI;
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
var deepResearchDeploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEEP_RESEARCH_DEPLOYMENT_NAME") ?? "o3-deep-research";
var modelDeploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o";
var bingConnectionId = Environment.GetEnvironmentVariable("BING_CONNECTION_ID") ?? throw new InvalidOperationException("BING_CONNECTION_ID is not set.");
// Configure extended network timeout for long-running Deep Research tasks.
PersistentAgentsAdministrationClientOptions persistentAgentsClientOptions = new();
persistentAgentsClientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(20);
// Get a client to create/retrieve server side agents with.
PersistentAgentsClient persistentAgentsClient = new(endpoint, new AzureCliCredential(), persistentAgentsClientOptions);
// Define and configure the Deep Research tool.
DeepResearchToolDefinition deepResearchTool = new(new DeepResearchDetails(
bingGroundingConnections: [new(bingConnectionId)],
model: deepResearchDeploymentName)
);
// Create an agent with the Deep Research tool on the Azure AI agent service.
AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync(
model: modelDeploymentName,
name: "DeepResearchAgent",
instructions: "You are a helpful Agent that assists in researching scientific topics.",
tools: [deepResearchTool]);
const string Task = "Research the current state of studies on orca intelligence and orca language, " +
"including what is currently known about orcas' cognitive capabilities and communication systems.";
Console.WriteLine($"# User: '{Task}'");
Console.WriteLine();
try
{
AgentThread thread = agent.GetNewThread();
await foreach (var response in agent.RunStreamingAsync(Task, thread))
{
Console.Write(response.Text);
}
}
finally
{
await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id);
}
@@ -0,0 +1,47 @@
# What this sample demonstrates
This sample demonstrates how to create an Azure AI Agent with the Deep Research Tool, which leverages the o3-deep-research reasoning model to perform comprehensive research on complex topics.
Key features:
- Configuring and using the Deep Research Tool with Bing grounding
- Creating a persistent AI agent with deep research capabilities
- Executing deep research queries and retrieving results
## Prerequisites
Before running this sample, ensure you have:
1. An Azure AI Foundry project set up
2. A deep research model deployment (e.g., o3-deep-research)
3. A model deployment (e.g., gpt-4o)
4. A Bing Connection configured in your Azure AI Foundry project
5. Azure CLI installed and authenticated
**Important**: Please visit the following documentation for detailed setup instructions:
- [Deep Research Tool Documentation](https://aka.ms/agents-deep-research)
- [Research Tool Setup](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/tools/deep-research#research-tool-setup)
Pay special attention to the purple `Note` boxes in the Azure documentation.
**Note**: The Bing Connection ID must be from the **project**, not the resource. It has the following format:
```
/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/<provider_name>/accounts/<account_name>/projects/<project_name>/connections/<connection_name>
```
## Environment Variables
Set the following environment variables:
```powershell
# Replace with your Azure AI Foundry project endpoint
$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/"
# Replace with your Bing connection ID from the project
$env:BING_CONNECTION_ID="/subscriptions/.../connections/your-bing-connection"
# Optional, defaults to o3-deep-research
$env:AZURE_FOUNDRY_PROJECT_DEEP_RESEARCH_DEPLOYMENT_NAME="o3-deep-research"
# Optional, defaults to gpt-4o
$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o"
@@ -44,6 +44,7 @@ Before you begin, ensure you have the following prerequisites:
|[Using plugins with an agent](./Agent_Step15_Plugins/)|This sample demonstrates how to use plugins with an agent|
|[Reducing chat history size](./Agent_Step16_ChatReduction/)|This sample demonstrates how to reduce the chat history to constrain its size, where chat history is maintained locally|
|[Background responses](./Agent_Step17_BackgroundResponses/)|This sample demonstrates how to use background responses for long-running operations with polling and resumption support|
|[Deep research with an agent](./Agent_Step18_DeepResearch/)|This sample demonstrates how to use the Deep Research Tool to perform comprehensive research on complex topics|
## Running the samples from the console
@@ -16,7 +16,7 @@ internal static class WorkflowFactory
internal static Workflow BuildWorkflow(IChatClient chatClient)
{
// Create executors
var startExecutor = new ConcurrentStartExecutor();
var startExecutor = new ChatForwardingExecutor("Start");
var aggregationExecutor = new ConcurrentAggregationExecutor();
AIAgent frenchAgent = GetLanguageAgent("French", chatClient);
AIAgent englishAgent = GetLanguageAgent("English", chatClient);
@@ -38,33 +38,11 @@ internal static class WorkflowFactory
private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient) =>
new(chatClient, instructions: $"You're a helpful assistant who always responds in {targetLanguage}.", name: $"{targetLanguage}Agent");
/// <summary>
/// Executor that starts the concurrent processing by sending messages to the agents.
/// </summary>
private sealed class ConcurrentStartExecutor() : Executor("ConcurrentStartExecutor")
{
protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder)
{
return routeBuilder
.AddHandler<List<ChatMessage>>(this.RouteMessages)
.AddHandler<TurnToken>(this.RouteTurnTokenAsync);
}
private ValueTask RouteMessages(List<ChatMessage> messages, IWorkflowContext context, CancellationToken cancellationToken)
{
return context.SendMessageAsync(messages, cancellationToken: cancellationToken);
}
private ValueTask RouteTurnTokenAsync(TurnToken token, IWorkflowContext context, CancellationToken cancellationToken)
{
return context.SendMessageAsync(token, cancellationToken: cancellationToken);
}
}
/// <summary>
/// Executor that aggregates the results from the concurrent agents.
/// </summary>
private sealed class ConcurrentAggregationExecutor() : Executor<List<ChatMessage>>("ConcurrentAggregationExecutor")
private sealed class ConcurrentAggregationExecutor() :
Executor<List<ChatMessage>>("ConcurrentAggregationExecutor"), IResettableExecutor
{
private readonly List<ChatMessage> _messages = [];
@@ -85,5 +63,12 @@ internal static class WorkflowFactory
await context.YieldOutputAsync(formattedMessages, cancellationToken);
}
}
/// <inheritdoc/>
public ValueTask ResetAsync()
{
this._messages.Clear();
return default;
}
}
}
@@ -28,6 +28,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
<ItemGroup>
@@ -10,7 +10,7 @@ namespace Demo.Workflows.Declarative.ConfirmInput;
/// and confirm it matches the original input.
/// </summary>
/// <remarks>
/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
/// </remarks>
internal sealed class Program
@@ -28,6 +28,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
<ItemGroup>
@@ -14,7 +14,7 @@ namespace Demo.Workflows.Declarative.DeepResearch;
/// using the Magentic orchestration pattern developed by AutoGen.
/// </summary>
/// <remarks>
/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
/// </remarks>
internal sealed class Program
@@ -29,6 +29,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
</Project>
@@ -28,6 +28,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
</Project>
@@ -28,6 +28,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
<ItemGroup>
@@ -15,7 +15,7 @@ namespace Demo.Workflows.Declarative.FunctionTools;
/// with function tools assigned. Exits the loop when the user enters "exit".
/// </summary>
/// <remarks>
/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
/// </remarks>
internal sealed class Program
@@ -42,7 +42,7 @@ internal sealed class Program
Console.WriteLine(code);
}
private const string DefaultWorkflow = "HelloWorld.yaml";
private const string DefaultWorkflow = "Marketing.yaml";
private string WorkflowFile { get; }
@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ProjectsDebugTargetFrameworks>net9.0</ProjectsDebugTargetFrameworks>
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<InjectIsExternalInitOnLegacy>true</InjectIsExternalInitOnLegacy>
<InjectSharedFoundryAgents>true</InjectSharedFoundryAgents>
<InjectSharedWorkflowsExecution>true</InjectSharedWorkflowsExecution>
<InjectSharedWorkflowsSettings>true</InjectSharedWorkflowsSettings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Logging" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="InputArguments.yaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,97 @@
#
# This workflow demonstrates providing input arguments to an agent.
#
# Example input:
# I'd like to go on vacation.
#
kind: Workflow
trigger:
kind: OnConversationStart
id: workflow_demo
actions:
# Capture the original user message for input to the location-aware agent
- kind: SetVariable
id: set_count_increment
variable: Local.InputMessage
value: =System.LastMessage
# Invoke the triage agent to determine location requirements
- kind: InvokeAzureAgent
id: solicit_input
conversationId: =System.ConversationId
agent:
name: LocationTriageAgent
input:
messages: =Local.ActionMessage
output:
messages: Local.TriageResponse
# Request input from the user based on the triage response
- kind: RequestExternalInput
id: request_requirements
variable: Local.NextInput
# Capture the most recent interaction for evaluation
- kind: SetTextVariable
id: set_status_message
variable: Local.LocationStatusInput
value: |-
AGENT - {MessageText(Local.TriageResponse)}
USER - {MessageText(Local.NextInput)}
# Evaluate the status of the location triage
- kind: InvokeAzureAgent
id: evaluate_location
agent:
name: LocationCaptureAgent
input:
messages: =UserMessage(Local.LocationStatusInput)
output:
responseObject: Local.LocationResponse
# Determine if the location information is complete
- kind: ConditionGroup
id: check_completion
conditions:
- condition: |-
=Local.LocationResponse.is_location_defined = false Or
Local.LocationResponse.is_location_confirmed = false
id: check_done
actions:
# Capture the action message for input to the triage agent
- kind: SetVariable
id: set_next_message
variable: Local.ActionMessage
value: =AgentMessage(Local.LocationResponse.action)
- kind: GotoAction
id: goto_solicit_input
actionId: solicit_input
elseActions:
# Create a new conversation so the prior context does not interfere
- kind: CreateConversation
id: conversation_location
conversationId: Local.LocationConversationId
# Invoke the location-aware agent with the location argument
# and loop until the user types "EXIT"
- kind: InvokeAzureAgent
id: location_response
conversationId: =Local.LocationConversationId
agent:
name: LocationAwareAgent
input:
messages: =Local.InputMessage
arguments:
location: =Local.LocationResponse.place
externalLoop:
when: =Upper(System.LastMessage.Text) <> "EXIT"
output:
autoSend: true
@@ -0,0 +1,147 @@
// Copyright (c) Microsoft. All rights reserved.
using Azure.AI.Agents;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using OpenAI.Responses;
using Shared.Foundry;
using Shared.Workflows;
namespace Demo.Workflows.Declarative.InputArguments;
/// <summary>
/// Demonstrate a workflow that consumes input arguments to dynamically enhance the agent
/// instructions. Exits the loop when the user enters "exit".
/// </summary>
/// <remarks>
/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
/// </remarks>
internal sealed class Program
{
public static async Task Main(string[] args)
{
// Initialize configuration
IConfiguration configuration = Application.InitializeConfig();
Uri foundryEndpoint = new(configuration.GetValue(Application.Settings.FoundryEndpoint));
// Ensure sample agents exist in Foundry.
await CreateAgentAsync(foundryEndpoint, configuration);
// Get input from command line or console
string workflowInput = Application.GetInput(args);
// Create the workflow factory. This class demonstrates how to initialize a
// declarative workflow from a YAML file. Once the workflow is created, it
// can be executed just like any regular workflow.
WorkflowFactory workflowFactory = new("InputArguments.yaml", foundryEndpoint);
// Execute the workflow: The WorkflowRunner demonstrates how to execute
// a workflow, handle the workflow events, and providing external input.
// This also includes the ability to checkpoint workflow state and how to
// resume execution.
WorkflowRunner runner = new();
await runner.ExecuteAsync(workflowFactory.CreateWorkflow, workflowInput);
}
private static async Task CreateAgentAsync(Uri foundryEndpoint, IConfiguration configuration)
{
AgentClient agentsClient = new(foundryEndpoint, new AzureCliCredential());
await agentsClient.CreateAgentAsync(
agentName: "LocationTriageAgent",
agentDefinition: DefineLocationTriageAgent(configuration),
agentDescription: "Chats with the user to solicit a location of interest.");
await agentsClient.CreateAgentAsync(
agentName: "LocationCaptureAgent",
agentDefinition: DefineLocationCaptureAgent(configuration),
agentDescription: "Evaluate the status of soliciting the location.");
await agentsClient.CreateAgentAsync(
agentName: "LocationAwareAgent",
agentDefinition: DefineLocationAwareAgent(configuration),
agentDescription: "Chats with the user with location awareness.");
}
private static PromptAgentDefinition DefineLocationTriageAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModelMini))
{
Instructions =
"""
Your only job is to solicit a location from the user.
Always repeat back the location when addressing the user, except when it is not known.
"""
};
private static PromptAgentDefinition DefineLocationCaptureAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModelMini))
{
Instructions =
"""
Request a location from the user. This location could be their own location
or perhaps a location they are interested in.
City level precision is sufficient.
If extrapolating region and country, confirm you have it right.
""",
TextOptions =
new ResponseTextOptions
{
TextFormat =
ResponseTextFormat.CreateJsonSchemaFormat(
"TaskEvaluation",
BinaryData.FromString(
"""
{
"type": "object",
"properties": {
"place": {
"type": "string",
"description": "Captures only your understanding of the location specified by the user without explanation, or 'unknown' if not yet defined."
},
"action": {
"type": "string",
"description": "The instruction for the next action to take regarding the need for additional detail or confirmation."
},
"is_location_defined": {
"type": "boolean",
"description": "True if the user location is understood."
},
"is_location_confirmed": {
"type": "boolean",
"description": "True if the user location is confirmed. An unambiguous location may be implicitly confirmed without explicit user confirmation."
}
},
"required": ["place", "action", "is_location_defined", "is_location_confirmed"],
"additionalProperties": false
}
"""),
jsonSchemaFormatDescription: null,
jsonSchemaIsStrict: true),
}
};
private static PromptAgentDefinition DefineLocationAwareAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModelMini))
{
// Parameterized instructions reference the "location" input argument.
Instructions =
"""
Talk to the user about their request.
Their request is related to a specific location: {{location}}.
""",
StructuredInputs =
{
["location"] =
new StructuredInputDefinition
{
IsRequired = false,
DefaultValue = BinaryData.FromString(@"""unknown"""),
Description = "The user's location",
}
}
};
}
@@ -28,6 +28,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
<ItemGroup>
@@ -13,7 +13,7 @@ namespace Demo.Workflows.Declarative.Marketing;
/// sequentially engaging in a task.
/// </summary>
/// <remarks>
/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
/// </remarks>
internal sealed class Program
@@ -86,12 +86,14 @@ To run the sampes from the command line:
1. From the root of the repository, navigate the console to the project folder:
```sh
cd dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher
cd dotnet/samples/GettingStarted/Workflows/Declarative/Marketing
dotnet run Marketing
```
2. Run the demo and optionally provided input:
```sh
dotnet run "How would you compute the value of PI?"
dotnet run "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours."
dotnet run c:/myworkflows/Marketing.yaml
```
> The sample will allow for interactive input in the absence of an input argument.
@@ -13,7 +13,7 @@ namespace Demo.Workflows.Declarative.StudentTeacher;
/// in an iterative conversation.
/// </summary>
/// <remarks>
/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
/// </remarks>
internal sealed class Program
@@ -28,6 +28,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
<ItemGroup>
@@ -14,7 +14,7 @@ namespace Demo.Workflows.Declarative.ToolApproval;
/// has an MCP tool that requires approval. Exits the loop when the user enters "exit".
/// </summary>
/// <remarks>
/// See the README.md file in the parent folder (../Declarative/README.md) for detailed
/// See the README.md file in the parent folder (../README.md) for detailed
/// information the configuration required to run this sample.
/// </remarks>
internal sealed class Program
@@ -28,6 +28,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
<ProjectReference Include="..\..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj" />
</ItemGroup>
<ItemGroup>