Skip to content

Commit 40e0538

Browse files
MattB-msftCopilot
andauthored
Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 02a1dcd commit 40e0538

5 files changed

Lines changed: 254 additions & 254 deletions

File tree

dotnet/agent-framework/sample-agent/Agent/MyAgent.cs

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,53 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
2-
// Licensed under the MIT License.
3-
4-
using Agent365AgentFrameworkSampleAgent.telemetry;
5-
using Agent365AgentFrameworkSampleAgent.Tools;
6-
using Microsoft.Agents.A365.Observability.Caching;
7-
using Microsoft.Agents.A365.Runtime.Utils;
8-
using Microsoft.Agents.A365.Tooling.Extensions.AgentFramework.Services;
9-
using Microsoft.Agents.AI;
10-
using Microsoft.Agents.Builder;
11-
using Microsoft.Agents.Builder.App;
12-
using Microsoft.Agents.Builder.State;
13-
using Microsoft.Agents.Core;
14-
using Microsoft.Agents.Core.Models;
15-
using Microsoft.Agents.Core.Serialization;
16-
using Microsoft.Extensions.AI;
17-
using System.Collections.Concurrent;
18-
using System.Text.Json;
19-
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Agent365AgentFrameworkSampleAgent.telemetry;
5+
using Agent365AgentFrameworkSampleAgent.Tools;
6+
using Microsoft.Agents.A365.Observability.Caching;
7+
using Microsoft.Agents.A365.Runtime.Utils;
8+
using Microsoft.Agents.A365.Tooling.Extensions.AgentFramework.Services;
9+
using Microsoft.Agents.AI;
10+
using Microsoft.Agents.Builder;
11+
using Microsoft.Agents.Builder.App;
12+
using Microsoft.Agents.Builder.State;
13+
using Microsoft.Agents.Core;
14+
using Microsoft.Agents.Core.Models;
15+
using Microsoft.Agents.Core.Serialization;
16+
using Microsoft.Extensions.AI;
17+
using System.Collections.Concurrent;
18+
using System.Text.Json;
19+
2020
namespace Agent365AgentFrameworkSampleAgent.Agent
2121
{
2222
public class MyAgent : AgentApplication
2323
{
2424
private readonly string AgentWelcomeMessage = "Hello! I can help you find information based on what I can access";
2525

26-
private readonly string AgentInstructions = """
27-
You will speak like a friendly and professional virtual assistant.
28-
29-
For questions about yourself, you should use the one of the tools: {{mcp_graph_getMyProfile}}, {{mcp_graph_getUserProfile}}, {{mcp_graph_getMyManager}}, {{mcp_graph_getUsersManager}}.
30-
31-
If you are working with weather information, the following instructions apply:
32-
Location is a city name, 2 letter US state codes should be resolved to the full name of the United States State.
33-
You may ask follow up questions until you have enough information to answer the customers question, but once you have the current weather or a forecast, make sure to format it nicely in text.
34-
- For current weather, Use the {{WeatherLookupTool.GetCurrentWeatherForLocation}}, you should include the current temperature, low and high temperatures, wind speed, humidity, and a short description of the weather.
35-
- For forecast's, Use the {{WeatherLookupTool.GetWeatherForecastForLocation}}, you should report on the next 5 days, including the current day, and include the date, high and low temperatures, and a short description of the weather.
36-
- You should use the {{DateTimePlugin.GetDateTime}} to get the current date and time.
37-
38-
Otherwise you should use the tools available to you to help answer the user's questions.
26+
private readonly string AgentInstructions = """
27+
You will speak like a friendly and professional virtual assistant.
28+
29+
For questions about yourself, you should use the one of the tools: {{mcp_graph_getMyProfile}}, {{mcp_graph_getUserProfile}}, {{mcp_graph_getMyManager}}, {{mcp_graph_getUsersManager}}.
30+
31+
If you are working with weather information, the following instructions apply:
32+
Location is a city name, 2 letter US state codes should be resolved to the full name of the United States State.
33+
You may ask follow up questions until you have enough information to answer the customers question, but once you have the current weather or a forecast, make sure to format it nicely in text.
34+
- For current weather, Use the {{WeatherLookupTool.GetCurrentWeatherForLocation}}, you should include the current temperature, low and high temperatures, wind speed, humidity, and a short description of the weather.
35+
- For forecast's, Use the {{WeatherLookupTool.GetWeatherForecastForLocation}}, you should report on the next 5 days, including the current day, and include the date, high and low temperatures, and a short description of the weather.
36+
- You should use the {{DateTimePlugin.GetDateTime}} to get the current date and time.
37+
38+
Otherwise you should use the tools available to you to help answer the user's questions.
3939
""";
4040

4141
private readonly IChatClient? _chatClient = null;
4242
private readonly IConfiguration? _configuration = null;
4343
private readonly IExporterTokenCache<AgenticTokenStruct>? _agentTokenCache = null;
4444
private readonly ILogger<MyAgent>? _logger = null;
45-
private IMcpToolRegistrationService? _toolService = null;
45+
private readonly IMcpToolRegistrationService? _toolService = null;
4646
// Setup reusable auto sign-in handlers
4747
private readonly string AgenticIdAuthHanlder = "agentic";
4848
private readonly string MyAuthHanlder = "me";
4949
// Temp
50-
private static ConcurrentDictionary<string, List<AITool>> _agentToolCache = new();
50+
private static readonly ConcurrentDictionary<string, List<AITool>> _agentToolCache = new();
5151

5252
public MyAgent(AgentApplicationOptions options,
5353
IChatClient chatClient,
@@ -265,5 +265,5 @@ private string GetToolCacheKey(ITurnState turnState)
265265
}
266266
return userToolCacheKey;
267267
}
268-
}
268+
}
269269
}
Lines changed: 124 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,137 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
2-
// Licensed under the MIT License.
3-
4-
using Agent365AgentFrameworkSampleAgent;
5-
using Agent365AgentFrameworkSampleAgent.Agent;
6-
using Agent365AgentFrameworkSampleAgent.telemetry;
7-
using Azure;
8-
using Azure.AI.OpenAI;
9-
using Microsoft.Agents.A365.Observability;
10-
using Microsoft.Agents.A365.Observability.Extensions.AgentFramework;
11-
using Microsoft.Agents.A365.Observability.Runtime;
12-
using Microsoft.Agents.A365.Tooling.Extensions.AgentFramework.Services;
13-
using Microsoft.Agents.A365.Tooling.Services;
14-
using Microsoft.Agents.Builder;
15-
using Microsoft.Agents.Core;
16-
using Microsoft.Agents.Hosting.AspNetCore;
17-
using Microsoft.Agents.Storage;
18-
using Microsoft.Agents.Storage.Transcript;
19-
using Microsoft.Extensions.AI;
20-
using System.Reflection;
21-
22-
23-
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Agent365AgentFrameworkSampleAgent;
5+
using Agent365AgentFrameworkSampleAgent.Agent;
6+
using Agent365AgentFrameworkSampleAgent.telemetry;
7+
using Azure;
8+
using Azure.AI.OpenAI;
9+
using Microsoft.Agents.A365.Observability;
10+
using Microsoft.Agents.A365.Observability.Extensions.AgentFramework;
11+
using Microsoft.Agents.A365.Observability.Runtime;
12+
using Microsoft.Agents.A365.Tooling.Extensions.AgentFramework.Services;
13+
using Microsoft.Agents.A365.Tooling.Services;
14+
using Microsoft.Agents.Builder;
15+
using Microsoft.Agents.Core;
16+
using Microsoft.Agents.Hosting.AspNetCore;
17+
using Microsoft.Agents.Storage;
18+
using Microsoft.Agents.Storage.Transcript;
19+
using Microsoft.Extensions.AI;
20+
using System.Reflection;
21+
22+
23+
2424
var builder = WebApplication.CreateBuilder(args);
2525

2626
// Setup Aspire service defaults, including OpenTelemetry, Service Discovery, Resilience, and Health Checks
27-
builder.ConfigureOpenTelemetry();
28-
29-
builder.Configuration.AddUserSecrets(Assembly.GetExecutingAssembly());
30-
builder.Services.AddControllers();
31-
builder.Services.AddHttpClient("WebClient", client => client.Timeout = TimeSpan.FromSeconds(600));
32-
builder.Services.AddHttpContextAccessor();
33-
builder.Logging.AddConsole();
34-
35-
// ********** Configure A365 Services **********
36-
// Configure observability.
37-
builder.Services.AddAgenticTracingExporter(clusterCategory: "production");
38-
39-
// Add A365 tracing with Agent Framework integration
27+
builder.ConfigureOpenTelemetry();
28+
29+
builder.Configuration.AddUserSecrets(Assembly.GetExecutingAssembly());
30+
builder.Services.AddControllers();
31+
builder.Services.AddHttpClient("WebClient", client => client.Timeout = TimeSpan.FromSeconds(600));
32+
builder.Services.AddHttpContextAccessor();
33+
builder.Logging.AddConsole();
34+
35+
// ********** Configure A365 Services **********
36+
// Configure observability.
37+
builder.Services.AddAgenticTracingExporter(clusterCategory: "production");
38+
39+
// Add A365 tracing with Agent Framework integration
4040
builder.AddA365Tracing(config =>
41-
{
41+
{
4242
config.WithAgentFramework();
43-
});
44-
45-
// Add A365 Tooling Server integration
46-
builder.Services.AddSingleton<IMcpToolRegistrationService, McpToolRegistrationService>();
47-
builder.Services.AddSingleton<IMcpToolServerConfigurationService, McpToolServerConfigurationService>();
48-
// ********** END Configure A365 Services **********
49-
50-
// Add AspNet token validation
51-
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);
52-
53-
// Register IStorage. For development, MemoryStorage is suitable.
54-
// For production Agents, persisted storage should be used so
55-
// that state survives Agent restarts, and operate correctly
56-
// in a cluster of Agent instances.
57-
builder.Services.AddSingleton<IStorage, MemoryStorage>();
58-
59-
// Add AgentApplicationOptions from config.
60-
builder.AddAgentApplicationOptions();
61-
62-
// Add the bot (which is transient)
63-
builder.AddAgent<MyAgent>();
64-
65-
// Register IChatClient with correct types
66-
builder.Services.AddSingleton<IChatClient>(sp => {
67-
68-
var confSvc = sp.GetRequiredService<IConfiguration>();
69-
var endpoint = confSvc["AIServices:AzureOpenAI:Endpoint"] ?? string.Empty;
70-
var apiKey = confSvc["AIServices:AzureOpenAI:ApiKey"] ?? string.Empty;
71-
var deployment = confSvc["AIServices:AzureOpenAI:DeploymentName"] ?? string.Empty;
72-
73-
// Validate OpenWeatherAPI key.
74-
var openWeatherApiKey = confSvc["OpenWeatherApiKey"] ?? string.Empty;
75-
76-
AssertionHelpers.ThrowIfNullOrEmpty(endpoint, "AIServices:AzureOpenAI:Endpoint configuration is missing and required.");
77-
AssertionHelpers.ThrowIfNullOrEmpty(apiKey, "AIServices:AzureOpenAI:ApiKey configuration is missing and required.");
78-
AssertionHelpers.ThrowIfNullOrEmpty(deployment, "AIServices:AzureOpenAI:DeploymentName configuration is missing and required.");
79-
AssertionHelpers.ThrowIfNullOrEmpty(openWeatherApiKey, "OpenWeatherApiKey configuration is missing and required.");
80-
81-
// Convert endpoint to Uri
82-
var endpointUri = new Uri(endpoint);
83-
84-
// Convert apiKey to ApiKeyCredential
85-
var apiKeyCredential = new AzureKeyCredential(apiKey);
86-
87-
// Create and return the AzureOpenAIClient's ChatClient
88-
return new AzureOpenAIClient(endpointUri, apiKeyCredential)
89-
.GetChatClient(deployment)
90-
.AsIChatClient()
91-
.AsBuilder()
92-
.UseFunctionInvocation()
93-
.UseOpenTelemetry(sourceName: AgentMetrics.SourceName, configure: (cfg) => cfg.EnableSensitiveData = true)
94-
.Build();
95-
});
96-
97-
// Uncomment to add transcript logging middleware to log all conversations to files
98-
builder.Services.AddSingleton<Microsoft.Agents.Builder.IMiddleware[]>([new TranscriptLoggerMiddleware(new FileTranscriptLogger())]);
99-
100-
var app = builder.Build();
101-
102-
if (app.Environment.IsDevelopment())
103-
{
104-
app.UseDeveloperExceptionPage();
105-
}
106-
107-
app.UseRouting();
108-
app.UseAuthentication();
109-
app.UseAuthorization();
110-
111-
112-
// Map the /api/messages endpoint to the AgentApplication
113-
app.MapPost("/api/messages", (HttpRequest request, HttpResponse response, IAgentHttpAdapter adapter, IAgent agent, CancellationToken cancellationToken) =>
43+
});
44+
45+
// Add A365 Tooling Server integration
46+
builder.Services.AddSingleton<IMcpToolRegistrationService, McpToolRegistrationService>();
47+
builder.Services.AddSingleton<IMcpToolServerConfigurationService, McpToolServerConfigurationService>();
48+
// ********** END Configure A365 Services **********
49+
50+
// Add AspNet token validation
51+
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);
52+
53+
// Register IStorage. For development, MemoryStorage is suitable.
54+
// For production Agents, persisted storage should be used so
55+
// that state survives Agent restarts, and operate correctly
56+
// in a cluster of Agent instances.
57+
builder.Services.AddSingleton<IStorage, MemoryStorage>();
58+
59+
// Add AgentApplicationOptions from config.
60+
builder.AddAgentApplicationOptions();
61+
62+
// Add the bot (which is transient)
63+
builder.AddAgent<MyAgent>();
64+
65+
// Register IChatClient with correct types
66+
builder.Services.AddSingleton<IChatClient>(sp => {
67+
68+
var confSvc = sp.GetRequiredService<IConfiguration>();
69+
var endpoint = confSvc["AIServices:AzureOpenAI:Endpoint"] ?? string.Empty;
70+
var apiKey = confSvc["AIServices:AzureOpenAI:ApiKey"] ?? string.Empty;
71+
var deployment = confSvc["AIServices:AzureOpenAI:DeploymentName"] ?? string.Empty;
72+
73+
// Validate OpenWeatherAPI key.
74+
var openWeatherApiKey = confSvc["OpenWeatherApiKey"] ?? string.Empty;
75+
76+
AssertionHelpers.ThrowIfNullOrEmpty(endpoint, "AIServices:AzureOpenAI:Endpoint configuration is missing and required.");
77+
AssertionHelpers.ThrowIfNullOrEmpty(apiKey, "AIServices:AzureOpenAI:ApiKey configuration is missing and required.");
78+
AssertionHelpers.ThrowIfNullOrEmpty(deployment, "AIServices:AzureOpenAI:DeploymentName configuration is missing and required.");
79+
AssertionHelpers.ThrowIfNullOrEmpty(openWeatherApiKey, "OpenWeatherApiKey configuration is missing and required.");
80+
81+
// Convert endpoint to Uri
82+
var endpointUri = new Uri(endpoint);
83+
84+
// Convert apiKey to ApiKeyCredential
85+
var apiKeyCredential = new AzureKeyCredential(apiKey);
86+
87+
// Create and return the AzureOpenAIClient's ChatClient
88+
return new AzureOpenAIClient(endpointUri, apiKeyCredential)
89+
.GetChatClient(deployment)
90+
.AsIChatClient()
91+
.AsBuilder()
92+
.UseFunctionInvocation()
93+
.UseOpenTelemetry(sourceName: AgentMetrics.SourceName, configure: (cfg) => cfg.EnableSensitiveData = true)
94+
.Build();
95+
});
96+
97+
// Uncomment to add transcript logging middleware to log all conversations to files
98+
builder.Services.AddSingleton<Microsoft.Agents.Builder.IMiddleware[]>([new TranscriptLoggerMiddleware(new FileTranscriptLogger())]);
99+
100+
var app = builder.Build();
101+
102+
if (app.Environment.IsDevelopment())
103+
{
104+
app.UseDeveloperExceptionPage();
105+
}
106+
107+
app.UseRouting();
108+
app.UseAuthentication();
109+
app.UseAuthorization();
110+
111+
112+
// Map the /api/messages endpoint to the AgentApplication
113+
app.MapPost("/api/messages", async (HttpRequest request, HttpResponse response, IAgentHttpAdapter adapter, IAgent agent, CancellationToken cancellationToken) =>
114114
{
115-
AgentMetrics.InvokeObservedHttpOperation("agent.process_message", async () =>
115+
await AgentMetrics.InvokeObservedHttpOperation("agent.process_message", async () =>
116116
{
117117
await adapter.ProcessAsync(request, response, agent, cancellationToken);
118-
});
119-
});
120-
121-
122-
if (app.Environment.IsDevelopment() || app.Environment.EnvironmentName == "Playground")
123-
{
124-
app.MapGet("/", () => "Agent Framework Example Weather Agent");
125-
app.UseDeveloperExceptionPage();
118+
});
119+
});
120+
121+
122+
if (app.Environment.IsDevelopment() || app.Environment.EnvironmentName == "Playground")
123+
{
124+
app.MapGet("/", () => "Agent Framework Example Weather Agent");
125+
app.UseDeveloperExceptionPage();
126126
app.MapControllers().AllowAnonymous();
127127

128128
// Hard coded for brevity and ease of testing.
129129
// In production, this should be set in configuration.
130-
app.Urls.Add($"http://localhost:3978");
131-
}
132-
else
133-
{
134-
app.MapControllers();
135-
}
136-
130+
app.Urls.Add($"http://localhost:3978");
131+
}
132+
else
133+
{
134+
app.MapControllers();
135+
}
136+
137137
app.Run();

dotnet/agent-framework/sample-agent/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Agent Framework (Simple) Sample
22

33
## Overview
4-
This is a simple sample showing how to use the [Agent Framework](https://github.com/microsoft/agent-framework) as an the orchestrator in an agent using the Microsoft Agent 365 SDK and Microsoft 365 Agents SDK
4+
This is a simple sample showing how to use the [Agent Framework](https://github.com/microsoft/agent-framework) as the orchestrator in an agent using the Microsoft Agent 365 SDK and Microsoft 365 Agents SDK
55
It covers:
66

77
- **Observability**: End-to-end tracing, caching, and monitoring for agent applications
@@ -18,7 +18,7 @@ For comprehensive documentation and guidance on building agents with the Microso
1818
- .NET 8.0 or higher
1919
- Microsoft Agent 365 SDK
2020
- Azure/OpenAI API credentials
21-
- OpenWeather Credentails (if using the OpenWeather Tool)
21+
- OpenWeather Credentials (if using the OpenWeather Tool)
2222
- see: https://openweathermap.org/price - You will need to create a free account to get an API key (its at the bottom of the page).
2323

2424
## Running the Agent

dotnet/agent-framework/sample-agent/Tools/DateTimeFunctionTool.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Agent365AgentFrameworkSampleAgent.Tools
77
{
88
public static class DateTimeFunctionTool
99
{
10-
[Description("Use the tool to be able to return back the date and time right now)")]
10+
[Description("Use this tool to get the current date and time")]
1111
public static string getDate(string input)
1212
{
1313
string date = DateTimeOffset.Now.ToString("D", null);

0 commit comments

Comments
 (0)