Remove instructions from base agent type, and ensure description is only required where necessary. (#154)

* Remove instructions from base agent type, and ensure description is only required where necessary.

* Fix code issue.

* Remove unused import.

* Remove typo

* Make the description parameter optional

* Also use name for handoffs if no description is provided.
This commit is contained in:
westey
2025-07-10 14:06:45 +01:00
committed by GitHub
Unverified
parent fbf1f10a8a
commit bc6573f475
8 changed files with 39 additions and 24 deletions
@@ -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));
}
}
@@ -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;
@@ -15,7 +15,7 @@ public abstract class OrchestrationActor : RuntimeActor
/// <summary>
/// Initializes a new instance of the <see cref="OrchestrationActor"/> class.
/// </summary>
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;
@@ -15,7 +15,7 @@ namespace Microsoft.Extensions.AI.Agents;
public abstract class Agent
{
/// <summary>
/// Gets the identifier of the agent (optional).
/// Gets the identifier of the agent.
/// </summary>
/// <value>
/// 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
/// </summary>
public virtual string? Description { get; }
/// <summary>
/// Gets the instructions for the agent (optional).
/// </summary>
public virtual string? Instructions { get; }
/// <summary>
/// Get a new <see cref="AgentThread"/> instance that is compatible with the agent.
/// </summary>
@@ -15,7 +15,7 @@ public readonly struct ActorMetadata : IEquatable<ActorMetadata>
/// <param name="type">The type of the actor.</param>
/// <param name="key">The unique key associated with the actor.</param>
/// <param name="description">A brief description of the actor.</param>
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<ActorMetadata>
/// <summary>
/// A brief description of the actor's purpose or functionality.
/// </summary>
public string Description { get; }
public string? Description { get; }
/// <inheritdoc/>
public override readonly bool Equals(object? obj) =>
@@ -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;
@@ -56,8 +56,10 @@ public sealed class ChatClientAgent : Agent
/// <inheritdoc/>
public override string? Description => this._agentOptions?.Description;
/// <inheritdoc/>
public override string? Instructions => this._agentOptions?.Instructions;
/// <summary>
/// Gets the instructions for the agent (optional).
/// </summary>
public string? Instructions => this._agentOptions?.Instructions;
/// <summary>
/// Gets of the default <see cref="Microsoft.Extensions.AI.ChatOptions"/> used by the agent.
@@ -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<InvalidOperationException>(() => 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)