Skip to content
Open
5 changes: 1 addition & 4 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</Folder>
<Folder Name="/Samples/GettingStarted/A2A/">
<File Path="samples/GettingStarted/A2A/README.md" />
<Project Path="samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj"/>
<Project Path="samples/GettingStarted/A2A/A2AAgent_AsFunctionTools/A2AAgent_AsFunctionTools.csproj" />
</Folder>
<Folder Name="/Samples/GettingStarted/AgentProviders/">
<File Path="samples/GettingStarted/AgentProviders/README.md" />
Expand Down Expand Up @@ -130,9 +130,6 @@
<Project Path="samples/GettingStarted/Workflows/_Foundational/04_AgentWorkflowPatterns/04_AgentWorkflowPatterns.csproj" />
<Project Path="samples/GettingStarted/Workflows/_Foundational/05_MultiModelService/05_MultiModelService.csproj" />
</Folder>
<Folder Name="/Samples/SemanticKernelMigration/">
<File Path="samples/SemanticKernelMigration/README.md" />
</Folder>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
<File Path=".gitignore" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
.GetChatClient(deploymentName);

// Create the ChatClientAgent with the specified name and instructions.
ChatClientAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant."));
ChatClientAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions() { Name = "HelpfulAssistant", Instructions = "You are a helpful assistant." });

// Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke the agent with some unstructured input.
AgentRunResponse<PersonInfo> response = await agent.RunAsync<PersonInfo>("Please provide information about John Smith, who is a 35-year-old software engineer.");
Expand All @@ -34,12 +34,11 @@
Console.WriteLine($"Occupation: {response.Result.Occupation}");

// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.")
ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions()
{
ChatOptions = new()
{
ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>()
}
Name = "HelpfulAssistant",
Instructions = "You are a helpful assistant.",
ChatOptions = new() { ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema<PersonInfo>() }
});

// Invoke the agent with some unstructured input while streaming, to extract the structured information from.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

// Add agent options to the service collection.
builder.Services.AddSingleton(
new ChatClientAgentOptions(instructions: "You are good at telling jokes.", name: "Joker"));
builder.Services.AddSingleton(new ChatClientAgentOptions() { Name = "Joker", Instructions = "You are good at telling jokes." });

// Add a chat client to the service collection.
builder.Services.AddKeyedChatClient("AzureOpenAI", (sp) => new AzureOpenAIClient(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ internal sealed class SloganWriterExecutor : Executor
/// <param name="chatClient">The chat client to use for the AI agent.</param>
public SloganWriterExecutor(string id, IChatClient chatClient) : base(id)
{
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.")
ChatClientAgentOptions agentOptions = new()
{
Instructions = "You are a professional slogan writer. You will be given a task to create a slogan.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<SloganResult>()
Expand Down Expand Up @@ -193,8 +194,9 @@ internal sealed class FeedbackExecutor : Executor<SloganResult>
/// <param name="chatClient">The chat client to use for the AI agent.</param>
public FeedbackExecutor(string id, IChatClient chatClient) : base(id)
{
ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.")
ChatClientAgentOptions agentOptions = new()
{
Instructions = "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<FeedbackResult>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ private static async Task Main()
/// </summary>
/// <returns>A ChatClientAgent configured for spam detection</returns>
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
new(chatClient, new ChatClientAgentOptions()
{
Instructions = "You are a spam detection assistant that identifies spam emails.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<DetectionResult>()
Expand All @@ -98,8 +99,9 @@ private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
/// </summary>
/// <returns>A ChatClientAgent configured for email assistance</returns>
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
new(chatClient, new ChatClientAgentOptions()
{
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ private static async Task Main()
/// </summary>
/// <returns>A ChatClientAgent configured for spam detection</returns>
private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.")
new(chatClient, new ChatClientAgentOptions()
{
Instructions = "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<DetectionResult>()
Expand All @@ -113,8 +114,9 @@ private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) =>
/// </summary>
/// <returns>A ChatClientAgent configured for email assistance</returns>
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
new(chatClient, new ChatClientAgentOptions()
{
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,9 @@ private static async Task Main()
/// </summary>
/// <returns>A ChatClientAgent configured for email analysis</returns>
private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.")
new(chatClient, new ChatClientAgentOptions()
{
Instructions = "You are a spam detection assistant that identifies spam emails.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<AnalysisResult>()
Expand All @@ -153,8 +154,9 @@ private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) =>
/// </summary>
/// <returns>A ChatClientAgent configured for email assistance</returns>
private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.")
new(chatClient, new ChatClientAgentOptions()
{
Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailResponse>()
Expand All @@ -166,8 +168,9 @@ private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) =>
/// </summary>
/// <returns>A ChatClientAgent configured for email summarization</returns>
private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) =>
new(chatClient, new ChatClientAgentOptions(instructions: "You are an assistant that helps users summarize emails.")
new(chatClient, new ChatClientAgentOptions()
{
Instructions = "You are an assistant that helps users summarize emails.",
ChatOptions = new()
{
ResponseFormat = ChatResponseFormat.ForJsonSchema<EmailSummary>()
Expand Down
22 changes: 11 additions & 11 deletions dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ public ChatClientAgent(IChatClient chatClient, string? instructions = null, stri
chatClient,
new ChatClientAgentOptions
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions
{
Tools = tools,
}
},
Name = name,
Description = description,
Instructions = instructions,
},
loggerFactory,
services)
Expand Down Expand Up @@ -451,7 +451,13 @@ await thread.AIContextProvider.InvokedAsync(new(inputMessages) { InvokeException
requestChatOptions.AllowMultipleToolCalls ??= this._agentOptions.ChatOptions.AllowMultipleToolCalls;
requestChatOptions.ConversationId ??= this._agentOptions.ChatOptions.ConversationId;
requestChatOptions.FrequencyPenalty ??= this._agentOptions.ChatOptions.FrequencyPenalty;
requestChatOptions.Instructions ??= this._agentOptions.ChatOptions.Instructions;

requestChatOptions.Instructions = !string.IsNullOrEmpty(requestChatOptions.Instructions) && !string.IsNullOrEmpty(this.Instructions)
? $"{this.Instructions}\n{requestChatOptions.Instructions}"
: (!string.IsNullOrEmpty(requestChatOptions.Instructions)
? requestChatOptions.Instructions
: this.Instructions);

requestChatOptions.MaxOutputTokens ??= this._agentOptions.ChatOptions.MaxOutputTokens;
requestChatOptions.ModelId ??= this._agentOptions.ChatOptions.ModelId;
requestChatOptions.PresencePenalty ??= this._agentOptions.ChatOptions.PresencePenalty;
Expand Down Expand Up @@ -606,12 +612,6 @@ await thread.AIContextProvider.InvokedAsync(new(inputMessages) { InvokeException
""");
}

if (!string.IsNullOrWhiteSpace(this.Instructions))
{
chatOptions ??= new();
chatOptions.Instructions = string.IsNullOrWhiteSpace(chatOptions.Instructions) ? this.Instructions : $"{this.Instructions}\n{chatOptions.Instructions}";
}

// Only create or update ChatOptions if we have an id on the thread and we don't have the same one already in ChatOptions.
if (!string.IsNullOrWhiteSpace(typedThread.ConversationId) && typedThread.ConversationId != chatOptions?.ConversationId)
{
Expand Down
67 changes: 42 additions & 25 deletions dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.Extensions.AI;

Expand All @@ -17,35 +16,15 @@ namespace Microsoft.Agents.AI;
/// </remarks>
public class ChatClientAgentOptions
{
private ChatOptions? _chatOptions;

/// <summary>
/// Initializes a new instance of the <see cref="ChatClientAgentOptions"/> class.
/// </summary>
public ChatClientAgentOptions()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ChatClientAgentOptions"/> class with the specified parameters.
/// </summary>
/// <remarks>If <paramref name="tools"/> is provided, a new <see cref="ChatOptions"/> instance is created
/// with the specified instructions and tools.</remarks>
/// <param name="instructions">The instructions or guidelines for the chat client agent. Can be <see langword="null"/> if not specified.</param>
/// <param name="name">The name of the chat client agent. Can be <see langword="null"/> if not specified.</param>
/// <param name="description">The description of the chat client agent. Can be <see langword="null"/> if not specified.</param>
/// <param name="tools">A list of <see cref="AITool"/> instances available to the chat client agent. Can be <see langword="null"/> if no
/// tools are specified.</param>
public ChatClientAgentOptions(string? instructions, string? name = null, string? description = null, IList<AITool>? tools = null)
{
this.Name = name;
this.Instructions = instructions;
this.Description = description;

if (tools is not null)
{
(this.ChatOptions ??= new()).Tools = tools;
}
}

/// <summary>
/// Gets or sets the agent id.
/// </summary>
Expand All @@ -59,7 +38,21 @@ public ChatClientAgentOptions(string? instructions, string? name = null, string?
/// <summary>
/// Gets or sets the agent instructions.
/// </summary>
public string? Instructions { get; set; }
public string? Instructions
{
get => this._chatOptions?.Instructions;
set
{
if (value is null && this._chatOptions is null)
{
// No instructions to set and no existing chat options, nothing to do.
return;
}

this._chatOptions ??= new();
this._chatOptions.Instructions = value;
}
}

/// <summary>
/// Gets or sets the agent description.
Expand All @@ -69,7 +62,31 @@ public ChatClientAgentOptions(string? instructions, string? name = null, string?
/// <summary>
/// Gets or sets the default chatOptions to use.
/// </summary>
public ChatOptions? ChatOptions { get; set; }
/// <remarks>
/// When providing instructions via both <see cref="Instructions"/> and <see cref="ChatOptions"/>,
/// will result in a new instruction different lines with the <see cref="Instructions"/> first.
/// </remarks>
public ChatOptions? ChatOptions
{
get => this._chatOptions;
set
{
var providedOptions = value;
if (providedOptions is not null)
{
// Ensure immutable copy of provided options.
providedOptions = providedOptions.Clone();
}

if (this._chatOptions is not null && providedOptions is not null && string.IsNullOrWhiteSpace(providedOptions.Instructions))
{
// Preserve existing agent options instructions if new ChatOptions does not have instructions set.
providedOptions.Instructions = this._chatOptions.Instructions;
}

this._chatOptions = providedOptions;
}
}

/// <summary>
/// Gets or sets a factory function to create an instance of <see cref="ChatMessageStore"/>
Expand Down
Loading
Loading