mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
a3a9147e61
* Rename AgentThread to AgentSession * Add more renames * Update readme files * Revert nullable variable change and further fixes. * Revert change in header name * Fix some comments and tests * Update changelog. * Address PR feedback. * Fixing code review comments. * Fix new errors after merging latest code.
115 lines
5.5 KiB
C#
115 lines
5.5 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.AI;
|
|
using Microsoft.Shared.Diagnostics;
|
|
|
|
namespace Microsoft.Agents.AI;
|
|
|
|
/// <summary>
|
|
/// Provides extensions for <see cref="AIAgent"/>.
|
|
/// </summary>
|
|
public static partial class AIAgentExtensions
|
|
{
|
|
/// <summary>
|
|
/// Creates a new <see cref="AIAgentBuilder"/> using the specified agent as the foundation for the builder pipeline.
|
|
/// </summary>
|
|
/// <param name="innerAgent">The <see cref="AIAgent"/> instance to use as the inner agent.</param>
|
|
/// <returns>A new <see cref="AIAgentBuilder"/> instance configured with the specified inner agent.</returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="innerAgent"/> is <see langword="null"/>.</exception>
|
|
/// <remarks>
|
|
/// This method provides a convenient way to convert an existing <see cref="AIAgent"/> instance into
|
|
/// a builder pattern, enabling easily wrapping the agent in layers of additional functionality.
|
|
/// It is functionally equivalent to using the <see cref="AIAgentBuilder(AIAgent)"/> constructor directly,
|
|
/// but provides a more fluent API when working with existing agent instances.
|
|
/// </remarks>
|
|
public static AIAgentBuilder AsBuilder(this AIAgent innerAgent)
|
|
{
|
|
_ = Throw.IfNull(innerAgent);
|
|
|
|
return new AIAgentBuilder(innerAgent);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="AIFunction"/> that runs the provided <see cref="AIAgent"/>.
|
|
/// </summary>
|
|
/// <param name="agent">The <see cref="AIAgent"/> to be represented as an invocable function.</param>
|
|
/// <param name="options">
|
|
/// Optional metadata to customize the function representation, such as name and description.
|
|
/// If not provided, defaults will be inferred from the agent's properties.
|
|
/// </param>
|
|
/// <param name="session">
|
|
/// Optional <see cref="AgentSession"/> to use for function invocations. If not provided, a new session
|
|
/// will be created for each function call, which may not preserve conversation context.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An <see cref="AIFunction"/> that can be used as a tool by other agents or AI models to invoke this agent.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException"><paramref name="agent"/> is <see langword="null"/>.</exception>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This extension method enables agents to participate in function calling scenarios, where they can be
|
|
/// invoked as tools by other agents or AI models. The resulting function accepts a query string as input and
|
|
/// returns the agent's response as a string, making it compatible with standard function calling interfaces
|
|
/// used by AI models.
|
|
/// </para>
|
|
/// <para>
|
|
/// The resulting <see cref="AIFunction"/> is stateful, referencing both the <paramref name="agent"/> and the optional
|
|
/// <paramref name="session"/>. Especially if a specific session is provided, avoid using the resulting function concurrently
|
|
/// in multiple conversations or in requests where the parallel function calls may result in concurrent usage of the session,
|
|
/// as that could lead to undefined and unpredictable behavior.
|
|
/// </para>
|
|
/// </remarks>
|
|
public static AIFunction AsAIFunction(this AIAgent agent, AIFunctionFactoryOptions? options = null, AgentSession? session = null)
|
|
{
|
|
Throw.IfNull(agent);
|
|
|
|
[Description("Invoke an agent to retrieve some information.")]
|
|
async Task<string> InvokeAgentAsync(
|
|
[Description("Input query to invoke the agent.")] string query,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
// Propagate any additional properties from the parent agent's run to the child agent if the parent is using a FunctionInvokingChatClient.
|
|
AgentRunOptions? agentRunOptions = FunctionInvokingChatClient.CurrentContext?.Options?.AdditionalProperties is AdditionalPropertiesDictionary dict
|
|
? new AgentRunOptions { AdditionalProperties = dict }
|
|
: null;
|
|
|
|
var response = await agent.RunAsync(query, session: session, options: agentRunOptions, cancellationToken: cancellationToken).ConfigureAwait(false);
|
|
return response.Text;
|
|
}
|
|
|
|
options ??= new();
|
|
options.Name ??= SanitizeAgentName(agent.Name);
|
|
options.Description ??= agent.Description;
|
|
|
|
return AIFunctionFactory.Create(InvokeAgentAsync, options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes characters from AI agent name that shouldn't be used in an AI function name.
|
|
/// </summary>
|
|
/// <param name="agentName">The AI agent name to sanitize.</param>
|
|
/// <returns>
|
|
/// The sanitized agent name with invalid characters replaced by underscores, or <c>null</c> if the input is <c>null</c>.
|
|
/// </returns>
|
|
private static string? SanitizeAgentName(string? agentName)
|
|
{
|
|
return agentName is null
|
|
? agentName
|
|
: InvalidNameCharsRegex().Replace(agentName, "_");
|
|
}
|
|
|
|
/// <summary>Regex that flags any character other than ASCII digits or letters.</summary>
|
|
#if NET
|
|
[GeneratedRegex("[^0-9A-Za-z]+")]
|
|
private static partial Regex InvalidNameCharsRegex();
|
|
#else
|
|
private static Regex InvalidNameCharsRegex() => s_invalidNameCharsRegex;
|
|
private static readonly Regex s_invalidNameCharsRegex = new("[^0-9A-Za-z]+", RegexOptions.Compiled);
|
|
#endif
|
|
}
|