diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs index fb83e266d3..e78638d959 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -30,7 +29,7 @@ public abstract class AgentActor : OrchestrationActor id, runtime, context, - VerifyDescription(agent), + agent.Description, logger) { this.Agent = agent; @@ -162,9 +161,4 @@ public abstract class AgentActor : OrchestrationActor } } } - - private static string VerifyDescription(Agent agent) - { - return agent.Description ?? throw new ArgumentException($"Missing agent description: {agent.Name ?? agent.Id}", nameof(agent)); - } } diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs index d779abbe27..797ee965fa 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections.Generic; using Microsoft.Extensions.AI.Agents; using Microsoft.Extensions.AI.Agents.Runtime; @@ -84,7 +85,12 @@ public static class OrchestrationHandoffsExtensions foreach (Agent target in targets) { - agentHandoffs[target.Name ?? target.Id] = target.Description ?? string.Empty; + if (string.IsNullOrWhiteSpace(target.Description) && string.IsNullOrWhiteSpace(target.Name)) + { + throw new InvalidOperationException($"The provided target agent with Id '{target.Id}' has no description or name, and no handoff description has been provided. At least one of these are required to register a handoff so that the appropriate target agent can be chosen."); + } + + agentHandoffs[target.Name ?? target.Id] = target.Description ?? target.Name!; } return handoffs; diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs index d3f3f53629..165a83f766 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs @@ -15,7 +15,7 @@ public abstract class OrchestrationActor : RuntimeActor /// /// Initializes a new instance of the class. /// - protected OrchestrationActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, string description, ILogger? logger = null) + protected OrchestrationActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, string? description = null, ILogger? logger = null) : base(id, runtime, description, logger) { this.Context = context; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Agent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Agent.cs index 62ad1d35ac..5e7d942477 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Agent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Agent.cs @@ -15,7 +15,7 @@ namespace Microsoft.Extensions.AI.Agents; public abstract class Agent { /// - /// Gets the identifier of the agent (optional). + /// Gets the identifier of the agent. /// /// /// The identifier of the agent. The default is a random GUID value, but for service agents, it will match the id of the agent in the service. @@ -32,11 +32,6 @@ public abstract class Agent /// public virtual string? Description { get; } - /// - /// Gets the instructions for the agent (optional). - /// - public virtual string? Instructions { get; } - /// /// Get a new instance that is compatible with the agent. /// diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs index 6e28c69f93..317907deaa 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs @@ -15,7 +15,7 @@ public readonly struct ActorMetadata : IEquatable /// The type of the actor. /// The unique key associated with the actor. /// A brief description of the actor. - public ActorMetadata(ActorType type, string key, string description) + public ActorMetadata(ActorType type, string key, string? description = null) { if (!ActorId.IsValidKey(key)) { @@ -46,7 +46,7 @@ public readonly struct ActorMetadata : IEquatable /// /// A brief description of the actor's purpose or functionality. /// - public string Description { get; } + public string? Description { get; } /// public override readonly bool Equals(object? obj) => diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/RuntimeActor.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/RuntimeActor.cs index 44187a196b..81da06e9f0 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/RuntimeActor.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/RuntimeActor.cs @@ -53,7 +53,7 @@ public abstract class RuntimeActor : IRuntimeActor protected RuntimeActor( ActorId id, IAgentRuntime runtime, - string description, + string? description = null, ILogger? logger = null) { this.Id = id; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs index 482ea7da30..d530f27cf2 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs @@ -56,8 +56,10 @@ public sealed class ChatClientAgent : Agent /// public override string? Description => this._agentOptions?.Description; - /// - public override string? Instructions => this._agentOptions?.Instructions; + /// + /// Gets the instructions for the agent (optional). + /// + public string? Instructions => this._agentOptions?.Instructions; /// /// Gets of the default used by the agent. diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs index b2878e0676..52357167ed 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/HandoffsTests.cs @@ -201,23 +201,41 @@ public class HandoffsTests } [Fact] - public void AddWithTargetWithNoDescriptionUsesEmptyString() + public void AddWithAgentWithNoDescriptionUsesName() { // Arrange OrchestrationHandoffs handoffs = new("source"); Agent sourceAgent = CreateAgent("source", "Source Agent"); - Agent targetAgent = CreateAgent("target", null); + Agent targetAgent1 = CreateAgent("target1", name: "target 1"); // Act - handoffs.Add(sourceAgent, targetAgent); + handoffs.Add(sourceAgent, targetAgent1); // Assert Assert.Single(handoffs); Assert.Equal("source", handoffs.FirstAgentName); + Assert.True(handoffs.ContainsKey("source")); + AgentHandoffs sourceHandoffs = handoffs["source"]; Assert.Single(sourceHandoffs); - Assert.Equal(string.Empty, sourceHandoffs["target"]); + Assert.Equal("target 1", sourceHandoffs["target 1"]); + } + + [Fact] + public void AddWithAgentWithNoDescriptionOrNameThrows() + { + // Arrange + OrchestrationHandoffs handoffs = new("source"); + + Agent sourceAgent = CreateAgent("source", "Source Agent"); + Agent targetAgent1 = CreateAgent("target1"); + + // Act + InvalidOperationException ex = Assert.Throws(() => handoffs.Add(sourceAgent, targetAgent1)); + + // Assert + Assert.Equal("The provided target agent with Id 'target1' has no description or name, and no handoff description has been provided. At least one of these are required to register a handoff so that the appropriate target agent can be chosen.", ex.Message); } private static ChatClientAgent CreateAgent(string id, string? description = null, string? name = null)